2424"""
2525import argparse
2626import base64
27+ import ctypes .util
2728import fnmatch
2829import gzip
2930import json
3031import logging
3132import os
3233import struct
3334import sys
35+ import warnings
3436from bisect import bisect_left
37+ from functools import lru_cache
3538from pathlib import Path
3639from typing import Any
40+ from typing import Callable
3741from typing import Dict
3842from typing import List
3943from typing import Tuple
@@ -534,6 +538,62 @@ def read_fn(_, count, offset, __):
534538 return prog
535539
536540
541+ @lru_cache (maxsize = 1 )
542+ def _load_demangler () -> Callable [[str ], str ]:
543+ # The GNU C++ standard library contains a function called __cxa_demangle
544+ # which we can use to translate C++ function names.
545+ #
546+ # https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
547+ # https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a00026.html#aaf2180d3f67420d4e937e85b281b94a0
548+ #
549+ # Signature:
550+ # char * __cxxabiv1::__cxa_demangle ( const char *__mangled_name,
551+ # char *__output_buffer,
552+ # size_t *__length,
553+ # int *__status
554+ # )
555+ # The output buffer, when not provided, is allocated and must be freed via
556+ # free().
557+ stdcxx_name = ctypes .util .find_library ("stdc++" )
558+ stdc_name = ctypes .util .find_library ("c" )
559+ if not stdcxx_name or not stdc_name :
560+ warnings .warn ("Cannot import C++ demangling" )
561+ return lambda s : s
562+ stdcxx = ctypes .CDLL (stdcxx_name )
563+ stdc = ctypes .CDLL (stdc_name )
564+ demangle_func_p = stdcxx .__cxa_demangle
565+ demangle_func_p .restype = ctypes .POINTER (ctypes .c_char )
566+
567+ # The status variable has the following return codes:
568+ status_codes = {
569+ 0 : "The demangling operation succeeded." ,
570+ - 1 : "A memory allocation failure occurred." ,
571+ - 2 : "mangled_name is not a valid name under the C++ ABI mangling rules." ,
572+ - 3 : "One of the arguments is invalid." ,
573+ }
574+
575+ def demanglefn (mangled : str ) -> str :
576+ in_buffer = ctypes .c_char_p (mangled .encode ("utf-8" ))
577+ status = ctypes .c_int ()
578+ result = demangle_func_p (in_buffer , None , None , ctypes .pointer (status ))
579+ if status .value != 0 :
580+ msg = status_codes .get (status .value , "unknown error" )
581+ warnings .warn (f"Unable to demangle '{ mangled } ': { msg } " )
582+ return mangled
583+ strval = ctypes .cast (result , ctypes .c_char_p ).value .decode ("utf-8" ) # type: ignore
584+ stdc .free (result )
585+ return strval
586+
587+ return demanglefn
588+
589+
590+ def demangle (mangled : str ) -> str :
591+ if mangled .startswith ("_Z" ):
592+ return _load_demangler ()(mangled )
593+ else :
594+ return mangled
595+
596+
537597def print_user_stack_trace (regs : Object ) -> None :
538598 """
539599 Prints the userspace stack trace for regs, with the module name included
@@ -542,15 +602,32 @@ def print_user_stack_trace(regs: Object) -> None:
542602 prog = regs .prog_
543603 trace = prog .stack_trace (regs )
544604 print (" ------ userspace ---------" )
545- for frame , line in zip (trace , str (trace ).split ("\n " )):
605+ for i , frame in enumerate (trace ):
606+ name = demangle (frame .name )
607+
608+ offset = ""
609+ try :
610+ sym = frame .symbol ()
611+ offset = f"+0x{ frame .pc - sym .address :x} /0x{ sym .size :x} "
612+ except LookupError :
613+ pass
614+
546615 mod_text = ""
547616 try :
548617 mod = prog .module (frame .pc )
549618 off = frame .pc - mod .address_range [0 ]
550- mod_text = f" (in { mod .name } +0x{ off :x} )"
619+ mod_text = f" (from { mod .name } +0x{ off :x} )"
551620 except LookupError :
552621 pass
553- print (" " + line .rstrip () + mod_text )
622+
623+ source_text = ""
624+ try :
625+ source_info = ":" .join (map (str , frame .source ()))
626+ source_text = f" ({ source_info } )"
627+ except LookupError :
628+ pass
629+
630+ print (f" #{ i :<2d} { name } { offset } { source_text } { mod_text } " )
554631
555632
556633def dump_print_process (
0 commit comments