tracy/common/TracySystem.cpp
Bartosz Taudul b0fc0d5dcc
Check if rpmalloc has to be initialized before each operation.
The C++11 spec states in [basic.stc.thread] thread storage duration:

2. A variable with thread storage duration shall be initialized before its
   first odr-use (3.2) and, if constructed, shall be destroyed on thread exit.

Previously Tracy relied on the TLS data being initialized:
- During thread creation (MSVC).
- Or during first use in a thread, but the initialization was performed for
  the whole TLS block.

It seems that new compilers are more granular with how they perform the
initialization, hence rpmalloc init has to be checked before each allocation,
as it cannot be "folded" into, for example, initialization of the profiler
itself.
2021-05-31 02:31:42 +02:00

285 lines
7.1 KiB
C++

#if defined _MSC_VER || defined __CYGWIN__ || defined _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifndef NOMINMAX
# define NOMINMAX
# endif
#endif
#ifdef _MSC_VER
# pragma warning(disable:4996)
#endif
#if defined _WIN32 || defined __CYGWIN__
# include <windows.h>
# include <malloc.h>
#else
# include <pthread.h>
# include <string.h>
# include <unistd.h>
#endif
#ifdef __linux__
# ifdef __ANDROID__
# include <sys/types.h>
# else
# include <sys/syscall.h>
# endif
# include <fcntl.h>
#elif defined __FreeBSD__
# include <sys/thr.h>
#elif defined __NetBSD__ || defined __DragonFly__
# include <sys/lwp.h>
#endif
#ifdef __MINGW32__
# define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "TracySystem.hpp"
#if defined _WIN32 || defined __CYGWIN__
extern "C" typedef HRESULT (WINAPI *t_SetThreadDescription)( HANDLE, PCWSTR );
extern "C" typedef HRESULT (WINAPI *t_GetThreadDescription)( HANDLE, PWSTR* );
#endif
#ifdef TRACY_ENABLE
# include <atomic>
# include "TracyAlloc.hpp"
#endif
namespace tracy
{
namespace detail
{
TRACY_API uint64_t GetThreadHandleImpl()
{
#if defined _WIN32 || defined __CYGWIN__
static_assert( sizeof( decltype( GetCurrentThreadId() ) ) <= sizeof( uint64_t ), "Thread handle too big to fit in protocol" );
return uint64_t( GetCurrentThreadId() );
#elif defined __APPLE__
uint64_t id;
pthread_threadid_np( pthread_self(), &id );
return id;
#elif defined __ANDROID__
return (uint64_t)gettid();
#elif defined __linux__
return (uint64_t)syscall( SYS_gettid );
#elif defined __FreeBSD__
long id;
thr_self( &id );
return id;
#elif defined __NetBSD__
return _lwp_self();
#elif defined __DragonFly__
return lwp_gettid();
#elif defined __OpenBSD__
return getthrid();
#else
static_assert( sizeof( decltype( pthread_self() ) ) <= sizeof( uint64_t ), "Thread handle too big to fit in protocol" );
return uint64_t( pthread_self() );
#endif
}
}
#ifdef TRACY_ENABLE
struct ThreadNameData
{
uint64_t id;
const char* name;
ThreadNameData* next;
};
std::atomic<ThreadNameData*>& GetThreadNameData();
#endif
#ifdef _MSC_VER
# pragma pack( push, 8 )
struct THREADNAME_INFO
{
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
};
# pragma pack(pop)
void ThreadNameMsvcMagic( const THREADNAME_INFO& info )
{
__try
{
RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
}
#endif
TRACY_API void SetThreadName( const char* name )
{
#if defined _WIN32 || defined __CYGWIN__
static auto _SetThreadDescription = (t_SetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "SetThreadDescription" );
if( _SetThreadDescription )
{
wchar_t buf[256];
mbstowcs( buf, name, 256 );
_SetThreadDescription( GetCurrentThread(), buf );
}
else
{
# if defined _MSC_VER
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0;
ThreadNameMsvcMagic( info );
# endif
}
#elif defined _GNU_SOURCE && !defined __EMSCRIPTEN__ && !defined __CYGWIN__
{
const auto sz = strlen( name );
if( sz <= 15 )
{
pthread_setname_np( pthread_self(), name );
}
else
{
char buf[16];
memcpy( buf, name, 15 );
buf[15] = '\0';
pthread_setname_np( pthread_self(), buf );
}
}
#endif
#ifdef TRACY_ENABLE
{
const auto sz = strlen( name );
char* buf = (char*)tracy_malloc( sz+1 );
memcpy( buf, name, sz );
buf[sz] = '\0';
auto data = (ThreadNameData*)tracy_malloc( sizeof( ThreadNameData ) );
data->id = detail::GetThreadHandleImpl();
data->name = buf;
data->next = GetThreadNameData().load( std::memory_order_relaxed );
while( !GetThreadNameData().compare_exchange_weak( data->next, data, std::memory_order_release, std::memory_order_relaxed ) ) {}
}
#endif
}
TRACY_API const char* GetThreadName( uint64_t id )
{
static char buf[256];
#ifdef TRACY_ENABLE
auto ptr = GetThreadNameData().load( std::memory_order_relaxed );
while( ptr )
{
if( ptr->id == id )
{
return ptr->name;
}
ptr = ptr->next;
}
#else
# if defined _WIN32 || defined __CYGWIN__
static auto _GetThreadDescription = (t_GetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetThreadDescription" );
if( _GetThreadDescription )
{
auto hnd = OpenThread( THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)id );
if( hnd != 0 )
{
PWSTR tmp;
_GetThreadDescription( hnd, &tmp );
auto ret = wcstombs( buf, tmp, 256 );
CloseHandle( hnd );
if( ret != 0 )
{
return buf;
}
}
}
# elif defined __linux__
int cs, fd;
char path[32];
# ifdef __ANDROID__
int tid = gettid();
# else
int tid = (int) syscall( SYS_gettid );
# endif
snprintf( path, sizeof( path ), "/proc/self/task/%d/comm", tid );
sprintf( buf, "%" PRIu64, id );
# ifndef __ANDROID__
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &cs );
# endif
if ( ( fd = open( path, O_RDONLY ) ) > 0) {
int len = read( fd, buf, 255 );
if( len > 0 )
{
buf[len] = 0;
if( len > 1 && buf[len-1] == '\n' )
{
buf[len-1] = 0;
}
}
close( fd );
}
# ifndef __ANDROID__
pthread_setcancelstate( cs, 0 );
# endif
return buf;
# endif
#endif
sprintf( buf, "%" PRIu64, id );
return buf;
}
TRACY_API const char* GetEnvVar( const char* name )
{
#if defined _WIN32 || defined __CYGWIN__
// unfortunately getenv() on Windows is just fundamentally broken. It caches the entire
// environment block once on startup, then never refreshes it again. If any environment
// strings are added or modified after startup of the CRT, those changes will not be
// seen by getenv(). This removes the possibility of an app using this SDK from
// programmatically setting any of the behaviour controlling envvars here.
//
// To work around this, we'll instead go directly to the Win32 environment strings APIs
// to get the current value.
static char buffer[1024];
DWORD const kBufferSize = DWORD(sizeof(buffer) / sizeof(buffer[0]));
DWORD count = GetEnvironmentVariableA(name, buffer, kBufferSize);
if( count == 0 )
return nullptr;
if( count >= kBufferSize )
{
char* buf = reinterpret_cast<char*>(_alloca(count + 1));
count = GetEnvironmentVariableA(name, buf, count + 1);
memcpy(buffer, buf, kBufferSize);
buffer[kBufferSize - 1] = 0;
}
return buffer;
#else
return getenv(name);
#endif
}
}
#ifdef __cplusplus
extern "C" {
#endif
TRACY_API void ___tracy_set_thread_name( const char* name ) { tracy::SetThreadName( name ); }
#ifdef __cplusplus
}
#endif