4 #include <elfutils/libdwfl.h>
5 #include <elfutils/libebl.h>
10 #include <sys/prctl.h>
11 #include <linux/prctl.h>
12 #include "crash-stack.h"
14 #include <elfutils/version.h>
16 #include <sys/ptrace.h>
18 #include <sys/types.h>
21 static FILE *outputfile = NULL;
22 static FILE *errfile = NULL;
30 const struct option opts[] = {
31 { "pid", required_argument, 0, OPT_PID },
32 { "output", required_argument, 0, OPT_OUTPUTFILE },
33 { "erroutput", required_argument, 0, OPT_ERRFILE },
37 extern char *__cxa_demangle (const char *mangled_name, char *output_buffer,
38 size_t *length, int *status);
40 static int module_callback (Dwfl_Module *module, void **userdata,
41 const char *name, Dwarf_Addr address,
44 if (name != NULL && name[0] == '[')
46 /* libdwfl couldn't get the module file - we will get it later from notes */
47 Mappings *mappings = arg;
48 if (mappings->elems < MAX_MAPPINGS_NUM)
50 size_t elems = mappings->elems;
51 mappings->tab[elems].m_start = address;
52 mappings->tab[elems].m_end = 0;
53 mappings->tab[elems].m_offset = 0;
54 mappings->tab[elems].m_name = NULL;
55 mappings->tab[elems].m_fd = -1;
56 mappings->tab[elems].m_elf = 0;
60 /* fprintf(errfile, "Got module %s @0x%llx\n", name, (long long)address);*/
64 static void getvalue (Elf *core, const void *from, size_t size, void *to)
69 .d_type = size == 32 ? ELF_T_WORD : ELF_T_XWORD,
70 .d_version = EV_CURRENT,
77 .d_buf = (void*)(from),
79 .d_version = out.d_version,
85 if (gelf_getclass (core) == ELFCLASS32)
86 data = elf32_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
88 data = elf64_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
90 fprintf (errfile, "failed to get value from core file\n");
93 static void updateMapping (Mappings *mappings, uint64_t mapping_start, uint64_t mapping_end,
94 uint64_t offset, const char *name)
97 for (i = 0; i < mappings->elems; i++)
99 if (mappings->tab[i].m_start == mapping_start)
101 mappings->tab[i].m_end = mapping_end;
102 mappings->tab[i].m_name = name;
103 mappings->tab[i].m_offset = offset;
104 mappings->tab[i].m_fd = open(name, O_RDONLY);
105 mappings->tab[i].m_elf = elf_begin(mappings->tab[i].m_fd, ELF_C_READ_MMAP, NULL);
111 static void parse_note_file (Elf *elf, const char *desc, uint64_t *values_cnt, uint64_t *page_size,
112 size_t *addr_size, const char **values, const char **filenames)
114 *addr_size = gelf_fsize (elf, ELF_T_ADDR, 1, EV_CURRENT);
115 getvalue(elf, desc, *addr_size*8, values_cnt);
116 getvalue(elf, desc + *addr_size, *addr_size*8, page_size);
117 /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
119 * Then the names of files.
121 *values = desc + 2 * *addr_size;
122 *filenames = *values + 3 * *addr_size * *values_cnt;
125 static void get_mapping_item(Elf *elf, size_t addr_size, const void *item,
126 uint64_t *mapping_start, uint64_t *mapping_end, uint64_t *offset_in_pages)
128 getvalue(elf, item, addr_size*8, mapping_start);
129 getvalue(elf, item + addr_size, addr_size*8, mapping_end);
130 getvalue(elf, item + 2 * addr_size, addr_size*8, offset_in_pages);
133 static char *try_symbol_from_elfs (Elf *core, Elf_Data *notes, uintptr_t address,
134 const char **module_name)
143 while ((new_pos = gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos)) > 0)
145 if (nhdr.n_type == NT_FILE)
147 uint64_t values_cnt = 0, page_size = 0;
149 const char *filenames;
150 size_t addr_size = 0;
152 parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, &addr_size, &values, &filenames);
155 for (ii = 0; ii < values_cnt; ii++)
157 uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
158 const char *item = values + 3 * addr_size * ii;
160 get_mapping_item (core, addr_size, item, &mapping_start, &mapping_end, &offset_in_pages);
162 if (mapping_start <= address && address < mapping_end)
166 fd = open(filenames, O_RDONLY);
170 elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
178 *module_name = filenames;
180 while ((scn = elf_nextscn (elf, scn)) != NULL)
183 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
184 if (shdr != NULL && (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
186 Elf_Data *sdata = elf_getdata (scn, NULL);
187 unsigned int nsyms = sdata->d_size / (gelf_getclass(elf) == ELFCLASS32 ?
191 uintptr_t address_offset = address;
192 if (shdr->sh_type == SHT_DYNSYM)
193 address_offset -= mapping_start;
194 for (cnt = 0; cnt < nsyms; ++cnt)
198 GElf_Sym *sym = gelf_getsymshndx(sdata, NULL, cnt, &sym_mem, &xndx);
199 if (sym != NULL && sym->st_shndx != SHN_UNDEF)
201 if (sym->st_value <= address_offset && address_offset < sym->st_value + sym->st_size)
203 symbol = strdup(elf_strptr (elf, shdr->sh_link, sym->st_name));
216 filenames += strlen(filenames)+1;
225 static Dwfl *open_dwfl_with_pid (pid_t pid)
230 if (ptrace (PTRACE_SEIZE, pid, NULL, PTRACE_O_TRACEEXIT) != 0)
232 fprintf(errfile, "PTRACE_SEIZE failed on PID %d: %m\n", pid);
236 ptrace (PTRACE_INTERRUPT, pid, 0, 0);
238 stopped_pid = waitpid(pid, &status, 0);
239 if (stopped_pid == -1 || stopped_pid != pid || !WIFSTOPPED(status))
241 fprintf(errfile, "waitpid failed: %m, stopped_pid=%d, status=%d\n", stopped_pid, status);
245 static const Dwfl_Callbacks proc_callbacks =
247 .find_elf = dwfl_linux_proc_find_elf,
248 .find_debuginfo = dwfl_standard_find_debuginfo,
249 .section_address = NULL,
250 .debuginfo_path = NULL
253 Dwfl *dwfl = dwfl_begin (&proc_callbacks);
256 fprintf (errfile, "process %d : Can't start dwfl (%s)\n", pid, dwfl_errmsg(-1));
260 if (dwfl_linux_proc_report (dwfl, pid) < 0)
262 fprintf (errfile, "process %d : dwfl report failed (%s)\n", pid, dwfl_errmsg(-1));
267 #if _ELFUTILS_PREREQ(0,158)
268 if (dwfl_linux_proc_attach (dwfl, pid, true) < 0)
270 fprintf (errfile, "process %d : dwfl attach failed (%s)\n", pid, dwfl_errmsg(-1));
278 static Dwfl *open_dwfl_with_core (Elf *core, const char *core_file_name)
280 static const Dwfl_Callbacks core_callbacks =
282 .find_elf = dwfl_build_id_find_elf,
283 .find_debuginfo = dwfl_standard_find_debuginfo,
284 .section_address = NULL,
285 .debuginfo_path = NULL
288 Dwfl *dwfl = dwfl_begin (&core_callbacks);
291 fprintf (errfile, "%s : Can't start dwfl (%s)\n", core_file_name, dwfl_errmsg(-1));
295 #if _ELFUTILS_PREREQ(0,158)
296 if (dwfl_core_file_report (dwfl, core, NULL) < 0)
298 if (dwfl_core_file_report (dwfl, core) < 0)
301 fprintf (errfile, "%s : dwfl report failed (%s)\n", core_file_name, dwfl_errmsg(-1));
306 #if _ELFUTILS_PREREQ(0,158)
307 if (dwfl_core_file_attach (dwfl, core) < 0)
309 fprintf (errfile, "%s : dwfl attach failed (%s)\n", core_file_name, dwfl_errmsg(-1));
317 static int get_registers_ptrace (pid_t pid)
320 uintptr_t regbuf[20];
322 data.iov_base = regbuf;
323 data.iov_len = sizeof (regbuf);
325 if (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, &data) != 0)
327 fprintf(errfile, "PTRACE_GETREGSET failed on PID %d: %m\n", pid);
333 i * sizeof (regbuf[0]) < data.iov_len && i < sizeof (regbuf)/sizeof (regbuf[0]);
336 void *reg = get_place_for_register_value ("", i);
339 memcpy (reg, ®buf[i], sizeof (regbuf[i]));
344 static Elf_Data *get_registers_core (Elf *core, const char *core_file_name, Mappings *mappings)
347 GElf_Phdr *phdr = gelf_getphdr (core, 0, &mem);
349 if (phdr == NULL || phdr->p_type != PT_NOTE)
351 fprintf (errfile, "%s : Missing note section at the first position in core file\n",
356 Elf_Data *notes = elf_getdata_rawchunk (core, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
359 fprintf (errfile, "%s : error getting notes (%s)\n", core_file_name, dwfl_errmsg(-1));
363 Ebl *ebl = ebl_openbackend (core);
366 fprintf (errfile, "%s : Can't initialize ebl\n", core_file_name);
376 /* registers should be in the first note! */
377 while ((new_pos = gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos)) > 0)
379 if (nhdr.n_type == NT_PRSTATUS && !got_regs)
381 GElf_Word regs_offset;
383 const Ebl_Register_Location *reglocs;
385 const Ebl_Core_Item *items;
389 if (0 == ebl_core_note (ebl, &nhdr, "CORE", ®s_offset, &nregloc,
390 ®locs, &nitems, &items))
393 "%s : error parsing notes (built with different build of libebl?)\n",
398 const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos
402 for (i = 0; i < nregloc; i++)
404 const char *register_location = regs_location + reglocs[i].offset;
406 for (regnum = reglocs[i].regno;
407 regnum < reglocs[i].regno + reglocs[i].count;
412 const char *prefix = 0;
413 const char *setname = 0;
415 ssize_t ret = ebl_register_info (ebl, regnum, regname,
416 sizeof(regname), &prefix, &setname,
420 fprintf (errfile, "%s : can't get register info\n", core_file_name);
423 void *place_for_reg_value = get_place_for_register_value (regname, regnum);
425 if (place_for_reg_value != NULL)
426 getvalue (core, register_location, bits, place_for_reg_value);
428 register_location += bits / 8 + reglocs[i].pad;
432 else if (nhdr.n_type == NT_FILE)
434 uint64_t values_cnt = 0, page_size = 0;
436 const char *filenames;
437 size_t addr_size = 0;
439 parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size,
440 &addr_size, &values, &filenames);
443 /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
445 * Then the names of files.
447 for (ii = 0; ii < values_cnt; ii++)
449 uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
450 const char *item = values + 3 * addr_size * ii;
452 get_mapping_item (core, addr_size, item, &mapping_start, &mapping_end,
454 updateMapping (mappings, mapping_start, mapping_end,
455 offset_in_pages*page_size, filenames);
456 filenames += strlen (filenames)+1;
461 ebl_closebackend (ebl);
465 static void printCallstack (Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pid,
468 fprintf (outputfile, "Call stack");
469 if (pid > 1) fprintf (outputfile, " for PID %d", pid);
470 fprintf (outputfile, ":\n");
472 char *dem_buffer = NULL;
474 for (it = 0; it != callstack->elems; ++it)
476 if (sizeof (callstack->tab[0]) > 4)
477 fprintf (outputfile, "0x%016llx: ", (long long)callstack->tab[it]);
479 fprintf (outputfile, "0x%08x: ", (int32_t)callstack->tab[it]);
480 Dwfl_Module *module = dwfl_addrmodule (dwfl, callstack->tab[it]);
483 char *demangled_symbol = 0;
484 const char *symbol = dwfl_module_addrname (module, callstack->tab[it]);
485 const char *fname = 0;
486 const char *module_name = dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
487 char *symbol_from_elf = 0;
490 symbol = symbol_from_elf = try_symbol_from_elfs (core, notes, callstack->tab[it], &fname);
492 if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z')
496 demangled_symbol = __cxa_demangle (symbol, dem_buffer, NULL, &status);
498 symbol = demangled_symbol;
501 fprintf (outputfile, "%s()", symbol);
503 fprintf (outputfile, "<unknown>");
505 if (demangled_symbol != 0)
506 free (demangled_symbol);
508 if (symbol_from_elf != 0)
509 free (symbol_from_elf);
511 fprintf (outputfile, " from %s\n", fname != NULL ? fname : module_name);
515 fprintf (outputfile, "unknown function\n");
520 int main(int argc, char **argv)
525 const char *core_file_name;
527 prctl (PR_SET_DUMPABLE, 0);
529 while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1)
537 outputfile = fopen(optarg, "w");
540 errfile = fopen(optarg, "w");
545 if (NULL == errfile) errfile = stderr;
546 if (NULL == outputfile) outputfile = stdout;
548 core_file_name = argv[optind];
551 elf_version (EV_CURRENT);
553 /* First, prepare dwfl and modules */
560 dwfl = open_dwfl_with_pid (pid);
567 "Usage: %s [--output file] [--erroutput file] [--pid <pid> | <core-file>]\n",
572 core_fd = open (core_file_name, O_RDONLY);
575 perror (core_file_name);
579 core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
582 fprintf (errfile, "%s : Can't open ELF (%s)\n", core_file_name, elf_errmsg(-1));
586 dwfl = open_dwfl_with_core (core, core_file_name);
595 dwfl_getmodules (dwfl, module_callback, &mappings, 0);
598 /* Now, get registers */
601 if (-1 == get_registers_ptrace (pid))
606 notes = get_registers_core (core, core_file_name, &mappings);
611 /* Unwind call stack */
614 create_crash_stack (dwfl, core, pid, &mappings, &callstack);
616 /* Print the results */
617 printCallstack (&callstack, dwfl, core, pid, notes);
620 dwfl_report_end (dwfl, NULL, NULL);
622 if (NULL != core) elf_end (core);
623 if (-1 != core_fd) close (core_fd);