crash-stack: Fix handle leak
[platform/core/system/crash-worker.git] / src / crash-stack / crash-stack.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <libelf.h>
4 #include <elfutils/libdwfl.h>
5 #include <elfutils/libebl.h>
6 #include <errno.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <sys/prctl.h>
11 #include <linux/prctl.h>
12 #include "crash-stack.h"
13 #include <string.h>
14 #include <elfutils/version.h>
15 #include <getopt.h>
16 #include <sys/ptrace.h>
17 #include <sys/uio.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20
21 static FILE *outputfile = NULL;
22 static FILE *errfile = NULL;
23
24 enum {
25     OPT_PID,
26     OPT_OUTPUTFILE,
27     OPT_ERRFILE
28 };
29
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 },
34     { 0, 0, 0, 0 }
35 };
36
37 extern char *__cxa_demangle (const char *mangled_name, char *output_buffer,
38                              size_t *length, int *status);
39
40 static int module_callback (Dwfl_Module *module, void **userdata,
41                                                    const char *name, Dwarf_Addr address,
42                                                    void *arg)
43 {
44   if (name != NULL && name[0] == '[')
45   {
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)
49     {
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;
57       mappings->elems++;
58     }
59   }
60 /*  fprintf(errfile, "Got module %s @0x%llx\n", name, (long long)address);*/
61   return DWARF_CB_OK;
62 }
63
64 static void getvalue (Elf *core, const void *from, size_t size, void *to)
65 {
66   Elf_Data out =
67   {
68     .d_buf = to,
69     .d_type = size == 32 ? ELF_T_WORD : ELF_T_XWORD,
70     .d_version = EV_CURRENT,
71     .d_size = size/8,
72     .d_off = 0,
73     .d_align = 0
74   };
75   Elf_Data in =
76   {
77     .d_buf = (void*)(from),
78     .d_type = out.d_type,
79     .d_version = out.d_version,
80     .d_size = out.d_size,
81     .d_off = 0,
82     .d_align = 0
83   };
84   Elf_Data *data;
85   if (gelf_getclass (core) == ELFCLASS32)
86     data = elf32_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
87   else
88     data = elf64_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
89   if (data == NULL)
90     fprintf (errfile, "failed to get value from core file\n");
91 }
92
93 static void updateMapping (Mappings *mappings, uint64_t mapping_start, uint64_t mapping_end,
94     uint64_t offset, const char *name)
95 {
96   int i;
97   for (i = 0; i < mappings->elems; i++)
98   {
99     if (mappings->tab[i].m_start == mapping_start)
100     {
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);
106       return;
107     }
108   }
109 }
110
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)
113 {
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>
118    *     count = values_cnt
119    * Then the names of files.
120    */
121   *values = desc + 2 * *addr_size;
122   *filenames = *values + 3 * *addr_size * *values_cnt;
123 }
124
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)
127 {
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);
131 }
132
133 static char *try_symbol_from_elfs (Elf *core, Elf_Data *notes, uintptr_t address,
134     const char **module_name)
135 {
136   GElf_Nhdr nhdr;
137   char *symbol = NULL;
138   size_t pos = 0;
139   size_t new_pos = 0;
140   size_t name_pos;
141   size_t desc_pos;
142
143   while ((new_pos = gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos)) > 0)
144   {
145     if (nhdr.n_type == NT_FILE)
146     {
147       uint64_t values_cnt = 0, page_size = 0;
148       const char *values;
149       const char *filenames;
150       size_t addr_size = 0;
151
152       parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size, &addr_size, &values, &filenames);
153
154       int ii;
155       for (ii = 0; ii < values_cnt; ii++)
156       {
157           uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
158           const char *item = values + 3 * addr_size * ii;
159
160           get_mapping_item (core, addr_size, item, &mapping_start, &mapping_end, &offset_in_pages);
161
162           if (mapping_start <= address && address < mapping_end)
163           {
164             Elf *elf;
165             int fd;
166             fd = open(filenames, O_RDONLY);
167             if (-1 == fd)
168               return NULL;
169
170             elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
171
172             if (NULL == elf) {
173               close(fd);
174               return NULL;
175             }
176
177             Elf_Scn *scn = NULL;
178             *module_name = filenames;
179
180             while ((scn = elf_nextscn (elf, scn)) != NULL)
181             {
182               GElf_Shdr shdr_mem;
183               GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
184               if (shdr != NULL && (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
185               {
186                 Elf_Data *sdata = elf_getdata (scn, NULL);
187                 unsigned int nsyms = sdata->d_size / (gelf_getclass(elf) == ELFCLASS32 ?
188                     sizeof (Elf32_Sym) :
189                     sizeof (Elf64_Sym));
190                 unsigned int cnt;
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)
195                 {
196                   GElf_Sym sym_mem;
197                   Elf32_Word xndx;
198                   GElf_Sym *sym = gelf_getsymshndx(sdata, NULL, cnt, &sym_mem, &xndx);
199                   if (sym != NULL && sym->st_shndx != SHN_UNDEF)
200                   {
201                     if (sym->st_value <= address_offset && address_offset < sym->st_value + sym->st_size)
202                     {
203                       symbol = strdup(elf_strptr (elf, shdr->sh_link, sym->st_name));
204                       break;
205                     }
206                   }
207                 }
208               }
209             }
210
211             elf_end(elf);
212             close(fd);
213             return symbol;
214           }
215
216           filenames += strlen(filenames)+1;
217       }
218     }
219     pos = new_pos;
220   }
221
222   return NULL;
223 }
224
225 static Dwfl *open_dwfl_with_pid (pid_t pid)
226 {
227   int status;
228   pid_t stopped_pid;
229
230   if (ptrace (PTRACE_SEIZE, pid, NULL, PTRACE_O_TRACEEXIT) != 0)
231   {
232     fprintf(errfile, "PTRACE_SEIZE failed on PID %d: %m\n", pid);
233     return NULL;
234   }
235
236   ptrace (PTRACE_INTERRUPT, pid, 0, 0);
237
238   stopped_pid = waitpid(pid, &status, 0);
239   if (stopped_pid == -1 || stopped_pid != pid || !WIFSTOPPED(status))
240   {
241     fprintf(errfile, "waitpid failed: %m, stopped_pid=%d, status=%d\n", stopped_pid, status);
242     return NULL;
243   }
244
245   static const Dwfl_Callbacks proc_callbacks =
246   {
247     .find_elf = dwfl_linux_proc_find_elf,
248     .find_debuginfo = dwfl_standard_find_debuginfo,
249     .section_address = NULL,
250     .debuginfo_path = NULL
251   };
252
253   Dwfl *dwfl = dwfl_begin (&proc_callbacks);
254   if (dwfl == NULL)
255   {
256     fprintf (errfile, "process %d : Can't start dwfl (%s)\n", pid, dwfl_errmsg(-1));
257     return NULL;
258   }
259
260   if (dwfl_linux_proc_report (dwfl, pid) < 0)
261   {
262     fprintf (errfile, "process %d : dwfl report failed (%s)\n", pid, dwfl_errmsg(-1));
263     dwfl_end (dwfl);
264     return NULL;
265   }
266
267 #if _ELFUTILS_PREREQ(0,158)
268   if (dwfl_linux_proc_attach (dwfl, pid, true) < 0)
269   {
270     fprintf (errfile, "process %d : dwfl attach failed (%s)\n", pid, dwfl_errmsg(-1));
271     dwfl_end (dwfl);
272     return NULL;
273   }
274 #endif
275   return dwfl;
276 }
277
278 static Dwfl *open_dwfl_with_core (Elf *core, const char *core_file_name)
279 {
280   static const Dwfl_Callbacks core_callbacks =
281   {
282     .find_elf = dwfl_build_id_find_elf,
283     .find_debuginfo = dwfl_standard_find_debuginfo,
284     .section_address = NULL,
285     .debuginfo_path = NULL
286   };
287
288   Dwfl *dwfl = dwfl_begin (&core_callbacks);
289   if (dwfl == NULL)
290   {
291     fprintf (errfile, "%s : Can't start dwfl (%s)\n", core_file_name, dwfl_errmsg(-1));
292     return NULL;
293   }
294
295 #if _ELFUTILS_PREREQ(0,158)
296   if (dwfl_core_file_report (dwfl, core, NULL) < 0)
297 #else
298   if (dwfl_core_file_report (dwfl, core) < 0)
299 #endif
300   {
301     fprintf (errfile, "%s : dwfl report failed (%s)\n", core_file_name, dwfl_errmsg(-1));
302     dwfl_end (dwfl);
303     return NULL;
304   }
305
306 #if _ELFUTILS_PREREQ(0,158)
307   if (dwfl_core_file_attach (dwfl, core) < 0)
308   {
309     fprintf (errfile, "%s : dwfl attach failed (%s)\n", core_file_name, dwfl_errmsg(-1));
310     dwfl_end (dwfl);
311     return NULL;
312   }
313 #endif
314   return dwfl;
315 }
316
317 static int get_registers_ptrace (pid_t pid)
318 {
319   struct iovec data;
320   uintptr_t regbuf[20];
321
322   data.iov_base = regbuf;
323   data.iov_len = sizeof (regbuf);
324
325   if (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, &data) != 0)
326   {
327     fprintf(errfile, "PTRACE_GETREGSET failed on PID %d: %m\n", pid);
328     return -1;
329   }
330
331   size_t i;
332   for (i = 0;
333        i * sizeof (regbuf[0]) < data.iov_len && i < sizeof (regbuf)/sizeof (regbuf[0]);
334        i++)
335   {
336     void *reg = get_place_for_register_value ("", i);
337
338     if (NULL != reg)
339       memcpy (reg, &regbuf[i], sizeof (regbuf[i]));
340   }
341   return 0;
342 }
343
344 static Elf_Data *get_registers_core (Elf *core, const char *core_file_name, Mappings *mappings)
345 {
346   GElf_Phdr mem;
347   GElf_Phdr *phdr = gelf_getphdr (core, 0, &mem);
348
349   if (phdr == NULL || phdr->p_type != PT_NOTE)
350   {
351     fprintf (errfile, "%s : Missing note section at the first position in core file\n",
352         core_file_name);
353     return NULL;
354   }
355
356   Elf_Data *notes = elf_getdata_rawchunk (core, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
357   if (notes == NULL)
358   {
359     fprintf (errfile, "%s : error getting notes (%s)\n", core_file_name, dwfl_errmsg(-1));
360     return NULL;
361   }
362
363   Ebl *ebl = ebl_openbackend (core);
364   if (ebl == NULL)
365   {
366     fprintf (errfile, "%s : Can't initialize ebl\n", core_file_name);
367     return NULL;
368   }
369
370   GElf_Nhdr nhdr;
371   size_t name_pos;
372   size_t desc_pos;
373   size_t pos = 0;
374   size_t new_pos = 0;
375   int got_regs = 0;
376   /* registers should be in the first note! */
377   while ((new_pos = gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos)) > 0)
378   {
379     if (nhdr.n_type == NT_PRSTATUS && !got_regs)
380     {
381       GElf_Word regs_offset;
382       size_t nregloc;
383       const Ebl_Register_Location *reglocs;
384       size_t nitems;
385       const Ebl_Core_Item *items;
386
387       got_regs = 1;
388
389       if (0 == ebl_core_note (ebl, &nhdr, "CORE", &regs_offset, &nregloc,
390             &reglocs, &nitems, &items))
391       {
392         fprintf (errfile,
393             "%s : error parsing notes (built with different build of libebl?)\n",
394             core_file_name);
395         return NULL;
396       }
397
398       const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos
399                                   + regs_offset;
400       unsigned i;
401
402       for (i = 0; i < nregloc; i++)
403       {
404         const char *register_location = regs_location + reglocs[i].offset;
405         int regnum;
406         for (regnum = reglocs[i].regno;
407              regnum < reglocs[i].regno + reglocs[i].count;
408              regnum++)
409         {
410           char regname[5];
411           int bits, type;
412           const char *prefix = 0;
413           const char *setname = 0;
414
415           ssize_t ret = ebl_register_info (ebl, regnum, regname,
416                                            sizeof(regname), &prefix, &setname,
417                                            &bits, &type);
418           if (ret < 0)
419           {
420             fprintf (errfile, "%s : can't get register info\n", core_file_name);
421             return NULL;
422           }
423           void *place_for_reg_value = get_place_for_register_value (regname, regnum);
424
425           if (place_for_reg_value != NULL)
426             getvalue (core, register_location, bits, place_for_reg_value);
427
428           register_location += bits / 8 + reglocs[i].pad;
429         }
430       }
431     }
432     else if (nhdr.n_type == NT_FILE)
433     {
434       uint64_t values_cnt = 0, page_size = 0;
435       const char *values;
436       const char *filenames;
437       size_t addr_size = 0;
438
439       parse_note_file(core, notes->d_buf + desc_pos, &values_cnt, &page_size,
440           &addr_size, &values, &filenames);
441
442       int ii;
443       /* First: triplets of <mapping-start> <mapping-end> <offset-in-pages>
444        *     count = values_cnt
445        * Then the names of files.
446        */
447       for (ii = 0; ii < values_cnt; ii++)
448       {
449           uint64_t mapping_start = 0, mapping_end = 0, offset_in_pages = 0;
450           const char *item = values + 3 * addr_size * ii;
451
452           get_mapping_item (core, addr_size, item, &mapping_start, &mapping_end,
453                             &offset_in_pages);
454           updateMapping (mappings, mapping_start, mapping_end,
455                          offset_in_pages*page_size, filenames);
456           filenames += strlen (filenames)+1;
457       }
458     }
459     pos = new_pos;
460   }
461   ebl_closebackend (ebl);
462   return notes;
463 }
464
465 static void printCallstack (Callstack *callstack, Dwfl *dwfl, Elf *core, pid_t pid,
466     Elf_Data *notes)
467 {
468   fprintf (outputfile, "Call stack");
469   if (pid > 1) fprintf (outputfile, " for PID %d", pid);
470   fprintf (outputfile, ":\n");
471
472   char *dem_buffer = NULL;
473   size_t it;
474   for (it = 0; it != callstack->elems; ++it)
475   {
476     if (sizeof (callstack->tab[0]) > 4)
477       fprintf (outputfile, "0x%016llx: ", (long long)callstack->tab[it]);
478     else
479       fprintf (outputfile, "0x%08x: ", (int32_t)callstack->tab[it]);
480     Dwfl_Module *module = dwfl_addrmodule (dwfl, callstack->tab[it]);
481     if (module)
482     {
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;
488       if (symbol == NULL)
489       {
490         symbol = symbol_from_elf = try_symbol_from_elfs (core, notes, callstack->tab[it], &fname);
491       }
492       if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z')
493       {
494         int status = -1;
495
496         demangled_symbol = __cxa_demangle (symbol, dem_buffer, NULL, &status);
497         if (status == 0)
498           symbol = demangled_symbol;
499       }
500       if (symbol != 0)
501         fprintf (outputfile, "%s()", symbol);
502       else
503         fprintf (outputfile, "<unknown>");
504
505       if (demangled_symbol != 0)
506         free (demangled_symbol);
507
508       if (symbol_from_elf != 0)
509         free (symbol_from_elf);
510
511       fprintf (outputfile, " from %s\n", fname != NULL ? fname : module_name);
512     }
513     else
514     {
515       fprintf (outputfile, "unknown function\n");
516     }
517   }
518 }
519
520 int main(int argc, char **argv)
521 {
522   int c;
523   pid_t pid = 0;
524
525   const char *core_file_name;
526
527   prctl (PR_SET_DUMPABLE, 0);
528
529   while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1)
530   {
531     switch (c)
532     {
533       case OPT_PID:
534         pid = atoi(optarg);
535         break;
536       case OPT_OUTPUTFILE:
537         outputfile = fopen(optarg, "w");
538         break;
539       case OPT_ERRFILE:
540         errfile = fopen(optarg, "w");
541         break;
542     }
543   }
544
545   if (NULL == errfile) errfile = stderr;
546   if (NULL == outputfile) outputfile = stdout;
547
548   core_file_name = argv[optind];
549   argc -= optind;
550
551   elf_version (EV_CURRENT);
552
553   /* First, prepare dwfl and modules */
554   Elf *core = NULL;
555   int core_fd = -1;
556   Dwfl *dwfl = NULL;
557
558   if (pid > 1)
559   {
560     dwfl = open_dwfl_with_pid (pid);
561   }
562   else
563   {
564     if (argc != 1)
565     {
566       fprintf (errfile,
567                "Usage: %s [--output file] [--erroutput file] [--pid <pid> | <core-file>]\n",
568                argv[0]);
569       return 1;
570     }
571
572     core_fd = open (core_file_name, O_RDONLY);
573     if (core_fd < 0)
574     {
575       perror (core_file_name);
576       return 2;
577     }
578
579     core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
580     if (core == NULL)
581     {
582       fprintf (errfile, "%s : Can't open ELF (%s)\n", core_file_name, elf_errmsg(-1));
583       return 3;
584     }
585
586     dwfl = open_dwfl_with_core (core, core_file_name);
587   }
588
589   if (NULL == dwfl)
590     return 1111;
591
592   Mappings mappings;
593   mappings.elems = 0;
594
595   dwfl_getmodules (dwfl, module_callback, &mappings, 0);
596   Elf_Data *notes = 0;
597
598   /* Now, get registers */
599   if (pid > 1)
600   {
601     if (-1 == get_registers_ptrace (pid))
602       return 3333;
603   }
604   else
605   {
606     notes = get_registers_core (core, core_file_name, &mappings);
607     if (NULL == notes)
608       return 2222;
609   }
610
611   /* Unwind call stack */
612   Callstack callstack;
613
614   create_crash_stack (dwfl, core, pid, &mappings, &callstack);
615
616   /* Print the results */
617   printCallstack (&callstack, dwfl, core, pid, notes);
618
619   /* Clean up */
620   dwfl_report_end (dwfl, NULL, NULL);
621   dwfl_end (dwfl);
622   if (NULL != core) elf_end (core);
623   if (-1 != core_fd) close (core_fd);
624
625   return 0;
626 }