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"
48 #include "vogl_applauncher.h"
50 // our demangle routine from libelftc_dem_gnu3.c (from libcxxrt)
51 extern "C" char * __cxa_demangle_gnu3(const char *org);
53 // fast(er) backtrace routine from libunwind
54 extern "C" int unw_backtrace_skip (void **buffer, int size, int skip);
56 // Our dlopen mutex to protect g_module_infos operations
57 static vogl::mutex &get_dlopen_mutex()
59 static vogl::mutex g_dlopen_mutex(0, true);
60 return g_dlopen_mutex;
63 // An array of all the modules that have been loaded in this process.
64 static vogl::vector<btrace_module_info> &get_module_infos()
66 static vogl::vector<btrace_module_info> s_module_infos;
67 return s_module_infos;
70 btrace_glinfo& btrace_get_glinfo()
72 static btrace_glinfo s_info;
77 btrace_get(uintptr_t *addrs, size_t count_addrs, uint32_t addrs_to_skip)
80 return unw_backtrace_skip((void **)addrs, (int)count_addrs, addrs_to_skip);
84 unw_context_t context;
86 unw_getcontext(&context);
87 unw_init_local(&cursor, &context);
89 while (count < count_addrs)
93 if (unw_step(&cursor) <= 0)
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), ...
106 addrs[count++] = (uintptr_t)addr;
109 // Get function name and offset from libunwind. Should match
110 // the libbacktrace code down below.
114 unw_get_proc_name(&cursor, function, sizeof(function), &offset);
115 printf ("0x%" PRIxPTR ": %s [+0x%" PRIxPTR "]\n", addr, function, offset);
124 btrace_get_calling_module()
127 unw_context_t context;
128 const char *calling_fname = NULL;
130 unw_getcontext(&context);
131 unw_init_local(&cursor, &context);
138 if (unw_step(&cursor) <= 0)
141 unw_get_reg(&cursor, UNW_REG_IP, &addr);
144 if (dladdr((void *)addr, &dl_info) == 0)
147 if (dl_info.dli_fname)
151 calling_fname = dl_info.dli_fname;
153 else if(strcmp(calling_fname, dl_info.dli_fname))
155 return dl_info.dli_fname;
164 btrace_module_search (const void *vkey, const void *ventry)
166 const uintptr_t *key = (const uintptr_t *)vkey;
167 const struct btrace_module_info *entry = (const struct btrace_module_info *) ventry;
171 if (addr < entry->base_address)
173 else if (addr >= entry->base_address + entry->address_size)
179 btrace_get_current_module()
181 void *paddr = __builtin_return_address(0);
182 uintptr_t addr = (uintptr_t)paddr;
183 vogl::scoped_mutex lock(get_dlopen_mutex());
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())
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;
196 // Well, that failed for some reason. Try dladdr.
198 if (dladdr(paddr, &dl_info) && dl_info.dli_fname)
199 return dl_info.dli_fname;
206 btrace_err_callback(void *data, const char *msg, int errnum)
208 VOGL_NOTE_UNUSED(data);
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.
217 const char *errstr = errnum ? strerror(errnum) : "";
218 printf("libbacktrace error: %s %s\n", msg, errstr);
224 btrace_syminfo_callback(void *data, uintptr_t addr, const char *symname, uintptr_t symval, uintptr_t symsize)
226 VOGL_NOTE_UNUSED(symsize);
230 btrace_info *info = (btrace_info *)data;
231 info->function = symname;
232 info->offset = addr - symval;
237 btrace_pcinfo_callback(void *data, uintptr_t addr, const char *file, int line, const char *func)
239 VOGL_NOTE_UNUSED(addr);
241 btrace_info *frame = (btrace_info *)data;
243 frame->filename = file;
244 frame->linenumber = line;
246 // Don't overwrite function string if we got a blank one for some reason.
248 frame->function = func;
253 backtrace_initialize_error_callback(void *data, const char *msg, int errnum)
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.
262 module_info_init_state(btrace_module_info *module_info)
264 if (!module_info->backtrace_state)
266 module_info->backtrace_state = backtrace_create_state(
267 module_info->filename, 0, backtrace_initialize_error_callback, NULL);
268 if (module_info->backtrace_state)
270 elf_get_uuid(module_info->backtrace_state, module_info->filename,
271 module_info->uuid, &module_info->uuid_len);
275 return !!module_info->backtrace_state;
279 btrace_resolve_addr(btrace_info *info, uintptr_t addr, uint32_t flags)
281 vogl::scoped_mutex lock(get_dlopen_mutex());
282 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
284 if (!module_infos.size())
285 btrace_dlopen_notify(NULL);
290 info->function = NULL;
291 info->filename = NULL;
292 info->linenumber = 0;
293 info->demangled_func_buf[0] = 0;
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);
299 info->module = module_info->filename;
301 if (module_info_init_state(module_info))
303 backtrace_fileline_initialize(module_info->backtrace_state, module_info->base_address,
304 module_info->is_exe, backtrace_initialize_error_callback, NULL);
306 // Get function name and offset.
307 backtrace_syminfo(module_info->backtrace_state, addr, btrace_syminfo_callback,
308 btrace_err_callback, info);
310 if (flags & BTRACE_RESOLVE_ADDR_GET_FILENAME)
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);
317 if ((flags & BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC) && info->function && info->function[0])
319 info->function = btrace_demangle_function(info->function, info->demangled_func_buf, sizeof(info->demangled_func_buf));
324 info->offset = addr - module_info->base_address;
328 if (!info->module || !info->module[0])
331 if (dladdr((void *)addr, &dl_info))
332 info->module = dl_info.dli_fname;
334 info->offset = addr - (uintptr_t)dl_info.dli_fbase;
339 const char *module_name = strrchr(info->module, '/');
341 info->module = module_name + 1;
354 get_hex_value(char ch)
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')
367 btrace_uuid_str_to_uuid(uint8_t uuid[20], const char *uuid_str)
371 for (len = 0; (len < 20) && *uuid_str; len++)
373 int val0 = get_hex_value(*uuid_str++);
374 int val1 = get_hex_value(*uuid_str++);
375 if (val0 < 0 || val1 < 0)
377 uuid[len] = (val0 << 4) | val1;
384 btrace_uuid_to_str(char uuid_str[41], const uint8_t *uuid, int len)
387 static const char hex[] = "0123456789abcdef";
391 for (i = 0; i < len; i++)
395 *uuid_str++ = hex[c >> 4];
396 *uuid_str++ = hex[c & 0xf];
402 btrace_get_machine_info(vogl::json_node *machine_info)
404 vogl::scoped_mutex lock(get_dlopen_mutex());
405 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
407 if (!module_infos.size())
408 btrace_dlopen_notify(NULL);
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++)
414 btrace_module_info &module_info = module_infos[i];
415 char uuid_str[sizeof(module_info.uuid) * 2 + 1];
417 // Make sure we've gotten the uuid.
418 module_info_init_state(&module_info);
420 btrace_uuid_to_str(uuid_str, module_info.uuid, sizeof(module_info.uuid));
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);
427 if (module_info.is_exe)
428 arr.add_value("(exe)");
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))
435 vogl::json_node &env_list_node = machine_info->add_array("environ_list");
437 char *ptr = proc_file.get_ptr();
438 uint size = proc_file.size_in_bytes();
440 while ((var < ptr + size) && *var)
442 env_list_node.add_value(var);
443 var += strlen(var) + 1;
447 if (vogl::file_utils::read_proc_file("/proc/self/cmdline", proc_file))
449 vogl::json_node &env_list_node = machine_info->add_array("cmdline");
451 char *ptr = proc_file.get_ptr();
452 uint size = proc_file.size_in_bytes();
454 while ((var < ptr + size) && *var)
456 env_list_node.add_value(var);
457 var += strlen(var) + 1;
462 if (!uname(&uname_data))
464 vogl::json_node &env_list_node = machine_info->add_array("uname");
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);
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))
478 vogl::json_node &env_list_node = machine_info->add_array("current_clocksource");
479 env_list_node.add_value(proc_file.get_ptr());
482 if (vogl::file_utils::read_proc_file("/proc/driver/nvidia/version", proc_file))
484 vogl::json_node &env_list_node = machine_info->add_array("nvidia_version");
485 env_list_node.add_value(proc_file.get_ptr());
487 if (vogl::file_utils::read_proc_file("/proc/driver/nvidia/gpus/0/information", proc_file))
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());
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);
507 uintptr_t addrs[128];
508 int count = btrace_get(addrs, VOGL_ARRAY_SIZE(addrs), 0);
510 for (i = 0; i < count; i++)
515 ret = btrace_resolve_addr(&info, addrs[i],
516 BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC);
518 printf(" 0x%" PRIxPTR " ", addrs[i]);
521 printf("%s", info.module);
523 if (info.function[0])
525 printf(": %s", info.function);
528 printf("+0x%" PRIxPTR, info.offset);
530 if (info.filename && info.filename[0])
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++)
539 slash_last = slash_cur;
543 const char *filename = slash_last ? slash_last : slash_cur;
544 printf(": %s:%d", filename, info.linenumber);
555 module_info_less_than_func(const btrace_module_info &key0, const btrace_module_info &key1)
557 //$ TODO: We can get rid of this function and it'll just call the operator< func?
562 dlopen_notify_callback(struct dl_phdr_info *info, size_t size, void *data)
564 VOGL_NOTE_UNUSED(size);
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();
572 // If we don't have a filename and we haven't added our main exe yet, do it.
573 if (!filename || !filename[0])
575 if (!module_infos.size() && !new_module_infos->size())
578 ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf));
579 if ((ret > 0) && (ret < (ssize_t)sizeof(buf)))
585 if (!filename || !filename[0])
589 uintptr_t addr_start = 0;
590 uintptr_t addr_end = 0;
592 for (int i = 0; i < info->dlpi_phnum; i++)
594 if (info->dlpi_phdr[i].p_type == PT_LOAD)
598 addr_start = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
599 addr_end = addr_start + info->dlpi_phdr[i].p_memsz;
603 uintptr_t addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr + info->dlpi_phdr[i].p_memsz;
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));
619 int ret = module_infos.find_sorted(module_info, module_info_less_than_func);
620 if (ret == vogl::cInvalidIndex)
622 module_info.filename = vogl::vogl_strdup(filename);
623 if (module_info.filename)
625 new_module_infos->push_back(module_info);
632 btrace_dlopen_add_module(const btrace_module_info &module_info_in)
634 vogl::scoped_mutex lock(get_dlopen_mutex());
635 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
637 int ret = module_infos.find_sorted(module_info_in, module_info_less_than_func);
638 if (ret == vogl::cInvalidIndex)
640 btrace_module_info module_info = module_info_in;
642 if (module_info_init_state(&module_info))
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))
648 module_infos.push_back(module_info);
659 btrace_get_debug_filename(const char *filename)
661 vogl::scoped_mutex lock(get_dlopen_mutex());
662 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
664 vogl::dynamic_string fname = filename;
665 for (uint i = 0; i < module_infos.size(); i++)
667 btrace_module_info &module_info = module_infos[i];
669 if (fname.compare(module_info.filename, true) == 0)
671 if (module_info_init_state(&module_info))
673 backtrace_fileline_initialize(module_info.backtrace_state, module_info.base_address,
674 module_info.is_exe, backtrace_initialize_error_callback, NULL);
676 return backtrace_get_debug_filename(module_info.backtrace_state);
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.
686 void __attribute__((force_align_arg_pointer))
690 btrace_dlopen_notify(const char *filename)
692 VOGL_NOTE_UNUSED(filename);
694 // Make sure the vogl heap is initialized.
695 vogl::vogl_init_heap();
697 vogl::scoped_mutex lock(get_dlopen_mutex());
698 vogl::vector<btrace_module_info> new_module_infos;
700 // Iterator through all the currently loaded modules.
701 dl_iterate_phdr(dlopen_notify_callback, &new_module_infos);
703 if (new_module_infos.size())
705 vogl::vector<btrace_module_info>& module_infos = get_module_infos();
706 module_infos.append(new_module_infos);
712 // Other possibilities:
713 // abi::__cxa_demangle() (cxxabi.h)
714 // bfd_demangle (bfd.h)
715 // cplus_demangle (demangle.h) libiberty code from gcc
717 // #include <cxxabi.h>
718 // char * abi::__cxa_demangle(const char* __mangled_name, char* __output_buffer,
719 // size_t* __length, int* __status);
721 // char *function = abi::__cxa_demangle(name, NULL, NULL, &status);
724 btrace_demangle_function(const char *name, char *buffer, size_t buflen)
726 char *function = NULL;
728 // Mangled-name is function or variable name...
729 if (name[0] == '_' && name[1] == 'Z')
730 function = __cxa_demangle_gnu3(name);
732 if (function && function[0])
733 snprintf(buffer, buflen, "%s", function);
735 snprintf(buffer, buflen, "%s", name);
737 buffer[buflen - 1] = 0;
744 // Based on code from this newsgroup:
745 // https://groups.google.com/forum/#!topic/comp.unix.programmer/paDVOCaLP7g
748 // http://mentorembedded.github.io/cxx-abi/abi.html#mangling
749 static const char *get_op_name(char op1, char op2)
751 static const struct op_overloads
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 ""
809 const struct op_overloads *op;
811 for (op = s_op_overloads; op->op_name; op++)
813 if (op1 == op->op_suffix[0] && op2 == op->op_suffix[1])
819 static const char *read_len(const char *src, int *len)
823 for (val = 0; (*src >= '0') && (*src <= '9'); src++)
833 void add_char(char **dst, size_t *buflen, char ch)
842 const char *add_string(char **dst, size_t *buflen, const char *str, size_t len)
844 if (len && (*buflen > len))
846 const char *ret = str + len;
858 btrace_demangle_function(const char *name, char *buffer, size_t buflen)
869 int is_thunk_neg = 0;
870 int thunk_offset = 0;
871 int is_nested_name = 0;
872 size_t buflen_orig = buflen;
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";
882 // Mangled-name is function or variable name...
883 if (name[0] != '_' || name[1] != 'Z')
885 snprintf(buffer, buflen, "%s", name);
886 buffer[buflen - 1] = 0;
899 else if (src[0] == 'T' && src[1] == 'V')
911 else if (src[0] == 'T' && src[1] == 'h')
922 src = read_len(src, &thunk_offset);
924 // Should be skipping over '_' and something else?
928 else if (src[0] == 'G' && src[1] == 'V')
930 // Guard variable for one-time initialization
931 src += 3; //$ TODO: Should this be += 3???
939 src = read_len(src, &srclen);
940 src = add_string(&dst, &buflen, src, srclen);
942 // _ZN3nsA3nsB3nsC3xxxD2Ev == nsA::nsB::nsC::xxx::~xxx()
943 while (is_nested_name)
945 if (src[0] == 'S' && src[1] == 't')
947 add_string(&dst, &buflen, "std", 3);
951 src = read_len(src, &srclen);
955 add_string(&dst, &buflen, "::", 2);
958 src = add_string(&dst, &buflen, src, srclen);
960 is_nested_name = (src[0] >= '1') && (src[0] <= '9');
965 add_string(&dst, &buflen, "::[vtbl]", 8);
970 add_string(&dst, &buflen, "::[GuardVar]", 12);
973 else if (src[0] == 'C' || src[0] == 'D')
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);
984 add_char(&dst, &buflen, '~');
986 add_string(&dst, &buflen, destbak, srclen);
988 else if (src[0] >= '0' && *src <= '9')
990 add_string(&dst, &buflen, "::", 2);
995 snprintf(thunk, sizeof(thunk), "[thunk%c%d]", is_thunk_neg ? '-' : '+', thunk_offset);
996 thunk[sizeof(thunk) - 1] = 0;
998 add_string(&dst, &buflen, thunk, strlen(thunk));
1001 src = read_len(src, &srclen);
1002 src = add_string(&dst, &buflen, src, srclen);
1007 else if (src[0] == 'c' && src[1] == 'v')
1011 add_string(&dst, &buflen, "::operator (", 12);
1013 src = read_len(src, &srclen);
1014 src = add_string(&dst, &buflen, src, srclen);
1016 add_char(&dst, &buflen, ')');
1020 const char *op_name = get_op_name(src[0], src[1]);
1023 add_string(&dst, &buflen, "::operator", 10);
1024 add_string(&dst, &buflen, op_name, strlen(op_name));
1030 add_string(&dst, &buflen, "()", 2);
1033 add_char(&dst, &buflen, '\0');
1034 buffer[buflen_orig - 1] = 0;