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