diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-03-09 17:11:53 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-09 17:11:53 +0100 |
commit | 548b84166917d6f5e2296123b85ad24aecd3801d (patch) | |
tree | 0ab0300e23a02df0fe3c0579627e4998bb122c00 /tools/perf/util | |
parent | cfb581bcd4f8c158c6f2b48bf5e232bb9e6855c0 (diff) | |
parent | 57d54889cd00db2752994b389ba714138652e60c (diff) |
Merge commit 'v2.6.34-rc1' into perf/urgent
Conflicts:
tools/perf/util/probe-event.c
Merge reason: Pick up -rc1 and resolve the conflict as well.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/probe-event.c | 55 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 1002 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 53 | ||||
-rw-r--r-- | tools/perf/util/string.c | 55 | ||||
-rw-r--r-- | tools/perf/util/string.h | 1 |
5 files changed, 566 insertions, 600 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index a21b6bf055a..53181dbfe4a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -119,14 +119,14 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) char c, nc = 0; /* * <Syntax> - * perf probe [EVENT=]SRC:LN - * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] + * perf probe [EVENT=]SRC[:LN|;PTN] + * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] * * TODO:Group name support */ - ptr = strchr(arg, '='); - if (ptr) { /* Event name */ + ptr = strpbrk(arg, ";=@+%"); + if (ptr && *ptr == '=') { /* Event name */ *ptr = '\0'; tmp = ptr + 1; ptr = strchr(arg, ':'); @@ -139,7 +139,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) arg = tmp; } - ptr = strpbrk(arg, ":+@%"); + ptr = strpbrk(arg, ";:+@%"); if (ptr) { nc = *ptr; *ptr++ = '\0'; @@ -156,7 +156,11 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) while (ptr) { arg = ptr; c = nc; - ptr = strpbrk(arg, ":+@%"); + if (c == ';') { /* Lazy pattern must be the last part */ + pp->lazy_line = strdup(arg); + break; + } + ptr = strpbrk(arg, ";:+@%"); if (ptr) { nc = *ptr; *ptr++ = '\0'; @@ -165,13 +169,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) case ':': /* Line number */ pp->line = strtoul(arg, &tmp, 0); if (*tmp != '\0') - semantic_error("There is non-digit charactor" - " in line number."); + semantic_error("There is non-digit char" + " in line number."); break; case '+': /* Byte offset from a symbol */ pp->offset = strtoul(arg, &tmp, 0); if (*tmp != '\0') - semantic_error("There is non-digit charactor" + semantic_error("There is non-digit character" " in offset."); break; case '@': /* File name */ @@ -179,9 +183,6 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) semantic_error("SRC@SRC is not allowed."); pp->file = strdup(arg); DIE_IF(pp->file == NULL); - if (ptr) - semantic_error("@SRC must be the last " - "option."); break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { @@ -196,11 +197,18 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) } /* Exclusion check */ + if (pp->lazy_line && pp->line) + semantic_error("Lazy pattern can't be used with line number."); + + if (pp->lazy_line && pp->offset) + semantic_error("Lazy pattern can't be used with offset."); + if (pp->line && pp->offset) semantic_error("Offset can't be used with line number."); - if (!pp->line && pp->file && !pp->function) - semantic_error("File always requires line number."); + if (!pp->line && !pp->lazy_line && pp->file && !pp->function) + semantic_error("File always requires line number or " + "lazy pattern."); if (pp->offset && !pp->function) semantic_error("Offset requires an entry function."); @@ -208,11 +216,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) if (pp->retprobe && !pp->function) semantic_error("Return probe requires an entry function."); - if ((pp->offset || pp->line) && pp->retprobe) - semantic_error("Offset/Line can't be used with return probe."); + if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) + semantic_error("Offset/Line/Lazy pattern can't be used with " + "return probe."); - pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", - pp->function, pp->file, pp->line, pp->offset, pp->retprobe); + pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", + pp->function, pp->file, pp->line, pp->offset, pp->retprobe, + pp->lazy_line); } /* Parse perf-probe event definition */ @@ -458,6 +468,8 @@ static void clear_probe_point(struct probe_point *pp) free(pp->function); if (pp->file) free(pp->file); + if (pp->lazy_line) + free(pp->lazy_line); for (i = 0; i < pp->nr_args; i++) free(pp->args[i]); if (pp->args) @@ -719,6 +731,7 @@ void del_trace_kprobe_events(struct strlist *dellist) } #define LINEBUF_SIZE 256 +#define NR_ADDITIONAL_LINES 2 static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) { @@ -779,5 +792,11 @@ void show_line_range(struct line_range *lr) show_one_line(fp, (l++) - lr->offset, false, false); show_one_line(fp, (l++) - lr->offset, false, true); } + + if (lr->end == INT_MAX) + lr->end = l + NR_ADDITIONAL_LINES; + while (l < lr->end && !feof(fp)) + show_one_line(fp, (l++) - lr->offset, false, false); + fclose(fp); } diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1b2124d12f6..e77dc886760 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -32,21 +32,13 @@ #include <stdarg.h> #include <ctype.h> +#include "string.h" #include "event.h" #include "debug.h" #include "util.h" #include "probe-finder.h" -/* Dwarf_Die Linkage to parent Die */ -struct die_link { - struct die_link *parent; /* Parent die */ - Dwarf_Die die; /* Current die */ -}; - -static Dwarf_Debug __dw_debug; -static Dwarf_Error __dw_error; - /* * Generic dwarf analysis helpers */ @@ -113,281 +105,190 @@ static int strtailcmp(const char *s1, const char *s2) return 0; } -/* Find the fileno of the target file. */ -static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) -{ - Dwarf_Signed cnt, i; - Dwarf_Unsigned found = 0; - char **srcs; - int ret; +/* Line number list operations */ - if (!fname) - return 0; +/* Add a line to line number list */ +static void line_list__add_line(struct list_head *head, unsigned int line) +{ + struct line_node *ln; + struct list_head *p; - ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); - if (ret == DW_DLV_OK) { - for (i = 0; i < cnt && !found; i++) { - if (strtailcmp(srcs[i], fname) == 0) - found = i + 1; - dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); - } - for (; i < cnt; i++) - dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); - dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); + /* Reverse search, because new line will be the last one */ + list_for_each_entry_reverse(ln, head, list) { + if (ln->line < line) { + p = &ln->list; + goto found; + } else if (ln->line == line) /* Already exist */ + return ; } - if (found) - pr_debug("found fno: %d\n", (int)found); - return found; + /* List is empty, or the smallest entry */ + p = head; +found: + pr_debug("line list: add a line %u\n", line); + ln = zalloc(sizeof(struct line_node)); + DIE_IF(ln == NULL); + ln->line = line; + INIT_LIST_HEAD(&ln->list); + list_add(&ln->list, p); } -static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf) +/* Check if the line in line number list */ +static int line_list__has_line(struct list_head *head, unsigned int line) { - Dwarf_Signed cnt, i; - char **srcs; - int ret = 0; - - if (!buf || !fno) - return -EINVAL; - - ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); - if (ret == DW_DLV_OK) { - if ((Dwarf_Unsigned)cnt > fno - 1) { - *buf = strdup(srcs[fno - 1]); - ret = 0; - pr_debug("found filename: %s\n", *buf); - } else - ret = -ENOENT; - for (i = 0; i < cnt; i++) - dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); - dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); - } else - ret = -EINVAL; - return ret; + struct line_node *ln; + + /* Reverse search, because new line will be the last one */ + list_for_each_entry(ln, head, list) + if (ln->line == line) + return 1; + + return 0; } -/* Compare diename and tname */ -static int die_compare_name(Dwarf_Die dw_die, const char *tname) +/* Init line number list */ +static void line_list__init(struct list_head *head) { - char *name; - int ret; - ret = dwarf_diename(dw_die, &name, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_OK) { - ret = strcmp(tname, name); - dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); - } else - ret = -1; - return ret; + INIT_LIST_HEAD(head); } -/* Check the address is in the subprogram(function). */ -static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr, - Dwarf_Signed *offs) +/* Free line number list */ +static void line_list__free(struct list_head *head) { - Dwarf_Addr lopc, hipc; - int ret; - - /* TODO: check ranges */ - ret = dwarf_lowpc(sp_die, &lopc, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_NO_ENTRY) - return 0; - ret = dwarf_highpc(sp_die, &hipc, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - if (lopc <= addr && addr < hipc) { - *offs = addr - lopc; - return 1; - } else - return 0; + struct line_node *ln; + while (!list_empty(head)) { + ln = list_first_entry(head, struct line_node, list); + list_del(&ln->list); + free(ln); + } } -/* Check the die is inlined function */ -static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die) +/* Dwarf wrappers */ + +/* Find the realpath of the target file. */ +static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) { - /* TODO: check strictly */ - Dwarf_Bool inl; + Dwarf_Files *files; + size_t nfiles, i; + const char *src; int ret; - ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - return inl; -} + if (!fname) + return NULL; -/* Get the offset of abstruct_origin */ -static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die) -{ - Dwarf_Attribute attr; - Dwarf_Off cu_offs; - int ret; + ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); + if (ret != 0) + return NULL; - ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - ret = dwarf_formref(attr, &cu_offs, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); - return cu_offs; + for (i = 0; i < nfiles; i++) { + src = dwarf_filesrc(files, i, NULL, NULL); + if (strtailcmp(src, fname) == 0) + break; + } + return src; } -/* Get entry pc(or low pc, 1st entry of ranges) of the die */ -static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die) +struct __addr_die_search_param { + Dwarf_Addr addr; + Dwarf_Die *die_mem; +}; + +static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) { - Dwarf_Attribute attr; - Dwarf_Addr addr; - Dwarf_Off offs; - Dwarf_Ranges *ranges; - Dwarf_Signed cnt; - int ret; + struct __addr_die_search_param *ad = data; - /* Try to get entry pc */ - ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_OK) { - ret = dwarf_formaddr(attr, &addr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); - return addr; + if (dwarf_tag(fn_die) == DW_TAG_subprogram && + dwarf_haspc(fn_die, ad->addr)) { + memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); + return DWARF_CB_ABORT; } + return DWARF_CB_OK; +} - /* Try to get low pc */ - ret = dwarf_lowpc(dw_die, &addr, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_OK) - return addr; - - /* Try to get ranges */ - ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - ret = dwarf_formref(attr, &offs, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL, - &__dw_error); - DIE_IF(ret != DW_DLV_OK); - addr = ranges[0].dwr_addr1; - dwarf_ranges_dealloc(__dw_debug, ranges, cnt); - return addr; +/* Search a real subprogram including this line, */ +static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) +{ + struct __addr_die_search_param ad; + ad.addr = addr; + ad.die_mem = die_mem; + /* dwarf_getscopes can't find subprogram. */ + if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) + return NULL; + else + return die_mem; } -/* - * Search a Die from Die tree. - * Note: cur_link->die should be deallocated in this function. - */ -static int __search_die_tree(struct die_link *cur_link, - int (*die_cb)(struct die_link *, void *), - void *data) +/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ +static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { - Dwarf_Die new_die; - struct die_link new_link; + Dwarf_Die child_die; int ret; - if (!die_cb) - return 0; - - /* Check current die */ - while (!(ret = die_cb(cur_link, data))) { - /* Check child die */ - ret = dwarf_child(cur_link->die, &new_die, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_OK) { - new_link.parent = cur_link; - new_link.die = new_die; - ret = __search_die_tree(&new_link, die_cb, data); - if (ret) - break; - } + ret = dwarf_child(sp_die, die_mem); + if (ret != 0) + return NULL; - /* Move to next sibling */ - ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die, - &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); - cur_link->die = new_die; - if (ret == DW_DLV_NO_ENTRY) - return 0; - } - dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); - return ret; -} + do { + if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && + dwarf_haspc(die_mem, addr)) + return die_mem; -/* Search a die in its children's die tree */ -static int search_die_from_children(Dwarf_Die parent_die, - int (*die_cb)(struct die_link *, void *), - void *data) -{ - struct die_link new_link; - int ret; + if (die_get_inlinefunc(die_mem, addr, &child_die)) { + memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); + return die_mem; + } + } while (dwarf_siblingof(die_mem, die_mem) == 0); - new_link.parent = NULL; - ret = dwarf_child(parent_die, &new_link.die, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_OK) - return __search_die_tree(&new_link, die_cb, data); - else - return 0; + return NULL; } -/* Find a locdesc corresponding to the address */ -static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc, - Dwarf_Addr addr) +/* Compare diename and tname */ +static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { - Dwarf_Signed lcnt; - Dwarf_Locdesc **llbuf; - int ret, i; - - ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - ret = DW_DLV_NO_ENTRY; - for (i = 0; i < lcnt; ++i) { - if (llbuf[i]->ld_lopc <= addr && - llbuf[i]->ld_hipc > addr) { - memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc)); - desc->ld_s = - malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); - DIE_IF(desc->ld_s == NULL); - memcpy(desc->ld_s, llbuf[i]->ld_s, - sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); - ret = DW_DLV_OK; - break; - } - dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); - dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); - } - /* Releasing loop */ - for (; i < lcnt; ++i) { - dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); - dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); - } - dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST); - return ret; + const char *name; + name = dwarf_diename(dw_die); + DIE_IF(name == NULL); + return strcmp(tname, name); } -/* Get decl_file attribute value (file number) */ -static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die) +/* Get entry pc(or low pc, 1st entry of ranges) of the die */ +static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) { - Dwarf_Attribute attr; - Dwarf_Unsigned fno; + Dwarf_Addr epc; int ret; - ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - dwarf_formudata(attr, &fno, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); - return fno; + ret = dwarf_entrypc(dw_die, &epc); + DIE_IF(ret == -1); + return epc; } -/* Get decl_line attribute value (line number) */ -static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) +/* Get a variable die */ +static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, + Dwarf_Die *die_mem) { - Dwarf_Attribute attr; - Dwarf_Unsigned lno; + Dwarf_Die child_die; + int tag; int ret; - ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - dwarf_formudata(attr, &lno, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); - return lno; + ret = dwarf_child(sp_die, die_mem); + if (ret != 0) + return NULL; + + do { + tag = dwarf_tag(die_mem); + if ((tag == DW_TAG_formal_parameter || + tag == DW_TAG_variable) && + (die_compare_name(die_mem, name) == 0)) + return die_mem; + + if (die_find_variable(die_mem, name, &child_die)) { + memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); + return die_mem; + } + } while (dwarf_siblingof(die_mem, die_mem) == 0); + + return NULL; } /* @@ -395,47 +296,45 @@ static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) */ /* Show a location */ -static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) +static void show_location(Dwarf_Op *op, struct probe_finder *pf) { - Dwarf_Small op; - Dwarf_Unsigned regn; - Dwarf_Signed offs; + unsigned int regn; + Dwarf_Word offs = 0; int deref = 0, ret; const char *regs; - op = loc->lr_atom; - + /* TODO: support CFA */ /* If this is based on frame buffer, set the offset */ - if (op == DW_OP_fbreg) { + if (op->atom == DW_OP_fbreg) { + if (pf->fb_ops == NULL) + die("The attribute of frame base is not supported.\n"); deref = 1; - offs = (Dwarf_Signed)loc->lr_number; - op = pf->fbloc.ld_s[0].lr_atom; - loc = &pf->fbloc.ld_s[0]; - } else - offs = 0; + offs = op->number; + op = &pf->fb_ops[0]; + } - if (op >= DW_OP_breg0 && op <= DW_OP_breg31) { - regn = op - DW_OP_breg0; - offs += (Dwarf_Signed)loc->lr_number; + if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { + regn = op->atom - DW_OP_breg0; + offs += op->number; deref = 1; - } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) { - regn = op - DW_OP_reg0; - } else if (op == DW_OP_bregx) { - regn = loc->lr_number; - offs += (Dwarf_Signed)loc->lr_number2; + } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { + regn = op->atom - DW_OP_reg0; + } else if (op->atom == DW_OP_bregx) { + regn = op->number; + offs += op->number2; deref = 1; - } else if (op == DW_OP_regx) { - regn = loc->lr_number; + } else if (op->atom == DW_OP_regx) { + regn = op->number; } else - die("Dwarf_OP %d is not supported.", op); + die("DW_OP %d is not supported.", op->atom); regs = get_arch_regstr(regn); if (!regs) - die("%lld exceeds max register number.", regn); + die("%u exceeds max register number.", regn); if (deref) - ret = snprintf(pf->buf, pf->len, - " %s=%+lld(%s)", pf->var, offs, regs); + ret = snprintf(pf->buf, pf->len, " %s=+%ju(%s)", + pf->var, (uintmax_t)offs, regs); else ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); DIE_IF(ret < 0); @@ -443,52 +342,37 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) } /* Show a variables in kprobe event format */ -static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) +static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; - Dwarf_Locdesc ld; + Dwarf_Op *expr; + size_t nexpr; int ret; - ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error); - if (ret != DW_DLV_OK) + if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) goto error; - ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base)); - if (ret != DW_DLV_OK) + /* TODO: handle more than 1 exprs */ + ret = dwarf_getlocation_addr(&attr, (pf->addr - pf->cu_base), + &expr, &nexpr, 1); + if (ret <= 0 || nexpr == 0) goto error; - /* TODO? */ - DIE_IF(ld.ld_cents != 1); - show_location(&ld.ld_s[0], pf); - free(ld.ld_s); - dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); + + show_location(expr, pf); + /* *expr will be cached in libdw. Don't free it. */ return ; error: + /* TODO: Support const_value */ die("Failed to find the location of %s at this address.\n" " Perhaps, it has been optimized out.", pf->var); } -static int variable_callback(struct die_link *dlink, void *data) -{ - struct probe_finder *pf = (struct probe_finder *)data; - Dwarf_Half tag; - int ret; - - ret = dwarf_tag(dlink->die, &tag, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if ((tag == DW_TAG_formal_parameter || - tag == DW_TAG_variable) && - (die_compare_name(dlink->die, pf->var) == 0)) { - show_variable(dlink->die, pf); - return 1; - } - /* TODO: Support struct members and arrays */ - return 0; -} - /* Find a variable in a subprogram die */ -static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) +static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { int ret; + Dwarf_Die vr_die; + /* TODO: Support struct members and arrays */ if (!is_c_varname(pf->var)) { /* Output raw parameters */ ret = snprintf(pf->buf, pf->len, " %s", pf->var); @@ -499,58 +383,51 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) pr_debug("Searching '%s' variable in context.\n", pf->var); /* Search child die for local variables and parameters. */ - ret = search_die_from_children(sp_die, variable_callback, pf); - if (!ret) + if (!die_find_variable(sp_die, pf->var, &vr_die)) die("Failed to find '%s' in this function.", pf->var); -} - -/* Get a frame base on the address */ -static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf) -{ - Dwarf_Attribute attr; - int ret; - ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base)); - DIE_IF(ret != DW_DLV_OK); - dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); -} - -static void free_current_frame_base(struct probe_finder *pf) -{ - free(pf->fbloc.ld_s); - memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc)); + show_variable(&vr_die, pf); } /* Show a probe point to output buffer */ -static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, - struct probe_finder *pf) +static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { struct probe_point *pp = pf->pp; - char *name; + Dwarf_Addr eaddr; + Dwarf_Die die_mem; + const char *name; char tmp[MAX_PROBE_BUFFER]; int ret, i, len; + Dwarf_Attribute fb_attr; + size_t nops; + + /* If no real subprogram, find a real one */ + if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { + sp_die = die_get_real_subprogram(&pf->cu_die, + pf->addr, &die_mem); + if (!sp_die) + die("Probe point is not found in subprograms."); + } /* Output name of probe point */ - ret = dwarf_diename(sp_die, &name, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (ret == DW_DLV_OK) { - ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, - (unsigned int)offs); + name = dwarf_diename(sp_die); + if (name) { + dwarf_entrypc(sp_die, &eaddr); + ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, + (unsigned long)(pf->addr - eaddr)); /* Copy the function name if possible */ if (!pp->function) { pp->function = strdup(name); - pp->offset = offs; + pp->offset = (size_t)(pf->addr - eaddr); } - dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); } else { /* This function has no name. */ - ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr); + ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", + (uintmax_t)pf->addr); if (!pp->function) { /* TODO: Use _stext */ pp->function = strdup(""); - pp->offset = (int)pf->addr; + pp->offset = (size_t)pf->addr; } } DIE_IF(ret < 0); @@ -558,8 +435,15 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, len = ret; pr_debug("Probe point found: %s\n", tmp); + /* Get the frame base attribute/ops */ + dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); + ret = dwarf_getlocation_addr(&fb_attr, (pf->addr - pf->cu_base), + &pf->fb_ops, &nops, 1); + if (ret <= 0 || nops == 0) + pf->fb_ops = NULL; + /* Find each argument */ - get_current_frame_base(sp_die, pf); + /* TODO: use dwarf_cfi_addrframe */ for (i = 0; i < pp->nr_args; i++) { pf->var = pp->args[i]; pf->buf = &tmp[len]; @@ -567,289 +451,327 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, find_variable(sp_die, pf); len += strlen(pf->buf); } - free_current_frame_base(pf); + + /* *pf->fb_ops will be cached in libdw. Don't free it. */ + pf->fb_ops = NULL; pp->probes[pp->found] = strdup(tmp); pp->found++; } -static int probeaddr_callback(struct die_link *dlink, void *data) +/* Find probe point from its line number */ +static void find_probe_point_by_line(struct probe_finder *pf) { - struct probe_finder *pf = (struct probe_finder *)data; - Dwarf_Half tag; - Dwarf_Signed offs; + Dwarf_Lines *lines; + Dwarf_Line *line; + size_t nlines, i; + Dwarf_Addr addr; + int lineno; int ret; - ret = dwarf_tag(dlink->die, &tag, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - /* Check the address is in this subprogram */ - if (tag == DW_TAG_subprogram && - die_within_subprogram(dlink->die, pf->addr, &offs)) { - show_probepoint(dlink->die, offs, pf); - return 1; + ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); + DIE_IF(ret != 0); + + for (i = 0; i < nlines; i++) { + line = dwarf_onesrcline(lines, i); + dwarf_lineno(line, &lineno); + if (lineno != pf->lno) + continue; + + /* TODO: Get fileno from line, but how? */ + if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) + continue; + + ret = dwarf_lineaddr(line, &addr); + DIE_IF(ret != 0); + pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", + (int)i, lineno, (uintmax_t)addr); + pf->addr = addr; + + show_probe_point(NULL, pf); + /* Continuing, because target line might be inlined. */ } - return 0; } -/* Find probe point from its line number */ -static void find_probe_point_by_line(struct probe_finder *pf) +/* Find lines which match lazy pattern */ +static int find_lazy_match_lines(struct list_head *head, + const char *fname, const char *pat) { - Dwarf_Signed cnt, i, clm; - Dwarf_Line *lines; - Dwarf_Unsigned lineno = 0; + char *fbuf, *p1, *p2; + int fd, line, nlines = 0; + struct stat st; + + fd = open(fname, O_RDONLY); + if (fd < 0) + die("failed to open %s", fname); + DIE_IF(fstat(fd, &st) < 0); + fbuf = malloc(st.st_size + 2); + DIE_IF(fbuf == NULL); + DIE_IF(read(fd, fbuf, st.st_size) < 0); + close(fd); + fbuf[st.st_size] = '\n'; /* Dummy line */ + fbuf[st.st_size + 1] = '\0'; + p1 = fbuf; + line = 1; + while ((p2 = strchr(p1, '\n')) != NULL) { + *p2 = '\0'; + if (strlazymatch(p1, pat)) { + line_list__add_line(head, line); + nlines++; + } + line++; + p1 = p2 + 1; + } + free(fbuf); + return nlines; +} + +/* Find probe points from lazy pattern */ +static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) +{ + Dwarf_Lines *lines; + Dwarf_Line *line; + size_t nlines, i; Dwarf_Addr addr; - Dwarf_Unsigned fno; + Dwarf_Die die_mem; + int lineno; int ret; - ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error); - DIE_IF(ret != DW_DLV_OK); + if (list_empty(&pf->lcache)) { + /* Matching lazy line pattern */ + ret = find_lazy_match_lines(&pf->lcache, pf->fname, + pf->pp->lazy_line); + if (ret <= 0) + die("No matched lines found in %s.", pf->fname); + } + + ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); + DIE_IF(ret != 0); + for (i = 0; i < nlines; i++) { + line = dwarf_onesrcline(lines, i); - for (i = 0; i < cnt; i++) { - ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - if (fno != pf->fno) + dwarf_lineno(line, &lineno); + if (!line_list__has_line(&pf->lcache, lineno)) continue; - ret = dwarf_lineno(lines[i], &lineno, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - if (lineno != pf->lno) + /* TODO: Get fileno from line, but how? */ + if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - ret = dwarf_lineoff(lines[i], &clm, &__dw_error); - DIE_IF(ret != DW_DLV_OK); + ret = dwarf_lineaddr(line, &addr); + DIE_IF(ret != 0); + if (sp_die) { + /* Address filtering 1: does sp_die include addr? */ + if (!dwarf_haspc(sp_die, addr)) + continue; + /* Address filtering 2: No child include addr? */ + if (die_get_inlinefunc(sp_die, addr, &die_mem)) + continue; + } - ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); - DIE_IF(ret != DW_DLV_OK); - pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n", - (int)i, (unsigned)lineno, (int)clm, addr); + pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", + (int)i, lineno, (unsigned long long)addr); pf->addr = addr; - /* Search a real subprogram including this line, */ - ret = search_die_from_children(pf->cu_die, - probeaddr_callback, pf); - if (ret == 0) - die("Probe point is not found in subprograms."); + + show_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } - dwarf_srclines_dealloc(__dw_debug, lines, cnt); + /* TODO: deallocate lines, but how? */ +} + +static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) +{ + struct probe_finder *pf = (struct probe_finder *)data; + struct probe_point *pp = pf->pp; + + if (pp->lazy_line) + find_probe_point_lazy(in_die, pf); + else { + /* Get probe address */ + pf->addr = die_get_entrypc(in_die); + pf->addr += pp->offset; + pr_debug("found inline addr: 0x%jx\n", + (uintmax_t)pf->addr); + + show_probe_point(in_die, pf); + } + + return DWARF_CB_OK; } /* Search function from function name */ -static int probefunc_callback(struct die_link *dlink, void *data) +static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct probe_finder *pf = (struct probe_finder *)data; struct probe_point *pp = pf->pp; - struct die_link *lk; - Dwarf_Signed offs; - Dwarf_Half tag; - int ret; - ret = dwarf_tag(dlink->die, &tag, &__dw_error); - DIE_IF(ret == DW_DLV_ERROR); - if (tag == DW_TAG_subprogram) { - if (die_compare_name(dlink->die, pp->function) == 0) { - if (pp->line) { /* Function relative line */ - pf->fno = die_get_decl_file(dlink->die); - pf->lno = die_get_decl_line(dlink->die) - + pp->line; - find_probe_point_by_line(pf); - return 1; - } - if (die_inlined_subprogram(dlink->die)) { - /* Inlined function, save it. */ - ret = dwarf_die_CU_offset(dlink->die, - &pf->inl_offs, - &__dw_error); - DIE_IF(ret != DW_DLV_OK); - pr_debug("inline definition offset %lld\n", - pf->inl_offs); - return 0; /* Continue to search */ - } - /* Get probe address */ - pf->addr = die_get_entrypc(dlink->die); + /* Check tag and diename */ + if (dwarf_tag(sp_die) != DW_TAG_subprogram || + die_compare_name(sp_die, pp->function) != 0) + return 0; + + pf->fname = dwarf_decl_file(sp_die); + if (pp->line) { /* Function relative line */ + dwarf_decl_line(sp_die, &pf->lno); + pf->lno += pp->line; + find_probe_point_by_line(pf); + } else if (!dwarf_func_inline(sp_die)) { + /* Real function */ + if (pp->lazy_line) + find_probe_point_lazy(sp_die, pf); + else { + pf->addr = die_get_entrypc(sp_die); pf->addr += pp->offset; / |