/** True system-specific crash handler that gets called first */ void PlatformCrashHandler(int32 Signal, siginfo_t* Info, void* Context) { fprintf(stderr, "Signal %d caught.\n", Signal); FLinuxCrashContext CrashContext; CrashContext.InitFromSignal(Signal, Info, Context); if (GCrashHandlerPointer) { GCrashHandlerPointer(CrashContext); } else { // call default one DefaultCrashHandler(CrashContext); } }
/** True system-specific crash handler that gets called first */ void PlatformCrashHandler(int32 Signal, siginfo_t* Info, void* Context) { fprintf(stderr, "Signal %d caught.\n", Signal); // Switch to malloc crash. FPlatformMallocCrash::Get().SetAsGMalloc(); FLinuxCrashContext CrashContext; CrashContext.InitFromSignal(Signal, Info, Context); if (GCrashHandlerPointer) { GCrashHandlerPointer(CrashContext); } else { // call default one DefaultCrashHandler(CrashContext); } }
/** * Generates information for crash reporter */ void GenerateCrashInfoAndLaunchReporter(const FLinuxCrashContext & Context) { // do not report crashes for tools (particularly for crash reporter itself) #if !IS_PROGRAM // create a crash-specific directory FString CrashInfoFolder = FString::Printf(TEXT("crashinfo-%s-pid-%d"), FApp::GetGameName(), getpid()); FString CrashInfoAbsolute = FPaths::ConvertRelativePathToFull(CrashInfoFolder); if (IFileManager::Get().MakeDirectory(*CrashInfoFolder)) { // generate "minidump" Context.GenerateReport(FPaths::Combine(*CrashInfoFolder, TEXT("diagnostics.txt"))); // generate "WER" GenerateWindowsErrorReport(FPaths::Combine(*CrashInfoFolder, TEXT("wermeta.xml"))); // generate "minidump" (just >1 byte) GenerateMinidump(FPaths::Combine(*CrashInfoFolder, TEXT("minidump.dmp"))); // copy log FString LogSrcAbsolute = FPlatformOutputDevices::GetAbsoluteLogFilename(); FString LogDstAbsolute = FPaths::Combine(*CrashInfoAbsolute, *FPaths::GetCleanFilename(LogSrcAbsolute)); FPaths::NormalizeDirectoryName(LogDstAbsolute); static_cast<void>(IFileManager::Get().Copy(*LogDstAbsolute, *LogSrcAbsolute)); // best effort, so don't care about result: couldn't copy -> tough, no log // try launching the tool and wait for its exit, if at all const TCHAR * RelativePathToCrashReporter = TEXT("../../../engine/binaries/linux/crashreportclient"); // FIXME: painfully hard-coded FProcHandle RunningProc = FPlatformProcess::CreateProc(RelativePathToCrashReporter, *(CrashInfoAbsolute + TEXT("/")), true, false, false, NULL, 0, NULL, NULL); if (FPlatformProcess::IsProcRunning(RunningProc)) { // do not wait indefinitely double kTimeOut = 3 * 60.0; double StartSeconds = FPlatformTime::Seconds(); for(;;) { if (!FPlatformProcess::IsProcRunning(RunningProc)) { break; } if (FPlatformTime::Seconds() - StartSeconds > kTimeOut) { break; } }; } } #endif FPlatformMisc::RequestExit(true); }
/** * Good enough default crash reporter. */ void DefaultCrashHandler(const FLinuxCrashContext & Context) { printf("DefaultCrashHandler: Signal=%d\n", Context.Signal); // at this point we should already be using malloc crash handler (see PlatformCrashHandler) const_cast<FLinuxCrashContext&>(Context).CaptureStackTrace(); if (GLog) { GLog->Flush(); } if (GWarn) { GWarn->Flush(); } if (GError) { GError->Flush(); GError->HandleError(); } return Context.GenerateCrashInfoAndLaunchReporter(); }
bool FLinuxPlatformStackWalk::ProgramCounterToHumanReadableString( int32 CurrentCallDepth, uint64 ProgramCounter, ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, FGenericCrashContext* Context ) { if (HumanReadableString && HumanReadableStringSize > 0) { ANSICHAR TempArray[MAX_SPRINTF]; if (CurrentCallDepth < 0) { if (PLATFORM_64BITS) { FCStringAnsi::Sprintf(TempArray, "[Callstack] 0x%016llx ", ProgramCounter); } else { FCStringAnsi::Sprintf(TempArray, "[Callstack] 0x%08x ", (uint32) ProgramCounter); } LinuxStackWalkHelpers::AppendToString(HumanReadableString, HumanReadableStringSize, Context, TempArray); // won't be able to display names here } else { if (PLATFORM_64BITS) { FCStringAnsi::Sprintf(TempArray, "[Callstack] %02d 0x%016llx ", CurrentCallDepth, ProgramCounter); } else { FCStringAnsi::Sprintf(TempArray, "[Callstack] %02d 0x%08x ", CurrentCallDepth, (uint32) ProgramCounter); } LinuxStackWalkHelpers::AppendToString(HumanReadableString, HumanReadableStringSize, Context, TempArray); // Get filename. { const char * FunctionName = LinuxStackWalkHelpers::GetFunctionName(Context, CurrentCallDepth); if (FunctionName) { LinuxStackWalkHelpers::AppendToString(HumanReadableString, HumanReadableStringSize, Context, FunctionName); } // try to add source file and line number, too FLinuxCrashContext* LinuxContext = static_cast< FLinuxCrashContext* >( Context ); if (LinuxContext) { const char * SourceFilename = NULL; int LineNumber; if (LinuxContext->GetInfoForAddress(reinterpret_cast< void* >( ProgramCounter ), NULL, &SourceFilename, &LineNumber) && FunctionName != NULL) { FCStringAnsi::Sprintf(TempArray, " [%s, line %d]", SourceFilename, LineNumber); LinuxStackWalkHelpers::AppendToString(HumanReadableString, HumanReadableStringSize, Context, TempArray); FCStringAnsi::Sprintf(TempArray, " [%s:%d]", SourceFilename, LineNumber); // if we were able to find info for this line, it means it's in our code // append program name (strong assumption of monolithic build here!) FCStringAnsi::Strcat(LinuxContext->MinidumpCallstackInfo, ARRAY_COUNT( LinuxContext->MinidumpCallstackInfo ) - 1, TCHAR_TO_ANSI(*FApp::GetName())); FCStringAnsi::Strcat(LinuxContext->MinidumpCallstackInfo, ARRAY_COUNT( LinuxContext->MinidumpCallstackInfo ) - 1, "!"); LinuxStackWalkHelpers::AppendFunctionNameIfAny(*LinuxContext, FunctionName, ProgramCounter); FCStringAnsi::Strcat(LinuxContext->MinidumpCallstackInfo, ARRAY_COUNT( LinuxContext->MinidumpCallstackInfo ) - 1, TempArray); } else { // if we were NOT able to find info for this line, it means it's something else FCStringAnsi::Strcat(LinuxContext->MinidumpCallstackInfo, ARRAY_COUNT( LinuxContext->MinidumpCallstackInfo ) - 1, "Unknown!"); LinuxStackWalkHelpers::AppendFunctionNameIfAny(*LinuxContext, FunctionName, ProgramCounter); } FCStringAnsi::Strcat(LinuxContext->MinidumpCallstackInfo, ARRAY_COUNT( LinuxContext->MinidumpCallstackInfo ) - 1, "\r\n"); // this one always uses Windows line terminators } } } return true; } return true; }
/** * Generates information for crash reporter */ void DLLEXPORT GenerateCrashInfoAndLaunchReporter(const FLinuxCrashContext & Context) { // do not report crashes for tools (particularly for crash reporter itself) #if !IS_PROGRAM // create a crash-specific directory FString CrashInfoFolder = FString::Printf(TEXT("crashinfo-%s-pid-%d-%s-%s"), FApp::GetGameName(), getpid(), *FDateTime::Now().ToString(), *FGuid::NewGuid().ToString()); FString CrashInfoAbsolute = FPaths::ConvertRelativePathToFull(CrashInfoFolder); if (IFileManager::Get().MakeDirectory(*CrashInfoFolder)) { // generate "minidump" Context.GenerateReport(FPaths::Combine(*CrashInfoFolder, TEXT("diagnostics.txt"))); // generate "WER" GenerateWindowsErrorReport(FPaths::Combine(*CrashInfoFolder, TEXT("wermeta.xml"))); // generate "minidump" (just >1 byte) GenerateMinidump(FPaths::Combine(*CrashInfoFolder, TEXT("minidump.dmp"))); // Introduces a new runtime crash context. Will replace all Windows related crash reporting. //FCStringAnsi::Strncpy(FilePath, CrashInfoFolder, PATH_MAX); //FCStringAnsi::Strcat(FilePath, PATH_MAX, "/" ); //FCStringAnsi::Strcat(FilePath, PATH_MAX, FGenericCrashContext::CrashContextRuntimeXMLNameA ); //SerializeAsXML( FilePath ); @todo uncomment after verification // copy log FString LogSrcAbsolute = FPlatformOutputDevices::GetAbsoluteLogFilename(); FString LogDstAbsolute = FPaths::Combine(*CrashInfoAbsolute, *FPaths::GetCleanFilename(LogSrcAbsolute)); FPaths::NormalizeDirectoryName(LogDstAbsolute); static_cast<void>(IFileManager::Get().Copy(*LogDstAbsolute, *LogSrcAbsolute)); // best effort, so don't care about result: couldn't copy -> tough, no log // try launching the tool and wait for its exit, if at all const TCHAR * RelativePathToCrashReporter = TEXT("../../../Engine/Binaries/Linux/CrashReportClient"); // FIXME: painfully hard-coded if (!FPaths::FileExists(RelativePathToCrashReporter)) { RelativePathToCrashReporter = TEXT("../../../engine/binaries/linux/crashreportclient"); // FIXME: even more painfully hard-coded } // show on the console printf("Starting %s\n", StringCast<ANSICHAR>(RelativePathToCrashReporter).Get()); FProcHandle RunningProc = FPlatformProcess::CreateProc(RelativePathToCrashReporter, *(CrashInfoAbsolute + TEXT("/")), true, false, false, NULL, 0, NULL, NULL); if (FPlatformProcess::IsProcRunning(RunningProc)) { // do not wait indefinitely double kTimeOut = 3 * 60.0; double StartSeconds = FPlatformTime::Seconds(); for(;;) { if (!FPlatformProcess::IsProcRunning(RunningProc)) { break; } if (FPlatformTime::Seconds() - StartSeconds > kTimeOut) { break; } FPlatformProcess::Sleep(1.0f); }; } } #endif FPlatformMisc::RequestExit(true); }