/* Get a Dwarf from offline image */ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, const char *path) { int fd; fd = open(path, O_RDONLY); if (fd < 0) return fd; dbg->dwfl = dwfl_begin(&offline_callbacks); if (!dbg->dwfl) goto error; dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); else close(fd); memset(dbg, 0, sizeof(*dbg)); return -ENOENT; }
/* Get a Dwarf from live kernel image */ static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, Dwarf_Addr *bias) { Dwarf *dbg; if (!dwflp) return NULL; *dwflp = dwfl_begin(&kernel_callbacks); if (!*dwflp) return NULL; /* Load the kernel dwarves: Don't care the result here */ dwfl_linux_kernel_report_kernel(*dwflp); dwfl_linux_kernel_report_modules(*dwflp); dbg = dwfl_addrdwarf(*dwflp, addr, bias); /* Here, check whether we could get a real dwarf */ if (!dbg) { pr_debug("Failed to find kernel dwarf at %lx\n", (unsigned long)addr); dwfl_end(*dwflp); *dwflp = NULL; } return dbg; }
static int debuginfo__init_offline_dwarf(struct debuginfo *self, const char *path) { Dwfl_Module *mod; int fd; fd = open(path, O_RDONLY); if (fd < 0) return fd; self->dwfl = dwfl_begin(&offline_callbacks); if (!self->dwfl) goto error; mod = dwfl_report_offline(self->dwfl, "", "", fd); if (!mod) goto error; self->dbg = dwfl_module_getdwarf(mod, &self->bias); if (!self->dbg) goto error; return 0; error: if (self->dwfl) dwfl_end(self->dwfl); else close(fd); memset(self, 0, sizeof(*self)); return -ENOENT; }
int main (int argc, char **argv) { char* args[3]; int res = 0; Dwfl *dwfl; Dwarf_Addr bias; if (argc != 2) fprintf(stderr, "Usage %s <file>", argv[0]); // Pretend "dwarfsrcfiles -e <file>" was given, so we can use standard // dwfl argp parser to open the file for us and get our Dwfl. Useful // in case argument is an ET_REL file (like kernel modules). libdwfl // will fix up relocations for us. args[0] = argv[0]; args[1] = "-e"; args[2] = argv[1]; argp_parse (dwfl_standard_argp (), 3, args, 0, NULL, &dwfl); Dwarf_Die *cu = NULL; while ((cu = dwfl_nextcu (dwfl, cu, &bias)) != NULL) res |= process_cu (cu); dwfl_end (dwfl); return res; }
void debuginfo__delete(struct debuginfo *dbg) { if (dbg) { if (dbg->dwfl) dwfl_end(dbg->dwfl); free(dbg); } }
void debuginfo__delete(struct debuginfo *self) { if (self) { if (self->dwfl) dwfl_end(self->dwfl); free(self); } }
void refl_end (struct refl *refl) { if (refl != NULL) { dwfl_end (refl->dwfl); } free (refl); }
int main (int argc, char *argv[]) { int remaining; /* Set locale. */ (void) setlocale (LC_ALL, ""); Dwfl *dwfl = NULL; (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &dwfl); assert (dwfl != NULL); int result = 0; /* Now handle the addresses. In case none are given on the command line, read from stdin. */ if (remaining == argc) { /* We use no threads here which can interfere with handling a stream. */ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); char *buf = NULL; size_t len = 0; while (!feof_unlocked (stdin)) { if (getline (&buf, &len, stdin) < 0) break; char *endp; uintmax_t addr = strtoumax (buf, &endp, 0); if (endp != buf) result |= handle_address (addr, dwfl); else result = 1; } free (buf); } else { do { char *endp; uintmax_t addr = strtoumax (argv[remaining], &endp, 0); if (endp != argv[remaining]) result |= handle_address (addr, dwfl); else result = 1; } while (++remaining < argc); } dwfl_end (dwfl); return result; }
// Create a libdw session with DWARF information for all loaded modules LibdwSession *libdwInit() { LibdwSession *session = stgCallocBytes(1, sizeof(LibdwSession), "libdwInit"); // Initialize ELF library if (elf_version(EV_CURRENT) == EV_NONE) { sysErrorBelch("libelf version too old!"); return NULL; } // Initialize a libdwfl session static char *debuginfo_path; static const Dwfl_Callbacks proc_callbacks = { .find_debuginfo = dwfl_standard_find_debuginfo, .debuginfo_path = &debuginfo_path, .find_elf = dwfl_linux_proc_find_elf, }; session->dwfl = dwfl_begin (&proc_callbacks); if (session->dwfl == NULL) { sysErrorBelch("dwfl_begin failed: %s", dwfl_errmsg(dwfl_errno())); free(session); return NULL; } // Report the loaded modules int ret = dwfl_linux_proc_report(session->dwfl, getpid()); if (ret < 0) { sysErrorBelch("dwfl_linux_proc_report failed: %s", dwfl_errmsg(dwfl_errno())); goto fail; } if (dwfl_report_end (session->dwfl, NULL, NULL) != 0) { sysErrorBelch("dwfl_report_end failed: %s", dwfl_errmsg(dwfl_errno())); goto fail; } pid_t pid = getpid(); if (! dwfl_attach_state(session->dwfl, NULL, pid, &thread_cbs, NULL)) { sysErrorBelch("dwfl_attach_state failed: %s", dwfl_errmsg(dwfl_errno())); goto fail; } return session; fail: dwfl_end(session->dwfl); free(session); return NULL; }
int main (void) { Dwfl *dwfl = dwfl_begin (&callbacks); for (int i = 0; i < 5; ++i) { dwfl_report_begin (dwfl); dwfl_report_module (dwfl, "module1", 0, 10); dwfl_report_end (dwfl, NULL, NULL); } dwfl_end (dwfl); return 0; }
int main(void) { Dwfl *dw; Dwfl_Module *mod; Dwarf_Addr bias; Dwarf_Die *die = NULL; dw = dwfl_begin(&cb); if (!dw) errx(1, "dwfl_begin %s", dwfl_errmsg(-1)); mod = dwfl_report_offline(dw, "", "tst", -1); if (!mod) errx(1, "dwfl_begin %s", dwfl_errmsg(-1)); while ((die = dwfl_nextcu(dw, die, &bias))) { print_die_rec(die, -1); printf("\n"); } dwfl_end(dw); return 0; }
/* Get a Dwarf from live kernel image */ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, Dwarf_Addr addr) { self->dwfl = dwfl_begin(&kernel_callbacks); if (!self->dwfl) return -EINVAL; /* Load the kernel dwarves: Don't care the result here */ dwfl_linux_kernel_report_kernel(self->dwfl); dwfl_linux_kernel_report_modules(self->dwfl); self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); /* Here, check whether we could get a real dwarf */ if (!self->dbg) { pr_debug("Failed to find kernel dwarf at %lx\n", (unsigned long)addr); dwfl_end(self->dwfl); memset(self, 0, sizeof(*self)); return -ENOENT; } return 0; }
static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, Dwarf_Addr addr) { self->dwfl = dwfl_begin(&kernel_callbacks); if (!self->dwfl) return -EINVAL; dwfl_linux_kernel_report_kernel(self->dwfl); dwfl_linux_kernel_report_modules(self->dwfl); self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); if (!self->dbg) { pr_debug("Failed to find kernel dwarf at %lx\n", (unsigned long)addr); dwfl_end(self->dwfl); memset(self, 0, sizeof(*self)); return -ENOENT; } return 0; }
void core_handle_free(struct core_handle *ch) { if (ch) { struct exe_mapping_data *seg = ch->segments, *next; while (seg) { free(seg->filename); next = seg->next; free(seg); seg = next; } if (ch->dwfl) dwfl_end(ch->dwfl); if (ch->eh) elf_end(ch->eh); if (ch->fd > 0) close(ch->fd); free(ch); } }
/* Get a Dwarf from offline image */ static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) { Dwfl_Module *mod; Dwarf *dbg = NULL; if (!dwflp) return NULL; *dwflp = dwfl_begin(&offline_callbacks); if (!*dwflp) return NULL; mod = dwfl_report_offline(*dwflp, "", "", fd); if (!mod) goto error; dbg = dwfl_module_getdwarf(mod, bias); if (!dbg) { error: dwfl_end(*dwflp); *dwflp = NULL; } return dbg; }
int main (int argc, char *argv[]) { int remaining; /* Set locale. */ (void) setlocale (LC_ALL, ""); struct args a = { .dwfl = NULL, .cu = NULL }; (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &a.dwfl); assert (a.dwfl != NULL); a.argv = &argv[remaining]; int result = 0; while ((a.cu = dwfl_nextcu (a.dwfl, a.cu, &a.dwbias)) != NULL) dwarf_getfuncs (a.cu, &handle_function, &a, 0); dwfl_end (a.dwfl); return result; }
struct core_handle * open_coredump(const char *elf_file, const char *exe_file, char **error_msg) { struct core_handle *ch = sr_mallocz(sizeof(*ch)); struct exe_mapping_data *head = NULL, **tail = &head; /* Initialize libelf, open the file and get its Elf handle. */ if (elf_version(EV_CURRENT) == EV_NONE) { set_error_elf("elf_version"); goto fail_free; } /* Open input file, and parse it. */ ch->fd = open(elf_file, O_RDONLY); if (ch->fd < 0) { set_error("Unable to open '%s': %s", elf_file, strerror(errno)); goto fail_free; } ch->eh = elf_begin(ch->fd, ELF_C_READ_MMAP, NULL); if (ch->eh == NULL) { set_error_elf("elf_begin"); goto fail_close; } /* Check that we are working with a coredump. */ GElf_Ehdr ehdr; if (gelf_getehdr(ch->eh, &ehdr) == NULL || ehdr.e_type != ET_CORE) { set_error("File '%s' is not a coredump", elf_file); goto fail_elf; } executable_file = exe_file; ch->cb.find_elf = find_elf_core; ch->cb.find_debuginfo = find_debuginfo_none; ch->cb.section_address = dwfl_offline_section_address; ch->dwfl = dwfl_begin(&ch->cb); #if _ELFUTILS_PREREQ(0, 158) if (dwfl_core_file_report(ch->dwfl, ch->eh, exe_file) == -1) #else if (dwfl_core_file_report(ch->dwfl, ch->eh) == -1) #endif { set_error_dwfl("dwfl_core_file_report"); goto fail_dwfl; } if (dwfl_report_end(ch->dwfl, NULL, NULL) != 0) { set_error_dwfl("dwfl_report_end"); goto fail_dwfl; } /* needed so that module filenames are available during unwinding */ ptrdiff_t ret = dwfl_getmodules(ch->dwfl, touch_module, &tail, 0); if (ret == -1) { set_error_dwfl("dwfl_getmodules"); goto fail_dwfl; } ch->segments = head; if (!head) { if (error_msg && !*error_msg) set_error("No segments found in coredump '%s'", elf_file); goto fail_dwfl; } return ch; fail_dwfl: dwfl_end(ch->dwfl); fail_elf: elf_end(ch->eh); fail_close: close(ch->fd); fail_free: free(ch); return NULL; }
int main (int argc, char *argv[]) { int remaining; int result = 0; /* We use no threads here which can interfere with handling a stream. */ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); /* Set locale. */ (void) setlocale (LC_ALL, ""); /* Make sure the message catalog can be found. */ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); /* Initialize the message catalog. */ (void) textdomain (PACKAGE_TARNAME); /* Parse and process arguments. This includes opening the modules. */ argp_children[0].argp = dwfl_standard_argp (); argp_children[0].group = 1; Dwfl *dwfl = NULL; (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl); assert (dwfl != NULL); /* Now handle the addresses. In case none are given on the command line, read from stdin. */ if (remaining == argc) { /* We use no threads here which can interfere with handling a stream. */ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); char *buf = NULL; size_t len = 0; ssize_t chars; while (!feof_unlocked (stdin)) { if ((chars = getline (&buf, &len, stdin)) < 0) break; if (buf[chars - 1] == '\n') buf[chars - 1] = '\0'; result = handle_address (buf, dwfl); } free (buf); } else { do result = handle_address (argv[remaining], dwfl); while (++remaining < argc); } dwfl_end (dwfl); #ifdef USE_DEMANGLE free (demangle_buffer); #endif return result; }
int coredump_make_stack_trace(int fd, const char *executable, char **ret) { static const Dwfl_Callbacks callbacks = { .find_elf = dwfl_build_id_find_elf, .find_debuginfo = dwfl_standard_find_debuginfo, }; struct stack_context c = {}; char *buf = NULL; size_t sz = 0; int r; assert(fd >= 0); assert(ret); if (lseek(fd, 0, SEEK_SET) == (off_t) -1) return -errno; c.f = open_memstream(&buf, &sz); if (!c.f) return -ENOMEM; elf_version(EV_CURRENT); c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); if (!c.elf) { r = -EINVAL; goto finish; } c.dwfl = dwfl_begin(&callbacks); if (!c.dwfl) { r = -EINVAL; goto finish; } if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) { r = -EINVAL; goto finish; } if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) { r = -EINVAL; goto finish; } if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) { r = -EINVAL; goto finish; } if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) { r = -EINVAL; goto finish; } c.f = safe_fclose(c.f); *ret = buf; buf = NULL; r = 0; finish: if (c.dwfl) dwfl_end(c.dwfl); if (c.elf) elf_end(c.elf); safe_fclose(c.f); free(buf); return r; }
/* 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)) 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 address 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 = call_probe_finder(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 probe points from debuginfo */ static int find_probes(int fd, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg = NULL; Dwfl *dwfl; Dwarf_Addr bias; /* Currently ignored */ int ret = 0; 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; } #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)) { /* 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); } if (ret < 0) break; } off = noff; } line_list__free(&pf->lcache); if (dwfl) dwfl_end(dwfl); return ret; } /* Add a found probe point into trace event list */ static int add_probe_trace_event(Dwarf_Die *sp_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; /* 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++]; ret = convert_to_trace_point(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); /* Find each argument */ 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(sp_die, pf); if (ret != 0) return ret; } return 0; } /* Find probe_trace_events specified by perf_probe_event from debuginfo */ int find_probe_trace_events(int fd, 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; /* 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 = find_probes(fd, &tf.pf); if (ret < 0) { free(*tevs); *tevs = NULL; 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, 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 *sp_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, *scopes = NULL; int ret, nscopes; /* 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++]; ret = convert_to_trace_point(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); /* Find local variables */ vl->vars = strlist__new(true, NULL); if (vl->vars == NULL) return -ENOMEM; af->child = true; die_find_child(sp_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; nscopes = dwarf_getscopes_die(sp_die, &scopes); while (nscopes-- > 1) die_find_child(&scopes[nscopes], collect_variables_cb, (void *)af, &die_mem); if (scopes) free(scopes); out: if (strlist__empty(vl->vars)) { strlist__delete(vl->vars); vl->vars = NULL; } return ret; } /* Find available variables at given probe point */ int find_available_vars_at(int fd, 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; /* Allocate result vls array */ *vls = zalloc(sizeof(struct variable_list) * max_vls); if (*vls == NULL) return -ENOMEM; af.vls = *vls; af.nvls = 0; ret = find_probes(fd, &af.pf); if (ret < 0) { /* Free vlist for error */ 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; }
/* Reverse search */ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) { Dwarf_Die cudie, spdie, indie; Dwarf *dbg = NULL; Dwfl *dwfl = NULL; Dwarf_Line *line; Dwarf_Addr laddr, eaddr, bias = 0; const char *tmp; int lineno, ret = 0; bool found = false; /* Open the live linux kernel */ dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); if (!dbg) { pr_warning("No debug information found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); ret = -EINVAL; goto end; } /* Adjust address with bias */ addr += bias; /* Find cu die */ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { pr_warning("Failed to find debug information for address %lx\n", addr); 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: if (dwfl) dwfl_end(dwfl); if (ret >= 0) ret = found ? 1 : 0; 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 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; }
void libdwFree(LibdwSession *session) { if (session == NULL) return; dwfl_end(session->dwfl); stgFree(session); }
/* * Return: * 0 if return address for the program counter @pc is on stack * 1 if return address is in LR and no new stack frame was allocated * 2 if return address is in LR and a new frame was allocated (but not * yet used) * -1 in case of errors */ static int check_return_addr(struct dso *dso, u64 map_start, Dwarf_Addr pc) { int rc = -1; Dwfl *dwfl; Dwfl_Module *mod; Dwarf_Frame *frame; int ra_regno; Dwarf_Addr start = pc; Dwarf_Addr end = pc; bool signalp; const char *exec_file = dso->long_name; dwfl = dso->dwfl; if (!dwfl) { dwfl = dwfl_begin(&offline_callbacks); if (!dwfl) { pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1)); return -1; } mod = dwfl_report_elf(dwfl, exec_file, exec_file, -1, map_start, false); if (!mod) { pr_debug("dwfl_report_elf() failed %s\n", dwarf_errmsg(-1)); /* * We normally cache the DWARF debug info and never * call dwfl_end(). But to prevent fd leak, free in * case of error. */ dwfl_end(dwfl); goto out; } dso->dwfl = dwfl; } mod = dwfl_addrmodule(dwfl, pc); if (!mod) { pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1)); goto out; } /* * To work with split debug info files (eg: glibc), check both * .eh_frame and .debug_frame sections of the ELF header. */ frame = get_eh_frame(mod, pc); if (!frame) { frame = get_dwarf_frame(mod, pc); if (!frame) goto out; } ra_regno = dwarf_frame_info(frame, &start, &end, &signalp); if (ra_regno < 0) { pr_debug("Return address register unavailable: %s\n", dwarf_errmsg(-1)); goto out; } rc = check_return_reg(ra_regno, frame); out: return rc; }
int main (int argc, char *argv[]) { int cnt; Dwfl *dwfl = NULL; (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &cnt, &dwfl); assert (dwfl != NULL); Dwarf_Die *cu = NULL; Dwarf_Addr bias; do { cu = dwfl_nextcu (dwfl, cu, &bias); if (cu != NULL) { Dwfl_Module *mod = dwfl_cumodule (cu); const char *modname = (dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL) ?: "<unknown>"); const char *cuname = (dwarf_diename (cu) ?: "<unknown>"); printf ("mod: %s CU: [%" PRIx64 "] %s\n", modname, dwarf_dieoffset (cu), cuname); size_t lines; if (dwfl_getsrclines (cu, &lines) != 0) continue; // No lines... for (size_t i = 0; i < lines; i++) { Dwfl_Line *line = dwfl_onesrcline (cu, i); Dwarf_Addr addr; int lineno; int colno; Dwarf_Word mtime; Dwarf_Word length; const char *src = dwfl_lineinfo (line, &addr, &lineno, &colno, &mtime, &length); Dwarf_Addr dw_bias; Dwarf_Line *dw_line = dwfl_dwarf_line (line, &dw_bias); assert (bias == dw_bias); Dwarf_Addr dw_addr; if (dwarf_lineaddr (dw_line, &dw_addr) != 0) error (EXIT_FAILURE, 0, "dwarf_lineaddr: %s", dwarf_errmsg (-1)); assert (addr == dw_addr + dw_bias); unsigned int dw_op_index; if (dwarf_lineop_index (dw_line, &dw_op_index) != 0) error (EXIT_FAILURE, 0, "dwarf_lineop_index: %s", dwarf_errmsg (-1)); int dw_lineno; if (dwarf_lineno (dw_line, &dw_lineno) != 0) error (EXIT_FAILURE, 0, "dwarf_lineno: %s", dwarf_errmsg (-1)); assert (lineno == dw_lineno); int dw_colno; if (dwarf_linecol (dw_line, &dw_colno) != 0) error (EXIT_FAILURE, 0, "dwarf_lineno: %s", dwarf_errmsg (-1)); assert (colno == dw_colno); bool begin; if (dwarf_linebeginstatement (dw_line, &begin) != 0) error (EXIT_FAILURE, 0, "dwarf_linebeginstatement: %s", dwarf_errmsg (-1)); bool end; if (dwarf_lineendsequence (dw_line, &end) != 0) error (EXIT_FAILURE, 0, "dwarf_lineendsequence: %s", dwarf_errmsg (-1)); bool pend; if (dwarf_lineprologueend (dw_line, &pend) != 0) error (EXIT_FAILURE, 0, "dwarf_lineprologueend: %s", dwarf_errmsg (-1)); bool ebegin; if (dwarf_lineepiloguebegin (dw_line, &ebegin) != 0) error (EXIT_FAILURE, 0, "dwarf_lineepiloguebegin: %s", dwarf_errmsg (-1)); bool block; if (dwarf_lineblock (dw_line, &block) != 0) error (EXIT_FAILURE, 0, "dwarf_lineblock: %s", dwarf_errmsg (-1)); unsigned int isa; if (dwarf_lineisa (dw_line, &isa) != 0) error (EXIT_FAILURE, 0, "dwarf_lineisa: %s", dwarf_errmsg (-1)); unsigned int disc; if (dwarf_linediscriminator (dw_line, &disc) != 0) error (EXIT_FAILURE, 0, "dwarf_linediscriminator: %s", dwarf_errmsg (-1)); const char *dw_src; Dwarf_Word dw_mtime; Dwarf_Word dw_length; dw_src = dwarf_linesrc (dw_line, &dw_mtime, &dw_length); assert (strcmp (src, dw_src) == 0); assert (mtime == dw_mtime); assert (length == dw_length); printf ("%zd %#" PRIx64 " %s:%d:%d\n" " time: %#" PRIX64 ", len: %" PRIu64 ", idx: %d, b: %d, e: %d" ", pe: %d, eb: %d, block: %d" ", isa: %d, disc: %d\n", i, addr, src, lineno, colno, mtime, length, dw_op_index, begin, end, pend, ebegin, block, isa, disc); Dwarf_Die *linecu = dwfl_linecu (line); assert (cu == linecu); Dwfl_Module *linemod = dwfl_linemodule (line); assert (mod == linemod); } } } while (cu != NULL); dwfl_end (dwfl); return 0; }