$(call cmd,rmfiles)
@find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
- -o -name '*.ko.*' \
- -o -name '*.dwo' \
+ -o -name '*.ko.*' -o -name '*.dwo' \
+ -o -name '*.sdtinfo.c' -o -name '*.sdtstub.S' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.symtypes' -o -name 'modules.order' \
-o -name 'modules.builtin' -o -name 'objects.builtin' \
char name[0];
} __aligned(sizeof(unsigned long)) dtrace_sdt_probeinfo_t;
+void dtrace_sdt_init(void);
void dtrace_sdt_register(struct module *);
+void dtrace_sdt_exit(void);
#endif /* __KERNEL__ */
struct sdt_probedesc *sdpd_next; /* next static probe */
} sdt_probedesc_t;
-extern void dtrace_register_builtins(void);
+/* extern void dtrace_register_builtins(void); */
#ifdef __cplusplus
}
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK,
NULL);
+ dtrace_sdt_init();
dtrace_sdt_register(dtrace_kmod);
}
EXPORT_SYMBOL(dtrace_os_init);
#define SDT_NOP_SIZE 5
const char *sdt_prefix = "__dtrace_probe_";
+uint8_t nops[SDT_NOP_SIZE];
/* This code is based on apply_alternatives and text_poke_early. It needs to
* run before SMP is initialized in order to avoid SMP problems with patching
restart_nmi();
}
-static int sdt_probe_add(struct module *mp, char *name, char *func,
- uintptr_t addr, struct text_poke_param *tpp,
- void *nops)
+static int sdt_probe_set(sdt_probedesc_t *sdp, char *name, char *func,
+ uintptr_t addr, struct text_poke_param *tpp)
{
- sdt_probedesc_t *sdp;
- uint8_t *instr;
-
- if ((sdp = kmalloc(sizeof(sdt_probedesc_t), GFP_KERNEL)) == NULL)
- return 1;
+ uint8_t *instr;
if ((sdp->sdpd_name = kstrdup(name, GFP_KERNEL)) == NULL) {
kfree(sdp);
/* adjust relocation address to beginning of call instruction */
instr = (uint8_t *)(addr - 1);
- /* TBD: use a kernel list? */
sdp->sdpd_offset = (uintptr_t)instr;
- sdp->sdpd_next = mp->sdt_probes;
- mp->sdt_probes = sdp;
tpp->addr = instr;
tpp->opcode = nops;
return 0;
}
+/*
+ * Register the SDT probes for the core kernel, i.e. SDT probes that reside in
+ * vmlinux. For SDT probes in kernel modules, we use dtrace_mod_notifier().
+ */
void dtrace_sdt_register(struct module *mod)
{
int i, cnt;
dtrace_sdt_probeinfo_t *pi =
(dtrace_sdt_probeinfo_t *)&dtrace_sdt_probes;
void *nextpi;
- uint8_t nops[SDT_NOP_SIZE];
+ sdt_probedesc_t *sdps;
struct text_poke_param *reqs;
if (mod == NULL) {
return;
}
+ /*
+ * Just in case we run into failures further on...
+ */
+ mod->sdt_probes = NULL;
+ mod->num_dtrace_probes = 0;
+
if (dtrace_sdt_nprobes == 0)
return;
/*
- * A little unusual, but potentially necessary. While we could use a
- * single NOP sequence of length SDT_NOP_SIZE, we need to consider the
- * fact that when a SDT probe point is enabled, a single invalid opcode
- * is written on the first byte of this NOP sequence. By using a
- * sequence of a 1-byte NOP, followed by a (SDT_NOP_SIZE - 1) byte NOP
- * sequence, we play it pretty safe.
+ * Allocate the array of SDT probe descriptions to be registered in the
+ * vmlinux pseudo-module.
*/
- add_nops(nops, 1);
- add_nops(nops + 1, SDT_NOP_SIZE - 1);
+ sdps = (sdt_probedesc_t *)vmalloc(dtrace_sdt_nprobes *
+ sizeof(sdt_probedesc_t));
+ if (sdps == NULL) {
+ pr_warning("%s: cannot allocate SDT probe array\n", __func__);
+ return;
+ }
/*
* Set up a batch of text_poke requests that will handle replacing all
vmalloc(dtrace_sdt_nprobes *
sizeof(struct text_poke_param));
if (reqs == NULL) {
- pr_warning("%s: failed to allocate text_poke_param array\n",
+ pr_warning("%s: cannot allocate text_poke_param array\n",
__func__);
+ vfree(sdps);
return;
}
for (i = cnt = 0; cnt < dtrace_sdt_nprobes; i++) {
char *func = pi->name + pi->name_len + 1;
- if (sdt_probe_add(dtrace_kmod, pi->name, func, pi->addr,
- &reqs[cnt], nops))
+ if (sdt_probe_set(&sdps[cnt], pi->name, func, pi->addr,
+ &reqs[cnt]))
pr_warning("%s: failed to add SDT probe %s\n",
__func__, pi->name);
else
pi = nextpi;
}
+ mod->sdt_probes = sdps;
+ mod->num_dtrace_probes = cnt;
+
text_poke_batch(reqs, cnt);
+
+ vfree(reqs);
}
static int __init nosdt(char *str)
early_param("nosdt", nosdt);
+static int dtrace_mod_notifier(struct notifier_block *nb, unsigned long val,
+ void *args)
+{
+ struct module *mod = args;
+ struct text_poke_param *reqs, *req;
+ int idx, cnt;
+ sdt_probedesc_t *sdp;
+
+ /*
+ * We only need to capture modules in the COMING state, we need a valid
+ * module structure as argument, and the module needs to actually have
+ * SDT probes. If not, ignore...
+ */
+ if (val != MODULE_STATE_COMING)
+ return NOTIFY_DONE;
+ if (!mod)
+ return NOTIFY_DONE;
+ if (mod->num_dtrace_probes == 0 || mod->sdt_probes == NULL)
+ return NOTIFY_DONE;
+
+ /*
+ * Set up a batch of text_poke requests that will handle replacing all
+ * calls at SDT probe locations with the NOP sequence. Allocate the
+ * requests array, and then fill it in.
+ */
+ reqs = (struct text_poke_param *)
+ vmalloc(dtrace_sdt_nprobes *
+ sizeof(struct text_poke_param));
+ if (reqs == NULL) {
+ pr_warning("%s: cannot allocate text_poke_param array (%s)\n",
+ __func__, mod->name);
+ return NOTIFY_DONE;
+ }
+
+ for (idx = cnt = 0, req = reqs, sdp = mod->sdt_probes;
+ idx < mod->num_dtrace_probes; idx++, sdp++) {
+ /*
+ * Fix-up the offset to reflect the relocated address of the
+ * probe. We subtract 1 to put us at the beginning of the call
+ * instruction. We verify that the offset won't put us beyond
+ * the module core, just to be safe.
+ */
+ sdp->sdpd_offset += (uintptr_t)mod->module_core - 1;
+ if (!within_module_core(sdp->sdpd_offset, mod)) {
+ pr_warning("%s: SDT probe outside module core %s\n",
+ __func__, mod->name);
+ continue;
+ }
+
+ req->addr = (uint8_t *)sdp->sdpd_offset;
+ req->opcode = nops;
+ req->len = SDT_NOP_SIZE;
+
+ cnt++;
+ req++;
+ }
+
+ text_poke_batch(reqs, cnt);
+
+ vfree(reqs);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block dtrace_modfix = {
+ .notifier_call = dtrace_mod_notifier,
+};
+
+void dtrace_sdt_init(void)
+{
+ /*
+ * A little unusual, but potentially necessary. While we could use a
+ * single NOP sequence of length SDT_NOP_SIZE, we need to consider the
+ * fact that when a SDT probe point is enabled, a single invalid opcode
+ * is written on the first byte of this NOP sequence. By using a
+ * sequence of a 1-byte NOP, followed by a (SDT_NOP_SIZE - 1) byte NOP
+ * sequence, we play it pretty safe.
+ */
+ add_nops(nops, 1);
+ add_nops(nops + 1, SDT_NOP_SIZE - 1);
+
+ register_module_notifier(&dtrace_modfix);
+}
+
#if defined(CONFIG_DT_DT_PERF) || defined(CONFIG_DT_DT_PERF_MODULE)
void dtrace_sdt_perf(void)
{
quiet_cmd_link_multi-y = LD $@
cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
+ifdef CONFIG_DTRACE
+
+# We need secondary expansion for the %.sdtstub.S creation rule
+.SECONDEXPANSION:
+
+sdtgen = scripts/dtrace_sdt.sh
+
+quiet_cmd_sdtstub = SDTSTB $@
+ cmd_sdtstub = $(sdtgen) sdtstub $@ \
+ $(filter $(addprefix $(obj)/, \
+ $($(subst $(obj)/,,$(@:.sdtstub.S=-objs))) \
+ $($(subst $(obj)/,,$(@:.sdtstub.S=-y)))), \
+ $(multi-objs-m))
+
+$(multi-used-m:.o=.sdtstub.S) : %.sdtstub.S: $(multi-objs-m)
+ $(call if_changed,sdtstub)
+
+$(multi-used-m:.o=.sdtstub.o) : %.sdtstub.o: %.sdtstub.S
+ $(call if_changed,as_o_S)
+
+$(multi-used-m) : %.o: %.sdtstub.o
+
+quiet_cmd_link_multi-m = LD [M] $@
+cmd_link_multi-m = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $*.sdtstub.o $(cmd_secanalysis)
+
+else
+
quiet_cmd_link_multi-m = LD [M] $@
cmd_link_multi-m = $(cmd_link_multi-y)
+endif
+
$(multi-used-y): FORCE
$(call if_changed,link_multi-y)
$(call multi_depend, $(multi-used-y), .o, -objs -y)
$(symverfile): __modpost ;
$(modules:.ko=.mod.c): __modpost ;
-# Step 5), compile all *.mod.c files
+# Step 5), compile all *.mod.c files (includes the generation of SDT data)
# modname is set to make c_flags define KBUILD_MODNAME
modname = $(notdir $(@:.mod.o=))
quiet_cmd_cc_o_c = CC $@
cmd_cc_o_c = $(CC) $(c_flags) $(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE) \
- -c -o $@ $<
+ -I$(dir $@) -c -o $@ $<
+
+ifdef CONFIG_DTRACE
+
+sdtgen = scripts/dtrace_sdt.sh
+
+quiet_cmd_sdtinfo = SDTINF $@
+ cmd_sdtinfo = $(sdtgen) sdtinfo $@ $< kmod
+
+$(modules:.ko=.sdtinfo.c): %.sdtinfo.c: %.o
+ $(call cmd,sdtinfo)
+
+$(modules:.ko=.mod.o): %.mod.o: %.mod.c %.sdtinfo.c FORCE
+ $(call if_changed_dep,cc_o_c)
+else
$(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
$(call if_changed_dep,cc_o_c)
+endif
+
targets += $(modules:.ko=.mod.o)
# Step 6), generate CTF for the entire kernel, or for the module alone if this
LANG=C
+opr="$1"
+shift
+if [ -z "$opr" ]; then
+ echo "ERROR: Missing operation" > /dev/stderr
+ exit 1
+fi
+
+tfn="$1"
+shift
+if [ -z "$tfn" ]; then
+ echo "ERROR: Missing target filename" > /dev/stderr
+ exit 1
+fi
+
ofn="$1"
lfn="$2"
+if [ -z "$ofn" ]; then
+ echo "ERROR: Missing object file argument" > /dev/stderr
+ exit 1
+fi
+
+if [ "$opr" = "sdtstub" ]; then
+ ${NM} -u $* | \
+ grep __dtrace_probe_ | sort | uniq | \
+ ${AWK} '{
+ printf("\t.globl %s\n\t.type %s,@function\n%s:\n",
+ $2, $2, $2);
+ count++;
+ }
+
+ END {
+ if (count)
+ print "\tret";
+ else
+ exit(1);
+ }' > $tfn
+ exit 0
+fi
+
+if [ "$opr" != "sdtinfo" ]; then
+ echo "ERROR: Invalid operation, should be sdtstub or sdtinfo" > /dev/stderr
+ exit 1
+fi
+
(
objdump -htr "$ofn" | \
- awk -v lfn=${lfn} \
+ awk -v lfn="${lfn}" \
'/^Sections:/ {
getline;
getline;
$3 == "F" {
printf "%16s %s F %s\n", $4, $1, $6;
- if (!lfn)
+ if (!lfn || lfn == "kmod")
printf "%s t %s\n", $1, $6;
next;
next;
}' | \
sort
- [ "x${lfn}" != "x" ] && nm ${lfn}
+ [ "x${lfn}" != "x" -a "x${lfn}" != "xkmod" ] && nm ${lfn}
) | \
- awk 'function addl(v0, v1, v0h, v0l, v1h, v1l, d, tmp) {
+ awk -v lfn="${lfn}" \
+ 'function addl(v0, v1, v0h, v0l, v1h, v1l, d, tmp) {
tmp = $0;
if (length(v0) > 8) {
d = length(v0);
}
BEGIN {
- print "#include <asm/types.h>";
- print "#if BITS_PER_LONG == 64";
- print "# define PTR .quad";
- print "# define ALGN .align 8";
- print "#else";
- print "# define PTR .long";
- print "# define ALGN .align 4";
- print "#endif";
-
- print "\t.section .rodata, \042a\042";
- print "";
-
- print ".globl dtrace_sdt_probes";
- print "\tALGN";
- print "dtrace_sdt_probes:";
+ if (lfn != "kmod") {
+ print "#include <asm/types.h>";
+ print "#if BITS_PER_LONG == 64";
+ print "# define PTR .quad";
+ print "# define ALGN .align 8";
+ print "#else";
+ print "# define PTR .long";
+ print "# define ALGN .align 4";
+ print "#endif";
+
+ print "\t.section .rodata, \042a\042";
+ print "";
+
+ print ".globl dtrace_sdt_probes";
+ print "\tALGN";
+ print "dtrace_sdt_probes:";
+ } else {
+ print "#include <linux/sdt.h>";
+ }
+
+ probec = 0;
}
$2 ~ /^[tT]$/ {
for (i = 1; i <= NF; i++) {
prb = $i;
pn = fun":"prb;
+ ad = addl(baseaddr, poffst[pn]);
+
+ if (lfn != "kmod") {
+ print "\tPTR\t0x" ad;
+ print "\tPTR\t" length(prb);
+ print "\tPTR\t" length(fun);
+ print "\t.asciz\t\042" prb "\042";
+ print "\t.asciz\t\042" fun "\042";
+ print "\tALGN";
+ } else {
+ if (probec == 0)
+ print "static sdt_probedesc_t\t_sdt_probes[] = {";
- print "\tPTR\t0x" addl(baseaddr, poffst[pn]);
- print "\tPTR\t" length(prb);
- print "\tPTR\t" length(fun);
- print "\t.asciz\t\042" prb "\042";
- print "\t.asciz\t\042" fun "\042";
- print "\tALGN";
+ print " {\042" prb "\042, \042"fun"\042, 0x" ad " },";
+ }
probec++;
}
}
END {
- print "";
- print ".globl dtrace_sdt_nprobes";
- print "\tALGN";
- print "dtrace_sdt_nprobes:";
- print "\tPTR\t" probec;
+ if (lfn != "kmod") {
+ print "";
+ print ".globl dtrace_sdt_nprobes";
+ print "\tALGN";
+ print "dtrace_sdt_nprobes:";
+ print "\tPTR\t" probec;
+ } else {
+ if (probec > 0)
+ print "};";
+ else
+ print "#define _sdt_probes\tNULL";
+
+ print "#define _sdt_probec\t" probec;
+ }
if (errc > 0) {
print errc " errors generating SDT probe data." > /dev/stderr;
exit 1;
}
- }'
+ }' > $tfn
+
+exit 0
# Generate the SDT probe point stubs object file
# ${1} output file
-sdtstubs()
+sdtstub()
{
- info MKSTUBS ${1}
- ${NM} -u ${KBUILD_VMLINUX_INIT} ${KBUILD_VMLINUX_MAIN} | \
- grep __dtrace_probe_ | sort | uniq | \
- ${AWK} '{
- printf("\t.globl %s\n\t.type %s,@function\n%s:\n",
- $2, $2, $2);
- }' > .tmp_sdtstubs.S
- echo " ret" >> .tmp_sdtstubs.S
+ info SDTSTB ${1}
+ ${srctree}/scripts/dtrace_sdt.sh sdtstub .tmp_sdtstub.S \
+ ${KBUILD_VMLINUX_INIT} ${KBUILD_VMLINUX_MAIN}
local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
- ${CC} ${aflags} -c -o ${1} .tmp_sdtstubs.S
+ ${CC} ${aflags} -c -o ${1} .tmp_sdtstub.S
}
# Generate the SDT probe info for object file ${1} and kernel image ${2}
# ${3} output file
sdtinfo()
{
- info DT-SDT ${3}
+ info SDTINF ${3}
- ${srctree}/scripts/dtrace_sdt.sh ${1} ${2} > .tmp_sdtinfo.S
+ ${srctree}/scripts/dtrace_sdt.sh sdtinfo .tmp_sdtinfo.S ${1} ${2}
local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
rm -f .old_version
rm -f .tmp_System.map
rm -f .tmp_kallsyms*
- rm -f .tmp_sdtstubs.*
+ rm -f .tmp_sdtstub.*
rm -f .tmp_sdtinfo.*
rm -f .tmp_version
rm -f .tmp_vmlinux*
. "./${KCONFIG_CONFIG}"
esac
-sdtstubso=""
+sdtstubo=""
sdtinfoo=""
if [ -n "${CONFIG_DTRACE}" ]; then
- sdtstubso=.tmp_sdtstubs.o
+ sdtstubo=.tmp_sdtstub.o
sdtinfoo=.tmp_sdtinfo.o
- sdtstubs ${sdtstubso}
+ sdtstub ${sdtstubo}
fi
#link vmlinux.o
fi
# step 1
- vmlinux_link "${sdtstubso} ${sdtinfoo}" .tmp_vmlinux1
+ vmlinux_link "${sdtstubo} ${sdtinfoo}" .tmp_vmlinux1
kallsyms .tmp_vmlinux1 .tmp_kallsyms1.o
if [ -n "${CONFIG_DTRACE}" ]; then
fi
# step 2
- vmlinux_link "${sdtstubso} .tmp_kallsyms1.o ${sdtinfoo}" .tmp_vmlinux2
+ vmlinux_link "${sdtstubo} .tmp_kallsyms1.o ${sdtinfoo}" .tmp_vmlinux2
kallsyms .tmp_vmlinux2 .tmp_kallsyms2.o
# step 2a
kallsymso=.tmp_kallsyms3.o
kallsyms_vmlinux=.tmp_vmlinux3
- vmlinux_link "${sdtstubso} .tmp_kallsyms2.o ${sdtinfoo}" .tmp_vmlinux3
+ vmlinux_link "${sdtstubo} .tmp_kallsyms2.o ${sdtinfoo}" .tmp_vmlinux3
kallsyms .tmp_vmlinux3 .tmp_kallsyms3.o
fi
fi
info LD vmlinux
-vmlinux_link "${sdtstubso} ${kallsymso} ${sdtinfoo}" vmlinux
+vmlinux_link "${sdtstubo} ${kallsymso} ${sdtinfoo}" vmlinux
if [ -n "${CONFIG_BUILDTIME_EXTABLE_SORT}" ]; then
info SORTEX vmlinux
**/
static void add_header(struct buffer *b, struct module *mod)
{
+ const char *modname;
+
+ if ((modname = strrchr(mod->name, '/')) != NULL)
+ modname++;
+ else
+ modname = mod->name;
+
buf_printf(b, "#include <linux/module.h>\n");
buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "#include <linux/compiler.h>\n");
buf_printf(b, "\n");
+ buf_printf(b, "#ifdef CONFIG_DTRACE\n");
+ buf_printf(b, "# include \"%s.sdtinfo.c\"\n", modname);
+ buf_printf(b, "#endif\n");
+ buf_printf(b, "\n");
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
buf_printf(b, "\n");
buf_printf(b, "__visible struct module __this_module\n");
"\t.exit = cleanup_module,\n"
"#endif\n");
buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n");
+ buf_printf(b, "#ifdef CONFIG_DTRACE\n");
+ buf_printf(b, "\t.sdt_probes = _sdt_probes,\n");
+ buf_printf(b, "\t.num_dtrace_probes = _sdt_probec,\n");
+ buf_printf(b, "#endif\n");
buf_printf(b, "};\n");
}