/**
 * 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);
}