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