See More

#include "StackTrace/StackTrace.h" #include "StackTrace/StaticVector.h" #include "StackTrace/Utilities.hpp" #include #include #include #include // Detect the OS // clang-format off #if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 ) || defined( _MSC_VER ) #define USE_WINDOWS #elif defined( __APPLE__ ) #define USE_MAC #elif defined( __linux ) || defined( __linux__ ) || defined( __unix ) || defined( __posix ) #define USE_LINUX #else #error Unknown OS #endif // clang-format on // Include system dependent headers // clang-format off // Detect the OS and include system dependent headers #ifdef USE_WINDOWS #include #include #include #include #include #include #include #include #pragma comment( lib, "version.lib" ) // for "VerQueryValue" #else #include #include #include #include #include #include #include #endif #ifdef USE_MAC #include #include #include #include #define SIGRTMIN SIGUSR1 #define SIGRTMAX SIGUSR2 #endif // clang-format on extern int thread_callstack_signal; namespace StackTrace { /**************************************************************************** * Get the native thread handle * ****************************************************************************/ std::thread::native_handle_type thisThread() { #if defined( USE_LINUX ) || defined( USE_MAC ) return pthread_self(); #elif defined( USE_WINDOWS ) return GetCurrentThread(); #else return std::thread::native_handle(); #endif } /**************************************************************************** * Get a list of all active threads * * Note this uses system calls and may not behave on all systems * ****************************************************************************/ #if defined( USE_LINUX ) || defined( USE_MAC ) static volatile std::thread::native_handle_type thread_handle; static volatile bool thread_id_finished; static void _activeThreads_signal_handler( int ) { auto handle = StackTrace::thisThread(); thread_handle = handle; thread_id_finished = true; } #endif #ifdef USE_LINUX static int get_tid( int pid, const char* line ) { char buf2[128] = { 0 }; int i1 = 0; while ( line[i1] == ' ' ) { i1++; } int i2 = i1; while ( line[i2] != ' ' ) { i2++; } memcpy( buf2, &line[i1], i2 - i1 ); buf2[i2 - i1 + 1] = 0; int pid2 = atoi( buf2 ); if ( pid2 != pid ) return -1; i1 = i2; while ( line[i1] == ' ' ) { i1++; } i2 = i1; while ( line[i2] != ' ' ) { i2++; } memcpy( buf2, &line[i1], i2 - i1 ); buf2[i2 - i1 + 1] = 0; int tid = atoi( buf2 ); return tid; } #endif static std::mutex StackTrace_mutex; std::vector<:thread::native_handle_type> activeThreads() { std::vector<:thread::native_handle_type> threads; #if defined( USE_LINUX ) // Get the system thread ids int N_tid = 0, tid[1024]; int pid = getpid(); char cmd[128]; sprintf( cmd, "ps -T -p %i", pid ); auto fun = [&N_tid, &tid, pid]( const char* line ) { int id = get_tid( pid, line ); if ( id != -1 && N_tid < 1024 ) tid[N_tid++] = id; }; StackTrace::Utilities::exec2( cmd, fun ); int myid = syscall( SYS_gettid ); for ( int i = 0; i < N_tid; i++ ) { if ( tid[i] == myid ) std::swap( tid[i], tid[--N_tid] ); } // Get the thread id using signaling StackTrace_mutex.lock(); auto thread0 = StackTrace::thisThread(); auto old = signal( thread_callstack_signal, _activeThreads_signal_handler ); for ( int i = 0; i < N_tid; i++ ) { thread_id_finished = false; thread_handle = thread0; syscall( SYS_tgkill, pid, tid[i], thread_callstack_signal ); auto t1 = std::chrono::high_resolution_clock::now(); auto t2 = t1; while ( !thread_id_finished && std::chrono::duration( t2 - t1 ).count() < 0.1 ) { std::this_thread::yield(); t2 = std::chrono::high_resolution_clock::now(); } if ( thread_handle != thread0 ) { std::thread::native_handle_type tmp = thread_handle; threads.push_back( tmp ); } } signal( thread_callstack_signal, old ); StackTrace_mutex.unlock(); #elif defined( USE_MAC ) thread_act_port_array_t thread_list; mach_msg_type_number_t thread_count = 0; task_threads( mach_task_self(), &thread_list, &thread_count ); auto old = signal( thread_callstack_signal, _activeThreads_signal_handler ); for ( int i = 0; i < static_cast( thread_count ); i++ ) { if ( thread_list[i] == mach_thread_self() ) continue; static bool called = false; if ( !called ) { called = true; std::cerr << "activeThreads not finished for MAC\n"; } } signal( thread_callstack_signal, old ); #elif defined( USE_WINDOWS ) HANDLE hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); if ( hThreadSnap != INVALID_HANDLE_VALUE ) { // Fill in the size of the structure before using it THREADENTRY32 te32; te32.dwSize = sizeof( THREADENTRY32 ); // Retrieve information about the first thread, and exit if unsuccessful if ( !Thread32First( hThreadSnap, &te32 ) ) { std::cerr << "Unable to get info about first thread\n"; CloseHandle( hThreadSnap ); // Must clean up the snapshot object! return {}; } // Now walk the thread list of the system do { // if ( te32.th32OwnerProcessID == dwOwnerPID ) // threads.push_back( te32.th32ThreadID ); } while ( Thread32Next( hThreadSnap, &te32 ) ); CloseHandle( hThreadSnap ); // Must clean up the snapshot object! } #else static bool print = true; if ( print ) { std::cerr << "activeThreads is not supported on this compiler/OS\n"; print = false; } #endif // Add the current thread threads.push_back( StackTrace::thisThread() ); // Sort the threads std::sort( threads.begin(), threads.end() ); return threads; } /**************************************************************************** * Register threads with the StackTrace * ****************************************************************************/ static std::mutex globalThreadMutex; static Utilities::staticVector<:thread::native_handle_type> globalRegisteredThreads; thread_local struct ThreadExiter { void registerThread() {}; ~ThreadExiter() { auto id = thisThread(); try { unregisterThread( id ); } catch ( ... ) { } } } exiter; void registerThread() { exiter.registerThread(); registerThread( thisThread() ); } void registerThread( std::thread::native_handle_type id ) { globalThreadMutex.lock(); auto i = globalRegisteredThreads.find( id ); if ( i == globalRegisteredThreads.size() ) globalRegisteredThreads.push_back( id ); globalThreadMutex.unlock(); } void unregisterThread( std::thread::native_handle_type id ) { globalThreadMutex.lock(); auto i = globalRegisteredThreads.find( id ); if ( i != globalRegisteredThreads.size() ) { std::swap( globalRegisteredThreads[i], globalRegisteredThreads.back() ); globalRegisteredThreads.resize( globalRegisteredThreads.size() - 1 ); } globalThreadMutex.unlock(); } std::vector<:thread::native_handle_type> registeredThreads() { return std::vector<:thread::native_handle_type>( globalRegisteredThreads.begin(), globalRegisteredThreads.end() ); } } // namespace StackTrace