]> git.cworth.org Git - vogl/blob - src/libbacktrace/btrace.cpp
Initial vogl checkin
[vogl] / src / libbacktrace / btrace.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 //
27 // btrace.cpp
28 //
29 #define __STDC_FORMAT_MACROS
30 #include <inttypes.h>
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <execinfo.h>
35 #include <link.h>
36 #include <sys/utsname.h>
37
38 // We need local unwinding only (should be more optimal than local + remote).
39 #define UNW_LOCAL_ONLY
40 #include <libunwind.h>
41
42 #include "btrace.h"
43 #include "backtrace.h"
44
45 #include "vogl_core.h"
46 #include "vogl_json.h"
47 #include "vogl_file_utils.h"
48 #include "vogl_applauncher.h"
49
50 // our demangle routine from libelftc_dem_gnu3.c (from libcxxrt)
51 extern "C" char * __cxa_demangle_gnu3(const char *org);
52
53 // fast(er) backtrace routine from libunwind
54 extern "C" int unw_backtrace_skip (void **buffer, int size, int skip);
55
56 // Our dlopen mutex to protect g_module_infos operations
57 static vogl::mutex &get_dlopen_mutex()
58 {
59     static vogl::mutex g_dlopen_mutex(0, true);
60     return g_dlopen_mutex;
61 }
62
63 // An array of all the modules that have been loaded in this process.
64 static vogl::vector<btrace_module_info> &get_module_infos()
65 {
66     static vogl::vector<btrace_module_info> s_module_infos;
67     return s_module_infos;
68 }
69
70 btrace_glinfo& btrace_get_glinfo()
71 {
72     static btrace_glinfo s_info;
73     return s_info;
74 }
75
76 int
77 btrace_get(uintptr_t *addrs, size_t count_addrs, uint32_t addrs_to_skip)
78 {
79 #if 1
80     return unw_backtrace_skip((void **)addrs, (int)count_addrs, addrs_to_skip);
81 #else
82     size_t count = 0;
83     unw_cursor_t cursor;
84     unw_context_t context;
85
86     unw_getcontext(&context);
87     unw_init_local(&cursor, &context);
88
89     while (count < count_addrs)
90     {
91         unw_word_t addr; 
92
93         if (unw_step(&cursor) <= 0)
94             break;
95
96         unw_get_reg(&cursor, UNW_REG_IP, &addr);
97         // Could retrieve registers via something like this:
98         //   unw_get_reg(&cursor, UNW_X86_EAX, &eax), ...
99
100         if (addrs_to_skip)
101         {
102             addrs_to_skip--;
103             continue;
104         }
105
106         addrs[count++] = (uintptr_t)addr; 
107
108 #if 0
109         // Get function name and offset from libunwind. Should match
110         //  the libbacktrace code down below.
111         unw_word_t offset;
112         char function[512];
113         function[0] = 0;
114         unw_get_proc_name(&cursor, function, sizeof(function), &offset);
115         printf ("0x%" PRIxPTR ": %s [+0x%" PRIxPTR "]\n", addr, function, offset);
116 #endif
117     }
118
119     return (int)count;
120 #endif
121 }
122
123 const char *
124 btrace_get_calling_module()
125 {
126     unw_cursor_t cursor;
127     unw_context_t context;
128     const char *calling_fname = NULL;
129
130     unw_getcontext(&context);
131     unw_init_local(&cursor, &context);
132
133     for(;;)
134     {
135         unw_word_t addr; 
136         Dl_info dl_info;
137
138         if (unw_step(&cursor) <= 0)
139             break;
140
141         unw_get_reg(&cursor, UNW_REG_IP, &addr);
142
143         // Get module name.
144         if (dladdr((void *)addr, &dl_info) == 0)
145             return NULL;
146
147         if (dl_info.dli_fname)
148         {
149             if (!calling_fname)
150             {
151                 calling_fname = dl_info.dli_fname;
152             }
153             else if(strcmp(calling_fname, dl_info.dli_fname))
154             {
155                 return dl_info.dli_fname;
156             }
157         }
158     }
159
160     return NULL;
161 }
162
163 static int
164 btrace_module_search (const void *vkey, const void *ventry)
165 {
166     const uintptr_t *key = (const uintptr_t *)vkey;
167     const struct btrace_module_info *entry = (const struct btrace_module_info *) ventry;
168     uintptr_t addr;
169  
170     addr = *key;
171     if (addr < entry->base_address)
172         return -1;
173     else if (addr >= entry->base_address + entry->address_size)
174         return 1;
175     return 0;
176 }
177
178 const char *
179 btrace_get_current_module()
180 {
181     void *paddr = __builtin_return_address(0);
182     uintptr_t addr = (uintptr_t)paddr;
183     vogl::scoped_mutex lock(get_dlopen_mutex());
184
185     // Try to search for the module name in our list. Should be faster than dladdr which
186     //  goes through a bunch of symbol information.
187     vogl::vector<btrace_module_info>& module_infos = get_module_infos();
188     if (module_infos.size())
189     {
190         btrace_module_info *module_info = (btrace_module_info *)bsearch(&addr,
191             &module_infos[0], module_infos.size(), sizeof(btrace_module_info), btrace_module_search);
192         if (module_info && module_info->filename)
193             return module_info->filename;
194     }
195
196     // Well, that failed for some reason. Try dladdr.
197     Dl_info dl_info;
198     if (dladdr(paddr, &dl_info) && dl_info.dli_fname)
199         return dl_info.dli_fname;
200
201     VOGL_ASSERT(0);
202     return NULL;
203 }
204
205 static void
206 btrace_err_callback(void *data, const char *msg, int errnum)
207 {
208     VOGL_NOTE_UNUSED(data);
209
210     if (errnum == -1)
211     {
212         // Missing dwarf information. This happens when folks haven't compiled with -g or they
213         //  stripped the symbols and we couldn't find em.
214     }
215     else
216     {
217         const char *errstr = errnum ? strerror(errnum) : "";
218         printf("libbacktrace error: %s %s\n", msg, errstr);
219         VOGL_ASSERT(0);
220     }
221 }
222
223 static void
224 btrace_syminfo_callback(void *data, uintptr_t addr, const char *symname, uintptr_t symval, uintptr_t symsize)
225 {
226     VOGL_NOTE_UNUSED(symsize);
227
228     if (symname)
229     {
230         btrace_info *info = (btrace_info *)data;
231         info->function = symname;
232         info->offset = addr - symval;
233     }
234 }
235
236 static int
237 btrace_pcinfo_callback(void *data, uintptr_t addr, const char *file, int line, const char *func)
238 {  
239     VOGL_NOTE_UNUSED(addr);
240
241     btrace_info *frame = (btrace_info *)data;
242
243     frame->filename = file;
244     frame->linenumber = line;
245
246     // Don't overwrite function string if we got a blank one for some reason.
247     if (func && func[0])
248         frame->function = func; 
249     return 0;
250 }
251
252 static void
253 backtrace_initialize_error_callback(void *data, const char *msg, int errnum)
254 {
255     VOGL_NOTE_UNUSED(data);
256     VOGL_NOTE_UNUSED(msg);
257     VOGL_NOTE_UNUSED(errnum);
258     // Backtrace_initialize only fails with alloc error and will be handled below.
259 }
260
261 static bool
262 module_info_init_state(btrace_module_info *module_info)
263 {
264     if (!module_info->backtrace_state)
265     {
266         module_info->backtrace_state = backtrace_create_state(
267                     module_info->filename, 0, backtrace_initialize_error_callback, NULL);
268         if (module_info->backtrace_state)
269         {
270             elf_get_uuid(module_info->backtrace_state, module_info->filename,
271                          module_info->uuid, &module_info->uuid_len);
272         }
273     }
274
275     return !!module_info->backtrace_state;
276 }
277
278 bool
279 btrace_resolve_addr(btrace_info *info, uintptr_t addr, uint32_t flags)
280 {
281     vogl::scoped_mutex lock(get_dlopen_mutex());
282     vogl::vector<btrace_module_info>& module_infos = get_module_infos();
283  
284     if (!module_infos.size())
285         btrace_dlopen_notify(NULL);
286
287     info->addr = addr;
288     info->offset = 0;
289     info->module = NULL;
290     info->function = NULL;
291     info->filename = NULL;
292     info->linenumber = 0;
293     info->demangled_func_buf[0] = 0;
294
295     btrace_module_info *module_info = (btrace_module_info *)bsearch(&addr,
296         &module_infos[0], module_infos.size(), sizeof(btrace_module_info), btrace_module_search);
297     if (module_info)
298     {
299         info->module = module_info->filename;
300
301         if (module_info_init_state(module_info))
302         {
303             backtrace_fileline_initialize(module_info->backtrace_state, module_info->base_address,
304                                           module_info->is_exe, backtrace_initialize_error_callback, NULL);
305
306             // Get function name and offset.
307             backtrace_syminfo(module_info->backtrace_state, addr, btrace_syminfo_callback,
308                               btrace_err_callback, info);
309
310             if (flags & BTRACE_RESOLVE_ADDR_GET_FILENAME)
311             {
312                 // Get filename and line number (and maybe function).
313                 backtrace_pcinfo(module_info->backtrace_state, addr, btrace_pcinfo_callback,
314                                  btrace_err_callback, info); 
315             }
316
317             if ((flags & BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC) && info->function && info->function[0])
318             {
319                 info->function = btrace_demangle_function(info->function, info->demangled_func_buf, sizeof(info->demangled_func_buf));
320             }
321         }
322
323         if (!info->offset)
324             info->offset = addr - module_info->base_address;
325     }
326
327     // Get module name.
328     if (!info->module || !info->module[0])
329     {
330         Dl_info dl_info;
331         if (dladdr((void *)addr, &dl_info))
332             info->module = dl_info.dli_fname;
333         if (!info->offset)
334             info->offset = addr - (uintptr_t)dl_info.dli_fbase;
335     }
336
337     if (info->module)
338     {
339         const char *module_name = strrchr(info->module, '/');
340         if (module_name)
341             info->module = module_name + 1;
342     }
343
344     if (!info->module)
345         info->module = "";
346     if (!info->function)
347         info->function = "";
348     if (!info->filename)
349         info->filename = "";
350     return 1; 
351 }
352
353 static int
354 get_hex_value(char ch)
355 {
356     if (ch >= 'A' && ch <= 'F')
357         return 10 + ch - 'A';
358     else if (ch >= 'a' && ch <= 'f')
359         return 10 + ch - 'a';
360     else if (ch >= '0' && ch <= '9')
361         return ch - '0';
362
363     return -1;
364 }
365
366 int
367 btrace_uuid_str_to_uuid(uint8_t uuid[20], const char *uuid_str)
368 {
369     int len;
370
371     for (len = 0; (len < 20) && *uuid_str; len++)
372     {
373         int val0 = get_hex_value(*uuid_str++);
374         int val1 = get_hex_value(*uuid_str++);
375         if (val0 < 0 || val1 < 0)
376             break;
377         uuid[len] = (val0 << 4) | val1;
378     }
379
380     return len;
381 }
382
383 void
384 btrace_uuid_to_str(char uuid_str[41], const uint8_t *uuid, int len) 
385 {
386     int i;
387     static const char hex[] = "0123456789abcdef";
388
389     if (len > 40)
390         len = 40;
391     for (i = 0; i < len; i++) 
392     {
393         uint8_t c = *uuid++;
394
395         *uuid_str++ = hex[c >> 4];
396         *uuid_str++ = hex[c & 0xf];
397     }    
398     *uuid_str = 0;
399 }
400
401 bool
402 btrace_get_machine_info(vogl::json_node *machine_info)
403 {
404     vogl::scoped_mutex lock(get_dlopen_mutex());
405     vogl::vector<btrace_module_info>& module_infos = get_module_infos();
406  
407     if (!module_infos.size())
408         btrace_dlopen_notify(NULL);
409
410     // Add the module list.
411     vogl::json_node &module_list_node = machine_info->add_array("module_list");
412     for (uint i = 0; i < module_infos.size(); i++)
413     {
414         btrace_module_info &module_info = module_infos[i];
415         char uuid_str[sizeof(module_info.uuid) * 2 + 1];
416
417         // Make sure we've gotten the uuid.
418         module_info_init_state(&module_info);
419
420         btrace_uuid_to_str(uuid_str, module_info.uuid, sizeof(module_info.uuid));
421
422         vogl::json_node &arr = module_list_node.add_array(module_info.filename);
423         arr.add_value(module_info.base_address);
424         arr.add_value(module_info.address_size);
425         arr.add_value(uuid_str);
426         
427         if (module_info.is_exe)
428             arr.add_value("(exe)");
429     }
430
431     // Add the environment variables.
432     vogl::growable_array<char, 2048> proc_file;
433     if (vogl::file_utils::read_proc_file("/proc/self/environ", proc_file))
434     {
435         vogl::json_node &env_list_node = machine_info->add_array("environ_list");
436
437         char *ptr = proc_file.get_ptr();
438         uint size = proc_file.size_in_bytes();
439         char *var = ptr;
440         while ((var < ptr + size) && *var)
441         {
442             env_list_node.add_value(var);
443             var += strlen(var) + 1;
444         }
445     }
446
447     if (vogl::file_utils::read_proc_file("/proc/self/cmdline", proc_file))
448     {
449         vogl::json_node &env_list_node = machine_info->add_array("cmdline");
450
451         char *ptr = proc_file.get_ptr();
452         uint size = proc_file.size_in_bytes();
453         char *var = ptr;
454         while ((var < ptr + size) && *var)
455         {
456             env_list_node.add_value(var);
457             var += strlen(var) + 1;
458         }
459     }
460
461     utsname uname_data;
462     if (!uname(&uname_data))
463     {
464         vogl::json_node &env_list_node = machine_info->add_array("uname");
465
466 #define ADD_KEY_VALUE(_val) if (uname_data._val) env_list_node.add_key_value(#_val, uname_data._val);
467         ADD_KEY_VALUE(sysname);
468         ADD_KEY_VALUE(nodename);
469         ADD_KEY_VALUE(release);
470         ADD_KEY_VALUE(version);
471         ADD_KEY_VALUE(machine);
472 #undef ADD_KEY_VALUE
473     }
474     
475     // Should be tsc or hpet (if TSC failed to calibrate).
476     if (vogl::file_utils::read_proc_file("/sys/devices/system/clocksource/clocksource0/current_clocksource", proc_file))
477     {
478         vogl::json_node &env_list_node = machine_info->add_array("current_clocksource");
479         env_list_node.add_value(proc_file.get_ptr());
480     }
481
482     if (vogl::file_utils::read_proc_file("/proc/driver/nvidia/version", proc_file))
483     {
484         vogl::json_node &env_list_node = machine_info->add_array("nvidia_version");
485         env_list_node.add_value(proc_file.get_ptr());
486     }
487     if (vogl::file_utils::read_proc_file("/proc/driver/nvidia/gpus/0/information", proc_file))
488     {
489         vogl::json_node &env_list_node = machine_info->add_array("nvidia_gpus_0_information");
490         env_list_node.add_value(proc_file.get_ptr());
491     }
492
493     btrace_glinfo &glinfo = btrace_get_glinfo();
494     vogl::json_node &glinfo_list_node = machine_info->add_array("glinfo");
495     glinfo_list_node.add_key_value("version_str", glinfo.version_str);
496     glinfo_list_node.add_key_value("glsl_version_str", glinfo.glsl_version_str);
497     glinfo_list_node.add_key_value("vendor_str", glinfo.vendor_str);
498     glinfo_list_node.add_key_value("renderer_str", glinfo.renderer_str);
499
500     return true;
501 }
502
503 int
504 btrace_dump()
505 {
506     int i;
507     uintptr_t addrs[128];
508     int count = btrace_get(addrs, VOGL_ARRAY_SIZE(addrs), 0);
509
510     for (i = 0; i < count; i++)
511     {
512         int ret;
513         btrace_info info;
514
515         ret = btrace_resolve_addr(&info, addrs[i],
516                                 BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC);
517
518         printf(" 0x%" PRIxPTR " ", addrs[i]);
519         if (ret)
520         {
521             printf("%s", info.module);
522
523             if (info.function[0])
524             {
525                 printf(": %s", info.function);
526             }
527
528             printf("+0x%" PRIxPTR, info.offset);
529
530             if (info.filename && info.filename[0])
531             {
532                 // Print last directory plus filename if possible.
533                 const char *slash_cur = info.filename;
534                 const char *slash_last = NULL;
535                 for (const char *ch = info.filename; *ch; ch++)
536                 {
537                     if (*ch == '/')
538                     {
539                         slash_last = slash_cur;
540                         slash_cur = ch + 1;
541                     }
542                 }
543                 const char *filename = slash_last ? slash_last : slash_cur;
544                 printf(": %s:%d", filename, info.linenumber); 
545             }
546         }
547
548         printf("\n");
549     }
550
551     return count;
552 }
553
554 static int
555 module_info_less_than_func(const btrace_module_info &key0, const btrace_module_info &key1)
556 {
557     //$ TODO: We can get rid of this function and it'll just call the operator< func?
558     return key0 < key1;
559 }
560
561 static int
562 dlopen_notify_callback(struct dl_phdr_info *info, size_t size, void *data)
563 {
564     VOGL_NOTE_UNUSED(size);
565
566     char buf[PATH_MAX];
567     bool is_exe = false;
568     const char *filename = info->dlpi_name;
569     vogl::vector<btrace_module_info> *new_module_infos = (vogl::vector<btrace_module_info> *)data;
570     vogl::vector<btrace_module_info>& module_infos = get_module_infos();
571
572     // If we don't have a filename and we haven't added our main exe yet, do it.
573     if (!filename || !filename[0])
574     {
575         if (!module_infos.size() && !new_module_infos->size())
576         {
577             is_exe = true;
578             ssize_t ret =  readlink("/proc/self/exe", buf, sizeof(buf));
579             if ((ret > 0) && (ret < (ssize_t)sizeof(buf)))
580             {
581                 buf[ret] = 0;
582                 filename = buf;
583             }
584         }
585         if (!filename || !filename[0])
586             return 0;
587     }
588
589     uintptr_t addr_start = 0;
590     uintptr_t addr_end = 0;
591
592     for (int i = 0; i < info->dlpi_phnum; i++)
593     {
594         if (info->dlpi_phdr[i].p_type == PT_LOAD)
595         {
596             if (addr_end == 0)
597             {
598                 addr_start = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; 
599                 addr_end = addr_start + info->dlpi_phdr[i].p_memsz;
600             }
601             else
602             {
603                 uintptr_t addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz; 
604                 if (addr > addr_end)
605                     addr_end = addr;
606             }
607         }
608     }
609
610     btrace_module_info module_info;
611     module_info.base_address = addr_start;
612     module_info.address_size = (uint32_t)(addr_end - addr_start);
613     module_info.filename = filename;
614     module_info.is_exe = is_exe;
615     module_info.backtrace_state = NULL;
616     module_info.uuid_len = 0;
617     memset(module_info.uuid, 0, sizeof(module_info.uuid));
618
619     int ret = module_infos.find_sorted(module_info, module_info_less_than_func);
620     if (ret == vogl::cInvalidIndex)
621     {
622         module_info.filename = vogl::vogl_strdup(filename);
623         if (module_info.filename)
624         {
625             new_module_infos->push_back(module_info);
626         }
627     }
628     return 0;
629 }
630
631 bool
632 btrace_dlopen_add_module(const btrace_module_info &module_info_in)
633 {
634     vogl::scoped_mutex lock(get_dlopen_mutex());
635     vogl::vector<btrace_module_info>& module_infos = get_module_infos();
636
637     int ret = module_infos.find_sorted(module_info_in, module_info_less_than_func);
638     if (ret == vogl::cInvalidIndex)
639     {
640         btrace_module_info module_info = module_info_in;
641
642         if (module_info_init_state(&module_info))
643         {
644             // Make sure the UUID of the file on disk matches with what we were asked for.
645             if ((module_info_in.uuid_len == module_info.uuid_len) &&
646                     !memcmp(module_info_in.uuid, module_info.uuid, module_info.uuid_len))
647             {
648                 module_infos.push_back(module_info);
649                 module_infos.sort();
650                 return true;
651             }
652         }
653     }
654
655     return false;
656 }
657
658 const char *
659 btrace_get_debug_filename(const char *filename)
660 {
661     vogl::scoped_mutex lock(get_dlopen_mutex());
662     vogl::vector<btrace_module_info>& module_infos = get_module_infos();
663    
664     vogl::dynamic_string fname = filename;
665     for (uint i = 0; i < module_infos.size(); i++)
666     {
667         btrace_module_info &module_info = module_infos[i];
668
669         if (fname.compare(module_info.filename, true) == 0)
670         {
671             if (module_info_init_state(&module_info))
672             {
673                 backtrace_fileline_initialize(module_info.backtrace_state, module_info.base_address,
674                                               module_info.is_exe, backtrace_initialize_error_callback, NULL);
675                 
676                 return backtrace_get_debug_filename(module_info.backtrace_state);
677             }
678         }
679     }
680     return NULL;
681 }
682
683 // This function is called from a dlopen hook, which means it could be
684 //  called from the driver or other code which hasn't aligned the stack.
685 #ifdef __i386
686 void __attribute__((force_align_arg_pointer)) 
687 #else
688 void
689 #endif
690 btrace_dlopen_notify(const char *filename)
691 {
692     VOGL_NOTE_UNUSED(filename);
693
694     // Make sure the vogl heap is initialized.
695     vogl::vogl_init_heap();
696
697     vogl::scoped_mutex lock(get_dlopen_mutex());
698     vogl::vector<btrace_module_info> new_module_infos;
699
700     // Iterator through all the currently loaded modules.
701     dl_iterate_phdr(dlopen_notify_callback, &new_module_infos);
702
703     if (new_module_infos.size())
704     {
705         vogl::vector<btrace_module_info>& module_infos = get_module_infos();
706         module_infos.append(new_module_infos);
707         module_infos.sort();
708     }
709 }
710
711 //
712 // Other possibilities:
713 //   abi::__cxa_demangle() (cxxabi.h)
714 //   bfd_demangle (bfd.h)
715 //   cplus_demangle (demangle.h) libiberty code from gcc
716 //
717 // #include <cxxabi.h>
718 // char * abi::__cxa_demangle(const char* __mangled_name, char* __output_buffer,
719 //    size_t* __length, int* __status);
720 // int status = 0;
721 // char *function = abi::__cxa_demangle(name, NULL, NULL, &status);
722 //
723 const char *
724 btrace_demangle_function(const char *name, char *buffer, size_t buflen)
725 {
726     char *function = NULL;
727
728     // Mangled-name is function or variable name...
729     if (name[0] == '_' && name[1] == 'Z')
730         function = __cxa_demangle_gnu3(name);
731
732     if (function && function[0])
733         snprintf(buffer, buflen, "%s", function);
734     else
735         snprintf(buffer, buflen, "%s", name);
736
737     buffer[buflen - 1] = 0;
738     free(function); 
739     return buffer;
740 }
741
742 #if 0
743
744 // Based on code from this newsgroup:
745 //   https://groups.google.com/forum/#!topic/comp.unix.programmer/paDVOCaLP7g
746 //
747 // And this spec:
748 //   http://mentorembedded.github.io/cxx-abi/abi.html#mangling
749 static const char *get_op_name(char op1, char op2)
750 {
751     static const struct op_overloads
752     {
753         char       op_suffix[2];
754         const char *op_name;
755     } s_op_overloads[] =
756     {
757         //$ TODO mikesart: missing these:
758         { { 'n', 'w' }, " new" },
759         { { 'n', 'a' }, " new[]" },
760         { { 'd', 'l' }, " delete" },
761         { { 'd', 'a' }, " delete[]" },
762         { { 'p', 's' }, "+(unary)" },
763         { { 'n', 'g' }, "-(unary)" },
764         { { 'd', 'e' }, "*(unary)" },
765         { { 'a', 'd' }, "&(unary)" },
766         { { 'c', 'o' }, "~" },
767         { { 'p', 'l' }, "+" },
768         { { 'm', 'i' }, "-" },
769         { { 'm', 'l' }, "*" },
770         { { 'd', 'v' }, "/" },
771         { { 'r', 'm' }, "%" },
772         { { 'a', 'n' }, "&" },
773         { { 'o', 'r' }, "|" },
774         { { 'e', 'o' }, "^" },
775         { { 'a', 'S' }, "=" },
776         { { 'p', 'L' }, "+=" },
777         { { 'm', 'I' }, "-=" },
778         { { 'm', 'L' }, "*=" },
779         { { 'd', 'V' }, "/=" },
780         { { 'r', 'M' }, "%=" },
781         { { 'a', 'N' }, "&=" },
782         { { 'o', 'R' }, "|=" },
783         { { 'e', 'O' }, "^=" },
784         { { 'l', 's' }, "<<" },
785         { { 'r', 's' }, ">>" },
786         { { 'l', 'S' }, "<<=" },
787         { { 'r', 'S' }, ">>=" },
788         { { 'e', 'q' }, "==" },
789         { { 'n', 'e' }, "!=" },
790         { { 'l', 't' }, "<" },
791         { { 'g', 't' }, ">" },
792         { { 'l', 'e' }, "<=" },
793         { { 'g', 'e' }, ">=" },
794         { { 'n', 't' }, "!" },
795         { { 'a', 'a' }, "&&" },
796         { { 'o', 'o' }, "||" },
797         { { 'p', 'p' }, "++" },
798         { { 'm', 'm' }, "--" },
799         { { 'c', 'm' }, "," },
800         { { 'p', 'm' }, "->*" },
801         { { 'p', 't' }, "->" },
802         { { 'c', 'l' }, "()" },
803         { { 'i', 'x' }, "[]" },
804         { { 'q', 'u' }, "?" },
805         { { 'c', 'v' }, "(cast)" }, // cv <type>        # cast
806         { { 'l', 'i' }, "\"\"" },   // li <source-name> # operator ""
807         { { 0, 0 }, NULL }
808     };
809     const struct op_overloads *op;
810
811     for (op = s_op_overloads; op->op_name; op++)
812     {
813         if (op1 == op->op_suffix[0] && op2 == op->op_suffix[1])
814             return op->op_name;
815     }
816     return NULL;
817 }
818
819 static const char *read_len(const char *src, int *len)
820 {
821     int val;
822
823     for (val = 0; (*src >= '0') && (*src <= '9'); src++)
824     {
825         val *= 10;
826         val += (*src - '0');
827     }
828
829     *len = val;
830     return src;
831 }
832
833 void add_char(char **dst, size_t *buflen, char ch)
834 {
835     if (*buflen > 0)
836     {
837         *(*dst)++ = ch;
838         (*buflen)--;
839     }
840 }
841
842 const char *add_string(char **dst, size_t *buflen, const char *str, size_t len)
843 {
844     if (len && (*buflen > len))
845     {
846         const char *ret = str + len;
847
848         *buflen -= len;
849         while (len-- > 0)
850             *(*dst)++ = *str++; 
851         return ret;
852     }
853
854     return str;
855 }
856
857 const char *
858 btrace_demangle_function(const char *name, char *buffer, size_t buflen)
859 {
860     int i;
861     int srclen;
862     const char *src;
863     char *dst = buffer;
864     char *destbak;
865     int is_vtbl = 0;
866     int is_guard = 0;
867     int do_parens = 1;
868     int is_thunk = 0;
869     int is_thunk_neg = 0;
870     int thunk_offset = 0;
871     int is_nested_name = 0;
872     size_t buflen_orig = buflen;
873 /*
874     name = "_ZN6google8protobuf16strtou32_adaptorEPKcPPci";
875     name = "_ZN6google8protobuf24SimpleDescriptorDatabase15DescriptorIndexIPKNS0_19FileDescriptorProtoEE12AddExtensionERKNS0_20FieldDescriptorProtoES5_";
876     name = "_ZN6google8protobuf7strings10SubstituteEPKcRKNS1_8internal13SubstituteArgES7_S7_S7_S7_S7_S7_S7_S7_S7_";
877     name = "_ZN6google8protobuf8compiler28SourceTreeDescriptorDatabase24ValidationErrorCollector8AddErrorERKSsS5_PKNS0_7MessageENS0_14DescriptorPool14ErrorCollector13ErrorLocationES5_";
878     name = "_ZNSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEED1Ev";
879     name = "_ZNSt3tr110_HashtableISt4pairIPKN6google8protobuf11MessageLiteEiES1_IKS7_NS3_8internal13ExtensionInfoEESaISB_ESt10_Select1stISB_ESt8equal_toIS7_ENS3_4hashIS7_EENS_8__detail18_Mod_range_hashingENSJ_20_Default_ranged_hashENSJ_20_Prime_rehash_policyELb0ELb0ELb1EE9_M_insertERKSB_NS_17integral_constantIbLb1EEE";
880     */
881
882     // Mangled-name is function or variable name...
883     if (name[0] != '_' || name[1] != 'Z')
884     {
885         snprintf(buffer, buflen, "%s", name);
886         buffer[buflen - 1] = 0;
887         return buffer;
888     }
889
890     // Skip over '_Z'
891     src = name + 2;
892
893     if (src[0] == 'N')
894     {
895         // nested-name
896         src++;
897         is_nested_name = 1;
898     }
899     else if (src[0] == 'T' && src[1] == 'V')
900     {
901         // virtual table
902         src += 2;
903         is_vtbl = 1;
904
905         if (src[0] == 'N')
906         {
907             src++;
908             is_nested_name = 1;
909         }
910     }
911     else if (src[0] == 'T' && src[1] == 'h')
912     {
913         // call-offset
914         src += 2;
915
916         if (*src == 'n')
917         {
918             is_thunk_neg = 1;
919             src++;
920         }
921
922         src = read_len(src, &thunk_offset);
923
924         // Should be skipping over '_' and something else?
925         src += 2;
926         is_thunk = 1;
927     }
928     else if (src[0] == 'G' && src[1] == 'V')
929     {
930         // Guard variable for one-time initialization
931         src += 3; //$ TODO: Should this be += 3???
932         if (*src == 'N')
933             src++;
934         is_guard = 1;
935     }
936
937     destbak = dst;
938
939     src = read_len(src, &srclen);
940     src = add_string(&dst, &buflen, src, srclen);
941
942     // _ZN3nsA3nsB3nsC3xxxD2Ev == nsA::nsB::nsC::xxx::~xxx()
943     while (is_nested_name)
944     {
945         if (src[0] == 'S' && src[1] == 't')
946         {
947             add_string(&dst, &buflen, "std", 3);
948             src += 2;
949         }
950
951         src = read_len(src, &srclen); 
952         if (!srclen)
953             break;
954
955         add_string(&dst, &buflen, "::", 2);
956         destbak = dst;
957
958         src = add_string(&dst, &buflen, src, srclen);
959
960         is_nested_name = (src[0] >= '1') && (src[0] <= '9');
961     }
962
963     if (is_vtbl)
964     {
965         add_string(&dst, &buflen, "::[vtbl]", 8);
966         do_parens = 0;
967     }
968     else if (is_guard)
969     {
970         add_string(&dst, &buflen, "::[GuardVar]", 12);
971         do_parens = 0;
972     }
973     else if (src[0] == 'C' || src[0] == 'D')
974     {
975         // <ctor-dtor-name> ::= C1      # complete object constructor
976         //                  ::= C2      # base object constructor
977         //                  ::= C3      # complete object allocating constructor
978         //                  ::= D0      # deleting destructor
979         //                  ::= D1      # complete object destructor
980         //                  ::= D2      # base object destructor
981         add_string(&dst, &buflen, "::", 2);
982
983         if (*src == 'D')
984             add_char(&dst, &buflen, '~');
985
986         add_string(&dst, &buflen, destbak, srclen);
987     }
988     else if (src[0] >= '0' && *src <= '9')
989     {
990         add_string(&dst, &buflen, "::", 2);
991
992         if (is_thunk)
993         {
994             char thunk[64];
995             snprintf(thunk, sizeof(thunk), "[thunk%c%d]", is_thunk_neg ? '-' : '+', thunk_offset);
996             thunk[sizeof(thunk) - 1] = 0;
997
998             add_string(&dst, &buflen, thunk, strlen(thunk));
999         }
1000
1001         src = read_len(src, &srclen);
1002         src = add_string(&dst, &buflen, src, srclen);
1003
1004         if (*src != 'E')
1005             do_parens = 0;
1006     }
1007     else if (src[0] == 'c' && src[1] == 'v')
1008     {
1009         src += 2;
1010
1011         add_string(&dst, &buflen, "::operator (", 12);
1012
1013         src = read_len(src, &srclen);
1014         src = add_string(&dst, &buflen, src, srclen);
1015
1016         add_char(&dst, &buflen, ')');
1017     }
1018     else
1019     {
1020         const char *op_name = get_op_name(src[0], src[1]);
1021         if (op_name)
1022         {
1023             add_string(&dst, &buflen, "::operator", 10); 
1024             add_string(&dst, &buflen, op_name, strlen(op_name));
1025         }
1026     }
1027
1028     if (do_parens)
1029     {
1030         add_string(&dst, &buflen, "()", 2);
1031     }
1032
1033     add_char(&dst, &buflen, '\0');
1034     buffer[buflen_orig - 1] = 0;
1035     return buffer;
1036 }
1037
1038 #endif