diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 24c50ef1a7..ff61e3a2ad 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -249,7 +249,14 @@ namespace OpenRA Settings = new Settings(Platform.ResolvePath(Path.Combine("^", "settings.yaml")), args); } - internal static void Initialize(Arguments args) + public static RunStatus InitializeAndRun(string[] args) + { + Initialize(new Arguments(args)); + GC.Collect(); + return Run(); + } + + static void Initialize(Arguments args) { Console.WriteLine("Platform is {0}", Platform.CurrentPlatform); @@ -790,7 +797,7 @@ namespace OpenRA } } - internal static RunStatus Run() + static RunStatus Run() { if (Settings.Graphics.MaxFramerate < 1) { diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index b1920b2770..6a67c3ff35 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -176,6 +176,7 @@ + diff --git a/OpenRA.Game/Support/ExceptionHandler.cs b/OpenRA.Game/Support/ExceptionHandler.cs new file mode 100644 index 0000000000..9625a0fa52 --- /dev/null +++ b/OpenRA.Game/Support/ExceptionHandler.cs @@ -0,0 +1,93 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace OpenRA +{ + public static class ExceptionHandler + { + public static void HandleFatalError(Exception ex) + { + var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log"; + Log.AddChannel("exception", exceptionName); + + if (Game.EngineVersion != null) + Log.Write("exception", "OpenRA engine version {0}", Game.EngineVersion); + + if (Game.ModData != null) + { + var mod = Game.ModData.Manifest.Metadata; + Log.Write("exception", "{0} mod version {1}", mod.Title, mod.Version); + } + + if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null) + { + var map = Game.OrderManager.World.Map; + Log.Write("exception", "on map {0} ({1} by {2}).", map.Uid, map.Title, map.Author); + } + + Log.Write("exception", "Date: {0:u}", DateTime.UtcNow); + Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion); + Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion); + var rpt = BuildExceptionReport(ex).ToString(); + Log.Write("exception", "{0}", rpt); + Console.Error.WriteLine(rpt); + } + + static StringBuilder BuildExceptionReport(Exception ex) + { + return BuildExceptionReport(ex, new StringBuilder(), 0); + } + + static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args) + { + return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine(); + } + + static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent) + { + if (ex == null) + return sb; + + sb.AppendIndentedFormatLine(indent, "Exception of type `{0}`: {1}", ex.GetType().FullName, ex.Message); + + var tle = ex as TypeLoadException; + var oom = ex as OutOfMemoryException; + if (tle != null) + { + sb.AppendIndentedFormatLine(indent, "TypeName=`{0}`", tle.TypeName); + } + else if (oom != null) + { + var gcMemoryBeforeCollect = GC.GetTotalMemory(false); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + sb.AppendIndentedFormatLine(indent, "GC Memory (post-collect)={0:N0}", GC.GetTotalMemory(false)); + sb.AppendIndentedFormatLine(indent, "GC Memory (pre-collect)={0:N0}", gcMemoryBeforeCollect); + + using (var p = Process.GetCurrentProcess()) + { + sb.AppendIndentedFormatLine(indent, "Working Set={0:N0}", p.WorkingSet64); + sb.AppendIndentedFormatLine(indent, "Private Memory={0:N0}", p.PrivateMemorySize64); + sb.AppendIndentedFormatLine(indent, "Virtual Memory={0:N0}", p.VirtualMemorySize64); + } + } + else + { + // TODO: more exception types + } + + if (ex.InnerException != null) + { + sb.AppendIndentedFormatLine(indent, "Inner"); + BuildExceptionReport(ex.InnerException, sb, indent + 1); + } + + sb.AppendIndentedFormatLine(indent, "{0}", ex.StackTrace); + + return sb; + } + } +} diff --git a/OpenRA.Game/Support/Program.cs b/OpenRA.Game/Support/Program.cs index ba0b980980..fca2fe0cfa 100644 --- a/OpenRA.Game/Support/Program.cs +++ b/OpenRA.Game/Support/Program.cs @@ -11,10 +11,7 @@ using System; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Reflection; -using System.Text; namespace OpenRA { @@ -31,109 +28,19 @@ namespace OpenRA static int Main(string[] args) { if (Debugger.IsAttached || args.Contains("--just-die")) - return (int)Run(args); + return (int)Game.InitializeAndRun(args); - AppDomain.CurrentDomain.UnhandledException += (_, e) => FatalError((Exception)e.ExceptionObject); + AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject); try { - return (int)Run(args); + return (int)Game.InitializeAndRun(args); } catch (Exception e) { - FatalError(e); + ExceptionHandler.HandleFatalError(e); return (int)RunStatus.Error; } } - - static void FatalError(Exception ex) - { - var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log"; - Log.AddChannel("exception", exceptionName); - - if (Game.EngineVersion != null) - Log.Write("exception", "OpenRA engine version {0}", Game.EngineVersion); - - if (Game.ModData != null) - { - var mod = Game.ModData.Manifest.Metadata; - Log.Write("exception", "{0} mod version {1}", mod.Title, mod.Version); - } - - if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null) - { - var map = Game.OrderManager.World.Map; - Log.Write("exception", "on map {0} ({1} by {2}).", map.Uid, map.Title, map.Author); - } - - Log.Write("exception", "Date: {0:u}", DateTime.UtcNow); - Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion); - Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion); - var rpt = BuildExceptionReport(ex).ToString(); - Log.Write("exception", "{0}", rpt); - Console.Error.WriteLine(rpt); - } - - static StringBuilder BuildExceptionReport(Exception ex) - { - return BuildExceptionReport(ex, new StringBuilder(), 0); - } - - static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args) - { - return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine(); - } - - static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent) - { - if (ex == null) - return sb; - - sb.AppendIndentedFormatLine(indent, "Exception of type `{0}`: {1}", ex.GetType().FullName, ex.Message); - - var tle = ex as TypeLoadException; - var oom = ex as OutOfMemoryException; - if (tle != null) - { - sb.AppendIndentedFormatLine(indent, "TypeName=`{0}`", tle.TypeName); - } - else if (oom != null) - { - var gcMemoryBeforeCollect = GC.GetTotalMemory(false); - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - sb.AppendIndentedFormatLine(indent, "GC Memory (post-collect)={0:N0}", GC.GetTotalMemory(false)); - sb.AppendIndentedFormatLine(indent, "GC Memory (pre-collect)={0:N0}", gcMemoryBeforeCollect); - - using (var p = Process.GetCurrentProcess()) - { - sb.AppendIndentedFormatLine(indent, "Working Set={0:N0}", p.WorkingSet64); - sb.AppendIndentedFormatLine(indent, "Private Memory={0:N0}", p.PrivateMemorySize64); - sb.AppendIndentedFormatLine(indent, "Virtual Memory={0:N0}", p.VirtualMemorySize64); - } - } - else - { - // TODO: more exception types - } - - if (ex.InnerException != null) - { - sb.AppendIndentedFormatLine(indent, "Inner"); - BuildExceptionReport(ex.InnerException, sb, indent + 1); - } - - sb.AppendIndentedFormatLine(indent, "{0}", ex.StackTrace); - - return sb; - } - - static RunStatus Run(string[] args) - { - Game.Initialize(new Arguments(args)); - GC.Collect(); - return Game.Run(); - } } } \ No newline at end of file