mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

TracyCallstack.cpp (14972B)


      1 #include <stdio.h>
      2 #include <string.h>
      3 #include "TracyCallstack.hpp"
      4 
      5 #ifdef TRACY_HAS_CALLSTACK
      6 
      7 #if TRACY_HAS_CALLSTACK == 1
      8 #  ifndef NOMINMAX
      9 #    define NOMINMAX
     10 #  endif
     11 #  include <windows.h>
     12 #  ifdef _MSC_VER
     13 #    pragma warning( push )
     14 #    pragma warning( disable : 4091 )
     15 #  endif
     16 #  include <dbghelp.h>
     17 #  ifdef _MSC_VER
     18 #    pragma warning( pop )
     19 #  endif
     20 #elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 6
     21 #  include "../libbacktrace/backtrace.hpp"
     22 #  include <dlfcn.h>
     23 #  include <cxxabi.h>
     24 #elif TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 5
     25 #  include <dlfcn.h>
     26 #  include <cxxabi.h>
     27 #endif
     28 
     29 namespace tracy
     30 {
     31 
     32 #if TRACY_HAS_CALLSTACK == 1
     33 
     34 enum { MaxCbTrace = 16 };
     35 
     36 int cb_num;
     37 CallstackEntry cb_data[MaxCbTrace];
     38 
     39 extern "C" { t_RtlWalkFrameChain RtlWalkFrameChain = 0; }
     40 
     41 #if defined __MINGW32__ && API_VERSION_NUMBER < 12
     42 extern "C" {
     43 // Actual required API_VERSION_NUMBER is unknown because it is undocumented. These functions are not present in at least v11.
     44 DWORD IMAGEAPI SymAddrIncludeInlineTrace(HANDLE hProcess, DWORD64 Address);
     45 BOOL IMAGEAPI SymQueryInlineTrace(HANDLE hProcess, DWORD64 StartAddress, DWORD StartContext, DWORD64 StartRetAddress,
     46     DWORD64 CurAddress, LPDWORD CurContext, LPDWORD CurFrameIndex);
     47 BOOL IMAGEAPI SymFromInlineContext(HANDLE hProcess, DWORD64 Address, ULONG InlineContext, PDWORD64 Displacement,
     48     PSYMBOL_INFO Symbol);
     49 BOOL IMAGEAPI SymGetLineFromInlineContext(HANDLE hProcess, DWORD64 qwAddr, ULONG InlineContext,
     50     DWORD64 qwModuleBaseAddress, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);
     51 };
     52 #endif
     53 
     54 void InitCallstack()
     55 {
     56 #ifdef UNICODE
     57     RtlWalkFrameChain = (t_RtlWalkFrameChain)GetProcAddress( GetModuleHandle( L"ntdll.dll" ), "RtlWalkFrameChain" );
     58 #else
     59     RtlWalkFrameChain = (t_RtlWalkFrameChain)GetProcAddress( GetModuleHandle( "ntdll.dll" ), "RtlWalkFrameChain" );
     60 #endif
     61     SymInitialize( GetCurrentProcess(), nullptr, true );
     62     SymSetOptions( SYMOPT_LOAD_LINES );
     63 }
     64 
     65 const char* DecodeCallstackPtrFast( uint64_t ptr )
     66 {
     67     static char ret[1024];
     68     const auto proc = GetCurrentProcess();
     69 
     70     char buf[sizeof( SYMBOL_INFO ) + 1024];
     71     auto si = (SYMBOL_INFO*)buf;
     72     si->SizeOfStruct = sizeof( SYMBOL_INFO );
     73     si->MaxNameLen = 1024;
     74 
     75     if( SymFromAddr( proc, ptr, nullptr, si ) == 0 )
     76     {
     77         *ret = '\0';
     78     }
     79     else
     80     {
     81         memcpy( ret, si->Name, si->NameLen );
     82         ret[si->NameLen] = '\0';
     83     }
     84     return ret;
     85 }
     86 
     87 CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
     88 {
     89     int write;
     90     const auto proc = GetCurrentProcess();
     91 #ifndef __CYGWIN__
     92     DWORD inlineNum = SymAddrIncludeInlineTrace( proc, ptr );
     93     if( inlineNum > MaxCbTrace - 1 ) inlineNum = MaxCbTrace - 1;
     94     DWORD ctx = 0;
     95     DWORD idx;
     96     BOOL doInline = FALSE;
     97     if( inlineNum != 0 ) doInline = SymQueryInlineTrace( proc, ptr, 0, ptr, ptr, &ctx, &idx );
     98     if( doInline )
     99     {
    100         write = inlineNum;
    101         cb_num = 1 + inlineNum;
    102     }
    103     else
    104 #endif
    105     {
    106         write = 0;
    107         cb_num = 1;
    108     }
    109 
    110     char buf[sizeof( SYMBOL_INFO ) + 1024];
    111     auto si = (SYMBOL_INFO*)buf;
    112     si->SizeOfStruct = sizeof( SYMBOL_INFO );
    113     si->MaxNameLen = 1024;
    114 
    115     if( SymFromAddr( proc, ptr, nullptr, si ) == 0 )
    116     {
    117         memcpy( si->Name, "[unknown]", 10 );
    118         si->NameLen = 9;
    119     }
    120 
    121     IMAGEHLP_LINE64 line;
    122     DWORD displacement = 0;
    123     line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    124 
    125     {
    126         auto name = (char*)tracy_malloc(si->NameLen + 1);
    127         memcpy(name, si->Name, si->NameLen);
    128         name[si->NameLen] = '\0';
    129 
    130         cb_data[write].name = name;
    131 
    132         const char* filename;
    133         if (SymGetLineFromAddr64(proc, ptr, &displacement, &line) == 0)
    134         {
    135             filename = "[unknown]";
    136             cb_data[write].line = 0;
    137         }
    138         else
    139         {
    140             filename = line.FileName;
    141             cb_data[write].line = line.LineNumber;
    142         }
    143 
    144         const auto fsz = strlen(filename);
    145         auto file = (char*)tracy_malloc(fsz + 1);
    146         memcpy(file, filename, fsz);
    147         file[fsz] = '\0';
    148 
    149         cb_data[write].file = file;
    150     }
    151 
    152 #ifndef __CYGWIN__
    153     if( doInline )
    154     {
    155         for( DWORD i=0; i<inlineNum; i++ )
    156         {
    157             auto& cb = cb_data[i];
    158 
    159             if( SymFromInlineContext( proc, ptr, ctx, nullptr, si ) == 0 )
    160             {
    161                 memcpy( si->Name, "[unknown]", 10 );
    162                 si->NameLen = 9;
    163             }
    164 
    165             auto name = (char*)tracy_malloc( si->NameLen + 1 );
    166             memcpy( name, si->Name, si->NameLen );
    167             name[si->NameLen] = '\0';
    168             cb.name = name;
    169 
    170             const char* filename;
    171             if( SymGetLineFromInlineContext( proc, ptr, ctx, 0, &displacement, &line ) == 0 )
    172             {
    173                 filename = "[unknown]";
    174                 cb.line = 0;
    175             }
    176             else
    177             {
    178                 filename = line.FileName;
    179                 cb.line = line.LineNumber;
    180             }
    181 
    182             const auto fsz = strlen( filename );
    183             auto file = (char*)tracy_malloc( fsz + 1 );
    184             memcpy( file, filename, fsz );
    185             file[fsz] = '\0';
    186             cb.file = file;
    187 
    188             ctx++;
    189         }
    190     }
    191 #endif
    192 
    193     return { cb_data, uint8_t( cb_num ) };
    194 }
    195 
    196 #elif TRACY_HAS_CALLSTACK == 4
    197 
    198 void InitCallstack()
    199 {
    200 }
    201 
    202 const char* DecodeCallstackPtrFast( uint64_t ptr )
    203 {
    204     static char ret[1024];
    205     auto vptr = (void*)ptr;
    206     char** sym = nullptr;
    207     const char* symname = nullptr;
    208     Dl_info dlinfo;
    209     if( dladdr( vptr, &dlinfo ) && dlinfo.dli_sname )
    210     {
    211         symname = dlinfo.dli_sname;
    212     }
    213     else
    214     {
    215         sym = backtrace_symbols( &vptr, 1 );
    216         if( sym )
    217         {
    218             symname = *sym;
    219         }
    220     }
    221     if( symname )
    222     {
    223         strcpy( ret, symname );
    224     }
    225     else
    226     {
    227         *ret = '\0';
    228     }
    229     return ret;
    230 }
    231 
    232 CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
    233 {
    234     static CallstackEntry cb;
    235     cb.line = 0;
    236 
    237     char* demangled = nullptr;
    238     const char* symname = nullptr;
    239     const char* symloc = nullptr;
    240     auto vptr = (void*)ptr;
    241     char** sym = nullptr;
    242     ptrdiff_t symoff = 0;
    243 
    244     Dl_info dlinfo;
    245     if( dladdr( vptr, &dlinfo ) )
    246     {
    247         symloc = dlinfo.dli_fname;
    248         symname = dlinfo.dli_sname;
    249         symoff = (char*)ptr - (char*)dlinfo.dli_saddr;
    250 
    251         if( symname && symname[0] == '_' )
    252         {
    253             size_t len = 0;
    254             int status;
    255             demangled = abi::__cxa_demangle( symname, nullptr, &len, &status );
    256             if( status == 0 )
    257             {
    258                 symname = demangled;
    259             }
    260         }
    261     }
    262 
    263     if( !symname )
    264     {
    265         sym = backtrace_symbols( &vptr, 1 );
    266         if( !sym )
    267         {
    268             symname = "[unknown]";
    269         }
    270         else
    271         {
    272             symname = *sym;
    273         }
    274     }
    275     if( !symloc )
    276     {
    277         symloc = "[unknown]";
    278     }
    279 
    280     if( symoff == 0 )
    281     {
    282         const auto namelen = strlen( symname );
    283         auto name = (char*)tracy_malloc( namelen + 1 );
    284         memcpy( name, symname, namelen );
    285         name[namelen] = '\0';
    286         cb.name = name;
    287     }
    288     else
    289     {
    290         char buf[32];
    291         const auto offlen = sprintf( buf, " + %td", symoff );
    292         const auto namelen = strlen( symname );
    293         auto name = (char*)tracy_malloc( namelen + offlen + 1 );
    294         memcpy( name, symname, namelen );
    295         memcpy( name + namelen, buf, offlen );
    296         name[namelen + offlen] = '\0';
    297         cb.name = name;
    298     }
    299 
    300     char buf[32];
    301     const auto addrlen = sprintf( buf, " [%p]", (void*)ptr );
    302     const auto loclen = strlen( symloc );
    303     auto loc = (char*)tracy_malloc( loclen + addrlen + 1 );
    304     memcpy( loc, symloc, loclen );
    305     memcpy( loc + loclen, buf, addrlen );
    306     loc[loclen + addrlen] = '\0';
    307     cb.file = loc;
    308 
    309     if( sym ) free( sym );
    310     if( demangled ) free( demangled );
    311 
    312     return { &cb, 1 };
    313 }
    314 
    315 #elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 6
    316 
    317 enum { MaxCbTrace = 16 };
    318 
    319 struct backtrace_state* cb_bts;
    320 int cb_num;
    321 CallstackEntry cb_data[MaxCbTrace];
    322 
    323 void InitCallstack()
    324 {
    325     cb_bts = backtrace_create_state( nullptr, 0, nullptr, nullptr );
    326 }
    327 
    328 static inline char* CopyString( const char* src )
    329 {
    330     const auto sz = strlen( src );
    331     auto dst = (char*)tracy_malloc( sz + 1 );
    332     memcpy( dst, src, sz );
    333     dst[sz] = '\0';
    334     return dst;
    335 }
    336 
    337 static int FastCallstackDataCb( void* data, uintptr_t pc, const char* fn, int lineno, const char* function )
    338 {
    339     if( function )
    340     {
    341         strcpy( (char*)data, function );
    342     }
    343     else
    344     {
    345         const char* symname = nullptr;
    346         auto vptr = (void*)pc;
    347         Dl_info dlinfo;
    348         if( dladdr( vptr, &dlinfo ) )
    349         {
    350             symname = dlinfo.dli_sname;
    351         }
    352         if( symname )
    353         {
    354             strcpy( (char*)data, symname );
    355         }
    356         else
    357         {
    358             *(char*)data = '\0';
    359         }
    360     }
    361     return 1;
    362 }
    363 
    364 static void FastCallstackErrorCb( void* data, const char* /*msg*/, int /*errnum*/ )
    365 {
    366     *(char*)data = '\0';
    367 }
    368 
    369 const char* DecodeCallstackPtrFast( uint64_t ptr )
    370 {
    371     static char ret[1024];
    372     backtrace_pcinfo( cb_bts, ptr, FastCallstackDataCb, FastCallstackErrorCb, ret );
    373     return ret;
    374 }
    375 
    376 static int CallstackDataCb( void* /*data*/, uintptr_t pc, const char* fn, int lineno, const char* function )
    377 {
    378     enum { DemangleBufLen = 64*1024 };
    379     char demangled[DemangleBufLen];
    380 
    381     if( !fn && !function )
    382     {
    383         const char* symname = nullptr;
    384         const char* symloc = nullptr;
    385         auto vptr = (void*)pc;
    386         ptrdiff_t symoff = 0;
    387 
    388         Dl_info dlinfo;
    389         if( dladdr( vptr, &dlinfo ) )
    390         {
    391             symloc = dlinfo.dli_fname;
    392             symname = dlinfo.dli_sname;
    393             symoff = (char*)pc - (char*)dlinfo.dli_saddr;
    394 
    395             if( symname && symname[0] == '_' )
    396             {
    397                 size_t len = DemangleBufLen;
    398                 int status;
    399                 abi::__cxa_demangle( symname, demangled, &len, &status );
    400                 if( status == 0 )
    401                 {
    402                     symname = demangled;
    403                 }
    404             }
    405         }
    406 
    407         if( !symname ) symname = "[unknown]";
    408         if( !symloc ) symloc = "[unknown]";
    409 
    410         if( symoff == 0 )
    411         {
    412             cb_data[cb_num].name = CopyString( symname );
    413         }
    414         else
    415         {
    416             char buf[32];
    417             const auto offlen = sprintf( buf, " + %td", symoff );
    418             const auto namelen = strlen( symname );
    419             auto name = (char*)tracy_malloc( namelen + offlen + 1 );
    420             memcpy( name, symname, namelen );
    421             memcpy( name + namelen, buf, offlen );
    422             name[namelen + offlen] = '\0';
    423             cb_data[cb_num].name = name;
    424         }
    425 
    426         char buf[32];
    427         const auto addrlen = sprintf( buf, " [%p]", (void*)pc );
    428         const auto loclen = strlen( symloc );
    429         auto loc = (char*)tracy_malloc( loclen + addrlen + 1 );
    430         memcpy( loc, symloc, loclen );
    431         memcpy( loc + loclen, buf, addrlen );
    432         loc[loclen + addrlen] = '\0';
    433         cb_data[cb_num].file = loc;
    434 
    435         cb_data[cb_num].line = 0;
    436     }
    437     else
    438     {
    439         if( !fn ) fn = "[unknown]";
    440         if( !function )
    441         {
    442             function = "[unknown]";
    443         }
    444         else
    445         {
    446             if( function[0] == '_' )
    447             {
    448                 size_t len = DemangleBufLen;
    449                 int status;
    450                 abi::__cxa_demangle( function, demangled, &len, &status );
    451                 if( status == 0 )
    452                 {
    453                     function = demangled;
    454                 }
    455             }
    456         }
    457 
    458         cb_data[cb_num].name = CopyString( function );
    459         cb_data[cb_num].file = CopyString( fn );
    460         cb_data[cb_num].line = lineno;
    461     }
    462 
    463     if( ++cb_num >= MaxCbTrace )
    464     {
    465         return 1;
    466     }
    467     else
    468     {
    469         return 0;
    470     }
    471 }
    472 
    473 static void CallstackErrorCb( void* /*data*/, const char* /*msg*/, int /*errnum*/ )
    474 {
    475     for( int i=0; i<cb_num; i++ )
    476     {
    477         tracy_free( (void*)cb_data[i].name );
    478         tracy_free( (void*)cb_data[i].file );
    479     }
    480 
    481     cb_data[0].name = CopyString( "[error]" );
    482     cb_data[0].file = CopyString( "[error]" );
    483     cb_data[0].line = 0;
    484 
    485     cb_num = 1;
    486 }
    487 
    488 CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
    489 {
    490     cb_num = 0;
    491     backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr );
    492     assert( cb_num > 0 );
    493     return { cb_data, uint8_t( cb_num ) };
    494 }
    495 
    496 #elif TRACY_HAS_CALLSTACK == 5
    497 
    498 void InitCallstack()
    499 {
    500 }
    501 
    502 const char* DecodeCallstackPtrFast( uint64_t ptr )
    503 {
    504     static char ret[1024];
    505     auto vptr = (void*)ptr;
    506     char** sym = nullptr;
    507     const char* symname = nullptr;
    508     Dl_info dlinfo;
    509     if( dladdr( vptr, &dlinfo ) && dlinfo.dli_sname )
    510     {
    511         symname = dlinfo.dli_sname;
    512     }
    513     if( symname )
    514     {
    515         strcpy( ret, symname );
    516     }
    517     else
    518     {
    519         *ret = '\0';
    520     }
    521     return ret;
    522 }
    523 
    524 CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
    525 {
    526     static CallstackEntry cb;
    527     cb.line = 0;
    528 
    529     char* demangled = nullptr;
    530     const char* symname = nullptr;
    531     const char* symloc = nullptr;
    532     auto vptr = (void*)ptr;
    533     char** sym = nullptr;
    534     ptrdiff_t symoff = 0;
    535 
    536     Dl_info dlinfo;
    537     if( dladdr( vptr, &dlinfo ) )
    538     {
    539         symloc = dlinfo.dli_fname;
    540         symname = dlinfo.dli_sname;
    541         symoff = (char*)ptr - (char*)dlinfo.dli_saddr;
    542 
    543         if( symname && symname[0] == '_' )
    544         {
    545             size_t len = 0;
    546             int status;
    547             demangled = abi::__cxa_demangle( symname, nullptr, &len, &status );
    548             if( status == 0 )
    549             {
    550                 symname = demangled;
    551             }
    552         }
    553     }
    554 
    555     if( !symname )
    556     {
    557         symname = "[unknown]";
    558     }
    559     if( !symloc )
    560     {
    561         symloc = "[unknown]";
    562     }
    563 
    564     if( symoff == 0 )
    565     {
    566         const auto namelen = strlen( symname );
    567         auto name = (char*)tracy_malloc( namelen + 1 );
    568         memcpy( name, symname, namelen );
    569         name[namelen] = '\0';
    570         cb.name = name;
    571     }
    572     else
    573     {
    574         char buf[32];
    575         const auto offlen = sprintf( buf, " + %td", symoff );
    576         const auto namelen = strlen( symname );
    577         auto name = (char*)tracy_malloc( namelen + offlen + 1 );
    578         memcpy( name, symname, namelen );
    579         memcpy( name + namelen, buf, offlen );
    580         name[namelen + offlen] = '\0';
    581         cb.name = name;
    582     }
    583 
    584     char buf[32];
    585     const auto addrlen = sprintf( buf, " [%p]", (void*)ptr );
    586     const auto loclen = strlen( symloc );
    587     auto loc = (char*)tracy_malloc( loclen + addrlen + 1 );
    588     memcpy( loc, symloc, loclen );
    589     memcpy( loc + loclen, buf, addrlen );
    590     loc[loclen + addrlen] = '\0';
    591     cb.file = loc;
    592 
    593     if( sym ) free( sym );
    594     if( demangled ) free( demangled );
    595 
    596     return { &cb, 1 };
    597 }
    598 
    599 #endif
    600 
    601 }
    602 
    603 #endif