int main (int argc, char *argv[]) { assert (argc >= 3); const char *name = argv[1]; ptrdiff_t cuoff = strtol (argv[2], NULL, 0); bool new_style = argc > 3; int fd = open (name, O_RDONLY); Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ); Dwarf_Die cudie_mem, *cudie = dwarf_offdie (dbg, cuoff, &cudie_mem); for (ptrdiff_t off = new_style ? DWARF_GETMACROS_START : 0; (off = dwarf_getmacros (cudie, mac, dbg, off)); ) if (off == -1) { puts (dwarf_errmsg (dwarf_errno ())); break; } dwarf_end (dbg); return 0; }
static void DC_get_die_from_CU_relative_offset(Dwarf_Die v, Dwarf_Off off,Dwarf_Die *retDie){ Dwarf_Error error; Dwarf_Off CUStart = DC_get_CU_start_offset(v); dwarf_offdie(d,CUStart + off,retDie,&error); }
DieHolder::DieHolder(Dwarf_Debug dbg, Dwarf_Off offset, bool const dealloc_die) { Dwarf_Die die = NULL; Dwarf_Error err = NULL; CHECK_DWERR(dwarf_offdie(dbg, offset, &die, &err), err, "cannot retrieve DIE from offset 0x%" DW_PR_DUx, offset); init(dbg, die, dealloc_die); }
DwarfDie DwarfDie::OffDie(Dwarf_Debug dwarf, Dwarf_Off ref) { Dwarf_Die die; Dwarf_Error derr; int error; error = dwarf_offdie(dwarf, ref, &die, &derr); if (error != 0) return DwarfDie(); else return DwarfDie(dwarf, die); }
int main (int argc, char **argv) { assert (argc > 1); int i = open (argv[1], O_RDONLY); assert (i >= 0); Dwarf *dw = dwarf_begin (i, DWARF_C_READ); assert (dw != NULL); Dwarf_Die die_mem, *die; die = dwarf_offdie (dw, 11, &die_mem); assert (die == &die_mem); assert (dwarf_tag (die) == 0); die = dwarf_offdie (dw, 11, &die_mem); assert (die == &die_mem); assert (dwarf_tag (die) == 0); dwarf_end (dw); return 0; }
int main (int argc, char *argv[]) { int cnt; for (cnt = 1; cnt < argc; ++cnt) { int fd = open (argv[cnt], O_RDONLY); Dwarf *dbg; printf ("file: %s\n", basename (argv[cnt])); dbg = dwarf_begin (fd, DWARF_C_READ); if (dbg == NULL) { printf ("%s not usable\n", argv[cnt]); close (fd); continue; } Dwarf_Off off = 0; Dwarf_Off old_off = 0; size_t hsize; Dwarf_Off abbrev; uint8_t addresssize; uint8_t offsetsize; while (dwarf_nextcu (dbg, off, &off, &hsize, &abbrev, &addresssize, &offsetsize) == 0) { printf ("New CU: off = %llu, hsize = %zu, ab = %llu, as = %" PRIu8 ", os = %" PRIu8 "\n", (unsigned long long int) old_off, hsize, (unsigned long long int) abbrev, addresssize, offsetsize); Dwarf_Die die; if (dwarf_offdie (dbg, old_off + hsize, &die) != NULL) handle (dbg, &die, 1); old_off = off; } dwarf_end (dbg); close (fd); } return 0; }
static void read_dwarf_info(simgrid::mc::ObjectInformation* info, Dwarf* dwarf) { // For each compilation unit: Dwarf_Off offset = 0; Dwarf_Off next_offset = 0; size_t length; while (dwarf_nextcu(dwarf, offset, &next_offset, &length, nullptr, nullptr, nullptr) == 0) { Dwarf_Die unit_die; if (dwarf_offdie(dwarf, offset + length, &unit_die) != nullptr) MC_dwarf_handle_children(info, &unit_die, &unit_die, nullptr, nullptr); offset = next_offset; } }
/* Now that we are at the end of the CU, check the range lists */ void check_range_array_info(Dwarf_Debug dbg) { if (range_array && range_array_count) { /* Traverse the range array and for each entry: Load the ranges Check for any outside conditions */ Dwarf_Off original_off = 0; Dwarf_Off die_off = 0; Dwarf_Unsigned index = 0; Dwarf_Die cu_die; int res; /* In case of errors, the correct DIE offset should be displayed. At this point we are at the end of the PU */ Dwarf_Off DIE_overall_offset_bak = DIE_overall_offset; for (index = 0; index < range_array_count; ++index) { Dwarf_Ranges *rangeset = 0; Dwarf_Signed rangecount = 0; Dwarf_Unsigned bytecount = 0; /* Get a range info record */ die_off = range_array[index].die_off; original_off = range_array[index].range_off; res = dwarf_offdie(dbg,die_off,&cu_die,&err); if (res != DW_DLV_OK) { print_error(dbg,"dwarf_offdie",res,err); } res = dwarf_get_ranges_a(dbg,original_off,cu_die, &rangeset,&rangecount,&bytecount,&err); if (res == DW_DLV_OK) { check_ranges_list(dbg,die_off,cu_die,original_off, rangeset,rangecount,bytecount); } dwarf_dealloc(dbg,cu_die,DW_DLA_DIE); }; reset_range_array_info(); /* Point back to the end of the PU */ DIE_overall_offset = DIE_overall_offset_bak; } }
static VALUE rdwarf_die_at(VALUE self, VALUE offset) { rdwarf_t *rd = GetRDwarf(self); Dwarf_Die die; Dwarf_Off cu_off; Dwarf_Error err; VALUE cu; chkerr1(dwarf_offdie(rd->shared_data->dbg, NUM2LL(offset), &die, &err), &err, self); chkerr1(dwarf_CU_dieoffset_given_die(die, &cu_off, &err), &err, self); rdwarf_compile_units(self); cu = rb_hash_aref(rd->shared_data->off2die, LL2NUM(cu_off)); if (NIL_P(cu)) { rb_raise(rd_eError, "Cannot find CU"); } return rd_die_new(rd->shared_data, self, cu, die); }
int type_of(Dwarf_Debug dbg, Dwarf_Die *die, Dwarf_Die *type_die, Dwarf_Error *err) { Dwarf_Attribute type; Dwarf_Off ref_off = 0; if (type_off(die, &ref_off, err) != DW_DLV_OK) { fprintf(stderr, "Error in getting type offset: %s\n", dwarf_errmsg(*err)); return -1; } int status; if ((status = dwarf_offdie(dbg, ref_off, type_die, err)) != DW_DLV_OK) { fprintf(stderr, "Error %d in getting die at offset: %s\n", status, dwarf_errmsg(*err)); return status; } return DW_DLV_OK; }
static Dwarf_Die find_cu_die(Dwarf_Debug dbg, Dwarf_Addr pc) { Dwarf_Error de = {0}; Dwarf_Die cu_die = NULL; Dwarf_Arange *arlist; Dwarf_Signed arcnt; Dwarf_Arange ar; Dwarf_Off die_offs; if (dwarf_get_aranges(dbg, &arlist, &arcnt, &de) != DW_DLV_OK || dwarf_get_arange(arlist, arcnt, pc, &ar, &de) != DW_DLV_OK || dwarf_get_cu_die_offset(ar, &die_offs, &de) != DW_DLV_OK || dwarf_offdie(dbg, die_offs, &cu_die, &de) != DW_DLV_OK) { NOTIFY_DWARF(de); /* Try to find it by walking all CU's and looking at their lowpc+highpc * entries, which should work if each has a single contiguous * range. Note that Cygwin and MinGW gcc don't seen to include * lowpc+highpc in their CU's. */ cu_die = find_cu_die_via_iter(dbg, pc); } return cu_die; }
BT_HIDDEN struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu) { Dwarf_Die *dwarf_die = NULL; struct bt_dwarf_die *die = NULL; if (!cu) { goto error; } dwarf_die = g_new0(Dwarf_Die, 1); if (!dwarf_die) { goto error; } dwarf_die = dwarf_offdie(cu->dwarf_info, cu->offset + cu->header_size, dwarf_die); if (!dwarf_die) { goto error; } die = g_new0(struct bt_dwarf_die, 1); if (!die) { goto error; } die->cu = cu; die->dwarf_die = dwarf_die; die->depth = 0; return die; error: g_free(dwarf_die); g_free(die); return NULL; }
static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); return param.retval; } int find_line_range(int fd, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg = NULL; Dwfl *dwfl; Dwarf_Addr bias; /* Currently ignored */ const char *comp_dir; dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); if (!dbg) { pr_warning("No debug information found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); return -EBADF; } /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (lr->file) lf.fname = cu_find_realpath(&lf.cu_die, lr->file); else lf.fname = 0; if (!lr->file || lf.fname) { if (lr->function) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } off = noff; } /* Store comp_dir */ if (lf.found) { comp_dir = cu_get_comp_dir(&lf.cu_die); if (comp_dir) { lr->comp_dir = strdup(comp_dir); if (!lr->comp_dir) ret = -ENOMEM; } } pr_debug("path: %s\n", lr->path); dwfl_end(dwfl); return (ret < 0) ? ret : lf.found; }
static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); return _param.retval; } struct pubname_callback_param { char *function; char *file; Dwarf_Die *cu_die; Dwarf_Die *sp_die; int found; }; static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) { struct pubname_callback_param *param = data; if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) return DWARF_CB_OK; if (die_compare_name(param->sp_die, param->function)) { if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) return DWARF_CB_OK; if (param->file && strtailcmp(param->file, dwarf_decl_file(param->sp_die))) return DWARF_CB_OK; param->found = 1; return DWARF_CB_ABORT; } } return DWARF_CB_OK; } /* Find probe points from debuginfo */ static int debuginfo__find_probes(struct debuginfo *dbg, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; int ret = 0; #if _ELFUTILS_PREREQ(0, 142) Elf *elf; GElf_Ehdr ehdr; GElf_Shdr shdr; /* Get the call frame information from this dwarf */ elf = dwarf_getelf(dbg->dbg); if (elf == NULL) return -EINVAL; if (gelf_getehdr(elf, &ehdr) == NULL) return -EINVAL; if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && shdr.sh_type == SHT_PROGBITS) { pf->cfi = dwarf_getcfi_elf(elf); } else { pf->cfi = dwarf_getcfi(dbg->dbg); } #endif off = 0; pf->lcache = intlist__new(NULL); if (!pf->lcache) return -ENOMEM; /* Fastpath: lookup by function name from .debug_pubnames section */ if (pp->function) { struct pubname_callback_param pubname_param = { .function = pp->function, .file = pp->file, .cu_die = &pf->cu_die, .sp_die = &pf->sp_die, .found = 0, }; struct dwarf_callback_param probe_param = { .data = pf, }; dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { ret = probe_point_search_cb(&pf->sp_die, &probe_param); if (ret) goto found; } } /* Loop on CUs (Compilation Unit) */ while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); if (!diep) continue; /* Check if target file is included. */ if (pp->file) pf->fname = cu_find_realpath(&pf->cu_die, pp->file); else pf->fname = NULL; if (!pp->file || pf->fname) { if (pp->function) ret = find_probe_point_by_func(pf); else if (pp->lazy_line) ret = find_probe_point_lazy(&pf->cu_die, pf); else { pf->lno = pp->line; ret = find_probe_point_by_line(pf); } if (ret < 0) break; } off = noff; } found: intlist__delete(pf->lcache); pf->lcache = NULL; return ret; } struct local_vars_finder { struct probe_finder *pf; struct perf_probe_arg *args; int max_args; int nargs; int ret; }; /* Collect available variables in this scope */ static int copy_variables_cb(Dwarf_Die *die_mem, void *data) { struct local_vars_finder *vf = data; struct probe_finder *pf = vf->pf; int tag; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { if (convert_variable_location(die_mem, vf->pf->addr, vf->pf->fb_ops, &pf->sp_die, NULL) == 0) { vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); if (vf->args[vf->nargs].var == NULL) { vf->ret = -ENOMEM; return DIE_FIND_CB_END; } pr_debug(" %s", vf->args[vf->nargs].var); vf->nargs++; } } if (dwarf_haspc(die_mem, vf->pf->addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; } static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, struct perf_probe_arg *args) { Dwarf_Die die_mem; int i; int n = 0; struct local_vars_finder vf = {.pf = pf, .args = args, .max_args = MAX_PROBE_ARGS, .ret = 0}; for (i = 0; i < pf->pev->nargs; i++) { /* var never be NULL */ if (strcmp(pf->pev->args[i].var, "$vars") == 0) { pr_debug("Expanding $vars into:"); vf.nargs = n; /* Special local variables */ die_find_child(sc_die, copy_variables_cb, (void *)&vf, &die_mem); pr_debug(" (%d)\n", vf.nargs - n); if (vf.ret < 0) return vf.ret; n = vf.nargs; } else { /* Copy normal argument */ args[n] = pf->pev->args[i]; n++; } } return n; } /* Add a found probe point into trace event list */ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); struct probe_trace_event *tev; struct perf_probe_arg *args; int ret, i; /* Check number of tevs */ if (tf->ntevs == tf->max_tevs) { pr_warning("Too many( > %d) probe point found.\n", tf->max_tevs); return -ERANGE; } tev = &tf->tevs[tf->ntevs++]; /* Trace point should be converted from subprogram DIE */ ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, pf->pev->point.retprobe, &tev->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); /* Expand special probe argument if exist */ args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); if (args == NULL) return -ENOMEM; ret = expand_probe_args(sc_die, pf, args); if (ret < 0) goto end; tev->nargs = ret; tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) { ret = -ENOMEM; goto end; } /* Find each argument */ for (i = 0; i < tev->nargs; i++) { pf->pvar = &args[i]; pf->tvar = &tev->args[i]; /* Variable should be found from scope DIE */ ret = find_variable(sc_die, pf); if (ret != 0) break; } end: free(args); return ret; } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ int debuginfo__find_trace_events(struct debuginfo *dbg, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, .mod = dbg->mod, .max_tevs = max_tevs}; int ret; /* Allocate result tevs array */ *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); if (*tevs == NULL) return -ENOMEM; tf.tevs = *tevs; tf.ntevs = 0; ret = debuginfo__find_probes(dbg, &tf.pf); if (ret < 0) { zfree(tevs); return ret; } return (ret < 0) ? ret : tf.ntevs; } #define MAX_VAR_LEN 64 /* Collect available variables in this scope */ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) { struct available_var_finder *af = data; struct variable_list *vl; char buf[MAX_VAR_LEN]; int tag, ret; vl = &af->vls[af->nvls - 1]; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, af->pf.fb_ops, &af->pf.sp_die, NULL); if (ret == 0) { ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); pr_debug2("Add new var: %s\n", buf); if (ret > 0) strlist__add(vl->vars, buf); } } if (af->child && dwarf_haspc(die_mem, af->pf.addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; } /* Add a found vars into available variables list */ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); struct variable_list *vl; Dwarf_Die die_mem; int ret; /* Check number of tevs */ if (af->nvls == af->max_vls) { pr_warning("Too many( > %d) probe point found.\n", af->max_vls); return -ERANGE; } vl = &af->vls[af->nvls++]; /* Trace point should be converted from subprogram DIE */ ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, pf->pev->point.retprobe, &vl->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, vl->point.offset); /* Find local variables */ vl->vars = strlist__new(true, NULL); if (vl->vars == NULL) return -ENOMEM; af->child = true; die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); /* Find external variables */ if (!af->externs) goto out; /* Don't need to search child DIE for externs. */ af->child = false; die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); out: if (strlist__empty(vl->vars)) { strlist__delete(vl->vars); vl->vars = NULL; } return ret; } /* * Find available variables at given probe point * Return the number of found probe points. Return 0 if there is no * matched probe point. Return <0 if an error occurs. */ int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, int max_vls, bool externs) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, .mod = dbg->mod, .max_vls = max_vls, .externs = externs}; int ret; /* Allocate result vls array */ *vls = zalloc(sizeof(struct variable_list) * max_vls); if (*vls == NULL) return -ENOMEM; af.vls = *vls; af.nvls = 0; ret = debuginfo__find_probes(dbg, &af.pf); if (ret < 0) { /* Free vlist for error */ while (af.nvls--) { zfree(&af.vls[af.nvls].point.symbol); strlist__delete(af.vls[af.nvls].vars); } zfree(vls); return ret; } return (ret < 0) ? ret : af.nvls; } /* Reverse search */ int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; Dwarf_Addr _addr = 0, baseaddr = 0; const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; int baseline = 0, lineno = 0, ret = 0; /* Find cu die */ if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { pr_warning("Failed to find debug information for address %lx\n", addr); ret = -EINVAL; goto end; } /* Find a corresponding line (filename and lineno) */ cu_find_lineinfo(&cudie, addr, &fname, &lineno); /* Don't care whether it failed or not */ /* Find a corresponding function (name, baseline and baseaddr) */ if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { /* Get function entry information */ func = basefunc = dwarf_diename(&spdie); if (!func || dwarf_entrypc(&spdie, &baseaddr) != 0 || dwarf_decl_line(&spdie, &baseline) != 0) { lineno = 0; goto post; } fname = dwarf_decl_file(&spdie); if (addr == (unsigned long)baseaddr) { /* Function entry - Relative line number is 0 */ lineno = baseline; goto post; } /* Track down the inline functions step by step */ while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { /* There is an inline function */ if (dwarf_entrypc(&indie, &_addr) == 0 && _addr == addr) { /* * addr is at an inline function entry. * In this case, lineno should be the call-site * line number. (overwrite lineinfo) */ lineno = die_get_call_lineno(&indie); fname = die_get_call_file(&indie); break; } else { /* * addr is in an inline function body. * Since lineno points one of the lines * of the inline function, baseline should * be the entry line of the inline function. */ tmp = dwarf_diename(&indie); if (!tmp || dwarf_decl_line(&indie, &baseline) != 0) break; func = tmp; spdie = indie; } } /* Verify the lineno and baseline are in a same file */ tmp = dwarf_decl_file(&spdie); if (!tmp || strcmp(tmp, fname) != 0) lineno = 0; } post: /* Make a relative line number or an offset */ if (lineno) ppt->line = lineno - baseline; else if (basefunc) { ppt->offset = addr - (unsigned long)baseaddr; func = basefunc; } /* Duplicate strings */ if (func) { ppt->function = strdup(func); if (ppt->function == NULL) { ret = -ENOMEM; goto end; } } if (fname) { ppt->file = strdup(fname); if (ppt->file == NULL) { zfree(&ppt->function); ret = -ENOMEM; goto end; } } end: if (ret == 0 && (fname || func)) ret = 1; /* Found a point */ return ret; } /* Add a line and store the src path */ static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { /* Copy source path */ if (!lr->path) { lr->path = strdup(src); if (lr->path == NULL) return -ENOMEM; } return intlist__add(lr->line_list, lineno); } static int line_range_walk_cb(const char *fname, int lineno, Dwarf_Addr addr __maybe_unused, void *data) { struct line_finder *lf = data; int err; if ((strtailcmp(fname, lf->fname) != 0) || (lf->lno_s > lineno || lf->lno_e < lineno)) return 0; err = line_range_add_line(fname, lineno, lf->lr); if (err < 0 && err != -EEXIST) return err; return 0; } /* Find line range from its line number */ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { int ret; ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); /* Update status */ if (ret >= 0) if (!intlist__empty(lf->lr->line_list)) ret = lf->found = 1; else ret = 0; /* Lines are not found */ else { zfree(&lf->lr->path); } return ret; }
static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); return param.retval; } int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; const char *comp_dir; /* Fastpath: lookup by function name from .debug_pubnames section */ if (lr->function) { struct pubname_callback_param pubname_param = { .function = lr->function, .file = lr->file, .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; struct dwarf_callback_param line_range_param = { .data = (void *)&lf, .retval = 0}; dwarf_getpubnames(dbg->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { line_range_search_cb(&lf.sp_die, &line_range_param); if (lf.found) goto found; } } /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (lr->file) lf.fname = cu_find_realpath(&lf.cu_die, lr->file); else lf.fname = 0; if (!lr->file || lf.fname) { if (lr->function) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } off = noff; } found: /* Store comp_dir */ if (lf.found) { comp_dir = cu_get_comp_dir(&lf.cu_die); if (comp_dir) { lr->comp_dir = strdup(comp_dir); if (!lr->comp_dir) ret = -ENOMEM; } } pr_debug("path: %s\n", lr->path); return (ret < 0) ? ret : lf.found; } /* * Find a src file from a DWARF tag path. Prepend optional source path prefix * and chop off leading directories that do not exist. Result is passed back as * a newly allocated path on success. * Return 0 if file was found and readable, -errno otherwise. */ int get_real_path(const char *raw_path, const char *comp_dir, char **new_path) { const char *prefix = symbol_conf.source_prefix; if (!prefix) { if (raw_path[0] != '/' && comp_dir) /* If not an absolute path, try to use comp_dir */ prefix = comp_dir; else { if (access(raw_path, R_OK) == 0) { *new_path = strdup(raw_path); return *new_path ? 0 : -ENOMEM; } else return -errno; } } *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); if (!*new_path) return -ENOMEM; for (;;) { sprintf(*new_path, "%s/%s", prefix, raw_path); if (access(*new_path, R_OK) == 0) return 0; if (!symbol_conf.source_prefix) { /* In case of searching comp_dir, don't retry */ zfree(new_path); return -errno; } switch (errno) { case ENAMETOOLONG: case ENOENT: case EROFS: case EFAULT: raw_path = strchr(++raw_path, '/'); if (!raw_path) { zfree(new_path); return -ENOENT; } continue; default: zfree(new_path); return -errno; } } }
/* Unified pubnames style output. The error checking here against maxoff may be useless (in that libdwarf may return an error if the offset is bad and we will not get called here). But we leave it in nonetheless as it looks sensible. In at least one gigantic executable such offsets turned out wrong. */ void print_pubname_style_entry(Dwarf_Debug dbg, char *line_title, char *name, Dwarf_Unsigned die_off, Dwarf_Unsigned cu_off, Dwarf_Unsigned global_cu_offset, Dwarf_Unsigned maxoff) { Dwarf_Die die = NULL; Dwarf_Off die_CU_off = 0; int dres = 0; int ddres = 0; int cudres = 0; char tmp_buf[100]; /* get die at die_off */ dres = dwarf_offdie(dbg, die_off, &die, &err); if (dres != DW_DLV_OK) { struct esb_s details; esb_constructor(&details); esb_append(&details,line_title); esb_append(&details," dwarf_offdie : " "die offset does not reference valid DIE. "); snprintf(tmp_buf,sizeof(tmp_buf),"0x%" DW_PR_DUx, die_off); esb_append(&details,tmp_buf); esb_append(&details,"."); print_error(dbg, esb_get_string(&details), dres, err); esb_destructor(&details); } /* get offset of die from its cu-header */ ddres = dwarf_die_CU_offset(die, &die_CU_off, &err); if (ddres != DW_DLV_OK) { struct esb_s details; esb_constructor(&details); esb_append(&details,line_title); esb_append(&details," cannot get CU die offset"); print_error(dbg, esb_get_string(&details), dres, err); esb_destructor(&details); die_CU_off = 0; } /* Get die at offset cu_off to check its existence. */ { Dwarf_Die cu_die = NULL; cudres = dwarf_offdie(dbg, cu_off, &cu_die, &err); if (cudres != DW_DLV_OK) { struct esb_s details; esb_constructor(&details); esb_append(&details,line_title); esb_append(&details," dwarf_offdie: " "cu die offset does not reference valid CU DIE. "); snprintf(tmp_buf,sizeof(tmp_buf),"0x%" DW_PR_DUx, cu_off); esb_append(&details,tmp_buf); esb_append(&details,"."); print_error(dbg, esb_get_string(&details), dres, err); esb_destructor(&details); } else { /* It exists, all is well. */ dwarf_dealloc(dbg, cu_die, DW_DLA_DIE); } } /* Display offsets */ if (display_offsets) { /* Print 'name'at the end for better layout */ printf("%s die-in-sect 0x%" DW_PR_XZEROS DW_PR_DUx ", cu-in-sect 0x%" DW_PR_XZEROS DW_PR_DUx "," " die-in-cu 0x%" DW_PR_XZEROS DW_PR_DUx ", cu-header-in-sect 0x%" DW_PR_XZEROS DW_PR_DUx , line_title, die_off, cu_off, (Dwarf_Unsigned) die_CU_off, /* Following is absolute offset of the ** beginning of the cu */ (Dwarf_Signed) (die_off - die_CU_off)); } if ((die_off - die_CU_off) != global_cu_offset) { printf(" error: real cuhdr 0x%" DW_PR_XZEROS DW_PR_DUx, global_cu_offset); exit(1); } /* Display offsets */ if (display_offsets && verbose) { printf(" cuhdr 0x%" DW_PR_XZEROS DW_PR_DUx , global_cu_offset); } /* Print 'name'at the end for better layout */ printf(" '%s'\n",name); dwarf_dealloc(dbg, die, DW_DLA_DIE); check_info_offset_sanity(line_title, "die offset", name, die_off, maxoff); check_info_offset_sanity(line_title, "die cu offset", name, die_CU_off, maxoff); check_info_offset_sanity(line_title, "cu offset", name, (die_off - die_CU_off), maxoff); }
/* Get all the data in .debug_pubnames */ void print_pubnames(Dwarf_Debug dbg) { Dwarf_Global *globbuf = NULL; Dwarf_Signed count = 0; Dwarf_Signed i = 0; Dwarf_Off die_off = 0; Dwarf_Off cu_off = 0; /* Offset to previous CU */ Dwarf_Off prev_cu_off = elf_max_address; char *name = 0; int res = 0; current_section_id = DEBUG_PUBNAMES; if (do_print_dwarf) { printf("\n.debug_pubnames\n"); } res = dwarf_get_globals(dbg, &globbuf, &count, &err); if (res == DW_DLV_ERROR) { print_error(dbg, "dwarf_get_globals", res, err); } else if (res == DW_DLV_NO_ENTRY) { /* (err == 0 && count == DW_DLV_NOCOUNT) means there are no pubnames. */ } else { Dwarf_Unsigned maxoff = get_info_max_offset(dbg); for (i = 0; i < count; i++) { int nres = 0; int cures3 = 0; Dwarf_Off global_cu_off = 0; nres = dwarf_global_name_offsets(globbuf[i], &name, &die_off, &cu_off, &err); deal_with_name_offset_err(dbg, "dwarf_global_name_offsets", name, die_off, nres, err); cures3 = dwarf_global_cu_offset(globbuf[i], &global_cu_off, &err); if (cures3 != DW_DLV_OK) { print_error(dbg, "dwarf_global_cu_offset", cures3, err); } if (check_pubname_attr) { Dwarf_Bool has_attr; int ares; int dres; Dwarf_Die die; /* We are processing a new set of pubnames for a different CU; get the producer ID, at 'cu_off' to see if we need to skip these pubnames */ if (cu_off != prev_cu_off) { /* Record offset for previous CU */ prev_cu_off = cu_off; dres = dwarf_offdie(dbg, cu_off, &die, &err); if (dres != DW_DLV_OK) { print_error(dbg, "print pubnames: dwarf_offdie a", dres,err); } { /* Get producer name for this CU and update compiler list */ struct esb_s producername; esb_constructor(&producername); get_producer_name(dbg,die,err,&producername); update_compiler_target(esb_get_string(&producername)); esb_destructor(&producername); } dwarf_dealloc(dbg, die, DW_DLA_DIE); } /* get die at die_off */ dres = dwarf_offdie(dbg, die_off, &die, &err); if (dres != DW_DLV_OK) { print_error(dbg, "print pubnames: dwarf_offdie b", dres, err); } ares = dwarf_hasattr(die, DW_AT_external, &has_attr, &err); if (ares == DW_DLV_ERROR) { print_error(dbg, "hassattr on DW_AT_external", ares, err); } /* Check for specific compiler */ if (checking_this_compiler()) { DWARF_CHECK_COUNT(pubname_attr_result,1); if (ares == DW_DLV_OK && has_attr) { /* Should the value of flag be examined? */ } else { DWARF_CHECK_ERROR2(pubname_attr_result,name, "pubname does not have DW_AT_external"); } } dwarf_dealloc(dbg, die, DW_DLA_DIE); } /* Now print pubname, after the test */ if (do_print_dwarf || (record_dwarf_error && check_verbose_mode)) { print_pubname_style_entry(dbg, "global", name, die_off, cu_off, global_cu_off, maxoff); record_dwarf_error = FALSE; /* Clear error condition */ } } dwarf_globals_dealloc(dbg, globbuf, count); } } /* print_pubnames() */
/* get all the data in .debug_aranges */ extern void print_aranges(Dwarf_Debug dbg) { Dwarf_Signed count = 0; Dwarf_Signed i = 0; Dwarf_Arange *arange_buf = NULL; int ares = 0; int aires = 0; Dwarf_Off prev_off = 0; /* Holds previous CU offset */ Dwarf_Bool first_cu = TRUE; Dwarf_Off cu_die_offset_prev = 0; /* Reset the global state, so we can traverse the debug_info */ seen_CU = FALSE; need_CU_name = TRUE; need_CU_base_address = TRUE; need_CU_high_address = TRUE; current_section_id = DEBUG_ARANGES; if (do_print_dwarf) { printf("\n.debug_aranges\n"); } ares = dwarf_get_aranges(dbg, &arange_buf, &count, &err); if (ares == DW_DLV_ERROR) { print_error(dbg, "dwarf_get_aranges", ares, err); } else if (ares == DW_DLV_NO_ENTRY) { /* no arange is included */ } else { for (i = 0; i < count; i++) { Dwarf_Unsigned segment = 0; Dwarf_Unsigned segment_entry_size = 0; Dwarf_Addr start = 0; Dwarf_Unsigned length = 0; Dwarf_Off cu_die_offset = 0; Dwarf_Die cu_die = NULL; aires = dwarf_get_arange_info_b(arange_buf[i], &segment, &segment_entry_size, &start, &length, &cu_die_offset, &err); if (aires != DW_DLV_OK) { print_error(dbg, "dwarf_get_arange_info", aires, err); } else { int dres; struct esb_s producer_name; esb_constructor(&producer_name); /* Get basic locations for error reporting */ dres = dwarf_offdie(dbg, cu_die_offset, &cu_die, &err); if (dres != DW_DLV_OK) { print_error(dbg, "dwarf_offdie", dres, err); } if (cu_name_flag) { if (should_skip_this_cu(dbg,cu_die,err)) { continue; } } /* Get producer name for this CU and update compiler list */ get_producer_name(dbg,cu_die,err,&producer_name); update_compiler_target(esb_get_string(&producer_name)); esb_destructor(&producer_name); if (!checking_this_compiler()) { continue; } if (check_aranges) { do_checking(dbg,arange_buf,i,cu_die_offset,first_cu, cu_die_offset_prev,cu_die); } /* Get the offset of the cu header itself in the section, but not for end-entries. */ if (start || length) { Dwarf_Off off = 0; int cures3 = dwarf_get_arange_cu_header_offset( arange_buf[i], &off, &err); if (cures3 != DW_DLV_OK) { print_error(dbg, "dwarf_get_cu_hdr_offset", cures3, err); } /* Print the CU information if different. */ if (prev_off != off || first_cu) { first_cu = FALSE; prev_off = off; /* We are faking the indent level. We do not know what level it is, really. If do_check_dwarf we do not want to do the die print call as it will do check/print we may not have asked for. And if we did ask for debug_info checks this will do the checks a second time! So only call print_one_die if printing. */ if (do_print_dwarf){ /* There is no die if its a set-end entry */ print_one_die(dbg, cu_die, /* print_information= */ (boolean2) TRUE, /* indent_level = */0, /* srcfiles= */ 0, /* cnt= */ 0, /* ignore_die_stack= */TRUE); } /* Reset the state, so we can traverse the debug_info */ seen_CU = FALSE; need_CU_name = TRUE; if (do_print_dwarf) { printf("\n"); } } if (do_print_dwarf) { /* Print current aranges record */ if (segment_entry_size) { printf( "\narange starts at seg,off 0x%" DW_PR_XZEROS DW_PR_DUx ",0x%" DW_PR_XZEROS DW_PR_DUx ", ", segment, (Dwarf_Unsigned)start); } else { printf("\narange starts at 0x%" DW_PR_XZEROS DW_PR_DUx ", ", (Dwarf_Unsigned)start); } printf("length of 0x%" DW_PR_XZEROS DW_PR_DUx ", cu_die_offset = 0x%" DW_PR_XZEROS DW_PR_DUx, length, (Dwarf_Unsigned)cu_die_offset); } if (verbose && do_print_dwarf) { printf(" cuhdr 0x%" DW_PR_XZEROS DW_PR_DUx "\n", (Dwarf_Unsigned)off); } dwarf_dealloc(dbg, cu_die, DW_DLA_DIE); cu_die = 0; } else { /* Must be a range end. We really do want to print this as there is a real record here, an 'arange end' record. */ if (do_print_dwarf) { printf("\narange end"); } }/* end start||length test */ } /* end aires DW_DLV_OK test */ /* print associated die too? */ dwarf_dealloc(dbg, arange_buf[i], DW_DLA_ARANGE); } dwarf_dealloc(dbg, arange_buf, DW_DLA_LIST); } }
/* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || die_compare_name(sp_die, pp->function) != 0) return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; param->retval = find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { /* Real function */ if (pp->lazy_line) param->retval = find_probe_point_lazy(sp_die, pf); else { if (dwarf_entrypc(sp_die, &pf->addr) != 0) { pr_warning("Failed to get entry pc of %s.\n", dwarf_diename(sp_die)); param->retval = -ENOENT; return DWARF_CB_ABORT; } pf->addr += pp->offset; /* TODO: Check the address in this function */ param->retval = convert_probe_point(sp_die, pf); } } else { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; /* Inlined function: search instances */ dwarf_func_inline_instances(sp_die, probe_point_inline_cb, &_param); param->retval = _param.retval; } return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ } static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); return _param.retval; } /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, struct kprobe_trace_event **tevs, int max_tevs) { struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; struct perf_probe_point *pp = &pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; int ret = 0; pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); if (pf.tevs == NULL) return -ENOMEM; *tevs = pf.tevs; pf.ntevs = 0; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) { pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); free(pf.tevs); *tevs = NULL; return -EBADF; } #if _ELFUTILS_PREREQ(0, 142) /* Get the call frame information from this dwarf */ pf.cfi = dwarf_getcfi(dbg); #endif off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && ret >= 0) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (pp->file) pf.fname = cu_find_realpath(&pf.cu_die, pp->file); else pf.fname = NULL; if (!pp->file || pf.fname) { if (pp->function) ret = find_probe_point_by_func(&pf); else if (pp->lazy_line) ret = find_probe_point_lazy(NULL, &pf); else { pf.lno = pp->line; ret = find_probe_point_by_line(&pf); } } off = noff; } line_list__free(&pf.lcache); dwarf_end(dbg); return (ret < 0) ? ret : pf.ntevs; } /* Reverse search */ int find_perf_probe_point(int fd, unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; Dwarf *dbg; Dwarf_Line *line; Dwarf_Addr laddr, eaddr; const char *tmp; int lineno, ret = 0; bool found = false; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) return -EBADF; /* Find cu die */ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { ret = -EINVAL; goto end; } /* Find a corresponding line */ line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); if (line) { if (dwarf_lineaddr(line, &laddr) == 0 && (Dwarf_Addr)addr == laddr && dwarf_lineno(line, &lineno) == 0) { tmp = dwarf_linesrc(line, NULL, NULL); if (tmp) { ppt->line = lineno; ppt->file = strdup(tmp); if (ppt->file == NULL) { ret = -ENOMEM; goto end; } found = true; } } } /* Find a corresponding function */ if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { tmp = dwarf_diename(&spdie); if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) goto end; if (ppt->line) { if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { /* addr in an inline function */ tmp = dwarf_diename(&indie); if (!tmp) goto end; ret = dwarf_decl_line(&indie, &lineno); } else { if (eaddr == addr) { /* Function entry */ lineno = ppt->line; ret = 0; } else ret = dwarf_decl_line(&spdie, &lineno); } if (ret == 0) { /* Make a relative line number */ ppt->line -= lineno; goto found; } } /* We don't have a line number, let's use offset */ ppt->offset = addr - (unsigned long)eaddr; found: ppt->function = strdup(tmp); if (ppt->function == NULL) { ret = -ENOMEM; goto end; } found = true; } end: dwarf_end(dbg); if (ret >= 0) ret = found ? 1 : 0; return ret; } /* Add a line and store the src path */ static int line_range_add_line(const char *src, unsigned int lineno, struct line_range *lr) { /* Copy real path */ if (!lr->path) { lr->path = strdup(src); if (lr->path == NULL) return -ENOMEM; } return line_list__add_line(&lr->line_list, lineno); } /* Search function declaration lines */ static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct line_finder *lf = param->data; const char *src; int lineno; src = dwarf_decl_file(sp_die); if (src && strtailcmp(src, lf->fname) != 0) return DWARF_CB_OK; if (dwarf_decl_line(sp_die, &lineno) != 0 || (lf->lno_s > lineno || lf->lno_e < lineno)) return DWARF_CB_OK; param->retval = line_range_add_line(src, lineno, lf->lr); if (param->retval < 0) return DWARF_CB_ABORT; return DWARF_CB_OK; } static int find_line_range_func_decl_lines(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); return param.retval; } /* Find line range from its line number */ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno, ret = 0; const char *src; Dwarf_Die die_mem; line_list__init(&lf->lr->line_list); if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { pr_warning("No source lines found in this CU.\n"); return -ENOENT; } /* Search probable lines on lines list */ for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); if (dwarf_lineno(line, &lineno) != 0 || (lf->lno_s > lineno || lf->lno_e < lineno)) continue; if (sp_die) { /* Address filtering 1: does sp_die include addr? */ if (dwarf_lineaddr(line, &addr) != 0 || !dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } /* TODO: Get fileno from line, but how? */ src = dwarf_linesrc(line, NULL, NULL); if (strtailcmp(src, lf->fname) != 0) continue; ret = line_range_add_line(src, lineno, lf->lr); if (ret < 0) return ret; } /* * Dwarf lines doesn't include function declarations. We have to * check functions list or given function. */ if (sp_die) { src = dwarf_decl_file(sp_die); if (src && dwarf_decl_line(sp_die, &lineno) == 0 && (lf->lno_s <= lineno && lf->lno_e >= lineno)) ret = line_range_add_line(src, lineno, lf->lr); } else ret = find_line_range_func_decl_lines(lf); /* Update status */ if (ret >= 0) if (!list_empty(&lf->lr->line_list)) ret = lf->found = 1; else ret = 0; /* Lines are not found */ else { free(lf->lr->path); lf->lr->path = NULL; } return ret; } static int line_range_inline_cb(Dwarf_Die *in_die, void *data) { struct dwarf_callback_param *param = data; param->retval = find_line_range_by_line(in_die, param->data); return DWARF_CB_ABORT; /* No need to find other instances */ } /* Search function from function name */ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) { struct dwarf_callback_param *param = data; struct line_finder *lf = param->data; struct line_range *lr = lf->lr; if (dwarf_tag(sp_die) == DW_TAG_subprogram && die_compare_name(sp_die, lr->function) == 0) { lf->fname = dwarf_decl_file(sp_die); dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; if (lf->lno_s < 0) /* Overflow */ lf->lno_s = INT_MAX; lf->lno_e = lr->offset + lr->end; if (lf->lno_e < 0) /* Overflow */ lf->lno_e = INT_MAX; pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); lr->start = lf->lno_s; lr->end = lf->lno_e; if (dwarf_func_inline(sp_die)) { struct dwarf_callback_param _param; _param.data = (void *)lf; _param.retval = 0; dwarf_func_inline_instances(sp_die, line_range_inline_cb, &_param); param->retval = _param.retval; } else param->retval = find_line_range_by_line(sp_die, lf); return DWARF_CB_ABORT; } return DWARF_CB_OK; } static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); return param.retval; } int find_line_range(int fd, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) { pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); return -EBADF; } /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (lr->file) lf.fname = cu_find_realpath(&lf.cu_die, lr->file); else lf.fname = 0; if (!lr->file || lf.fname) { if (lr->function) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } off = noff; } pr_debug("path: %lx\n", (unsigned long)lr->path); dwarf_end(dbg); return (ret < 0) ? ret : lf.found; }
static int get_type(Dwarf_Debug dbg, Dwarf_Die die) { Dwarf_Attribute attr; Dwarf_Off off; Dwarf_Die type_die; Dwarf_Half tag; int ret; ret = dwarf_attr(die, DW_AT_type, &attr, NULL); if (ret == DW_DLV_ERROR) { fprintf(stderr, "SET dwarf: Error in dwarf_attr()\n"); return 1; } if (ret == DW_DLV_NO_ENTRY) return 0; /* "DW_AT_type" is of the form "DW_FORM_ref4" */ ret = dwarf_global_formref(attr, &off, NULL); dwarf_dealloc(dbg, attr, DW_DLA_ATTR); if (ret != DW_DLV_OK) { fprintf(stderr, "SET dwarf: Error in dwarf_global_formref()\n"); return 1; } /* Get the referenced DIE which is the type of "die" */ ret = dwarf_offdie(dbg, off, &type_die, NULL); if (ret != DW_DLV_OK) { fprintf(stderr, "SET dwarf: Error in dwarf_offdie()\n"); return 1; } /* Get the type */ ret = dwarf_tag(type_die, &tag, NULL); if (ret != DW_DLV_OK) { fprintf(stderr, "SET dwarf: Error in dwarf_tag()\n"); dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } switch (tag) { case DW_TAG_base_type: ret = get_size(dbg, type_die); if (ret != 0) { dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } break; case DW_TAG_pointer_type: /* Recursive call */ /* FIXME */ // get_type(dbg, type_die); ret = get_size(dbg, type_die); if (ret != 0) { dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } break; case DW_TAG_structure_type: ret = get_size(dbg, type_die); if (ret != 0) { dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } break; case DW_TAG_union_type: ret = get_size(dbg, type_die); if (ret != 0) { dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } break; case DW_TAG_enumeration_type: ret = get_size(dbg, type_die); if (ret != 0) { dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } break; /* The types above have "DW_AT_byte_size" */ case DW_TAG_subroutine_type: /* function pointer */ /* FIXME: Can be transformed to the real function by runtime address */ break; case DW_TAG_typedef: case DW_TAG_const_type: case DW_TAG_volatile_type: ret = get_type(dbg, type_die); if (ret != 0) { dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 1; } break; default: dwarf_dealloc(dbg, type_die, DW_DLA_DIE); fprintf(stderr, "SET dwarf: Unspecified type\n"); return 1; } dwarf_dealloc(dbg, type_die, DW_DLA_DIE); return 0; }
int main (int argc, char *argv[]) { for (int i = 1; i < argc; ++i) { int fd = open (argv[i], O_RDONLY); Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ); if (dbg != NULL) { Dwarf_Off off = 0; size_t cuhl; Dwarf_Off noff; while (dwarf_nextcu (dbg, off, &noff, &cuhl, NULL, NULL, NULL) == 0) { Dwarf_Die die_mem; Dwarf_Die *die = dwarf_offdie (dbg, off + cuhl, &die_mem); Dwarf_Die iter_mem; Dwarf_Die *iter = &iter_mem; dwarf_child (die, &iter_mem); while (1) { if (dwarf_tag (iter) == DW_TAG_variable) { Dwarf_Attribute attr_mem; Dwarf_Die form_mem; dwarf_formref_die (dwarf_attr (iter, DW_AT_type, &attr_mem), &form_mem); } if (dwarf_siblingof (iter, &iter_mem) != 0) break; } off = noff; } off = 0; uint64_t type_sig; while (dwarf_next_unit (dbg, off, &noff, &cuhl, NULL, NULL, NULL, NULL, &type_sig, NULL) == 0) { Dwarf_Die die_mem; Dwarf_Die *die = dwarf_offdie_types (dbg, off + cuhl, &die_mem); if (die == NULL) printf ("fail\n"); else printf ("ok\n"); off = noff; } dwarf_end (dbg); } close (fd); } }
static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); return param.retval; } int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; const char *comp_dir; /* Fastpath: lookup by function name from .debug_pubnames section */ if (lr->function) { struct pubname_callback_param pubname_param = { .function = lr->function, .file = lr->file, .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; struct dwarf_callback_param line_range_param = { .data = (void *)&lf, .retval = 0}; dwarf_getpubnames(self->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { line_range_search_cb(&lf.sp_die, &line_range_param); if (lf.found) goto found; } } /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >= 0) { if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); if (!diep) continue; /* Check if target file is included. */ if (lr->file) lf.fname = cu_find_realpath(&lf.cu_die, lr->file); else lf.fname = 0; if (!lr->file || lf.fname) { if (lr->function) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } off = noff; } found: /* Store comp_dir */ if (lf.found) { comp_dir = cu_get_comp_dir(&lf.cu_die); if (comp_dir) { lr->comp_dir = strdup(comp_dir); if (!lr->comp_dir) ret = -ENOMEM; } } pr_debug("path: %s\n", lr->path); return (ret < 0) ? ret : lf.found; }
/** * Finds a function name in DWARF DIE (Debug Information Entry). * For more info on DWARF format, see http://www.dwarfstd.org/Download.php , http://www.ibm.com/developerworks/library/os-debugging/ * * @return true if we need to stop search (i.e. either found it or some error happened) */ bool FindFunctionNameInDIE(Dwarf_Debug DebugInfo, Dwarf_Die Die, Dwarf_Addr Addr, const char **OutFuncName) { Dwarf_Error ErrorInfo; Dwarf_Half Tag; Dwarf_Unsigned LowerPC, HigherPC; char *TempFuncName; int ReturnCode; if (dwarf_tag(Die, &Tag, &ErrorInfo) != DW_DLV_OK || Tag != DW_TAG_subprogram || dwarf_attrval_unsigned(Die, DW_AT_low_pc, &LowerPC, &ErrorInfo) != DW_DLV_OK || dwarf_attrval_unsigned(Die, DW_AT_high_pc, &HigherPC, &ErrorInfo) != DW_DLV_OK || Addr < LowerPC || HigherPC <= Addr ) { return false; } // found it *OutFuncName = NULL; Dwarf_Attribute SubAt; ReturnCode = dwarf_attr(Die, DW_AT_name, &SubAt, &ErrorInfo); if (ReturnCode == DW_DLV_ERROR) { return true; // error, but stop the search } else if (ReturnCode == DW_DLV_OK) { if (dwarf_formstring(SubAt, &TempFuncName, &ErrorInfo)) { *OutFuncName = NULL; } else { *OutFuncName = TempFuncName; } return true; } // DW_AT_Name is not present, look in DW_AT_specification Dwarf_Attribute SpecAt; if (dwarf_attr(Die, DW_AT_specification, &SpecAt, &ErrorInfo)) { // not found, tough luck return false; } Dwarf_Off Offset; if (dwarf_global_formref(SpecAt, &Offset, &ErrorInfo)) { return false; } Dwarf_Die SpecDie; if (dwarf_offdie(DebugInfo, Offset, &SpecDie, &ErrorInfo)) { return false; } if (dwarf_attrval_string(SpecDie, DW_AT_name, OutFuncName, &ErrorInfo)) { *OutFuncName = NULL; } return true; }
static int find_line_range_by_func(struct line_finder *lf) { struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); return param.retval; } int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; const char *comp_dir; if (lr->function) { struct pubname_callback_param pubname_param = { .function = lr->function, .file = lr->file, .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; struct dwarf_callback_param line_range_param = { .data = (void *)&lf, .retval = 0}; dwarf_getpubnames(self->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { line_range_search_cb(&lf.sp_die, &line_range_param); if (lf.found) goto found; } } while (!lf.found && ret >= 0) { if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); if (!diep) continue; if (lr->file) lf.fname = cu_find_realpath(&lf.cu_die, lr->file); else lf.fname = 0; if (!lr->file || lf.fname) { if (lr->function) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } off = noff; } found: if (lf.found) { comp_dir = cu_get_comp_dir(&lf.cu_die); if (comp_dir) { lr->comp_dir = strdup(comp_dir); if (!lr->comp_dir) ret = -ENOMEM; } } pr_debug("path: %s\n", lr->path); return (ret < 0) ? ret : lf.found; }
/* simple but too slow */ struct dwarf_subprogram_t *read_from_globals(Dwarf_Debug dbg) { Dwarf_Global *globals = NULL; Dwarf_Signed nglobals; Dwarf_Off offset; Dwarf_Die die; Dwarf_Addr lowpc = 0; Dwarf_Addr highpc = 0; Dwarf_Error err; Dwarf_Attribute attrib = 0; struct dwarf_subprogram_t *subprograms = NULL; struct dwarf_subprogram_t *subprogram = NULL; char *name; int i; int ret; ret = dwarf_get_globals(dbg, &globals, &nglobals, &err); DWARF_ASSERT(ret, err); if (ret != DW_DLV_OK) fatal("unable to get dwarf globals"); for (i = 0; i < nglobals; i++) { ret = dwarf_global_die_offset(globals[i], &offset, &err); DWARF_ASSERT(ret, err); /* TODO: this function does a linear search, making it pretty damn * slow.. see libdwarf/dwarf_die_deliv.c:_dwarf_find_CU_Context * for details */ ret = dwarf_offdie(dbg, offset, &die, &err); DWARF_ASSERT(ret, err); ret = dwarf_lowpc(die, &lowpc, &err); DWARF_ASSERT(ret, err); ret = dwarf_highpc(die, &highpc, &err); DWARF_ASSERT(ret, err); /* TODO: when would these not be defined? */ if (lowpc && highpc) { subprogram = malloc(sizeof(*subprogram)); if (!subprogram) fatal("unable to allocate memory for subprogram"); memset(subprogram, 0, sizeof(*subprogram)); ret = dwarf_attr(die, DW_AT_MIPS_linkage_name, &attrib, &err); if (ret == DW_DLV_OK) { ret = dwarf_formstring(attrib, &name, &err); DWARF_ASSERT(ret, err); dwarf_dealloc(dbg, attrib, DW_DLA_ATTR); } else { ret = dwarf_globname(globals[i], &name, &err); DWARF_ASSERT(ret, err); } subprogram->lowpc = lowpc; subprogram->highpc = highpc; subprogram->name = name; subprogram->next = subprograms; subprograms = subprogram; } dwarf_dealloc(dbg, die, DW_DLA_DIE); } return subprograms; }
int main (int argc, char *argv[]) { int result = 0; int cnt; for (cnt = 1; cnt < argc; ++cnt) { int fd = open (argv[cnt], O_RDONLY); Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ); if (dbg == NULL) { printf ("%s not usable\n", argv[cnt]); result = 1; if (fd != -1) close (fd); continue; } Dwarf_Off o = 0; Dwarf_Off ncu; Dwarf_Off ao; size_t cuhl; uint8_t asz; uint8_t osz; while (dwarf_nextcu (dbg, o, &ncu, &cuhl, &ao, &asz, &osz) == 0) { printf ("cuhl = %zu, o = %llu, asz = %hhu, osz = %hhu, ncu = %llu\n", cuhl, (unsigned long long int) ao, asz, osz, (unsigned long long int) ncu); Dwarf_Die die_mem; Dwarf_Die *die = dwarf_offdie (dbg, o + cuhl, &die_mem); if (die == NULL) { printf ("%s: cannot get CU die\n", argv[cnt]); result = 1; break; } Dwarf_Files *files; size_t nfiles; if (dwarf_getsrcfiles (die, &files, &nfiles) != 0) { printf ("%s: cannot get files\n", argv[cnt]); result = 1; break; } for (int i = 0; i < nfiles; ++i) printf (" file[%d] = \"%s\"\n", i, dwarf_filesrc (files, i, NULL, NULL)); o = ncu; } dwarf_end (dbg); close (fd); } return result; }
int main(int argc, char **argv) { Dwarf_Off off, lastoff; Dwarf *dw; size_t hdr_size; int cufd, error; argv0 = argv[0]; if (argc < 3) usage(); structname = argv[1]; binary = argv[2]; elf_version(EV_CURRENT); cufd = open(binary, O_RDONLY); if (cufd == -1) err(EX_USAGE, "open"); dw = dwarf_begin(cufd, DWARF_C_READ); if (dw == NULL) { error = dwarf_errno(); if (error == DWARF_E_NO_REGFILE) errx(EX_USAGE, "%s: Not a regular file", binary); dwarf_err_errno(EX_DATAERR, error, "dwarf_begin"); } get_elf_pointer_size(dw); /* XXX worry about .debug_types sections later. */ lastoff = off = 0; while (dwarf_nextcu(dw, off, &off, &hdr_size, NULL, NULL, NULL) == 0) { Dwarf_Die cu_die, die; int x; if (dwarf_offdie(dw, lastoff + hdr_size, &cu_die) == NULL) continue; lastoff = off; /* * A CU may be empty because e.g. an empty (or fully #if0'd) * file is compiled. */ if (dwarf_child(&cu_die, &die)) continue; /* Loop through all DIEs in the CU. */ do { if (isstruct(dwarf_tag(&die)) && dwarf_haschildren(&die) && dwarf_diename(&die) && strcmp(dwarf_diename(&die), structname) == 0) { structprobe(dw, &die); goto out; } } while ((x = dwarf_siblingof(&die, &die)) == 0); if (x == -1) dwarf_err(EX_DATAERR, "dwarf_siblingof"); } out: if (dwarf_end(dw)) dwarf_err(EX_SOFTWARE, "dwarf_end"); return (EX_OK); }
static int dwarf_producer(const char *file) { FILE *fp = fopen(file, "rb"); struct stat statbuf; if (lstat(file, &statbuf)) { perror(file); return -1; } size_t size = statbuf.st_size; unsigned char *buffer = (unsigned char *)malloc(size); if (fread(buffer, size, 1, fp) < sizeof buffer) { } int sfd = shm_open("something", O_CREAT | O_RDWR, 0); if (ftruncate (sfd, size) < 0) { perror(file); return -2; } // we could do without mmap! void *addr = mmap ( NULL , size, PROT_READ | PROT_WRITE, MAP_SHARED , sfd, 0); memcpy(addr, buffer, size); /* int fd = open(file, O_RDONLY); if (fd < 0) { error(0, errno, "open '%s' failed", file); return -1; } Dwarf *dw = dwarf_begin(fd, DWARF_C_READ); */ Dwarf *dw = dwarf_begin(sfd, DWARF_C_READ); if (dw == NULL) { error(0, 0, "dwarf_begin '%s': %s", file, dwarf_errmsg(-1)); return -1; } bool producers_found = false; Dwarf_Off cuoffset; Dwarf_Off ncuoffset = 0; size_t hsize; while (dwarf_nextcu(dw, cuoffset = ncuoffset, &ncuoffset, &hsize, NULL, NULL, NULL) == 0) { Dwarf_Off cudieoff = cuoffset + hsize; Dwarf_Die cudie; if (dwarf_offdie(dw, cudieoff, &cudie) == NULL) { error(0, 0, "Empty CU in '%s' at offset %" PRIx64 ": %s", file, cudieoff, dwarf_errmsg(-1)); continue; } const char *name = dwarf_diename(&cudie); Dwarf_Attribute attr; const char *producer; if (dwarf_attr(&cudie, DW_AT_producer, &attr) == NULL) continue; producer = dwarf_formstring(&attr); if (producer == NULL) { error(0, 0, "Couldn't get producer for CU in '%s' at offset %" PRIx64 ": %s", file, cudieoff, dwarf_errmsg(-1)); continue; } printf("%s CU [%" PRIx64 "] %s: %s\n", file, cudieoff, (name ?: "<unknown>"), producer); producers_found = true; } if (!producers_found) { error(0, 0, "no DW_AT_producers found in '%s'", file); return -1; } // cleanup munmap(addr, size); close(sfd); shm_unlink("something"); fclose(fp); return 0; }
static int find_probe_point_by_func(struct probe_finder *pf) { struct dwarf_callback_param _param = {.data = (void *)pf, .retval = 0}; dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); return _param.retval; } struct pubname_callback_param { char *function; char *file; Dwarf_Die *cu_die; Dwarf_Die *sp_die; int found; }; static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) { struct pubname_callback_param *param = data; if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) return DWARF_CB_OK; if (die_compare_name(param->sp_die, param->function)) { if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) return DWARF_CB_OK; if (param->file && strtailcmp(param->file, dwarf_decl_file(param->sp_die))) return DWARF_CB_OK; param->found = 1; return DWARF_CB_ABORT; } } return DWARF_CB_OK; } static int debuginfo__find_probes(struct debuginfo *self, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; int ret = 0; #if _ELFUTILS_PREREQ(0, 142) pf->cfi = dwarf_getcfi(self->dbg); #endif off = 0; line_list__init(&pf->lcache); if (pp->function) { struct pubname_callback_param pubname_param = { .function = pp->function, .file = pp->file, .cu_die = &pf->cu_die, .sp_die = &pf->sp_die, .found = 0, }; struct dwarf_callback_param probe_param = { .data = pf, }; dwarf_getpubnames(self->dbg, pubname_search_cb, &pubname_param, 0); if (pubname_param.found) { ret = probe_point_search_cb(&pf->sp_die, &probe_param); if (ret) goto found; } } while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); if (!diep) continue; if (pp->file) pf->fname = cu_find_realpath(&pf->cu_die, pp->file); else pf->fname = NULL; if (!pp->file || pf->fname) { if (pp->function) ret = find_probe_point_by_func(pf); else if (pp->lazy_line) ret = find_probe_point_lazy(NULL, pf); else { pf->lno = pp->line; ret = find_probe_point_by_line(pf); } if (ret < 0) break; } off = noff; } found: line_list__free(&pf->lcache); return ret; } static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) { struct trace_event_finder *tf = container_of(pf, struct trace_event_finder, pf); struct probe_trace_event *tev; int ret, i; if (tf->ntevs == tf->max_tevs) { pr_warning("Too many( > %d) probe point found.\n", tf->max_tevs); return -ERANGE; } tev = &tf->tevs[tf->ntevs++]; ret = convert_to_trace_point(&pf->sp_die, pf->addr, pf->pev->point.retprobe, &tev->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, tev->point.offset); tev->nargs = pf->pev->nargs; tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); if (tev->args == NULL) return -ENOMEM; for (i = 0; i < pf->pev->nargs; i++) { pf->pvar = &pf->pev->args[i]; pf->tvar = &tev->args[i]; ret = find_variable(sc_die, pf); if (ret != 0) return ret; } return 0; } int debuginfo__find_trace_events(struct debuginfo *self, struct perf_probe_event *pev, struct probe_trace_event **tevs, int max_tevs) { struct trace_event_finder tf = { .pf = {.pev = pev, .callback = add_probe_trace_event}, .max_tevs = max_tevs}; int ret; *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); if (*tevs == NULL) return -ENOMEM; tf.tevs = *tevs; tf.ntevs = 0; ret = debuginfo__find_probes(self, &tf.pf); if (ret < 0) { free(*tevs); *tevs = NULL; return ret; } return (ret < 0) ? ret : tf.ntevs; } #define MAX_VAR_LEN 64 static int collect_variables_cb(Dwarf_Die *die_mem, void *data) { struct available_var_finder *af = data; struct variable_list *vl; char buf[MAX_VAR_LEN]; int tag, ret; vl = &af->vls[af->nvls - 1]; tag = dwarf_tag(die_mem); if (tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) { ret = convert_variable_location(die_mem, af->pf.addr, af->pf.fb_ops, NULL); if (ret == 0) { ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); pr_debug2("Add new var: %s\n", buf); if (ret > 0) strlist__add(vl->vars, buf); } } if (af->child && dwarf_haspc(die_mem, af->pf.addr)) return DIE_FIND_CB_CONTINUE; else return DIE_FIND_CB_SIBLING; } static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) { struct available_var_finder *af = container_of(pf, struct available_var_finder, pf); struct variable_list *vl; Dwarf_Die die_mem; int ret; if (af->nvls == af->max_vls) { pr_warning("Too many( > %d) probe point found.\n", af->max_vls); return -ERANGE; } vl = &af->vls[af->nvls++]; ret = convert_to_trace_point(&pf->sp_die, pf->addr, pf->pev->point.retprobe, &vl->point); if (ret < 0) return ret; pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, vl->point.offset); vl->vars = strlist__new(true, NULL); if (vl->vars == NULL) return -ENOMEM; af->child = true; die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); if (!af->externs) goto out; af->child = false; die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); out: if (strlist__empty(vl->vars)) { strlist__delete(vl->vars); vl->vars = NULL; } return ret; } int debuginfo__find_available_vars_at(struct debuginfo *self, struct perf_probe_event *pev, struct variable_list **vls, int max_vls, bool externs) { struct available_var_finder af = { .pf = {.pev = pev, .callback = add_available_vars}, .max_vls = max_vls, .externs = externs}; int ret; *vls = zalloc(sizeof(struct variable_list) * max_vls); if (*vls == NULL) return -ENOMEM; af.vls = *vls; af.nvls = 0; ret = debuginfo__find_probes(self, &af.pf); if (ret < 0) { while (af.nvls--) { if (af.vls[af.nvls].point.symbol) free(af.vls[af.nvls].point.symbol); if (af.vls[af.nvls].vars) strlist__delete(af.vls[af.nvls].vars); } free(af.vls); *vls = NULL; return ret; } return (ret < 0) ? ret : af.nvls; }
/* Get all the data in .debug_static_funcs On error, this allows some dwarf memory leaks. */ extern void print_static_funcs(Dwarf_Debug dbg) { Dwarf_Func *funcbuf = NULL; Dwarf_Signed count = 0; Dwarf_Signed i = 0; Dwarf_Off die_off = 0; Dwarf_Off cu_off = 0; int gfres = 0; printf("\n.debug_static_func\n"); gfres = dwarf_get_funcs(dbg, &funcbuf, &count, &err); if (gfres == DW_DLV_ERROR) { print_error(dbg, "dwarf_get_funcs", gfres, err); } else if (gfres == DW_DLV_NO_ENTRY) { /* no static funcs */ } else { Dwarf_Unsigned maxoff = get_info_max_offset(dbg); for (i = 0; i < count; i++) { int fnres; int cures3; Dwarf_Unsigned global_cu_off = 0; char *name = 0; fnres = dwarf_func_name_offsets(funcbuf[i], &name, &die_off, &cu_off, &err); deal_with_name_offset_err(dbg, "dwarf_func_name_offsets", name, die_off, fnres, err); cures3 = dwarf_func_cu_offset(funcbuf[i], &global_cu_off, &err); if (cures3 != DW_DLV_OK) { print_error(dbg, "dwarf_global_cu_offset", cures3, err); } print_pubname_style_entry(dbg, "static-func", name, die_off, cu_off, global_cu_off, maxoff); /* print associated die too? */ if (check_pubname_attr) { Dwarf_Bool has_attr; int ares; int dres; Dwarf_Die die; /* get die at die_off */ dres = dwarf_offdie(dbg, die_off, &die, &err); if (dres != DW_DLV_OK) { print_error(dbg, "dwarf_offdie", dres, err); } ares = dwarf_hasattr(die, DW_AT_external, &has_attr, &err); if (ares == DW_DLV_ERROR) { print_error(dbg, "hassattr on DW_AT_external", ares, err); } pubname_attr_result.checks++; if (ares == DW_DLV_OK && has_attr) { /* Should the value of flag be examined? */ } else { DWARF_CHECK_ERROR2(pubname_attr_result,name, "pubname does not have DW_AT_external") } dwarf_dealloc(dbg, die, DW_DLA_DIE); } } dwarf_funcs_dealloc(dbg, funcbuf, count); } } /* print_static_funcs() */