// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using EpicGames.Core; using UnrealBuildBase; using UnrealBuildTool; namespace AutomationTool.Benchmark { /// /// Base class for running tasks /// abstract class BenchmarkTaskBase { /// /// True or false based on whether the task failed /// public bool Failed { get; protected set; } /// /// Failure message /// public string FailureString { get; protected set; } /// /// Don't report this test /// public bool SkipReport { get; protected set; } /// /// Time the task took (does not include prequisites) /// public TimeSpan TaskTime { get; protected set; } /// /// Time the task started /// public DateTime StartTime { get; protected set; } /// /// Perform any prerequisites the task requires /// virtual protected bool PerformPrequisites() { return true; } /// /// Perform post-task cleanup /// virtual protected void PerformCleanup() { } /// /// Perform the actual task that is measured /// protected abstract bool PerformTask(); /// /// Return a name for this task for reporting /// /// public string TaskName { get; set; } /// /// A list of modifiers that can be considered when /// protected List TaskModifiers { get { return InternalModifiers; } } private readonly List InternalModifiers = new List(); /// /// Run the task. Performs any prerequisites, then the actual task itself /// public void Run() { try { if (PerformPrequisites()) { StartTime = DateTime.Now; if (!PerformTask()) { FailureString = "Task Failed"; Failed = true; } } else { FailureString = "Prequisites Failed"; Failed = true; } } catch (Exception Ex) { FailureString = string.Format("Exception: {0}", Ex.ToString()); Failed = true; } if (StartTime != DateTime.MinValue) { TaskTime = DateTime.Now - StartTime; } if (Failed) { Log.TraceError("{0} failed. {1}", GetFullTaskName(), FailureString); } try { PerformCleanup(); } catch (Exception Ex) { Log.TraceError("Cleanup of {0} failed. {1}", GetFullTaskName(), Ex); } } /// /// Report how long the task took /// public void Report() { if (!Failed) { Log.TraceInformation("Task {0}:\t\t\t\t{1}", GetFullTaskName(), TaskTime.ToString(@"hh\:mm\:ss")); } else { Log.TraceInformation("Task {0}::\t\t\t\t{1} Failed. {2}", GetFullTaskName(), TaskTime.ToString(@"hh\:mm\:ss"), FailureString); } } /// /// Returns a full name to use in reporting and logging /// /// public string GetFullTaskName() { string Name = TaskName; if (TaskModifiers.Count > 0) { Name = string.Format("{0} ({1})", Name, string.Join(" ", TaskModifiers)); } return Name; } public override string ToString() { return GetFullTaskName(); } } [Flags] public enum DDCTaskOptions { None = 0, WarmDDC = 1 << 0, ColdDDC = 1 << 1, //NoDDC = 1 << 2, NoShaderDDC = 1 << 3, HotDDC = 1 << 4, NoXGE = 1 << 5, // don't use XGE for shader compilation KeepMemoryDDC = 1 << 6, } abstract class BenchmarkEditorTaskBase : BenchmarkTaskBase { protected DDCTaskOptions TaskOptions; protected FileReference ProjectFile = null; protected string EditorArgs = ""; protected string ProjectName { get { return ProjectFile == null ? "UE" : ProjectFile.GetFileNameWithoutAnyExtensions(); } } protected BenchmarkEditorTaskBase(FileReference InProjectFile, DDCTaskOptions InTaskOptions, string InEditorArgs) { TaskOptions = InTaskOptions; EditorArgs = InEditorArgs.Trim().Replace(" ", " "); ProjectFile = InProjectFile; if (TaskOptions == DDCTaskOptions.None || TaskOptions.HasFlag(DDCTaskOptions.WarmDDC)) { TaskModifiers.Add("warmddc"); } if (TaskOptions.HasFlag(DDCTaskOptions.ColdDDC)) { TaskModifiers.Add("coldddc"); } if (TaskOptions.HasFlag(DDCTaskOptions.HotDDC)) { TaskModifiers.Add("hotddc"); } if (TaskOptions.HasFlag(DDCTaskOptions.NoShaderDDC)) { TaskModifiers.Add("noshaderddc"); } if (TaskOptions.HasFlag(DDCTaskOptions.KeepMemoryDDC)) { TaskModifiers.Add("withbootddc"); } if (!string.IsNullOrEmpty(EditorArgs)) { TaskModifiers.Add(EditorArgs); } } private Dictionary StoredEnvVars = new Dictionary(); private List CachePaths = new List(); private string GetXPlatformEnvironmentKey(string InKey) { // Mac uses _ in place of - if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Win64) { InKey = InKey.Replace("-", "_"); } return InKey; } protected override bool PerformPrequisites() { if (TaskOptions.HasFlag(DDCTaskOptions.ColdDDC)) { StoredEnvVars.Clear(); CachePaths.Clear(); // We put our temp DDC paths in here DirectoryReference BasePath = DirectoryReference.Combine(Unreal.EngineDirectory, "BenchmarkDDC"); // For Linux and Mac the ENV vars will be UE_BootDataCachePath and UE_LocalDataCachePath IEnumerable DDCEnvVars = new string[] { GetXPlatformEnvironmentKey("UE-BootDataCachePath"), GetXPlatformEnvironmentKey("UE-LocalDataCachePath") }; if (TaskOptions.HasFlag(DDCTaskOptions.KeepMemoryDDC)) { DDCEnvVars = DDCEnvVars.Where(E => !E.Contains("UE-Boot")); } // get all current environment vars and set them to our temp dir foreach (var Key in DDCEnvVars) { // save current key StoredEnvVars.Add(Key, Environment.GetEnvironmentVariable(Key)); // create a new dir for this key DirectoryReference Dir = DirectoryReference.Combine(BasePath, Key); if (DirectoryReference.Exists(Dir)) { DirectoryReference.Delete(Dir, true); } DirectoryReference.CreateDirectory(Dir); // save this dir and set it as the env var CachePaths.Add(Dir); Environment.SetEnvironmentVariable(Key, Dir.FullName); } // remove project files DirectoryReference ProjectDDC = DirectoryReference.Combine(ProjectFile.Directory, "DerivedDataCache"); CommandUtils.DeleteDirectory_NoExceptions(ProjectDDC.FullName); // remove S3 files DirectoryReference S3DDC = DirectoryReference.Combine(ProjectFile.Directory, "Saved", "S3DDC"); CommandUtils.DeleteDirectory_NoExceptions(S3DDC.FullName); } return base.PerformPrequisites(); } protected override void PerformCleanup() { // restore keys foreach (var KV in StoredEnvVars) { Environment.SetEnvironmentVariable(KV.Key, KV.Value); } foreach (var Dir in CachePaths) { CommandUtils.DeleteDirectory_NoExceptions(Dir.FullName); } CachePaths.Clear(); StoredEnvVars.Clear(); } } }