The open source OpenXR runtime
1#include <limits>
2#include <new>
3#include <stdio.h>
4#include <string.h>
5#include "TracyCallstack.hpp"
6#include "TracyFastVector.hpp"
7#include "TracyStringHelpers.hpp"
8#include "../common/TracyAlloc.hpp"
9#include "TracyDebug.hpp"
10
11#ifdef TRACY_HAS_CALLSTACK
12
13#if TRACY_HAS_CALLSTACK == 1
14# ifndef NOMINMAX
15# define NOMINMAX
16# endif
17# include <windows.h>
18# include <psapi.h>
19# include <algorithm>
20# ifdef _MSC_VER
21# pragma warning( push )
22# pragma warning( disable : 4091 )
23# endif
24# include <dbghelp.h>
25# ifdef _MSC_VER
26# pragma warning( pop )
27# endif
28#elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
29# include "../libbacktrace/backtrace.hpp"
30# include <algorithm>
31# include <dlfcn.h>
32# include <cxxabi.h>
33# include <stdlib.h>
34# include "TracyFastVector.hpp"
35#elif TRACY_HAS_CALLSTACK == 5
36# include <dlfcn.h>
37# include <cxxabi.h>
38#endif
39
40#ifdef TRACY_DBGHELP_LOCK
41# include "TracyProfiler.hpp"
42
43# define DBGHELP_INIT TracyConcat( TRACY_DBGHELP_LOCK, Init() )
44# define DBGHELP_LOCK TracyConcat( TRACY_DBGHELP_LOCK, Lock() );
45# define DBGHELP_UNLOCK TracyConcat( TRACY_DBGHELP_LOCK, Unlock() );
46
47extern "C"
48{
49 void DBGHELP_INIT;
50 void DBGHELP_LOCK;
51 void DBGHELP_UNLOCK;
52};
53#endif
54
55#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 5 || TRACY_HAS_CALLSTACK == 6
56// If you want to use your own demangling functionality (e.g. for another language),
57// define TRACY_DEMANGLE and provide your own implementation of the __tracy_demangle
58// function. The input parameter is a function name. The demangle function must
59// identify whether this name is mangled, and fail if it is not. Failure is indicated
60// by returning nullptr. If demangling succeeds, a pointer to the C string containing
61// demangled function must be returned. The demangling function is responsible for
62// managing memory for this string. It is expected that it will be internally reused.
63// When a call to ___tracy_demangle is made, previous contents of the string memory
64// do not need to be preserved. Function may return string of any length, but the
65// profiler can choose to truncate it.
66extern "C" const char* ___tracy_demangle( const char* mangled );
67
68#ifndef TRACY_DEMANGLE
69constexpr size_t ___tracy_demangle_buffer_len = 1024*1024;
70char* ___tracy_demangle_buffer;
71
72void ___tracy_init_demangle_buffer()
73{
74 ___tracy_demangle_buffer = (char*)tracy::tracy_malloc( ___tracy_demangle_buffer_len );
75}
76
77void ___tracy_free_demangle_buffer()
78{
79 tracy::tracy_free( ___tracy_demangle_buffer );
80}
81
82extern "C" const char* ___tracy_demangle( const char* mangled )
83{
84 if( !mangled || mangled[0] != '_' ) return nullptr;
85 if( strlen( mangled ) > ___tracy_demangle_buffer_len ) return nullptr;
86 int status;
87 size_t len = ___tracy_demangle_buffer_len;
88 return abi::__cxa_demangle( mangled, ___tracy_demangle_buffer, &len, &status );
89}
90#endif
91#endif
92
93namespace tracy
94{
95
96#if TRACY_HAS_CALLSTACK == 1
97
98enum { MaxCbTrace = 64 };
99enum { MaxNameSize = 8*1024 };
100
101int cb_num;
102CallstackEntry cb_data[MaxCbTrace];
103
104extern "C"
105{
106 typedef DWORD (__stdcall *t_SymAddrIncludeInlineTrace)( HANDLE hProcess, DWORD64 Address );
107 typedef BOOL (__stdcall *t_SymQueryInlineTrace)( HANDLE hProcess, DWORD64 StartAddress, DWORD StartContext, DWORD64 StartRetAddress, DWORD64 CurAddress, LPDWORD CurContext, LPDWORD CurFrameIndex );
108 typedef BOOL (__stdcall *t_SymFromInlineContext)( HANDLE hProcess, DWORD64 Address, ULONG InlineContext, PDWORD64 Displacement, PSYMBOL_INFO Symbol );
109 typedef BOOL (__stdcall *t_SymGetLineFromInlineContext)( HANDLE hProcess, DWORD64 qwAddr, ULONG InlineContext, DWORD64 qwModuleBaseAddress, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64 );
110
111 TRACY_API ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain = 0;
112 t_SymAddrIncludeInlineTrace _SymAddrIncludeInlineTrace = 0;
113 t_SymQueryInlineTrace _SymQueryInlineTrace = 0;
114 t_SymFromInlineContext _SymFromInlineContext = 0;
115 t_SymGetLineFromInlineContext _SymGetLineFromInlineContext = 0;
116}
117
118
119struct ModuleCache
120{
121 uint64_t start;
122 uint64_t end;
123 char* name;
124};
125
126static FastVector<ModuleCache>* s_modCache;
127
128
129struct KernelDriver
130{
131 uint64_t addr;
132 const char* mod;
133 const char* path;
134};
135
136KernelDriver* s_krnlCache = nullptr;
137size_t s_krnlCacheCnt;
138
139
140void InitCallstackCritical()
141{
142 ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" );
143}
144
145void InitCallstack()
146{
147 _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymAddrIncludeInlineTrace" );
148 _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymQueryInlineTrace" );
149 _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymFromInlineContext" );
150 _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymGetLineFromInlineContext" );
151
152#ifdef TRACY_DBGHELP_LOCK
153 DBGHELP_INIT;
154 DBGHELP_LOCK;
155#endif
156
157 SymInitialize( GetCurrentProcess(), nullptr, true );
158 SymSetOptions( SYMOPT_LOAD_LINES );
159
160 DWORD needed;
161 LPVOID dev[4096];
162 if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 )
163 {
164 char windir[MAX_PATH];
165 if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 );
166 const auto windirlen = strlen( windir );
167
168 const auto sz = needed / sizeof( LPVOID );
169 s_krnlCache = (KernelDriver*)tracy_malloc( sizeof(KernelDriver) * sz );
170 int cnt = 0;
171 for( size_t i=0; i<sz; i++ )
172 {
173 char fn[MAX_PATH];
174 const auto len = GetDeviceDriverBaseNameA( dev[i], fn, sizeof( fn ) );
175 if( len != 0 )
176 {
177 auto buf = (char*)tracy_malloc_fast( len+3 );
178 buf[0] = '<';
179 memcpy( buf+1, fn, len );
180 memcpy( buf+len+1, ">", 2 );
181 s_krnlCache[cnt] = KernelDriver { (uint64_t)dev[i], buf };
182
183 const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) );
184 if( len != 0 )
185 {
186 char full[MAX_PATH];
187 char* path = fn;
188
189 if( memcmp( fn, "\\SystemRoot\\", 12 ) == 0 )
190 {
191 memcpy( full, windir, windirlen );
192 strcpy( full + windirlen, fn + 11 );
193 path = full;
194 }
195
196 SymLoadModuleEx( GetCurrentProcess(), nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0 );
197
198 const auto psz = strlen( path );
199 auto pptr = (char*)tracy_malloc_fast( psz+1 );
200 memcpy( pptr, path, psz );
201 pptr[psz] = '\0';
202 s_krnlCache[cnt].path = pptr;
203 }
204
205 cnt++;
206 }
207 }
208 s_krnlCacheCnt = cnt;
209 std::sort( s_krnlCache, s_krnlCache + s_krnlCacheCnt, []( const KernelDriver& lhs, const KernelDriver& rhs ) { return lhs.addr > rhs.addr; } );
210 }
211
212 s_modCache = (FastVector<ModuleCache>*)tracy_malloc( sizeof( FastVector<ModuleCache> ) );
213 new(s_modCache) FastVector<ModuleCache>( 512 );
214
215 HANDLE proc = GetCurrentProcess();
216 HMODULE mod[1024];
217 if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 )
218 {
219 const auto sz = needed / sizeof( HMODULE );
220 for( size_t i=0; i<sz; i++ )
221 {
222 MODULEINFO info;
223 if( GetModuleInformation( proc, mod[i], &info, sizeof( info ) ) != 0 )
224 {
225 const auto base = uint64_t( info.lpBaseOfDll );
226 char name[1024];
227 const auto res = GetModuleFileNameA( mod[i], name, 1021 );
228 if( res > 0 )
229 {
230 // This may be a new module loaded since our call to SymInitialize.
231 // Just in case, force DbgHelp to load its pdb !
232 SymLoadModuleEx(proc, NULL, name, NULL, (DWORD64)info.lpBaseOfDll, info.SizeOfImage, NULL, 0);
233
234 auto ptr = name + res;
235 while( ptr > name && *ptr != '\\' && *ptr != '/' ) ptr--;
236 if( ptr > name ) ptr++;
237 const auto namelen = name + res - ptr;
238 auto cache = s_modCache->push_next();
239 cache->start = base;
240 cache->end = base + info.SizeOfImage;
241 cache->name = (char*)tracy_malloc_fast( namelen+3 );
242 cache->name[0] = '[';
243 memcpy( cache->name+1, ptr, namelen );
244 cache->name[namelen+1] = ']';
245 cache->name[namelen+2] = '\0';
246 }
247 }
248 }
249 }
250
251#ifdef TRACY_DBGHELP_LOCK
252 DBGHELP_UNLOCK;
253#endif
254}
255
256void EndCallstack()
257{
258}
259
260const char* DecodeCallstackPtrFast( uint64_t ptr )
261{
262 static char ret[MaxNameSize];
263 const auto proc = GetCurrentProcess();
264
265 char buf[sizeof( SYMBOL_INFO ) + MaxNameSize];
266 auto si = (SYMBOL_INFO*)buf;
267 si->SizeOfStruct = sizeof( SYMBOL_INFO );
268 si->MaxNameLen = MaxNameSize;
269
270#ifdef TRACY_DBGHELP_LOCK
271 DBGHELP_LOCK;
272#endif
273 if( SymFromAddr( proc, ptr, nullptr, si ) == 0 )
274 {
275 *ret = '\0';
276 }
277 else
278 {
279 memcpy( ret, si->Name, si->NameLen );
280 ret[si->NameLen] = '\0';
281 }
282#ifdef TRACY_DBGHELP_LOCK
283 DBGHELP_UNLOCK;
284#endif
285 return ret;
286}
287
288const char* GetKernelModulePath( uint64_t addr )
289{
290 assert( addr >> 63 != 0 );
291 if( !s_krnlCache ) return nullptr;
292 auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
293 if( it == s_krnlCache + s_krnlCacheCnt ) return nullptr;
294 return it->path;
295}
296
297static const char* GetModuleNameAndPrepareSymbols( uint64_t addr )
298{
299 if( ( addr >> 63 ) != 0 )
300 {
301 if( s_krnlCache )
302 {
303 auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
304 if( it != s_krnlCache + s_krnlCacheCnt )
305 {
306 return it->mod;
307 }
308 }
309 return "<kernel>";
310 }
311
312 for( auto& v : *s_modCache )
313 {
314 if( addr >= v.start && addr < v.end )
315 {
316 return v.name;
317 }
318 }
319
320 HMODULE mod[1024];
321 DWORD needed;
322 HANDLE proc = GetCurrentProcess();
323
324 InitRpmalloc();
325 if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 )
326 {
327 const auto sz = needed / sizeof( HMODULE );
328 for( size_t i=0; i<sz; i++ )
329 {
330 MODULEINFO info;
331 if( GetModuleInformation( proc, mod[i], &info, sizeof( info ) ) != 0 )
332 {
333 const auto base = uint64_t( info.lpBaseOfDll );
334 if( addr >= base && addr < base + info.SizeOfImage )
335 {
336 char name[1024];
337 const auto res = GetModuleFileNameA( mod[i], name, 1021 );
338 if( res > 0 )
339 {
340 // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize)
341 SymLoadModuleEx(proc, NULL, name, NULL, (DWORD64)info.lpBaseOfDll, info.SizeOfImage, NULL, 0);
342 auto ptr = name + res;
343 while( ptr > name && *ptr != '\\' && *ptr != '/' ) ptr--;
344 if( ptr > name ) ptr++;
345 const auto namelen = name + res - ptr;
346 auto cache = s_modCache->push_next();
347 cache->start = base;
348 cache->end = base + info.SizeOfImage;
349 cache->name = (char*)tracy_malloc_fast( namelen+3 );
350 cache->name[0] = '[';
351 memcpy( cache->name+1, ptr, namelen );
352 cache->name[namelen+1] = ']';
353 cache->name[namelen+2] = '\0';
354 return cache->name;
355 }
356 }
357 }
358 }
359 }
360 return "[unknown]";
361}
362
363CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
364{
365 CallstackSymbolData sym;
366 IMAGEHLP_LINE64 line;
367 DWORD displacement = 0;
368 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
369#ifdef TRACY_DBGHELP_LOCK
370 DBGHELP_LOCK;
371#endif
372 const auto res = SymGetLineFromAddr64( GetCurrentProcess(), ptr, &displacement, &line );
373 if( res == 0 || line.LineNumber >= 0xF00000 )
374 {
375 sym.file = "[unknown]";
376 sym.line = 0;
377 sym.needFree = false;
378 }
379 else
380 {
381 sym.file = CopyString( line.FileName );
382 sym.line = line.LineNumber;
383 sym.needFree = true;
384 }
385#ifdef TRACY_DBGHELP_LOCK
386 DBGHELP_UNLOCK;
387#endif
388 return sym;
389}
390
391CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
392{
393 int write;
394 const auto proc = GetCurrentProcess();
395 InitRpmalloc();
396
397#ifdef TRACY_DBGHELP_LOCK
398 DBGHELP_LOCK;
399#endif
400
401 const auto moduleName = GetModuleNameAndPrepareSymbols(ptr);
402
403#if !defined TRACY_NO_CALLSTACK_INLINES
404 BOOL doInline = FALSE;
405 DWORD ctx = 0;
406 DWORD inlineNum = 0;
407 if( _SymAddrIncludeInlineTrace )
408 {
409 inlineNum = _SymAddrIncludeInlineTrace( proc, ptr );
410 if( inlineNum > MaxCbTrace - 1 ) inlineNum = MaxCbTrace - 1;
411 DWORD idx;
412 if( inlineNum != 0 ) doInline = _SymQueryInlineTrace( proc, ptr, 0, ptr, ptr, &ctx, &idx );
413 }
414 if( doInline )
415 {
416 write = inlineNum;
417 cb_num = 1 + inlineNum;
418 }
419 else
420#endif
421 {
422 write = 0;
423 cb_num = 1;
424 }
425
426 char buf[sizeof( SYMBOL_INFO ) + MaxNameSize];
427 auto si = (SYMBOL_INFO*)buf;
428 si->SizeOfStruct = sizeof( SYMBOL_INFO );
429 si->MaxNameLen = MaxNameSize;
430
431 const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0;
432
433 IMAGEHLP_LINE64 line;
434 DWORD displacement = 0;
435 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
436
437 {
438 const char* filename;
439 const auto res = SymGetLineFromAddr64( proc, ptr, &displacement, &line );
440 if( res == 0 || line.LineNumber >= 0xF00000 )
441 {
442 filename = "[unknown]";
443 cb_data[write].line = 0;
444 }
445 else
446 {
447 filename = line.FileName;
448 cb_data[write].line = line.LineNumber;
449 }
450
451 cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName );
452 cb_data[write].file = CopyStringFast( filename );
453 if( symValid )
454 {
455 cb_data[write].symLen = si->Size;
456 cb_data[write].symAddr = si->Address;
457 }
458 else
459 {
460 cb_data[write].symLen = 0;
461 cb_data[write].symAddr = 0;
462 }
463 }
464
465#if !defined TRACY_NO_CALLSTACK_INLINES
466 if( doInline )
467 {
468 for( DWORD i=0; i<inlineNum; i++ )
469 {
470 auto& cb = cb_data[i];
471 const auto symInlineValid = _SymFromInlineContext( proc, ptr, ctx, nullptr, si ) != 0;
472 const char* filename;
473 if( _SymGetLineFromInlineContext( proc, ptr, ctx, 0, &displacement, &line ) == 0 )
474 {
475 filename = "[unknown]";
476 cb.line = 0;
477 }
478 else
479 {
480 filename = line.FileName;
481 cb.line = line.LineNumber;
482 }
483
484 cb.name = symInlineValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName );
485 cb.file = CopyStringFast( filename );
486 if( symInlineValid )
487 {
488 cb.symLen = si->Size;
489 cb.symAddr = si->Address;
490 }
491 else
492 {
493 cb.symLen = 0;
494 cb.symAddr = 0;
495 }
496
497 ctx++;
498 }
499 }
500#endif
501#ifdef TRACY_DBGHELP_LOCK
502 DBGHELP_UNLOCK;
503#endif
504
505 return { cb_data, uint8_t( cb_num ), moduleName };
506}
507
508#elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
509
510enum { MaxCbTrace = 64 };
511
512struct backtrace_state* cb_bts;
513int cb_num;
514CallstackEntry cb_data[MaxCbTrace];
515int cb_fixup;
516
517#ifdef TRACY_DEBUGINFOD
518debuginfod_client* s_debuginfod;
519
520struct DebugInfo
521{
522 uint8_t* buildid;
523 size_t buildid_size;
524 char* filename;
525 int fd;
526};
527
528FastVector<DebugInfo> s_di_known( 16 );
529#endif
530
531#ifdef __linux
532struct KernelSymbol
533{
534 uint64_t addr;
535 const char* name;
536 const char* mod;
537};
538
539KernelSymbol* s_kernelSym = nullptr;
540size_t s_kernelSymCnt;
541
542static void InitKernelSymbols()
543{
544 FILE* f = fopen( "/proc/kallsyms", "rb" );
545 if( !f ) return;
546 tracy::FastVector<KernelSymbol> tmpSym( 1024 );
547 size_t linelen = 16 * 1024; // linelen must be big enough to prevent reallocs in getline()
548 auto linebuf = (char*)tracy_malloc( linelen );
549 ssize_t sz;
550 while( ( sz = getline( &linebuf, &linelen, f ) ) != -1 )
551 {
552 auto ptr = linebuf;
553 uint64_t addr = 0;
554 while( *ptr != ' ' )
555 {
556 auto v = *ptr;
557 if( v >= '0' && v <= '9' )
558 {
559 v -= '0';
560 }
561 else if( v >= 'a' && v <= 'f' )
562 {
563 v -= 'a';
564 v += 10;
565 }
566 else if( v >= 'A' && v <= 'F' )
567 {
568 v -= 'A';
569 v += 10;
570 }
571 else
572 {
573 assert( false );
574 }
575 assert( ( v & ~0xF ) == 0 );
576 addr <<= 4;
577 addr |= v;
578 ptr++;
579 }
580 if( addr == 0 ) continue;
581 ptr++;
582 if( *ptr != 'T' && *ptr != 't' ) continue;
583 ptr += 2;
584 const auto namestart = ptr;
585 while( *ptr != '\t' && *ptr != '\n' ) ptr++;
586 const auto nameend = ptr;
587 const char* modstart = nullptr;
588 const char* modend;
589 if( *ptr == '\t' )
590 {
591 ptr += 2;
592 modstart = ptr;
593 while( *ptr != ']' ) ptr++;
594 modend = ptr;
595 }
596
597 auto strname = (char*)tracy_malloc_fast( nameend - namestart + 1 );
598 memcpy( strname, namestart, nameend - namestart );
599 strname[nameend-namestart] = '\0';
600
601 char* strmod = nullptr;
602 if( modstart )
603 {
604 strmod = (char*)tracy_malloc_fast( modend - modstart + 1 );
605 memcpy( strmod, modstart, modend - modstart );
606 strmod[modend-modstart] = '\0';
607 }
608
609 auto sym = tmpSym.push_next();
610 sym->addr = addr;
611 sym->name = strname;
612 sym->mod = strmod;
613 }
614 tracy_free_fast( linebuf );
615 fclose( f );
616 if( tmpSym.empty() ) return;
617
618 std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr > rhs.addr; } );
619 s_kernelSymCnt = tmpSym.size();
620 s_kernelSym = (KernelSymbol*)tracy_malloc_fast( sizeof( KernelSymbol ) * s_kernelSymCnt );
621 memcpy( s_kernelSym, tmpSym.data(), sizeof( KernelSymbol ) * s_kernelSymCnt );
622 TracyDebug( "Loaded %zu kernel symbols\n", s_kernelSymCnt );
623}
624#endif
625
626char* NormalizePath( const char* path )
627{
628 if( path[0] != '/' ) return nullptr;
629
630 const char* ptr = path;
631 const char* end = path;
632 while( *end ) end++;
633
634 char* res = (char*)tracy_malloc( end - ptr + 1 );
635 size_t rsz = 0;
636
637 while( ptr < end )
638 {
639 const char* next = ptr;
640 while( next < end && *next != '/' ) next++;
641 size_t lsz = next - ptr;
642 switch( lsz )
643 {
644 case 2:
645 if( memcmp( ptr, "..", 2 ) == 0 )
646 {
647 const char* back = res + rsz - 1;
648 while( back > res && *back != '/' ) back--;
649 rsz = back - res;
650 ptr = next + 1;
651 continue;
652 }
653 break;
654 case 1:
655 if( *ptr == '.' )
656 {
657 ptr = next + 1;
658 continue;
659 }
660 break;
661 case 0:
662 ptr = next + 1;
663 continue;
664 }
665 if( rsz != 1 ) res[rsz++] = '/';
666 memcpy( res+rsz, ptr, lsz );
667 rsz += lsz;
668 ptr = next + 1;
669 }
670
671 if( rsz == 0 )
672 {
673 memcpy( res, "/", 2 );
674 }
675 else
676 {
677 res[rsz] = '\0';
678 }
679 return res;
680}
681
682void InitCallstackCritical()
683{
684}
685
686void InitCallstack()
687{
688 cb_bts = backtrace_create_state( nullptr, 0, nullptr, nullptr );
689 ___tracy_init_demangle_buffer();
690
691#ifdef __linux
692 InitKernelSymbols();
693#endif
694#ifdef TRACY_DEBUGINFOD
695 s_debuginfod = debuginfod_begin();
696#endif
697}
698
699#ifdef TRACY_DEBUGINFOD
700void ClearDebugInfoVector( FastVector<DebugInfo>& vec )
701{
702 for( auto& v : vec )
703 {
704 tracy_free( v.buildid );
705 tracy_free( v.filename );
706 if( v.fd >= 0 ) close( v.fd );
707 }
708 vec.clear();
709}
710
711DebugInfo* FindDebugInfo( FastVector<DebugInfo>& vec, const uint8_t* buildid_data, size_t buildid_size )
712{
713 for( auto& v : vec )
714 {
715 if( v.buildid_size == buildid_size && memcmp( v.buildid, buildid_data, buildid_size ) == 0 )
716 {
717 return &v;
718 }
719 }
720 return nullptr;
721}
722
723int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size, const char* filename )
724{
725 auto buildid = (uint8_t*)buildid_data;
726 auto it = FindDebugInfo( s_di_known, buildid, buildid_size );
727 if( it ) return it->fd >= 0 ? dup( it->fd ) : -1;
728
729 int fd = debuginfod_find_debuginfo( s_debuginfod, buildid, buildid_size, nullptr );
730 it = s_di_known.push_next();
731 it->buildid_size = buildid_size;
732 it->buildid = (uint8_t*)tracy_malloc( buildid_size );
733 memcpy( it->buildid, buildid, buildid_size );
734 const auto fnsz = strlen( filename ) + 1;
735 it->filename = (char*)tracy_malloc( fnsz );
736 memcpy( it->filename, filename, fnsz );
737 it->fd = fd >= 0 ? fd : -1;
738 TracyDebug( "DebugInfo descriptor query: %i, fn: %s\n", fd, filename );
739 return it->fd;
740}
741
742const uint8_t* GetBuildIdForImage( const char* image, size_t& size )
743{
744 assert( image );
745 for( auto& v : s_di_known )
746 {
747 if( strcmp( image, v.filename ) == 0 )
748 {
749 size = v.buildid_size;
750 return v.buildid;
751 }
752 }
753 return nullptr;
754}
755
756debuginfod_client* GetDebuginfodClient()
757{
758 return s_debuginfod;
759}
760#endif
761
762void EndCallstack()
763{
764 ___tracy_free_demangle_buffer();
765#ifdef TRACY_DEBUGINFOD
766 ClearDebugInfoVector( s_di_known );
767 debuginfod_end( s_debuginfod );
768#endif
769}
770
771const char* DecodeCallstackPtrFast( uint64_t ptr )
772{
773 static char ret[1024];
774 auto vptr = (void*)ptr;
775 const char* symname = nullptr;
776 Dl_info dlinfo;
777 if( dladdr( vptr, &dlinfo ) && dlinfo.dli_sname )
778 {
779 symname = dlinfo.dli_sname;
780 }
781 if( symname )
782 {
783 strcpy( ret, symname );
784 }
785 else
786 {
787 *ret = '\0';
788 }
789 return ret;
790}
791
792static int SymbolAddressDataCb( void* data, uintptr_t pc, uintptr_t lowaddr, const char* fn, int lineno, const char* function )
793{
794 auto& sym = *(CallstackSymbolData*)data;
795 if( !fn )
796 {
797 sym.file = "[unknown]";
798 sym.line = 0;
799 sym.needFree = false;
800 }
801 else
802 {
803 sym.file = NormalizePath( fn );
804 if( !sym.file ) sym.file = CopyString( fn );
805 sym.line = lineno;
806 sym.needFree = true;
807 }
808
809 return 1;
810}
811
812static void SymbolAddressErrorCb( void* data, const char* /*msg*/, int /*errnum*/ )
813{
814 auto& sym = *(CallstackSymbolData*)data;
815 sym.file = "[unknown]";
816 sym.line = 0;
817 sym.needFree = false;
818}
819
820CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
821{
822 CallstackSymbolData sym;
823 backtrace_pcinfo( cb_bts, ptr, SymbolAddressDataCb, SymbolAddressErrorCb, &sym );
824 return sym;
825}
826
827static int CallstackDataCb( void* /*data*/, uintptr_t pc, uintptr_t lowaddr, const char* fn, int lineno, const char* function )
828{
829 cb_data[cb_num].symLen = 0;
830 cb_data[cb_num].symAddr = (uint64_t)lowaddr;
831
832 if( !fn && !function )
833 {
834 const char* symname = nullptr;
835 auto vptr = (void*)pc;
836 ptrdiff_t symoff = 0;
837
838 Dl_info dlinfo;
839 if( dladdr( vptr, &dlinfo ) )
840 {
841 symname = dlinfo.dli_sname;
842 symoff = (char*)pc - (char*)dlinfo.dli_saddr;
843 const char* demangled = ___tracy_demangle( symname );
844 if( demangled ) symname = demangled;
845 }
846
847 if( !symname ) symname = "[unknown]";
848
849 if( symoff == 0 )
850 {
851 const auto len = std::min<size_t>( strlen( symname ), std::numeric_limits<uint16_t>::max() );
852 cb_data[cb_num].name = CopyStringFast( symname, len );
853 }
854 else
855 {
856 char buf[32];
857 const auto offlen = sprintf( buf, " + %td", symoff );
858 const auto namelen = std::min<size_t>( strlen( symname ), std::numeric_limits<uint16_t>::max() - offlen );
859 auto name = (char*)tracy_malloc_fast( namelen + offlen + 1 );
860 memcpy( name, symname, namelen );
861 memcpy( name + namelen, buf, offlen );
862 name[namelen + offlen] = '\0';
863 cb_data[cb_num].name = name;
864 }
865
866 cb_data[cb_num].file = CopyStringFast( "[unknown]" );
867 cb_data[cb_num].line = 0;
868 }
869 else
870 {
871 if( !fn ) fn = "[unknown]";
872 if( !function )
873 {
874 function = "[unknown]";
875 }
876 else
877 {
878 const char* demangled = ___tracy_demangle( function );
879 if( demangled ) function = demangled;
880 }
881
882 const auto len = std::min<size_t>( strlen( function ), std::numeric_limits<uint16_t>::max() );
883 cb_data[cb_num].name = CopyStringFast( function, len );
884 cb_data[cb_num].file = NormalizePath( fn );
885 if( !cb_data[cb_num].file ) cb_data[cb_num].file = CopyStringFast( fn );
886 cb_data[cb_num].line = lineno;
887 }
888
889 if( ++cb_num >= MaxCbTrace )
890 {
891 return 1;
892 }
893 else
894 {
895 return 0;
896 }
897}
898
899static void CallstackErrorCb( void* /*data*/, const char* /*msg*/, int /*errnum*/ )
900{
901 for( int i=0; i<cb_num; i++ )
902 {
903 tracy_free_fast( (void*)cb_data[i].name );
904 tracy_free_fast( (void*)cb_data[i].file );
905 }
906
907 cb_data[0].name = CopyStringFast( "[error]" );
908 cb_data[0].file = CopyStringFast( "[error]" );
909 cb_data[0].line = 0;
910
911 cb_num = 1;
912}
913
914void SymInfoCallback( void* /*data*/, uintptr_t pc, const char* symname, uintptr_t symval, uintptr_t symsize )
915{
916 cb_data[cb_num-1].symLen = (uint32_t)symsize;
917 cb_data[cb_num-1].symAddr = (uint64_t)symval;
918}
919
920void SymInfoError( void* /*data*/, const char* /*msg*/, int /*errnum*/ )
921{
922 cb_data[cb_num-1].symLen = 0;
923 cb_data[cb_num-1].symAddr = 0;
924}
925
926CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
927{
928 InitRpmalloc();
929 if( ptr >> 63 == 0 )
930 {
931 cb_num = 0;
932 backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr );
933 assert( cb_num > 0 );
934
935 backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr );
936
937 const char* symloc = nullptr;
938 Dl_info dlinfo;
939 if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname;
940
941 return { cb_data, uint8_t( cb_num ), symloc ? symloc : "[unknown]" };
942 }
943#ifdef __linux
944 else if( s_kernelSym )
945 {
946 auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
947 if( it != s_kernelSym + s_kernelSymCnt )
948 {
949 cb_data[0].name = CopyStringFast( it->name );
950 cb_data[0].file = CopyStringFast( "<kernel>" );
951 cb_data[0].line = 0;
952 cb_data[0].symLen = 0;
953 cb_data[0].symAddr = it->addr;
954 return { cb_data, 1, it->mod ? it->mod : "<kernel>" };
955 }
956 }
957#endif
958
959 cb_data[0].name = CopyStringFast( "[unknown]" );
960 cb_data[0].file = CopyStringFast( "<kernel>" );
961 cb_data[0].line = 0;
962 cb_data[0].symLen = 0;
963 cb_data[0].symAddr = 0;
964 return { cb_data, 1, "<kernel>" };
965}
966
967#elif TRACY_HAS_CALLSTACK == 5
968
969void InitCallstackCritical()
970{
971}
972
973void InitCallstack()
974{
975 ___tracy_init_demangle_buffer();
976}
977
978void EndCallstack()
979{
980 ___tracy_free_demangle_buffer();
981}
982
983const char* DecodeCallstackPtrFast( uint64_t ptr )
984{
985 static char ret[1024];
986 auto vptr = (void*)ptr;
987 const char* symname = nullptr;
988 Dl_info dlinfo;
989 if( dladdr( vptr, &dlinfo ) && dlinfo.dli_sname )
990 {
991 symname = dlinfo.dli_sname;
992 }
993 if( symname )
994 {
995 strcpy( ret, symname );
996 }
997 else
998 {
999 *ret = '\0';
1000 }
1001 return ret;
1002}
1003
1004CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
1005{
1006 const char* symloc = nullptr;
1007 Dl_info dlinfo;
1008 if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname;
1009 if( !symloc ) symloc = "[unknown]";
1010 return CallstackSymbolData { symloc, 0, false, 0 };
1011}
1012
1013CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
1014{
1015 static CallstackEntry cb;
1016 cb.line = 0;
1017
1018 const char* symname = nullptr;
1019 const char* symloc = nullptr;
1020 auto vptr = (void*)ptr;
1021 ptrdiff_t symoff = 0;
1022 void* symaddr = nullptr;
1023
1024 Dl_info dlinfo;
1025 if( dladdr( vptr, &dlinfo ) )
1026 {
1027 symloc = dlinfo.dli_fname;
1028 symname = dlinfo.dli_sname;
1029 symoff = (char*)ptr - (char*)dlinfo.dli_saddr;
1030 symaddr = dlinfo.dli_saddr;
1031 const char* demangled = ___tracy_demangle( symname );
1032 if( demangled ) symname = demangled;
1033 }
1034
1035 if( !symname ) symname = "[unknown]";
1036 if( !symloc ) symloc = "[unknown]";
1037
1038 if( symoff == 0 )
1039 {
1040 const auto len = std::min<size_t>( strlen( symname ), std::numeric_limits<uint16_t>::max() );
1041 cb.name = CopyString( symname, len );
1042 }
1043 else
1044 {
1045 char buf[32];
1046 const auto offlen = sprintf( buf, " + %td", symoff );
1047 const auto namelen = std::min<size_t>( strlen( symname ), std::numeric_limits<uint16_t>::max() - offlen );
1048 auto name = (char*)tracy_malloc( namelen + offlen + 1 );
1049 memcpy( name, symname, namelen );
1050 memcpy( name + namelen, buf, offlen );
1051 name[namelen + offlen] = '\0';
1052 cb.name = name;
1053 }
1054
1055 cb.file = CopyString( "[unknown]" );
1056 cb.symLen = 0;
1057 cb.symAddr = (uint64_t)symaddr;
1058
1059 return { &cb, 1, symloc };
1060}
1061
1062#endif
1063
1064}
1065
1066#endif