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