/// <summary> /// Kills all running processes. /// </summary> public static void KillAll() { List <IProcess> ProcessesToKill = new List <IProcess>(); lock (SyncObject) { foreach (var ProcResult in ActiveProcesses) { if (!ProcResult.HasExited) { ProcessesToKill.Add(ProcResult); } } ActiveProcesses.Clear(); } // Remove processes that can't be killed for (int ProcessIndex = ProcessesToKill.Count - 1; ProcessIndex >= 0; --ProcessIndex) { var ProcessName = ProcessesToKill[ProcessIndex].GetProcessName(); if (!String.IsNullOrEmpty(ProcessName) && !CanBeKilled(ProcessName)) { CommandUtils.LogLog("Ignoring process \"{0}\" because it can't be killed.", ProcessName); ProcessesToKill.RemoveAt(ProcessIndex); } } if (ProcessesToKill.Count > 0) { CommandUtils.LogLog("Trying to kill {0} spawned processes.", ProcessesToKill.Count); foreach (var Proc in ProcessesToKill) { CommandUtils.LogLog(" {0}", Proc.GetProcessName()); } if (CommandUtils.IsBuildMachine) { for (int Cnt = 0; Cnt < 9; Cnt++) { bool AllDone = true; foreach (var Proc in ProcessesToKill) { try { if (!Proc.HasExited) { AllDone = false; CommandUtils.LogLog("Waiting for process: {0}", Proc.GetProcessName()); } } catch (Exception) { CommandUtils.LogWarning("Exception Waiting for process"); AllDone = false; } } try { if (ProcessResult.HasAnyDescendants(Process.GetCurrentProcess())) { AllDone = false; CommandUtils.Log("Waiting for descendants of main process..."); } } catch (Exception Ex) { CommandUtils.LogWarning("Exception Waiting for descendants of main process. " + Ex); AllDone = false; } if (AllDone) { break; } Thread.Sleep(10000); } } foreach (var Proc in ProcessesToKill) { var ProcName = Proc.GetProcessName(); try { if (!Proc.HasExited) { CommandUtils.LogLog("Killing process: {0}", ProcName); Proc.StopProcess(false); } } catch (Exception Ex) { CommandUtils.LogWarning("Exception while trying to kill process {0}:", ProcName); CommandUtils.LogWarning(LogUtils.FormatException(Ex)); } } try { if (CommandUtils.IsBuildMachine && ProcessResult.HasAnyDescendants(Process.GetCurrentProcess())) { CommandUtils.LogLog("current process still has descendants, trying to kill them..."); ProcessResult.KillAllDescendants(Process.GetCurrentProcess()); } } catch (Exception) { CommandUtils.LogWarning("Exception killing descendants of main process"); } } }
/// <summary> /// Wraps an action in an exception block. /// Ensures individual actions can be performed and exceptions won't prevent further actions from being executed. /// Useful for shutdown code where shutdown may be in several stages and it's important that all stages get a chance to run. /// </summary> /// <param name="Action"></param> private static void NoThrow(System.Action Action, string ActionDesc) { try { Action(); } catch (Exception Ex) { Log.TraceError("Exception performing nothrow action \"{0}\": {1}", ActionDesc, LogUtils.FormatException(Ex)); } }
public void WriteLine(string Source, TraceEventType Verbosity, string Message) { Message = LogUtils.FormatMessage(Source, Verbosity, Message); WriteToFile(Message); }
/// <summary> /// Runs a commandlet using Engine/Binaries/Win64/UE4Editor-Cmd.exe. /// </summary> /// <param name="ProjectFile">Project name.</param> /// <param name="UE4Exe">The name of the UE4 Editor executable to use.</param> /// <param name="Commandlet">Commandlet name.</param> /// <param name="Parameters">Command line parameters (without -run=)</param> public static void RunCommandlet(FileReference ProjectName, string UE4Exe, string Commandlet, string Parameters = null) { Log("Running UE4Editor {0} for project {1}", Commandlet, ProjectName); var CWD = Path.GetDirectoryName(UE4Exe); string EditorExe = UE4Exe; if (String.IsNullOrEmpty(CWD)) { EditorExe = HostPlatform.Current.GetUE4ExePath(UE4Exe); CWD = CombinePaths(CmdEnv.LocalRoot, HostPlatform.Current.RelativeBinariesFolder); } PushDir(CWD); DateTime StartTime = DateTime.UtcNow; string LocalLogFile = LogUtils.GetUniqueLogName(CombinePaths(CmdEnv.EngineSavedFolder, Commandlet)); Log("Commandlet log file is {0}", LocalLogFile); string Args = String.Format( "{0} -run={1} {2} -abslog={3} -stdout -CrashForUAT -unattended {5}{4}", (ProjectName == null) ? "" : CommandUtils.MakePathSafeToUseWithCommandLine(ProjectName.FullName), Commandlet, String.IsNullOrEmpty(Parameters) ? "" : Parameters, CommandUtils.MakePathSafeToUseWithCommandLine(LocalLogFile), IsBuildMachine ? "-buildmachine" : "", (GlobalCommandLine.Verbose || GlobalCommandLine.AllowStdOutLogVerbosity) ? "-AllowStdOutLogVerbosity " : "" ); ERunOptions Opts = ERunOptions.Default; if (GlobalCommandLine.UTF8Output) { Args += " -UTF8Output"; Opts |= ERunOptions.UTF8Output; } var RunResult = Run(EditorExe, Args, Options: Opts, Identifier: Commandlet); PopDir(); // If we're running on a Mac, dump all the *.crash files that were generated while the editor was running. if (HostPlatform.Current.HostEditorPlatform == UnrealTargetPlatform.Mac) { // If the exit code indicates the main process crashed, introduce a small delay because the crash report is written asynchronously. // If we exited normally, still check without waiting in case SCW or some other child process crashed. if (RunResult.ExitCode > 128) { CommandUtils.Log("Pausing before checking for crash logs..."); Thread.Sleep(10 * 1000); } // Create a list of directories containing crash logs, and add the system log folder List <string> CrashDirs = new List <string>(); CrashDirs.Add("/Library/Logs/DiagnosticReports"); // Add the user's log directory too string HomeDir = Environment.GetEnvironmentVariable("HOME"); if (!String.IsNullOrEmpty(HomeDir)) { CrashDirs.Add(Path.Combine(HomeDir, "Library/Logs/DiagnosticReports")); } // Check each directory for crash logs List <FileInfo> CrashFileInfos = new List <FileInfo>(); foreach (string CrashDir in CrashDirs) { try { DirectoryInfo CrashDirInfo = new DirectoryInfo(CrashDir); if (CrashDirInfo.Exists) { CrashFileInfos.AddRange(CrashDirInfo.EnumerateFiles("*.crash", SearchOption.TopDirectoryOnly).Where(x => x.LastWriteTimeUtc >= StartTime)); } } catch (UnauthorizedAccessException) { // Not all account types can access /Library/Logs/DiagnosticReports } } // Dump them all to the log foreach (FileInfo CrashFileInfo in CrashFileInfos) { // snmpd seems to often crash (suspect due to it being starved of CPU cycles during cooks) if (!CrashFileInfo.Name.StartsWith("snmpd_")) { CommandUtils.Log("Found crash log - {0}", CrashFileInfo.FullName); try { string[] Lines = File.ReadAllLines(CrashFileInfo.FullName); foreach (string Line in Lines) { CommandUtils.Log("Crash: {0}", Line); } } catch (Exception Ex) { CommandUtils.LogWarning("Failed to read file ({0})", Ex.Message); } } } } // Copy the local commandlet log to the destination folder. string DestLogFile = LogUtils.GetUniqueLogName(CombinePaths(CmdEnv.LogFolder, Commandlet)); if (!CommandUtils.CopyFile_NoExceptions(LocalLogFile, DestLogFile)) { CommandUtils.LogWarning("Commandlet {0} failed to copy the local log file from {1} to {2}. The log file will be lost.", Commandlet, LocalLogFile, DestLogFile); } string ProjectStatsDirectory = CombinePaths((ProjectName == null)? CombinePaths(CmdEnv.LocalRoot, "Engine") : Path.GetDirectoryName(ProjectName.FullName), "Saved", "Stats"); if (Directory.Exists(ProjectStatsDirectory)) { string DestCookerStats = CmdEnv.LogFolder; foreach (var StatsFile in Directory.EnumerateFiles(ProjectStatsDirectory, "*.csv")) { if (!CommandUtils.CopyFile_NoExceptions(StatsFile, CombinePaths(DestCookerStats, Path.GetFileName(StatsFile)))) { CommandUtils.LogWarning("Commandlet {0} failed to copy the local log file from {1} to {2}. The log file will be lost.", Commandlet, StatsFile, CombinePaths(DestCookerStats, Path.GetFileName(StatsFile))); } } } // else // { // CommandUtils.LogWarning("Failed to find directory {0} will not save stats", ProjectStatsDirectory); // } // Whether it was copied correctly or not, delete the local log as it was only a temporary file. CommandUtils.DeleteFile_NoExceptions(LocalLogFile); // Throw an exception if the execution failed. Draw attention to signal exit codes on Posix systems, rather than just printing the exit code if (RunResult.ExitCode != 0) { string ExitCodeDesc = ""; if (RunResult.ExitCode > 128 && RunResult.ExitCode < 128 + 32) { if (RunResult.ExitCode == 139) { ExitCodeDesc = " (segmentation fault)"; } else { ExitCodeDesc = String.Format(" (signal {0})", RunResult.ExitCode - 128); } } throw new CommandletException(DestLogFile, RunResult.ExitCode, "Editor terminated with exit code {0}{1} while running {2}{3}; see log {4}", RunResult.ExitCode, ExitCodeDesc, Commandlet, (ProjectName == null)? "" : String.Format(" for {0}", ProjectName), DestLogFile); } }
/// <summary> /// Copies a file. /// </summary> /// <param name="SourceName">Source name</param> /// <param name="TargetName">Target name</param> /// <returns>True if the operation was successful, false otherwise.</returns> public static bool SafeCopyFile(string SourceName, string TargetName, bool bQuiet = false) { if (!bQuiet) { Log.WriteLine(TraceEventType.Information, "SafeCopyFile {0} {1}", SourceName, TargetName); } const int MaxAttempts = 10; int Attempts = 0; bool Result = true; do { Result = true; bool Retry = true; try { File.Copy(SourceName, TargetName, overwrite: true); Retry = !File.Exists(TargetName); if (!Retry) { FileInfo SourceInfo = new FileInfo(SourceName); FileInfo TargetInfo = new FileInfo(TargetName); if (SourceInfo.Length != TargetInfo.Length) { Log.WriteLine(TraceEventType.Warning, "Size mismatch {0} = {1} to {2} = {3}", SourceName, SourceInfo.Length, TargetName, TargetInfo.Length); Retry = true; } if (!((SourceInfo.LastWriteTimeUtc - TargetInfo.LastWriteTimeUtc).TotalSeconds < 1 && (SourceInfo.LastWriteTimeUtc - TargetInfo.LastWriteTimeUtc).TotalSeconds > -1)) { Log.WriteLine(TraceEventType.Warning, "Date mismatch {0} = {1} to {2} = {3}", SourceName, SourceInfo.LastWriteTimeUtc, TargetName, TargetInfo.LastWriteTimeUtc); Retry = true; } } } catch (Exception Ex) { Log.WriteLine(System.Diagnostics.TraceEventType.Warning, "SafeCopyFile Exception was {0}", LogUtils.FormatException(Ex)); Retry = true; } if (Retry) { if (Attempts + 1 < MaxAttempts) { Log.WriteLine(TraceEventType.Warning, "Failed to copy {0} to {1}, deleting, waiting 10s and retrying.", SourceName, TargetName); if (File.Exists(TargetName)) { SafeDeleteFile(TargetName); } Thread.Sleep(10000); } else { Log.WriteLine(TraceEventType.Warning, "Failed to copy {0} to {1}", SourceName, TargetName); } Result = false; } } while (Result == false && ++Attempts < MaxAttempts); return(Result); }
public void WriteLine(string Source, TraceEventType Verbosity, string Format, params object[] Args) { var Message = LogUtils.FormatMessage(Source, Verbosity, Format, Args); WriteToFile(Message); }
/// <summary> /// Copies a file. /// </summary> /// <param name="SourceName">Source name</param> /// <param name="TargetName">Target name</param> /// <returns>True if the operation was successful, false otherwise.</returns> public static bool SafeCopyFile(string SourceName, string TargetName, bool bQuiet = false, bool bFilterSpecialLinesFromIniFiles = false) { if (!bQuiet) { Log.TraceLog("SafeCopyFile {0} {1}", SourceName, TargetName); } const int MaxAttempts = 10; int Attempts = 0; bool Result = true; do { Result = true; bool Retry = true; try { bool bSkipSizeCheck = false; if (bFilterSpecialLinesFromIniFiles && Path.GetExtension(SourceName) == ".ini") { FilterIniFile(SourceName, TargetName); // ini files may change size, don't check bSkipSizeCheck = true; } else { if (File.Exists(SourceName)) { File.Copy(SourceName, TargetName, overwrite: true); } else { Log.TraceWarning("Skip copying file {0} because it doesn't exist.", SourceName); } } Retry = !File.Exists(TargetName); if (!Retry) { FileInfo SourceInfo = new FileInfo(SourceName); FileInfo TargetInfo = new FileInfo(TargetName); if (!bSkipSizeCheck && SourceInfo.Length != TargetInfo.Length) { Log.TraceWarning("Size mismatch {0} = {1} to {2} = {3}", SourceName, SourceInfo.Length, TargetName, TargetInfo.Length); Retry = true; } // Timestamps should be no more than 2 seconds out - assuming this as exFAT filesystems store timestamps at 2 second intervals: // http://ntfs.com/exfat-time-stamp.htm if (!((SourceInfo.LastWriteTimeUtc - TargetInfo.LastWriteTimeUtc).TotalSeconds < 2 && (SourceInfo.LastWriteTimeUtc - TargetInfo.LastWriteTimeUtc).TotalSeconds > -2)) { Log.TraceWarning("Date mismatch {0} = {1} to {2} = {3}", SourceName, SourceInfo.LastWriteTimeUtc, TargetName, TargetInfo.LastWriteTimeUtc); Retry = true; } } } catch (Exception Ex) { Log.TraceWarning("SafeCopyFile Exception was {0}", LogUtils.FormatException(Ex)); Retry = true; } if (Retry) { if (Attempts + 1 < MaxAttempts) { Log.TraceWarning("Failed to copy {0} to {1}, deleting, waiting 10s and retrying.", SourceName, TargetName); if (File.Exists(TargetName)) { SafeDeleteFile(TargetName); } Thread.Sleep(10000); } else { Log.TraceWarning("Failed to copy {0} to {1}", SourceName, TargetName); } Result = false; } }while (Result == false && ++Attempts < MaxAttempts); return(Result); }
public static int Main() { var CommandLine = SharedUtils.ParseCommandLine(); LogUtils.InitLogging(CommandLine); ExitCode ReturnCode = ExitCode.Success; try { // ensure we can resolve any external assemblies as necessary. AssemblyUtils.InstallAssemblyResolver(Path.GetDirectoryName(Assembly.GetEntryAssembly().GetOriginalLocation())); HostPlatform.Initialize(); Log.TraceVerbose("{2}: Running on {0} as a {1}-bit process.", HostPlatform.Current.GetType().Name, Environment.Is64BitProcess ? 64 : 32, DateTime.UtcNow.ToString("o")); XmlConfigLoader.Init(); // Log if we're running from the launcher var ExecutingAssemblyLocation = Assembly.GetExecutingAssembly().Location; if (string.Compare(ExecutingAssemblyLocation, Assembly.GetEntryAssembly().GetOriginalLocation(), StringComparison.OrdinalIgnoreCase) != 0) { Log.TraceVerbose("Executed from AutomationToolLauncher ({0})", ExecutingAssemblyLocation); } Log.TraceVerbose("CWD={0}", Environment.CurrentDirectory); // Hook up exit callbacks var Domain = AppDomain.CurrentDomain; Domain.ProcessExit += Domain_ProcessExit; Domain.DomainUnload += Domain_ProcessExit; HostPlatform.Current.SetConsoleCtrlHandler(CtrlHandlerDelegateInstance); var Version = AssemblyUtils.ExecutableVersion; Log.TraceVerbose("{0} ver. {1}", Version.ProductName, Version.ProductVersion); // Don't allow simultaneous execution of AT (in the same branch) ReturnCode = InternalUtils.RunSingleInstance(MainProc, CommandLine); } catch (AutomationException Ex) { Log.TraceError("AutomationTool terminated with exception: {0}", Ex); ReturnCode = Ex.ErrorCode; } catch (Exception Ex) { // Catch all exceptions and propagate the ErrorCode if we are given one. Log.TraceError("AutomationTool terminated with exception: {0}", Ex); ReturnCode = ExitCode.Error_Unknown; } finally { // In all cases, do necessary shut down stuff, but don't let any additional exceptions leak out while trying to shut down. // Make sure there's no directories on the stack. NoThrow(() => CommandUtils.ClearDirStack(), "Clear Dir Stack"); // Try to kill process before app domain exits to leave the other KillAll call to extreme edge cases NoThrow(() => { if (ShouldKillProcesses && !Utils.IsRunningOnMono) { ProcessManager.KillAll(); } }, "Kill All Processes"); Log.TraceInformation("AutomationTool exiting with ExitCode={0} ({1})", (int)ReturnCode, ReturnCode); // Can't use NoThrow here because the code logs exceptions. We're shutting down logging! LogUtils.ShutdownLogging(); } // STOP: No code beyond the return statement should go beyond this point! // Nothing should happen after the finally block above is finished. return((int)ReturnCode); }
/// <summary> /// Main method. /// </summary> /// <param name="Arguments">Command line</param> public static ExitCode Process(string[] Arguments, StartupTraceListener StartupListener) { // Initial check for local or build machine runs BEFORE we parse the command line (We need this value set // in case something throws the exception while parsing the command line) IsBuildMachine = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("uebp_LOCAL_ROOT")) || Arguments.Any(x => x.Equals("-BuildMachine", StringComparison.InvariantCultureIgnoreCase)); // Scan the command line for commands to execute. var CommandsToExecute = new List <CommandInfo>(); string OutScriptsForProjectFileName; var AdditionalScriptsFolders = new List <string>(); ParseCommandLine(Arguments, CommandsToExecute, out OutScriptsForProjectFileName, AdditionalScriptsFolders); // Get the path to the telemetry file, if present string TelemetryFile = CommandUtils.ParseParamValue(Arguments, "-Telemetry"); Log.TraceVerbose("IsBuildMachine={0}", IsBuildMachine); Environment.SetEnvironmentVariable("IsBuildMachine", IsBuildMachine ? "1" : "0"); // should we kill processes on exit ShouldKillProcesses = !GlobalCommandLine.NoKill; Log.TraceVerbose("ShouldKillProcesses={0}", ShouldKillProcesses); if (CommandsToExecute.Count == 0 && GlobalCommandLine.Help) { DisplayHelp(); return(ExitCode.Success); } // Disable AutoSDKs if specified on the command line if (GlobalCommandLine.NoAutoSDK) { PlatformExports.PreventAutoSDKSwitching(); } // Setup environment Log.TraceLog("Setting up command environment."); CommandUtils.InitCommandEnvironment(); // Create the log file, and flush the startup listener to it TraceListener LogTraceListener = LogUtils.AddLogFileListener(CommandUtils.CmdEnv.LogFolder, CommandUtils.CmdEnv.FinalLogFolder); StartupListener.CopyTo(LogTraceListener); Trace.Listeners.Remove(StartupListener); // Initialize UBT if (!UnrealBuildTool.PlatformExports.Initialize()) { Log.TraceInformation("Failed to initialize UBT"); return(ExitCode.Error_Unknown); } // Clean rules folders up ProjectUtils.CleanupFolders(); // Compile scripts. Compiler = new ScriptCompiler(); using (TelemetryStopwatch ScriptCompileStopwatch = new TelemetryStopwatch("ScriptCompile")) { Compiler.FindAndCompileAllScripts(OutScriptsForProjectFileName, AdditionalScriptsFolders); } if (GlobalCommandLine.CompileOnly) { Log.TraceInformation("Compilation successful, exiting (CompileOnly)"); return(ExitCode.Success); } if (GlobalCommandLine.List) { ListAvailableCommands(Compiler.Commands); return(ExitCode.Success); } if (GlobalCommandLine.Help) { DisplayHelp(CommandsToExecute, Compiler.Commands); return(ExitCode.Success); } // Enable or disable P4 support CommandUtils.InitP4Support(CommandsToExecute, Compiler.Commands); if (CommandUtils.P4Enabled) { Log.TraceLog("Setting up Perforce environment."); CommandUtils.InitP4Environment(); CommandUtils.InitDefaultP4Connection(); } // Find and execute commands. ExitCode Result = Execute(CommandsToExecute, Compiler.Commands); if (TelemetryFile != null) { Directory.CreateDirectory(Path.GetDirectoryName(TelemetryFile)); CommandUtils.Telemetry.Write(TelemetryFile); } return(Result); }
/// <summary> /// Runs a commandlet using Engine/Binaries/Win64/UE4Editor-Cmd.exe. /// </summary> /// <param name="ProjectName">Project name.</param> /// <param name="UE4Exe">The name of the UE4 Editor executable to use.</param> /// <param name="Commandlet">Commandlet name.</param> /// <param name="Parameters">Command line parameters (without -run=)</param> public static void RunCommandlet(string ProjectName, string UE4Exe, string Commandlet, string Parameters = null) { Log("Running UE4Editor {0} for project {1}", Commandlet, ProjectName); var CWD = Path.GetDirectoryName(UE4Exe); string EditorExe = UE4Exe; if (String.IsNullOrEmpty(CWD)) { EditorExe = HostPlatform.Current.GetUE4ExePath(UE4Exe); CWD = CombinePaths(CmdEnv.LocalRoot, HostPlatform.Current.RelativeBinariesFolder); } PushDir(CWD); string LocalLogFile = LogUtils.GetUniqueLogName(CombinePaths(CmdEnv.EngineSavedFolder, Commandlet)); Log("Commandlet log file is {0}", LocalLogFile); string Args = String.Format( "{0} -run={1} {2} -abslog={3} -stdout -FORCELOGFLUSH -CrashForUAT -unattended {5}{4}", (ProjectName == null) ? "" : CommandUtils.MakePathSafeToUseWithCommandLine(ProjectName), Commandlet, String.IsNullOrEmpty(Parameters) ? "" : Parameters, CommandUtils.MakePathSafeToUseWithCommandLine(LocalLogFile), IsBuildMachine ? "-buildmachine" : "", GlobalCommandLine.Verbose ? "-AllowStdOutLogVerbosity" : "" ); ERunOptions Opts = ERunOptions.Default; if (GlobalCommandLine.UTF8Output) { Args += " -UTF8Output"; Opts |= ERunOptions.UTF8Output; } var RunResult = Run(EditorExe, Args, Options: Opts); PopDir(); // Draw attention to signal exit codes on Posix systems, rather than just printing the exit code if (RunResult.ExitCode > 128 && RunResult.ExitCode < 128 + 32) { if (RunResult.ExitCode == 139) { CommandUtils.LogError("Editor terminated abnormally due to a segmentation fault"); } else { CommandUtils.LogError("Editor terminated abnormally with signal {0}", RunResult.ExitCode - 128); } } // Copy the local commandlet log to the destination folder. string DestLogFile = LogUtils.GetUniqueLogName(CombinePaths(CmdEnv.LogFolder, Commandlet)); if (!CommandUtils.CopyFile_NoExceptions(LocalLogFile, DestLogFile)) { CommandUtils.LogWarning("Commandlet {0} failed to copy the local log file from {1} to {2}. The log file will be lost.", Commandlet, LocalLogFile, DestLogFile); } // Whether it was copied correctly or not, delete the local log as it was only a temporary file. CommandUtils.DeleteFile_NoExceptions(LocalLogFile); if (RunResult.ExitCode != 0) { throw new AutomationException("BUILD FAILED: Failed while running {0} for {1}; see log {2}", Commandlet, ProjectName, DestLogFile); } }
public static int Main() { var CommandLine = SharedUtils.ParseCommandLine(); HostPlatform.Initialize(); LogUtils.InitLogging(CommandLine); Log.WriteLine(TraceEventType.Information, "Running on {0}", HostPlatform.Current.GetType().Name); XmlConfigLoader.Init(); // Log if we're running from the launcher var ExecutingAssemblyLocation = CommandUtils.CombinePaths(Assembly.GetExecutingAssembly().Location); if (String.Compare(ExecutingAssemblyLocation, CommandUtils.CombinePaths(InternalUtils.ExecutingAssemblyLocation), true) != 0) { Log.WriteLine(TraceEventType.Information, "Executed from AutomationToolLauncher ({0})", ExecutingAssemblyLocation); } Log.WriteLine(TraceEventType.Information, "CWD={0}", Environment.CurrentDirectory); // Hook up exit callbacks var Domain = AppDomain.CurrentDomain; Domain.ProcessExit += Domain_ProcessExit; Domain.DomainUnload += Domain_ProcessExit; HostPlatform.Current.SetConsoleCtrlHandler(ProgramCtrlHandler); var Version = InternalUtils.ExecutableVersion; Log.WriteLine(TraceEventType.Verbose, "{0} ver. {1}", Version.ProductName, Version.ProductVersion); try { // Don't allow simultaneous execution of AT (in the same branch) ReturnCode = InternalUtils.RunSingleInstance(MainProc, CommandLine); } catch (Exception Ex) { Log.WriteLine(TraceEventType.Error, "AutomationTool terminated with exception:"); Log.WriteLine(TraceEventType.Error, LogUtils.FormatException(Ex)); Log.WriteLine(TraceEventType.Error, Ex.Message); if (ReturnCode == 0) { ReturnCode = (int)ErrorCodes.Error_Unknown; } } // Make sure there's no directiories on the stack. CommandUtils.ClearDirStack(); Environment.ExitCode = ReturnCode; // Try to kill process before app domain exits to leave the other KillAll call to extreme edge cases if (ShouldKillProcesses) { ProcessManager.KillAll(); } Log.WriteLine(TraceEventType.Information, "AutomationTool exiting with ExitCode={0}", ReturnCode); LogUtils.CloseFileLogging(); return(ReturnCode); }