/** * 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); }
void FLinuxCrashContext::GenerateCrashInfoAndLaunchReporter(bool bReportingNonCrash) const { // do not report crashes for tools (particularly for crash reporter itself) #if !IS_PROGRAM // create a crash-specific directory FString CrashGuid; if (!FParse::Value(FCommandLine::Get(), TEXT("CrashGUID="), CrashGuid) || CrashGuid.Len() <= 0) { CrashGuid = FGuid::NewGuid().ToString(); } FString CrashInfoFolder = FString::Printf(TEXT("%sinfo-%s-pid-%d-%s"), bReportingNonCrash ? TEXT("ensure") : TEXT("crash"), FApp::GetGameName(), getpid(), *CrashGuid); FString CrashInfoAbsolute = FPaths::ConvertRelativePathToFull(CrashInfoFolder); if (IFileManager::Get().MakeDirectory(*CrashInfoFolder)) { // generate "minidump" GenerateReport(FPaths::Combine(*CrashInfoFolder, TEXT("Diagnostics.txt"))); // generate "WER" GenerateWindowsErrorReport(FPaths::Combine(*CrashInfoFolder, TEXT("wermeta.xml")), bReportingNonCrash); // 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 } FString CrashReportClientArguments; // Suppress the user input dialog if we're running in unattended mode bool bNoDialog = FApp::IsUnattended() || IsRunningDedicatedServer(); if (bNoDialog) { CrashReportClientArguments += TEXT(" -Unattended "); } CrashReportClientArguments += CrashInfoAbsolute + TEXT("/"); if (bReportingNonCrash) { // If we're reporting non-crash, try to avoid spinning here and instead do that in the tick. // However, if there was already a crash reporter running (i.e. we hit ensure() too quickly), take a hitch here if (FPlatformProcess::IsProcRunning(LinuxCrashReporterTracker::CurrentlyRunningCrashReporter)) { // do not wait indefinitely, allow 45 second hitch (anticipating callstack parsing) const double kEnsureTimeOut = 45.0; const double kEnsureSleepInterval = 0.1; if (!LinuxCrashReporterTracker::WaitForProcWithTimeout(LinuxCrashReporterTracker::CurrentlyRunningCrashReporter, kEnsureTimeOut, kEnsureSleepInterval)) { FPlatformProcess::TerminateProc(LinuxCrashReporterTracker::CurrentlyRunningCrashReporter); } LinuxCrashReporterTracker::Tick(0.001f); // tick one more time to make it clean up after itself } LinuxCrashReporterTracker::CurrentlyRunningCrashReporter = FPlatformProcess::CreateProc(RelativePathToCrashReporter, *CrashReportClientArguments, true, false, false, NULL, 0, NULL, NULL); LinuxCrashReporterTracker::CurrentTicker = FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateStatic(&LinuxCrashReporterTracker::Tick), 1.f); } else { // spin here until CrashReporter exits FProcHandle RunningProc = FPlatformProcess::CreateProc(RelativePathToCrashReporter, *CrashReportClientArguments, true, false, false, NULL, 0, NULL, NULL); // do not wait indefinitely - can be more generous about the hitch than in ensure() case const double kCrashTimeOut = 3 * 60.0; const double kCrashSleepInterval = 1.0; if (!LinuxCrashReporterTracker::WaitForProcWithTimeout(RunningProc, kCrashTimeOut, kCrashSleepInterval)) { FPlatformProcess::TerminateProc(RunningProc); } FPlatformProcess::CloseProc(RunningProc); } } #endif if (!bReportingNonCrash) { // remove the handler for this signal and re-raise it (which should generate the proper core dump) // print message to stdout directly, it may be too late for the log (doesn't seem to be printed during a crash in the thread) printf("Engine crash handling finished; re-raising signal %d for the default handler. Good bye.\n", Signal); fflush(stdout); struct sigaction ResetToDefaultAction; FMemory::Memzero(ResetToDefaultAction); ResetToDefaultAction.sa_handler = SIG_DFL; sigfillset(&ResetToDefaultAction.sa_mask); sigaction(Signal, &ResetToDefaultAction, nullptr); raise(Signal); } }
/** * 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); }