The open source OpenXR runtime
1#ifdef _MSC_VER
2# pragma warning(disable:4996)
3#endif
4#if defined _WIN32
5# ifndef WIN32_LEAN_AND_MEAN
6# define WIN32_LEAN_AND_MEAN
7# endif
8# ifndef NOMINMAX
9# define NOMINMAX
10# endif
11# include <windows.h>
12# include <malloc.h>
13# include "TracyUwp.hpp"
14#else
15# include <pthread.h>
16# include <string.h>
17# include <unistd.h>
18#endif
19
20#ifdef __linux__
21# ifdef __ANDROID__
22# include <sys/types.h>
23# else
24# include <sys/syscall.h>
25# endif
26# include <fcntl.h>
27#elif defined __FreeBSD__
28# include <sys/thr.h>
29#elif defined __NetBSD__ || defined __DragonFly__
30# include <sys/lwp.h>
31#endif
32
33#ifdef __MINGW32__
34# define __STDC_FORMAT_MACROS
35#endif
36#include <inttypes.h>
37#include <stdio.h>
38#include <stdlib.h>
39
40#include "TracySystem.hpp"
41
42#if defined _WIN32
43extern "C" typedef HRESULT (WINAPI *t_SetThreadDescription)( HANDLE, PCWSTR );
44extern "C" typedef HRESULT (WINAPI *t_GetThreadDescription)( HANDLE, PWSTR* );
45#endif
46
47#ifdef TRACY_ENABLE
48# include <atomic>
49# include "TracyAlloc.hpp"
50#endif
51
52namespace tracy
53{
54
55namespace detail
56{
57
58TRACY_API uint32_t GetThreadHandleImpl()
59{
60#if defined _WIN32
61 static_assert( sizeof( decltype( GetCurrentThreadId() ) ) <= sizeof( uint32_t ), "Thread handle too big to fit in protocol" );
62 return uint32_t( GetCurrentThreadId() );
63#elif defined __APPLE__
64 uint64_t id;
65 pthread_threadid_np( pthread_self(), &id );
66 return uint32_t( id );
67#elif defined __ANDROID__
68 return (uint32_t)gettid();
69#elif defined __linux__
70 return (uint32_t)syscall( SYS_gettid );
71#elif defined __FreeBSD__
72 long id;
73 thr_self( &id );
74 return id;
75#elif defined __NetBSD__
76 return _lwp_self();
77#elif defined __DragonFly__
78 return lwp_gettid();
79#elif defined __OpenBSD__
80 return getthrid();
81#elif defined __EMSCRIPTEN__
82 // Not supported, but let it compile.
83 return 0;
84#else
85 // To add support for a platform, retrieve and return the kernel thread identifier here.
86 //
87 // Note that pthread_t (as for example returned by pthread_self()) is *not* a kernel
88 // thread identifier. It is a pointer to a library-allocated data structure instead.
89 // Such pointers will be reused heavily, making the pthread_t non-unique. Additionally
90 // a 64-bit pointer cannot be reliably truncated to 32 bits.
91 #error "Unsupported platform!"
92#endif
93
94}
95
96}
97
98#ifdef TRACY_ENABLE
99struct ThreadNameData
100{
101 uint32_t id;
102 const char* name;
103 ThreadNameData* next;
104};
105std::atomic<ThreadNameData*>& GetThreadNameData();
106#endif
107
108#ifdef _MSC_VER
109# pragma pack( push, 8 )
110struct THREADNAME_INFO
111{
112 DWORD dwType;
113 LPCSTR szName;
114 DWORD dwThreadID;
115 DWORD dwFlags;
116};
117# pragma pack( pop )
118
119void ThreadNameMsvcMagic( const THREADNAME_INFO& info )
120{
121 __try
122 {
123 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
124 }
125 __except(EXCEPTION_EXECUTE_HANDLER)
126 {
127 }
128}
129#endif
130
131TRACY_API void SetThreadName( const char* name )
132{
133#if defined _WIN32
134# ifdef TRACY_UWP
135 static auto _SetThreadDescription = &::SetThreadDescription;
136# else
137 static auto _SetThreadDescription = (t_SetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "SetThreadDescription" );
138# endif
139 if( _SetThreadDescription )
140 {
141 wchar_t buf[256];
142 mbstowcs( buf, name, 256 );
143 _SetThreadDescription( GetCurrentThread(), buf );
144 }
145 else
146 {
147# if defined _MSC_VER
148 THREADNAME_INFO info;
149 info.dwType = 0x1000;
150 info.szName = name;
151 info.dwThreadID = GetCurrentThreadId();
152 info.dwFlags = 0;
153 ThreadNameMsvcMagic( info );
154# endif
155 }
156#elif defined _GNU_SOURCE && !defined __EMSCRIPTEN__
157 {
158 const auto sz = strlen( name );
159 if( sz <= 15 )
160 {
161#if defined __APPLE__
162 pthread_setname_np( name );
163#else
164 pthread_setname_np( pthread_self(), name );
165#endif
166 }
167 else
168 {
169 char buf[16];
170 memcpy( buf, name, 15 );
171 buf[15] = '\0';
172#if defined __APPLE__
173 pthread_setname_np( buf );
174#else
175 pthread_setname_np( pthread_self(), buf );
176#endif
177 }
178 }
179#endif
180#ifdef TRACY_ENABLE
181 {
182 const auto sz = strlen( name );
183 char* buf = (char*)tracy_malloc( sz+1 );
184 memcpy( buf, name, sz );
185 buf[sz] = '\0';
186 auto data = (ThreadNameData*)tracy_malloc_fast( sizeof( ThreadNameData ) );
187 data->id = detail::GetThreadHandleImpl();
188 data->name = buf;
189 data->next = GetThreadNameData().load( std::memory_order_relaxed );
190 while( !GetThreadNameData().compare_exchange_weak( data->next, data, std::memory_order_release, std::memory_order_relaxed ) ) {}
191 }
192#endif
193}
194
195TRACY_API const char* GetThreadName( uint32_t id )
196{
197 static char buf[256];
198#ifdef TRACY_ENABLE
199 auto ptr = GetThreadNameData().load( std::memory_order_relaxed );
200 while( ptr )
201 {
202 if( ptr->id == id )
203 {
204 return ptr->name;
205 }
206 ptr = ptr->next;
207 }
208#endif
209
210#if defined _WIN32
211# ifdef TRACY_UWP
212 static auto _GetThreadDescription = &::GetThreadDescription;
213# else
214 static auto _GetThreadDescription = (t_GetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetThreadDescription" );
215# endif
216 if( _GetThreadDescription )
217 {
218 auto hnd = OpenThread( THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)id );
219 if( hnd != 0 )
220 {
221 PWSTR tmp;
222 _GetThreadDescription( hnd, &tmp );
223 auto ret = wcstombs( buf, tmp, 256 );
224 CloseHandle( hnd );
225 if( ret != 0 )
226 {
227 return buf;
228 }
229 }
230 }
231#elif defined __linux__
232 int cs, fd;
233 char path[32];
234 snprintf( path, sizeof( path ), "/proc/self/task/%d/comm", id );
235 sprintf( buf, "%" PRIu32, id );
236# ifndef __ANDROID__
237 pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &cs );
238# endif
239 if ( ( fd = open( path, O_RDONLY ) ) > 0) {
240 int len = read( fd, buf, 255 );
241 if( len > 0 )
242 {
243 buf[len] = 0;
244 if( len > 1 && buf[len-1] == '\n' )
245 {
246 buf[len-1] = 0;
247 }
248 }
249 close( fd );
250 }
251# ifndef __ANDROID__
252 pthread_setcancelstate( cs, 0 );
253# endif
254 return buf;
255#endif
256
257 sprintf( buf, "%" PRIu32, id );
258 return buf;
259}
260
261TRACY_API const char* GetEnvVar( const char* name )
262{
263#if defined _WIN32
264 // unfortunately getenv() on Windows is just fundamentally broken. It caches the entire
265 // environment block once on startup, then never refreshes it again. If any environment
266 // strings are added or modified after startup of the CRT, those changes will not be
267 // seen by getenv(). This removes the possibility of an app using this SDK from
268 // programmatically setting any of the behaviour controlling envvars here.
269 //
270 // To work around this, we'll instead go directly to the Win32 environment strings APIs
271 // to get the current value.
272 static char buffer[1024];
273 DWORD const kBufferSize = DWORD(sizeof(buffer) / sizeof(buffer[0]));
274 DWORD count = GetEnvironmentVariableA(name, buffer, kBufferSize);
275
276 if( count == 0 )
277 return nullptr;
278
279 if( count >= kBufferSize )
280 {
281 char* buf = reinterpret_cast<char*>(_alloca(count + 1));
282 count = GetEnvironmentVariableA(name, buf, count + 1);
283 memcpy(buffer, buf, kBufferSize);
284 buffer[kBufferSize - 1] = 0;
285 }
286
287 return buffer;
288#else
289 return getenv(name);
290#endif
291}
292
293}
294
295#ifdef __cplusplus
296extern "C" {
297#endif
298
299TRACY_API void ___tracy_set_thread_name( const char* name ) { tracy::SetThreadName( name ); }
300
301#ifdef __cplusplus
302}
303#endif