static NOINLINE const char* _expand_environment( hash_t key, char* var ) { if( key == HASH_EXECUTABLE_NAME ) return environment_executable_name(); else if( key == HASH_EXECUTABLE_DIRECTORY ) return environment_executable_directory(); else if( key == HASH_EXECUTABLE_PATH ) return environment_executable_path(); else if( key == HASH_INITIAL_WORKING_DIRECTORY ) return environment_initial_working_directory(); else if( key == HASH_CURRENT_WORKING_DIRECTORY ) return environment_current_working_directory(); else if( key == HASH_HOME_DIRECTORY ) return environment_home_directory(); else if( key == HASH_TEMPORARY_DIRECTORY ) return environment_temporary_directory(); else if( string_equal_substr( var, "variable[", 9 ) ) //variable[varname] - Environment variable named "varname" { const char* value; unsigned int end_pos = string_find( var, ']', 9 ); if( end_pos != STRING_NPOS ) var[end_pos] = 0; value = environment_variable( var ); if( end_pos != STRING_NPOS ) var[end_pos] = ']'; return value; } return ""; }
unsigned int stacktrace_capture( void** trace, unsigned int max_depth, unsigned int skip_frames ) { unsigned int num_frames = 0; if( !trace ) return 0; if( !max_depth ) max_depth = BUILD_SIZE_STACKTRACE_DEPTH; if( max_depth > BUILD_SIZE_STACKTRACE_DEPTH ) max_depth = BUILD_SIZE_STACKTRACE_DEPTH; if( !_stackwalk_initialized ) { if( !_initialize_stackwalker() ) { memset( trace, 0, sizeof( void* ) * max_depth ); return num_frames; } } #if FOUNDATION_PLATFORM_WINDOWS && ( FOUNDATION_COMPILER_MSVC || FOUNDATION_COMPILER_INTEL ) // Add 1 skip frame for this function call ++skip_frames; # if USE_CAPTURESTACKBACKTRACE if( CallRtlCaptureStackBackTrace ) { void* local_trace[BUILD_SIZE_STACKTRACE_DEPTH]; if( max_depth + skip_frames > BUILD_SIZE_STACKTRACE_DEPTH ) max_depth = BUILD_SIZE_STACKTRACE_DEPTH - skip_frames; num_frames = (unsigned int)CallRtlCaptureStackBackTrace( skip_frames, max_depth, local_trace, 0 ); if( num_frames > max_depth ) num_frames = max_depth; memcpy( trace, local_trace, sizeof( void* ) * num_frames ); memset( trace + num_frames, 0, sizeof( void* ) * ( max_depth - num_frames ) ); } else { # else { # endif # if FOUNDATION_PLATFORM_ARCH_X86_64 // Raise an exception so helper has access to context record. __try { RaiseException( 0, // Application-defined exception code. 0, // Zero indicates continuable exception. 0, // Number of arguments in args array (ignored if args is null) 0 ); // Array of arguments } __except( _capture_stack_trace_helper( trace, max_depth, skip_frames, (GetExceptionInformation())->ContextRecord ) ) { } # else // Use a bit of inline assembly to capture the information relevant to stack walking which is // basically EIP and EBP. CONTEXT context; memset( &context, 0, sizeof( CONTEXT ) ); context.ContextFlags = CONTEXT_FULL; log_warnf( WARNING_DEPRECATED, "********** REIMPLEMENT FALLBACK STACKTRACE **********" ); /* Use a fake function call to pop the return address and retrieve EIP.*/ __asm { call FakeStackTraceCall FakeStackTraceCall: pop eax mov context.Eip, eax mov context.Ebp, ebp mov context.Esp, esp } // Capture the back trace. _capture_stack_trace_helper( trace, max_depth, skip_frames, &context ); # endif } #elif FOUNDATION_PLATFORM_APPLE //TODO: Implement #elif FOUNDATION_PLATFORM_POSIX // Add 1 skip frames for this function call skip_frames += 1; void* localframes[BUILD_SIZE_STACKTRACE_DEPTH]; num_frames = (unsigned int)backtrace( localframes, BUILD_SIZE_STACKTRACE_DEPTH ); if( num_frames > skip_frames ) { num_frames -= skip_frames; if( num_frames > max_depth ) num_frames = max_depth; memcpy( trace, localframes + skip_frames, sizeof( void* ) * num_frames ); } else trace[0] = 0; #endif return num_frames; } static bool _symbol_resolve_initialized = false; static bool _initialize_symbol_resolve() { if( _symbol_resolve_initialized ) return true; #if FOUNDATION_PLATFORM_WINDOWS { unsigned int options; void* dll = LoadLibraryA( "PSAPI.DLL" ); if( !dll ) return _symbol_resolve_initialized; CallEnumProcesses = (EnumProcessesFn)GetProcAddress( dll, "EnumProcesses" ); CallEnumProcessModules = (EnumProcessModulesFn)GetProcAddress( dll, "EnumProcessModules" ); CallGetModuleFileNameEx = (GetModuleFileNameExFn)GetProcAddress( dll, "GetModuleFileNameExA" ); CallGetModuleBaseName = (GetModuleBaseNameFn)GetProcAddress( dll, "GetModuleBaseNameA" ); CallGetModuleInformation = (GetModuleInformationFn)GetProcAddress( dll, "GetModuleInformation" ); if( !CallEnumProcesses || !CallEnumProcessModules || !CallGetModuleFileNameEx || !CallGetModuleBaseName || !CallGetModuleInformation ) return _symbol_resolve_initialized; dll = LoadLibraryA( "DBGHELP.DLL" ); if( !dll ) return _symbol_resolve_initialized; CallSymInitialize = (SymInitializeFn)GetProcAddress( dll, "SymInitialize" ); CallSymSetOptions = (SymSetOptionsFn)GetProcAddress( dll, "SymSetOptions" ); CallSymGetOptions = (SymGetOptionsFn)GetProcAddress( dll, "SymGetOptions" ); CallSymLoadModule64 = (SymLoadModule64Fn)GetProcAddress( dll, "SymLoadModule64" ); CallSymSetSearchPath = (SymSetSearchPathFn)GetProcAddress( dll, "SymSetSearchPath" ); CallSymGetModuleInfo64 = (SymGetModuleInfo64Fn)GetProcAddress( dll, "SymGetModuleInfo64" ); CallSymGetLineFromAddr64 = (SymGetLineFromAddr64Fn)GetProcAddress( dll, "SymGetLineFromAddr64" ); CallSymGetSymFromAddr64 = (SymGetSymFromAddr64Fn)GetProcAddress( dll, "SymGetSymFromAddr64" ); CallSymGetModuleBase64 = (SymGetModuleBase64Fn)GetProcAddress( dll, "SymGetModuleBase64" ); CallSymFunctionTableAccess64 = (SymFunctionTableAccess64Fn)GetProcAddress( dll, "SymFunctionTableAccess64" ); if( !CallSymInitialize || !CallSymSetOptions || !CallSymGetOptions || !CallSymLoadModule64 || !CallSymSetSearchPath || !CallSymGetModuleInfo64 || !CallSymGetLineFromAddr64 || !CallSymGetSymFromAddr64 || !CallSymGetModuleBase64 || !CallSymFunctionTableAccess64 ) return _symbol_resolve_initialized; options = CallSymGetOptions(); options |= SYMOPT_LOAD_LINES; options |= SYMOPT_DEBUG; options |= SYMOPT_UNDNAME; options |= SYMOPT_LOAD_LINES; options |= SYMOPT_FAIL_CRITICAL_ERRORS; options |= SYMOPT_DEFERRED_LOADS; options |= SYMOPT_ALLOW_ABSOLUTE_SYMBOLS; options |= SYMOPT_EXACT_SYMBOLS; options |= SYMOPT_CASE_INSENSITIVE; CallSymSetOptions( options ); CallSymInitialize( GetCurrentProcess(), 0, TRUE ); } _load_process_modules(); _symbol_resolve_initialized = true; #else _symbol_resolve_initialized = true; #endif return _symbol_resolve_initialized; } static NOINLINE char** _resolve_stack_frames( void** frames, unsigned int max_frames ) { #if FOUNDATION_PLATFORM_WINDOWS char** lines = 0; char symbol_buffer[ sizeof( IMAGEHLP_SYMBOL64 ) + 512 ]; PIMAGEHLP_SYMBOL64 symbol; DWORD displacement = 0; uint64_t displacement64 = 0; unsigned int iaddr = 0; unsigned int last_error; bool found = false; HANDLE process_handle = GetCurrentProcess(); int buffer_offset = 0; bool last_was_main = false; IMAGEHLP_LINE64 line64; IMAGEHLP_MODULE64 module64; for( iaddr = 0; ( iaddr < max_frames ) && !last_was_main; ++iaddr ) { char* resolved = 0; const char* function_name = "??"; const char* file_name = "??"; const char* module_name = "??"; unsigned int line_number = 0; //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; // Initialize symbol. symbol = (PIMAGEHLP_SYMBOL64)symbol_buffer; memset( symbol, 0, sizeof( symbol_buffer ) ); symbol->SizeOfStruct = sizeof( symbol_buffer ); symbol->MaxNameLength = 512; // Get symbol from address. if( CallSymGetSymFromAddr64 && CallSymGetSymFromAddr64( process_handle, (uint64_t)((uintptr_t)frames[iaddr]), &displacement64, symbol ) ) { int offset = 0; while( symbol->Name[offset] < 32 ) ++offset; function_name = symbol->Name + offset; } else { // No symbol found for this address. last_error = GetLastError(); } memset( &line64, 0, sizeof( line64 ) ); line64.SizeOfStruct = sizeof( line64 ); if( CallSymGetLineFromAddr64 && CallSymGetLineFromAddr64( process_handle, (uint64_t)((uintptr_t)frames[iaddr]), &displacement, &line64 ) ) { file_name = line64.FileName; line_number = line64.LineNumber; } memset( &module64, 0, sizeof( module64 ) ); module64.SizeOfStruct = sizeof( module64 ); if( CallSymGetModuleInfo64 && CallSymGetModuleInfo64( process_handle, (uint64_t)((uintptr_t)frames[iaddr]), &module64 ) ) { int last_slash = STRING_NPOS; module_name = module64.ImageName; last_slash = string_rfind( module_name, '\\', STRING_NPOS ); if( last_slash != STRING_NPOS ) module_name += last_slash + 1; } resolved = string_format( "[" STRING_FORMAT_POINTER "] %s (%s:%d +%d bytes) [in %s]", frames[iaddr], function_name, file_name, line_number, displacement, module_name ); array_push( lines, resolved ); if( string_equal( function_name, "main" ) ) last_was_main = true; } return lines; #elif FOUNDATION_PLATFORM_LINUX char** addrs = 0; char** lines = 0; const char** args = 0; process_t* proc = process_allocate(); unsigned int num_frames = 0; unsigned int requested_frames = 0; bool last_was_main = false; if( !string_length( environment_executable_path() ) ) { for( unsigned int iaddr = 0; iaddr < max_frames; ++iaddr ) { //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; array_push( lines, string_format( "[" STRING_FORMAT_POINTER "]", frames[iaddr] ) ); } return lines; } array_push( args, "-e" ); array_push( args, environment_executable_path() ); array_push( args, "-f" ); for( unsigned int iaddr = 0; iaddr < max_frames; ++iaddr ) { //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; char* addr = string_format( STRING_FORMAT_POINTER, frames[iaddr] ); array_push( addrs, addr ); array_push( args, addr ); ++requested_frames; } process_set_working_directory( proc, environment_initial_working_directory() ); process_set_executable_path( proc, "/usr/bin/addr2line" ); process_set_arguments( proc, args, array_size( args ) ); process_set_flags( proc, PROCESS_ATTACHED | PROCESS_STDSTREAMS ); process_spawn( proc ); stream_t* procout = process_stdout( proc ); while( !stream_eos( procout ) && ( num_frames < requested_frames ) && !last_was_main ) { char* function = stream_read_line( procout, '\n' ); char* filename = stream_read_line( procout, '\n' ); array_push( lines, string_format( "[" STRING_FORMAT_POINTER "] %s (%s)", frames[num_frames], function && string_length( function ) ? function : "??", filename && string_length( filename ) ? filename : "??" ) ); if( string_equal( function, "main" ) ) last_was_main = true; string_deallocate( function ); string_deallocate( filename ); ++num_frames; } process_wait( proc ); process_deallocate( proc ); string_array_deallocate( addrs ); array_deallocate( args ); return lines; #else char** lines = 0; for( unsigned int iaddr = 0; iaddr < max_frames; ++iaddr ) { //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; array_push( lines, string_format( "[" STRING_FORMAT_POINTER "]\n", frames[iaddr] ) ); } return lines; #endif }