Prototype version of stack trace unwinder
[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
14 extern char *__cxa_demangle (const char *mangled_name, char *output_buffer,
15                              size_t *length, int *status);
16
17 /*
18 static int frame_callback (Dwfl_Frame *state, void *arg)
19 {
20   Regs *regs = static_cast<Regs*>(arg);
21   dwfl_frame_pc (state, &regs->pc, NULL);
22   return DWARF_CB_ABORT;
23 }
24
25 static int thread_callback (Dwfl_Thread *thread, void *thread_arg)
26 {
27   dwfl_thread_getframes (thread, frame_callback, thread_arg);
28   return DWARF_CB_ABORT;
29 }
30 */
31
32 static int module_callback (Dwfl_Module *module, void **userdata,
33                                                    const char *name, Dwarf_Addr address,
34                                                    void *arg)
35 {
36   /* To get something from module file do something like:
37
38      GElf_Addr bias;
39      Elf *elf = dwfl_module_getelf (module, &bias);
40
41      cout << "Module : " << name << " @" << hex << address << " bias " << bias << endl;
42
43      Elf_Data *data = elf_getdata_rawchunk (elf, 0, 4, ELF_T_BYTE);
44      cout << " " << static_cast<char*>(data->d_buf)+1 << endl;
45    */
46   return DWARF_CB_OK;
47 }
48
49 void getvalue (Elf *core, const void *from, size_t size, void *to)
50 {
51   Elf_Data out =
52   {
53     .d_buf = to,
54     .d_type = size == 32 ? ELF_T_WORD : ELF_T_XWORD,
55     .d_version = EV_CURRENT,
56     .d_size = size/8,
57     .d_off = 0,
58     .d_align = 0
59   };
60   Elf_Data in =
61   {
62     .d_buf = (void*)(from),
63     .d_type = out.d_type,
64     .d_version = out.d_version,
65     .d_size = out.d_size,
66     .d_off = 0,
67     .d_align = 0
68   };
69   Elf_Data *data;
70   if (gelf_getclass (core) == ELFCLASS32)
71     data = elf32_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
72   else
73     data = elf64_xlatetom (&out, &in, elf_getident (core, NULL)[EI_DATA]);
74   if (data == NULL)
75     fprintf (stderr, "failed to get value from core file\n");
76 }
77
78 int main(int argc, char **argv)
79 {
80   prctl (PR_SET_DUMPABLE, 0);
81
82   if (argc != 2)
83   {
84     fprintf (stderr, "Usage: %s <core-file>\n", argv[0]);
85     return 1;
86   }
87
88   int core_fd = open (argv[1], O_RDONLY);
89   if (core_fd < 0)
90   {
91     perror (argv[1]);
92     return 2;
93   }
94
95   elf_version (EV_CURRENT);
96
97   Elf *core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
98   if (core == NULL)
99   {
100     fprintf (stderr, "%s : Can't open ELF (%s)\n", argv[1], elf_errmsg(-1));
101     return 3;
102   }
103
104   const Dwfl_Callbacks core_callbacks =
105   {
106     .find_elf = dwfl_build_id_find_elf,
107     .find_debuginfo = dwfl_standard_find_debuginfo,
108     .section_address = NULL,
109     .debuginfo_path = NULL
110   };
111
112   Dwfl *dwfl = dwfl_begin (&core_callbacks);
113   if (dwfl == NULL)
114   {
115     fprintf (stderr, "%s : Can't start dwfl (%s)\n", argv[1], dwfl_errmsg(-1));
116     return 4;
117   }
118
119   if (dwfl_core_file_report (dwfl, core, NULL) < 0)
120   {
121     fprintf (stderr, "%s : dwfl report failed (%s)\n", argv[1], dwfl_errmsg(-1));
122     return 5;
123   }
124
125   if (dwfl_core_file_attach (dwfl, core) < 0)
126   {
127     fprintf (stderr, "%s : dwfl attach failed (%s)\n", argv[1], dwfl_errmsg(-1));
128     return 6;
129   }
130
131    Regs *regs = get_regs_struct();
132 /*
133    To unwind with libelf do this:
134
135   dwfl_getthreads (dwfl, thread_callback, regs);
136
137 */
138
139   dwfl_getmodules (dwfl, module_callback, 0, 0);
140
141   GElf_Phdr mem;
142   GElf_Phdr *phdr = gelf_getphdr (core, 0, &mem);
143   if (phdr == NULL || phdr->p_type != PT_NOTE)
144   {
145     fprintf (stderr, "%s : Missing note section at the first position in core file\n", argv[1]);
146     return 8;
147   }
148
149   Elf_Data *notes = elf_getdata_rawchunk (core, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
150   if (notes == NULL)
151   {
152     fprintf (stderr, "%s : error getting notes (%s)\n", argv[1], dwfl_errmsg(-1));
153     return 9;
154   }
155
156   Ebl *ebl = ebl_openbackend (core);
157   if (ebl == NULL)
158   {
159     fprintf (stderr, "%s : Can't initialize ebl\n", argv[1]);
160     return 7;
161   }
162
163   GElf_Nhdr nhdr;
164   size_t name_pos;
165   size_t desc_pos;
166   size_t pos = 0;
167   /* registers should be in the first note! */
168   if (gelf_getnote (notes, pos, &nhdr, &name_pos, &desc_pos) > 0)
169   {
170     if (nhdr.n_type == NT_PRSTATUS)
171     {
172       GElf_Word regs_offset;
173       size_t nregloc;
174       const Ebl_Register_Location *reglocs;
175       size_t nitems;
176       const Ebl_Core_Item *items;
177
178       if (0 == ebl_core_note (ebl, &nhdr, "CORE", &regs_offset, &nregloc, &reglocs, &nitems, &items))
179       {
180         fprintf (stderr, "%s : error parsing notes\n", argv[1]);
181         return 10;
182       }
183
184       const char *regs_location = (const char *)(notes->d_buf) + pos + desc_pos + regs_offset;
185       unsigned i;
186
187       for (i = 0; i < nregloc; i++)
188       {
189         const char *register_location = regs_location + reglocs[i].offset;
190         int regnum;
191         for (regnum = reglocs[i].regno; regnum < reglocs[i].regno + reglocs[i].count; regnum++)
192         {
193           char regname[5];
194           int bits, type;
195           const char *prefix = 0;
196           const char *setname = 0;
197           ssize_t ret = ebl_register_info (ebl, regnum, regname, sizeof(regname), &prefix, &setname, &bits, &type);
198           if (ret < 0)
199           {
200             fprintf (stderr, "%s : can't get register info\n", argv[1]);
201             return 11;
202           }
203           void *place_for_reg_value = get_place_for_register_value (regname, regnum);
204
205           if (place_for_reg_value != NULL)
206             getvalue (core, register_location, bits, place_for_reg_value);
207
208           register_location += bits / 8 + reglocs[i].pad;
209         }
210       }
211     }
212   }
213
214 /*  for (int i = 0; i < 20; i++)
215   {
216     char name[100] = {0};
217     int bits = 0, type = 0;
218     const char *setname = 0;
219     const char *prefix = 0;
220     ssize_t ret = ebl_register_info (ebl, i, name, sizeof(name), &prefix, &setname, &bits, &type);
221     printf ("ebl_register_info %d ret: %d, name: %s, prefix: %s, setname: %s, bits: %d, type: %d\n",
222         i, ret, name, prefix, setname, bits, type);
223   }
224 */
225 /*  printf ("PC: 0x%llx\n", (unsigned long long)regs.pc);
226   printf ("SP: 0x%llx\n", (unsigned long long)regs.sp);*/
227
228   Callstack callstack;
229
230   create_crash_stack (regs, dwfl, core, &callstack);
231
232   char *dem_buffer = NULL;
233   size_t it;
234
235   printf ("Call stack:\n");
236   for (it = 0; it != callstack.elems; ++it)
237   {
238     if (sizeof (callstack.tab[0]) > 4)
239       printf ("0x%016llx: ", (int64_t)callstack.tab[it]);
240     else
241       printf ("0x%08x: ", (int32_t)callstack.tab[it]);
242     Dwfl_Module *module = dwfl_addrmodule (dwfl, callstack.tab[it]);
243     if (module)
244     {
245       char *demangled_symbol = 0;
246       const char *symbol = dwfl_module_addrname (module, callstack.tab[it]);
247       if (symbol != 0 && symbol[0] == '_' && symbol[1] == 'Z')
248       {
249         int status = -1;
250
251         demangled_symbol = __cxa_demangle (symbol, dem_buffer, NULL, &status);
252         if (status == 0)
253           symbol = demangled_symbol;
254       }
255       if (symbol != 0)
256         printf ("%s()", symbol);
257
258       if (demangled_symbol != 0)
259         free (demangled_symbol);
260
261       printf (" from %s\n", dwfl_module_info (module, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
262     }
263     else
264     {
265       printf ("unknown function\n");
266     }
267   }
268
269   dwfl_report_end (dwfl, NULL, NULL);
270   dwfl_end (dwfl);
271   elf_end (core);
272   close (core_fd);
273
274   return 0;
275 }