1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
29 #define __STDC_FORMAT_MACROS
36 #include <sys/utsname.h>
38 // We need local unwinding only (should be more optimal than local + remote).
39 #define UNW_LOCAL_ONLY
40 #include <libunwind.h>
43 #include "backtrace.h"
45 #include "vogl_core.h"
46 #include "vogl_json.h"
47 #include "vogl_file_utils.h"
49 // our demangle routine from libelftc_dem_gnu3.c (from libcxxrt)
50 extern "C" char * __cxa_demangle_gnu3(const char *org);
52 // fast(er) backtrace routine from libunwind
53 extern "C" int unw_backtrace_skip (void **buffer, int size, int skip);
55 // Our dlopen mutex to protect g_module_infos operations
56 static vogl::mutex &get_dlopen_mutex()
58 static vogl::mutex g_dlopen_mutex(0, true);
59 return g_dlopen_mutex;
62 // An array of all the modules that have been loaded in this process.
63 static vogl::vector<btrace_module_info> &get_module_infos()
65 static vogl::vector<btrace_module_info> s_module_infos;
66 return s_module_infos;
69 btrace_glinfo& btrace_get_glinfo()
71 static btrace_glinfo s_info;
76 btrace_get(uintptr_t *addrs, size_t count_addrs, uint32_t addrs_to_skip)
79 return unw_backtrace_skip((void **)addrs, (int)count_addrs, addrs_to_skip);
83 unw_context_t context;
85 unw_getcontext(&context);
86 unw_init_local(&cursor, &context);
88 while (count < count_addrs)
92 if (unw_step(&cursor) <= 0)
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), ...
105 addrs[count++] = (uintptr_t)addr;
108 // Get function name and offset from libunwind. Should match
109 // the libbacktrace code down below.
113 unw_get_proc_name(&cursor, function, sizeof(function), &offset);
114 printf ("0x%" PRIxPTR ": %s [+0x%" PRIxPTR "]\n", addr, function, offset);
123 btrace_get_calling_module()
126 unw_context_t context;
127 const char *calling_fname = NULL;
129 unw_getcontext(&context);
130 unw_init_local(&cursor, &context);
137 if (unw_step(&cursor) <= 0)
140 unw_get_reg(&cursor, UNW_REG_IP, &addr);
143 if (dladdr((void *)addr, &dl_info) == 0)
146 if (dl_info.dli_fname)
150 calling_fname = dl_info.dli_fname;
152 else if(strcmp(calling_fname, dl_info.dli_fname))
154 return dl_info.dli_fname;
163 btrace_module_search (const void *vkey, const void *ventry)
165 const uintptr_t *key = (const uintptr_t *)vkey;
166 const struct btrace_module_info *entry = (const struct btrace_module_info *) ventry;
170 if (addr < entry->base_address)
172 else if (addr >= entry->base_address + entry->address_size)
178 btrace_get_current_module()
180 void *paddr = __builtin_return_address(0);
181 uintptr_t addr = (uintptr_t)paddr;
182 vogl::scoped_mutex lock(get_dlopen_mutex());
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())
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;
195 // Well, that failed for some reason. Try dladdr.
197 if (dladdr(paddr, &dl_info) && dl_info.dli_fname)
198 return dl_info.dli_fname;
205 btrace_err_callback(void *data, const char *msg, int errnum)
207 VOGL_NOTE_UNUSED(data);
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.
216 const char *errstr = errnum ? strerror(errnum) : "";
217 printf("libbacktrace error: %s %s\n", msg, errstr);
223 btrace_syminfo_callback(void *data, uintptr_t addr, const char *symname, uintptr_t symval, uintptr_t symsize)
225 VOGL_NOTE_UNUSED(symsize);
229 btrace_info *info = (btrace_info *)data;
230 info->function = symname;
231 info->offset = addr - symval;
236 btrace_pcinfo_callback(void *data, uintptr_t addr, const char *file, int line, const char *func)
238 VOGL_NOTE_UNUSED(addr);
240 btrace_info *frame = (btrace_info *)data;
242 frame->filename = file;
243 frame->linenumber = line;
245 // Don't overwrite function string if we got a blank one for some reason.
247 frame->function = func;
252 backtrace_initialize_error_callback(void *data, const char *msg, int errnum)
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.
261 module_info_init_state(btrace_module_info *module_info)
263 if (!module_info->backtrace_state)
265 module_info->backtrace_state = backtrace_create_state(
266 module_info->filename, 0, backtrace_initialize_error_callback, NULL);
267 if (module_info->backtrace_state)
269 elf_get_uuid(module_info->backtrace_state, module_info->filename,
270 module_info->uuid, &module_info->uuid_len);
274 return !!module_info->backtrace_state;
278 btrace_resolve_addr(btrace_info *info, uintptr_t addr, uint32_t flags)
280 vogl::scoped_mutex lock(get_dlopen_mutex());
281 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
283 if (!module_infos.size())
284 btrace_dlopen_notify(NULL);
289 info->function = NULL;
290 info->filename = NULL;
291 info->linenumber = 0;
292 info->demangled_func_buf[0] = 0;
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);
298 info->module = module_info->filename;
300 if (module_info_init_state(module_info))
302 backtrace_fileline_initialize(module_info->backtrace_state, module_info->base_address,
303 module_info->is_exe, backtrace_initialize_error_callback, NULL);
305 // Get function name and offset.
306 backtrace_syminfo(module_info->backtrace_state, addr, btrace_syminfo_callback,
307 btrace_err_callback, info);
309 if (flags & BTRACE_RESOLVE_ADDR_GET_FILENAME)
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);
316 if ((flags & BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC) && info->function && info->function[0])
318 info->function = btrace_demangle_function(info->function, info->demangled_func_buf, sizeof(info->demangled_func_buf));
323 info->offset = addr - module_info->base_address;
327 if (!info->module || !info->module[0])
330 if (dladdr((void *)addr, &dl_info))
331 info->module = dl_info.dli_fname;
333 info->offset = addr - (uintptr_t)dl_info.dli_fbase;
338 const char *module_name = strrchr(info->module, '/');
340 info->module = module_name + 1;
353 get_hex_value(char ch)
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')
366 btrace_uuid_str_to_uuid(uint8_t uuid[20], const char *uuid_str)
370 for (len = 0; (len < 20) && *uuid_str; len++)
372 int val0 = get_hex_value(*uuid_str++);
373 int val1 = get_hex_value(*uuid_str++);
374 if (val0 < 0 || val1 < 0)
376 uuid[len] = (val0 << 4) | val1;
383 btrace_uuid_to_str(char uuid_str[41], const uint8_t *uuid, int len)
386 static const char hex[] = "0123456789abcdef";
390 for (i = 0; i < len; i++)
394 *uuid_str++ = hex[c >> 4];
395 *uuid_str++ = hex[c & 0xf];
401 btrace_get_machine_info(vogl::json_node *machine_info)
403 vogl::scoped_mutex lock(get_dlopen_mutex());
404 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
406 if (!module_infos.size())
407 btrace_dlopen_notify(NULL);
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++)
413 btrace_module_info &module_info = module_infos[i];
414 char uuid_str[sizeof(module_info.uuid) * 2 + 1];
416 // Make sure we've gotten the uuid.
417 module_info_init_state(&module_info);
419 btrace_uuid_to_str(uuid_str, module_info.uuid, sizeof(module_info.uuid));
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);
426 if (module_info.is_exe)
427 arr.add_value("(exe)");
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))
434 vogl::json_node &env_list_node = machine_info->add_array("environ_list");
436 char *ptr = proc_file.get_ptr();
437 uint size = proc_file.size_in_bytes();
439 while ((var < ptr + size) && *var)
441 env_list_node.add_value(var);
442 var += strlen(var) + 1;
446 if (vogl::file_utils::read_proc_file("/proc/self/cmdline", proc_file))
448 vogl::json_node &env_list_node = machine_info->add_array("cmdline");
450 char *ptr = proc_file.get_ptr();
451 uint size = proc_file.size_in_bytes();
453 while ((var < ptr + size) && *var)
455 env_list_node.add_value(var);
456 var += strlen(var) + 1;
461 if (!uname(&uname_data))
463 vogl::json_node &env_list_node = machine_info->add_array("uname");
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);
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))
477 vogl::json_node &env_list_node = machine_info->add_array("current_clocksource");
478 env_list_node.add_value(proc_file.get_ptr());
481 if (vogl::file_utils::read_proc_file("/proc/driver/nvidia/version", proc_file))
483 vogl::json_node &env_list_node = machine_info->add_array("nvidia_version");
484 env_list_node.add_value(proc_file.get_ptr());
486 if (vogl::file_utils::read_proc_file("/proc/driver/nvidia/gpus/0/information", proc_file))
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());
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);
506 uintptr_t addrs[128];
507 int count = btrace_get(addrs, VOGL_ARRAY_SIZE(addrs), 0);
509 for (i = 0; i < count; i++)
514 ret = btrace_resolve_addr(&info, addrs[i],
515 BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC);
517 printf(" 0x%" PRIxPTR " ", addrs[i]);
520 printf("%s", info.module);
522 if (info.function[0])
524 printf(": %s", info.function);
527 printf("+0x%" PRIxPTR, info.offset);
529 if (info.filename && info.filename[0])
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++)
538 slash_last = slash_cur;
542 const char *filename = slash_last ? slash_last : slash_cur;
543 printf(": %s:%d", filename, info.linenumber);
554 module_info_less_than_func(const btrace_module_info &key0, const btrace_module_info &key1)
556 //$ TODO: We can get rid of this function and it'll just call the operator< func?
561 dlopen_notify_callback(struct dl_phdr_info *info, size_t size, void *data)
563 VOGL_NOTE_UNUSED(size);
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();
571 // If we don't have a filename and we haven't added our main exe yet, do it.
572 if (!filename || !filename[0])
574 if (!module_infos.size() && !new_module_infos->size())
577 ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf));
578 if ((ret > 0) && (ret < (ssize_t)sizeof(buf)))
584 if (!filename || !filename[0])
588 uintptr_t addr_start = 0;
589 uintptr_t addr_end = 0;
591 for (int i = 0; i < info->dlpi_phnum; i++)
593 if (info->dlpi_phdr[i].p_type == PT_LOAD)
597 addr_start = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
598 addr_end = addr_start + info->dlpi_phdr[i].p_memsz;
602 uintptr_t addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
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));
618 int ret = module_infos.find_sorted(module_info, module_info_less_than_func);
619 if (ret == vogl::cInvalidIndex)
621 module_info.filename = vogl::vogl_strdup(filename);
622 if (module_info.filename)
624 new_module_infos->push_back(module_info);
631 btrace_dlopen_add_module(const btrace_module_info &module_info_in)
633 vogl::scoped_mutex lock(get_dlopen_mutex());
634 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
636 int ret = module_infos.find_sorted(module_info_in, module_info_less_than_func);
637 if (ret == vogl::cInvalidIndex)
639 btrace_module_info module_info = module_info_in;
641 if (module_info_init_state(&module_info))
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))
647 module_infos.push_back(module_info);
658 btrace_get_debug_filename(const char *filename)
660 vogl::scoped_mutex lock(get_dlopen_mutex());
661 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
663 vogl::dynamic_string fname = filename;
664 for (uint i = 0; i < module_infos.size(); i++)
666 btrace_module_info &module_info = module_infos[i];
668 if (fname.compare(module_info.filename, true) == 0)
670 if (module_info_init_state(&module_info))
672 backtrace_fileline_initialize(module_info.backtrace_state, module_info.base_address,
673 module_info.is_exe, backtrace_initialize_error_callback, NULL);
675 return backtrace_get_debug_filename(module_info.backtrace_state);
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.
685 void __attribute__((force_align_arg_pointer))
689 btrace_dlopen_notify(const char *filename)
691 VOGL_NOTE_UNUSED(filename);
693 // Make sure the vogl heap is initialized.
694 vogl::vogl_init_heap();
696 vogl::scoped_mutex lock(get_dlopen_mutex());
697 vogl::vector<btrace_module_info> new_module_infos;
699 // Iterator through all the currently loaded modules.
700 dl_iterate_phdr(dlopen_notify_callback, &new_module_infos);
702 if (new_module_infos.size())
704 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
705 module_infos.append(new_module_infos);
711 // Other possibilities:
712 // abi::__cxa_demangle() (cxxabi.h)
713 // bfd_demangle (bfd.h)
714 // cplus_demangle (demangle.h) libiberty code from gcc
716 // #include <cxxabi.h>
717 // char * abi::__cxa_demangle(const char* __mangled_name, char* __output_buffer,
718 // size_t* __length, int* __status);
720 // char *function = abi::__cxa_demangle(name, NULL, NULL, &status);
723 btrace_demangle_function(const char *name, char *buffer, size_t buflen)
725 char *function = NULL;
727 // Mangled-name is function or variable name...
728 if (name[0] == '_' && name[1] == 'Z')
729 function = __cxa_demangle_gnu3(name);
731 if (function && function[0])
732 snprintf(buffer, buflen, "%s", function);
734 snprintf(buffer, buflen, "%s", name);
736 buffer[buflen - 1] = 0;
743 // Based on code from this newsgroup:
744 // https://groups.google.com/forum/#!topic/comp.unix.programmer/paDVOCaLP7g
747 // http://mentorembedded.github.io/cxx-abi/abi.html#mangling
748 static const char *get_op_name(char op1, char op2)
750 static const struct op_overloads
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 ""
808 const struct op_overloads *op;
810 for (op = s_op_overloads; op->op_name; op++)
812 if (op1 == op->op_suffix[0] && op2 == op->op_suffix[1])
818 static const char *read_len(const char *src, int *len)
822 for (val = 0; (*src >= '0') && (*src <= '9'); src++)
832 void add_char(char **dst, size_t *buflen, char ch)
841 const char *add_string(char **dst, size_t *buflen, const char *str, size_t len)
843 if (len && (*buflen > len))
845 const char *ret = str + len;
857 btrace_demangle_function(const char *name, char *buffer, size_t buflen)
868 int is_thunk_neg = 0;
869 int thunk_offset = 0;
870 int is_nested_name = 0;
871 size_t buflen_orig = buflen;
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";
881 // Mangled-name is function or variable name...
882 if (name[0] != '_' || name[1] != 'Z')
884 snprintf(buffer, buflen, "%s", name);
885 buffer[buflen - 1] = 0;
898 else if (src[0] == 'T' && src[1] == 'V')
910 else if (src[0] == 'T' && src[1] == 'h')
921 src = read_len(src, &thunk_offset);
923 // Should be skipping over '_' and something else?
927 else if (src[0] == 'G' && src[1] == 'V')
929 // Guard variable for one-time initialization
930 src += 3; //$ TODO: Should this be += 3???
938 src = read_len(src, &srclen);
939 src = add_string(&dst, &buflen, src, srclen);
941 // _ZN3nsA3nsB3nsC3xxxD2Ev == nsA::nsB::nsC::xxx::~xxx()
942 while (is_nested_name)
944 if (src[0] == 'S' && src[1] == 't')
946 add_string(&dst, &buflen, "std", 3);
950 src = read_len(src, &srclen);
954 add_string(&dst, &buflen, "::", 2);
957 src = add_string(&dst, &buflen, src, srclen);
959 is_nested_name = (src[0] >= '1') && (src[0] <= '9');
964 add_string(&dst, &buflen, "::[vtbl]", 8);
969 add_string(&dst, &buflen, "::[GuardVar]", 12);
972 else if (src[0] == 'C' || src[0] == 'D')
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);
983 add_char(&dst, &buflen, '~');
985 add_string(&dst, &buflen, destbak, srclen);
987 else if (src[0] >= '0' && *src <= '9')
989 add_string(&dst, &buflen, "::", 2);
994 snprintf(thunk, sizeof(thunk), "[thunk%c%d]", is_thunk_neg ? '-' : '+', thunk_offset);
995 thunk[sizeof(thunk) - 1] = 0;
997 add_string(&dst, &buflen, thunk, strlen(thunk));
1000 src = read_len(src, &srclen);
1001 src = add_string(&dst, &buflen, src, srclen);
1006 else if (src[0] == 'c' && src[1] == 'v')
1010 add_string(&dst, &buflen, "::operator (", 12);
1012 src = read_len(src, &srclen);
1013 src = add_string(&dst, &buflen, src, srclen);
1015 add_char(&dst, &buflen, ')');
1019 const char *op_name = get_op_name(src[0], src[1]);
1022 add_string(&dst, &buflen, "::operator", 10);
1023 add_string(&dst, &buflen, op_name, strlen(op_name));
1029 add_string(&dst, &buflen, "()", 2);
1032 add_char(&dst, &buflen, '\0');
1033 buffer[buflen_orig - 1] = 0;