From 65df9534b283cd3a7e9c2d8bbdee5ad0d50b0135 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Sun, 29 Jul 2012 04:30:18 -0400 Subject: [PATCH] dtrace: changed the logic for determining SDT probe point locations The previous version did not account for probes that might be placed in non-.text segments. New code also avoids extra passes that were not necessary. Added on-cpu and off-cpu sched probes. Signed-off-by: Kris Van Hees --- include/linux/sdt.h | 3 +- kernel/dtrace/sdt_register.c | 13 +- kernel/sched/core.c | 3 + scripts/Makefile | 2 - scripts/dtrace_relocs.c | 387 ----------------------------------- scripts/dtrace_sdt.sh | 175 ++++++++++++---- 6 files changed, 143 insertions(+), 440 deletions(-) delete mode 100644 scripts/dtrace_relocs.c diff --git a/include/linux/sdt.h b/include/linux/sdt.h index 62db6436dfb2..4542171c6b7f 100644 --- a/include/linux/sdt.h +++ b/include/linux/sdt.h @@ -129,8 +129,7 @@ extern void sdt_probe_enable(sdt_instr_t *); extern void sdt_probe_disable(sdt_instr_t *); typedef struct dtrace_sdt_probeinfo { - unsigned long offset; - unsigned long base; + unsigned long addr; unsigned long name_len; unsigned long func_len; char name[0]; diff --git a/kernel/dtrace/sdt_register.c b/kernel/dtrace/sdt_register.c index 25e9dda2cff1..6298c6b4d45b 100644 --- a/kernel/dtrace/sdt_register.c +++ b/kernel/dtrace/sdt_register.c @@ -31,8 +31,8 @@ void sdt_probe_disable(sdt_instr_t *addr) } EXPORT_SYMBOL(sdt_probe_disable); -static int sdt_probe_resolve(struct module *mp, char *name, char *func, - uintptr_t offset, uintptr_t base, void *nops) +static int sdt_probe_add(struct module *mp, char *name, char *func, + uintptr_t addr, void *nops) { sdt_probedesc_t *sdp; uint8_t *instr; @@ -51,8 +51,8 @@ static int sdt_probe_resolve(struct module *mp, char *name, char *func, return 1; } - /* convert relative instr to absolute */ - instr = (uint8_t *)((uintptr_t)_text + base + offset - 1); + /* adjust relocation address to beginning of call instruction */ + instr = (uint8_t *)(addr - 1); /* TBD: use a kernel list? */ sdp->sdpd_offset = (uintptr_t)instr; @@ -97,9 +97,8 @@ void dtrace_register_builtins(void) for (cnt = 0; cnt < dtrace_sdt_nprobes; cnt++) { char *func = pi->name + pi->name_len + 1; - if (sdt_probe_resolve(dtrace_kmod, pi->name, func, - pi->offset, pi->base, nops)) - pr_warning("%s: cannot resolve %s\n", + if (sdt_probe_add(dtrace_kmod, pi->name, func, pi->addr, nops)) + pr_warning("%s: failed to add SDT probe %s\n", __func__, pi->name); nextpi = (void *)pi + sizeof(dtrace_sdt_probeinfo_t) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 123673291ffb..5c97598a45e8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2179,6 +2179,8 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, trace_sched_switch(prev, next); sched_info_switch(rq, prev, next); perf_event_task_sched_out(prev, next); + DTRACE_SCHED2(off__cpu, struct task_struct *, next, + struct task_struct *, next); fire_sched_out_preempt_notifiers(prev, next); prepare_lock_switch(rq, next); prepare_arch_switch(next); @@ -2230,6 +2232,7 @@ static struct rq *finish_task_switch(struct task_struct *prev) finish_lock_switch(rq, prev); finish_arch_post_lock_switch(); + DTRACE_SCHED(on__cpu); fire_sched_in_preempt_notifiers(current); if (mm) mmdrop(mm); diff --git a/scripts/Makefile b/scripts/Makefile index df832ed9c0a3..4cdb5168e91b 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -3,7 +3,6 @@ # the kernel for the build process. # --------------------------------------------------------------------------- # kallsyms: Find all symbols in vmlinux -# dtrace_relocs: find and save all __dtrace_probe_ calling probepoints # pnmttologo: Convert pnm files to logo files # conmakehash: Create chartable # conmakehash: Create arrays for initializing the kernel console tables @@ -12,7 +11,6 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include hostprogs-$(CONFIG_KALLSYMS) += kallsyms -hostprogs-$(CONFIG_DTRACE) += dtrace_relocs hostprogs-$(CONFIG_LOGO) += pnmtologo hostprogs-$(CONFIG_VT) += conmakehash hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount diff --git a/scripts/dtrace_relocs.c b/scripts/dtrace_relocs.c deleted file mode 100644 index a34f0e7d50cb..000000000000 --- a/scripts/dtrace_relocs.c +++ /dev/null @@ -1,387 +0,0 @@ -/* Generate assembler source containing __dtrace_probe_* calls (reloc info) - * - * inspired by scripts/kallsyms.c - * - * (C) 2011 Oracle Corporation - * - * Usage: dtrace_relocs input_file_text output_file_elf - */ - -#include -#include -#include -#include -#include -#include - -//#define INFO 1 -//#define SCANF 1 - -struct sym_entry { - unsigned long long addr; - unsigned char is_section; - unsigned char section_used; - int section_index; - unsigned long long section_base; - unsigned int len; - char *sym; -}; - -struct text_range { - const char *stext, *etext; - unsigned long long start, end; -}; - -static unsigned long long _stext; // from System.map - -static struct sym_entry *table; -static unsigned int table_size, table_cnt; -static int this_section_index; -static unsigned long long this_section_addr; -static int relocs_count; - -static void usage(void) -{ - fprintf(stderr, "Usage: dtrace_relocs input_file_text output_file_elf\n"); - exit(1); -} - -// skip over whitespace (spaces or tabs) -static char *deblank(char *str) -{ - while (*str == ' ' || *str == '\t') - str++; - return str; -} - -static int find_section(char *sect, int sectlen) -{ - int ix; - struct sym_entry *sym = table; - -#if 0 - fprintf(stderr, "%s: search for sect=<%s>, sectlen=%d:\n", - __func__, sect, sectlen); -#endif - - for (ix = 0; ix < table_cnt; ix++, sym++) { -#if 0 - if (sym->is_section && strlen(sym->sym) == sectlen) - fprintf(stderr, "%s: ix=%d, symlen=%d, symname=<%s>\n", - __func__, ix, strlen(sym->sym), sym->sym); -#endif - if (sym->is_section && strlen(sym->sym) == sectlen && - strncmp(sym->sym, sect, sectlen) == 0) { - sym->section_used = true; - this_section_addr = sym->addr; - return sym->section_index; - } - } - - return -1; -} - -static int get_this_section(char buf[500]) -{ - char *sect; - int sectlen; - int sect_index; - - if (strncmp(buf, "RELOCATION RECORDS FOR [", 24) != 0) { - fprintf(stderr, "Bad relocation header: %s\n", buf); - exit(2); - } - - sect = buf + 24; - sectlen = strlen(sect); // includes a trailing newline -#if 0 - fprintf(stderr, "%s: sect=<%s>, sectlen=%d\n", - __func__, sect, sectlen); -#endif - if (*(sect + sectlen - 3) != ']' || *(sect + sectlen - 2) != ':') { - fprintf(stderr, "Bad relocation header: %s\n", buf); - exit(2); - } - *(sect + sectlen - 3) = '\0'; - sectlen -= 3; // drop the "]:\n" -#if 0 - fprintf(stderr, "%s: isolated section name=<%s>\n", __func__, sect); -#endif - sect_index = find_section(sect, sectlen); - if (sect_index < 0) { - fprintf(stderr, "Bad section name in relocation header: %s\n", - sect); - exit(2); - } - - return sect_index; -} - -/* - * scans 2 lines of section info; - * first line is already in buf; - * second line is noise for now; - */ -static int get_section_info(FILE *fin, char buf[500], struct sym_entry *sect) -{ - int rc; - int sect_index; - char sect_name[200], sect_align[100]; - unsigned long sect_size, file_offset; - unsigned long long vma, lma; - char sect_flags[500]; - char *flags; - - rc = sscanf(buf, " %d %s %lx %llx %llx %lx %s \n", - §_index, (char *)§_name, §_size, &vma, &lma, - &file_offset, (char *)§_align); -#ifdef SCANF - fprintf(stderr, "%s: sscanf.1 rc= %d\n", __func__, rc); -#endif - if (rc != 7) - return -1; - - if (!fgets(sect_flags, sizeof(sect_flags), fin)) - return -1; - -#ifdef SCANF - fprintf(stderr, "%s: fgets.2 read=<%s>", __func__, sect_flags); -#endif - flags = deblank(sect_flags); - - sect->addr = file_offset; - sect->is_section = true; - sect->section_used = false; - sect->section_index = sect_index; - sect->len = sect_size; - sect->sym = malloc(strlen(sect_name) + 1); - if (!sect->sym) { - fprintf(stderr, "relocs failure: " - "unable to allocate required amount of memory\n"); - exit(1); - } - strcpy((char *)sect->sym, sect_name); - -#ifdef INFO - fprintf(stderr, "sect: index=%d, name=%s (%d), addr/offset=0x%llx, sect_size=0x%x, align=%s, vma=0x%llx, lma=0x%llx, flags=%s\n", - sect_index, sect->sym, strlen(sect->sym), sect->addr, sect->len, sect_align, - vma, lma, flags); -#endif - - return 0; -} - -static int get_symbol_info(char buf[500], struct sym_entry *s) -{ - int rc; - unsigned long long relo_offset, pp_offset; - char relo_type[200]; - char probepoint[200]; - - //rc = sscanf(buf, " %llx %s %200s-%llx \n", - rc = sscanf(buf, " %llx %s %[^ -]-%llx \n", - &relo_offset, (char *)&relo_type, - (char *)&probepoint, &pp_offset); -#ifdef SCANF - fprintf(stderr, "%s: sscanf.1 rc= %d\n", __func__, rc); -#endif - if (rc != 4) - return -1; - - s->addr = relo_offset; - s->len = strlen(probepoint); - s->is_section = false; - s->section_used = false; - s->section_index = -1; - s->section_base = this_section_addr; - s->sym = malloc(s->len + 1); - if (!s->sym) { - fprintf(stderr, "relocs failure: " - "unable to allocate required amount of memory\n"); - exit(1); - } - strcpy((char *)s->sym, probepoint); - -#ifdef INFO - fprintf(stderr, "sym: addr/offset=0x%llx, strlen=%d, type=%s, name=%s\n", - s->addr, s->len, relo_type, s->sym); -#endif - - relocs_count++; - return 0; -} - -static void get_text_addr(char buf[500], char *str_match, - unsigned long long *_adr) -{ - int rc; - unsigned long long adr; - char relo_type[100]; - char symbol_name[200]; - - rc = sscanf(buf, "%llx %s %s\n", - &adr, (char *)&relo_type, - (char *)&symbol_name); -#ifdef SCANF - fprintf(stderr, "%s: sscanf.1 rc= %d\n", __func__, rc); -#endif - if (rc != 3) - return; - - if (strcmp(relo_type, "T")) - return; - if (strcmp(symbol_name, str_match)) - return; - - *_adr = adr; -#ifdef INFO - fprintf(stderr, "found '%s':_addr/offset=0x%llx, type=%s, name=%s\n", - str_match, adr, relo_type, symbol_name); -#endif -} - -static void read_info(FILE *fin) -{ - char buf[500]; - bool in_sections = false, in_symbols = false; - - while (!feof(fin)) { - if (table_cnt >= table_size) { - table_size += 10000; - table = realloc(table, sizeof(*table) * table_size); - if (!table) { - fprintf(stderr, "out of memory\n"); - exit(1); - } - } - - if (!fgets(buf, sizeof(buf), fin)) - break; -#ifdef SCANF - fprintf(stderr, "dtr: buf=<%s>\n", buf); -#endif - - if (strncmp(buf, "Sections:", 9) == 0) { - in_sections = true; - continue; - } - if (strncmp(buf, "RELOCATION RECORDS", 11) == 0) { - in_sections = false; - in_symbols = true; - // isolate & look up section name, get its index - // this call also sets 'this_section_addr' - this_section_index = get_this_section(buf); - continue; - } - - if (in_sections) { - if (strncmp(buf, "Idx ", 4) != 0) - if (get_section_info(fin, buf, &table[table_cnt]) == 0) - table_cnt++; - continue; - } - - if (in_symbols) { - if (get_symbol_info(buf, &table[table_cnt]) == 0) - table_cnt++; - else { - if (_stext == 0) - get_text_addr(buf, "_stext", &_stext); - } - } - } -} - -static void output_label(FILE *fout, char *label) -{ - fprintf(fout, ".globl %s\n", label); - fprintf(fout, "\tALGN\n"); - fprintf(fout, "%s:\n", label); -} - -static void write_relocs(FILE *fout) -{ - unsigned int i; - int reloc_count = 0; - - fprintf(fout, "#include \n"); - fprintf(fout, "#if BITS_PER_LONG == 64\n"); - fprintf(fout, "#define PTR .quad\n"); - fprintf(fout, "#define ALGN .align 8\n"); - fprintf(fout, "#else\n"); - fprintf(fout, "#define PTR .long\n"); - fprintf(fout, "#define ALGN .align 4\n"); - fprintf(fout, "#endif\n"); - - fprintf(fout, "\t.section .rodata, \"a\"\n"); - fprintf(fout, "\n"); - - output_label(fout, "dtrace_relocs_count"); - fprintf(fout, "\tPTR\t%d\n", relocs_count); - fprintf(fout, "\n"); - - /* - * Provide proper symbols relocatability by their '_stext' - * relativeness. The symbol names cannot be used to construct - * normal symbol references as the list of symbols contains - * symbols that are declared static and are private to their - * .o files. This prevents .tmp_kallsyms.o or any other - * object from referencing them. - */ - output_label(fout, "dtrace_relocs"); - for (i = 0; i < table_cnt; i++) { - // for reloc symbols (not sections): - // print symbol relative address, section base address, - // call target string length, call target string/name; - if (!table[i].is_section) { - fprintf(fout, "\tPTR\t%#llx\n", _stext + table[i].addr); - fprintf(fout, "\tPTR\t%#llx\n", table[i].section_base); - fprintf(fout, "\tPTR\t%d\n", table[i].len); - fprintf(fout, "\t.asciz\t\"%s\"\n", table[i].sym); - fprintf(fout, "\tALGN\n"); - reloc_count++; - } - } - - fprintf(fout, "\n"); - - if (reloc_count != relocs_count) { - fprintf(fout, "relocs error: reloc counters do not agree (%d vs. %d\n)", - relocs_count, reloc_count); - exit(3); - } -} - -int main(int argc, char *argv[]) -{ - char *infile, *outfile; - FILE *fin, *fout; - - - if (argc != 3) - usage(); - - infile = argv[1]; - outfile = argv[2]; - - fin = fopen(infile, "r"); - if (!fin) { - fprintf(stderr, "relocs: cannot open input file '%s'\n", - infile); - exit(2); - } - fout = fopen(outfile, "w"); - if (!fout) { - fprintf(stderr, "relocs: cannot create output file '%s'\n", - outfile); - exit(2); - } - - read_info(fin); - fclose(fin); - - write_relocs(fout); - fclose(fout); - - return 0; -} diff --git a/scripts/dtrace_sdt.sh b/scripts/dtrace_sdt.sh index b7a27de89043..0d85f18c556c 100755 --- a/scripts/dtrace_sdt.sh +++ b/scripts/dtrace_sdt.sh @@ -2,46 +2,115 @@ LANG=C -fn="$1" - -objdump -htr "$fn" | \ - awk '/^Sections:/ { - getline; - getline; - while ($0 !~ /SYMBOL/) { - sect = $2; - addr = $6; +ofn="$1" +lfn="$2" +( + objdump -htr "$ofn" | \ + awk -v lfn=${lfn} \ + '/^Sections:/ { getline; - if (/CODE/) - sectbase[sect] = addr; - getline; + while ($0 !~ /SYMBOL/) { + sect = $2; + addr = $6; + + getline; + if (/CODE/) + sectbase[sect] = addr; + + getline; + } + next; } - next; - } - / F / { - printf "%16s %s F %s\n", $4, $1, $6; - next; - } + / F / { + printf "%16s %s F %s\n", $4, $1, $6; - /^RELOC/ { - sub(/^[^\[]+\[/, ""); - sub(/].*$/, ""); - sect = $1; - next; + if (!lfn) + printf "%s t %s\n", $1, $6; + + next; + } + + /^RELOC/ { + sub(/^[^\[]+\[/, ""); + sub(/].*$/, ""); + sect = $1; + next; + } + + /__dtrace_probe_/ { + $3 = substr($3, 16); + sub(/-.*$/, "", $3); + printf "%16s %s R %s %s\n", sect, $1, $3, sectbase[sect]; + next; + }' | \ + sort + [ "x${lfn}" != "x" ] && nm ${lfn} +) | \ + awk 'function addl(v0, v1, v0h, v0l, v1h, v1l, d, tmp) { + tmp = $0; + if (length(v0) > 8) { + d = length(v0); + v0h = strtonum("0x"substr(v0, 1, d - 8)); + v0l = strtonum("0x"substr(v0, d - 8 + 1)); + d = length(v1); + v1h = strtonum("0x"substr(v1, 1, d - 8)); + v1l = strtonum("0x"substr(v1, d - 8 + 1)); + + v0h += v1h; + v0l += v1l; + + d = sprintf("%x", v0l); + if (length(d) > 8) + v0h++; + + d = sprintf("%x%x", v0h, v0l); + } else { + v0 = strtonum("0x"v0); + v1 = strtonum("0x"v1); + d = sprintf("%x", v0 + v1); + } + $0 = tmp; + + return d; } - /__dtrace_probe_/ { - $3 = substr($3, 16); - sub(/-.*$/, "", $3); + function subl(v0, v1, v0h, v0l, v1h, v1l, d, tmp) { + tmp = $0; + if (length(v0) > 8) { + d = length(v0); + v0h = strtonum("0x"substr(v0, 1, d - 8)); + v0l = strtonum("0x"substr(v0, d - 8 + 1)); + d = length(v1); + v1h = strtonum("0x"substr(v1, 1, d - 8)); + v1l = strtonum("0x"substr(v1, d - 8 + 1)); - printf "%16s %s R %s %s\n", sect, $1, $3, sectbase[sect]; - next; - }' | \ - sort | \ - awk 'BEGIN { + if (v0l > v1l) { + if (v0h >= v1h) { + d = sprintf("%x%x", v0h - v1h, v0l - v1l); + } else { + printf "#error Invalid addresses: %x vs %x", v0, v1 \ + > /dev/stderr; + errc++; + } + } else { + printf "#error Invalid addresses: %x vs %x", v0, v1 \ + > /dev/stderr; + errc++; + } + } else { + v0 = strtonum("0x"v0); + v1 = strtonum("0x"v1); + d = sprintf("%x", v0 - v1); + } + $0 = tmp; + + return d; + } + + BEGIN { print "#include "; print "#if BITS_PER_LONG == 64"; print "# define PTR .quad"; @@ -59,21 +128,38 @@ objdump -htr "$fn" | \ print "dtrace_sdt_probes:"; } - / F / { - fun = $4; + NF < 4 { + fun = $3; + + if (fun in prdata) { + baseaddr = $1; + sub(/^0+/, "", baseaddr); + + $0 = prdata[fun]; + sub(/^0+/, "", $1); + sub(/^0+/, "", $4); + + print "\tPTR\t0x" addl(baseaddr, subl($1, $4)); + print "\tPTR\t" length($3); + print "\tPTR\t" length(fun); + print "\t.asciz\t\042" $3 "\042"; + print "\t.asciz\t\042" fun "\042"; + print "\tALGN"; + + probec++; + } next; } - / R / { - print "\tPTR\t0x" $2; - print "\tPTR\t0x" $5; - print "\tPTR\t" length($4); - print "\tPTR\t" length(fun); - print "\t.asciz\t\042" $4 "\042"; - print "\t.asciz\t\042" fun "\042"; - print "\tALGN"; + $3 == "F" { + fun = $4; + addr = $2; + next; + } - probec++; + $3 == "R" { + prdata[fun] = $2 " " $5 " " $4 " " addr; + next; } END { @@ -82,4 +168,9 @@ objdump -htr "$fn" | \ print "\tALGN"; print "dtrace_sdt_nprobes:"; print "\tPTR\t" probec; + + if (errc > 0) { + print errc " errors generating SDT probe data." > /dev/stderr; + exit 1; + } }' -- 2.50.1