/// <summary> /// Gets the log message for the specified stack trace. /// </summary> /// <param name="stackTrace">The stack trace, which must not be null.</param> /// <param name="cache">The cache of Harmony methods.</param> /// <param name="message">The location where the message will be stored.</param> internal static void GetStackTraceLog(StackTrace stackTrace, HarmonyMethodCache cache, StringBuilder message) { var registry = ModDebugRegistry.Instance; ModDebugInfo mod; int n = stackTrace.FrameCount; for (int i = 0; i < n; i++) { var frame = stackTrace.GetFrame(i); var method = frame?.GetMethod(); if (method == null) { // Try to output as much as possible method = cache.ParseInternalName(frame, message); } else { method = DebugUtils.GetOriginalMethod(method); } if (method != null) { // Try to give as much debug info as possible int line = frame.GetFileLineNumber(), chr = frame.GetFileColumnNumber(); message.Append(" at "); DebugUtils.AppendMethod(message, method); if (line > 0 || chr > 0) { message.AppendFormat(" ({0:D}, {1:D})", line, chr); } else { message.AppendFormat(" [{0:D}]", frame.GetILOffset()); } // The blame game var type = method.DeclaringType; var asm = type.Assembly; if (type.IsBaseGameType()) { message.Append(" <Klei>"); } else if (asm == typeof(string).Assembly) { message.Append(" <mscorlib>"); } else if ((mod = registry.OwnerOfType(type)) != null) { message.Append(" <"); message.Append(mod.ModName ?? "unknown"); message.Append(">"); } else if (asm.FullName.Contains("Unity")) { message.Append(" <Unity>"); } message.AppendLine(); DebugUtils.GetPatchInfo(method, message); } } }
/// <summary> /// Gets the log message for the specified exception. /// </summary> /// <param name="e">The exception, which must not be null.</param> /// <param name="cache">The cache of Harmony methods.</param> /// <returns>The log message for this exception.</returns> private static string GetExceptionLog(Exception e, HarmonyMethodCache cache) { // Better breakdown of the stack trace var message = new StringBuilder(8192); if (e is ReflectionTypeLoadException loadException) { message.AppendLine("Exception(s) when loading types:"); foreach (var cause in loadException.LoaderExceptions) { if (cause != null) { message.Append(GetExceptionLog(cause, cache)); } } } else { var stackTrace = new StackTrace(e); message.AppendFormat("{0}: {1}", e.GetType().Name, e.Message ?? "<no message>"); message.AppendLine(); ModLoadHandler.CrashingMod = DebugUtils.GetFirstModOnCallStack(stackTrace); GetStackTraceLog(stackTrace, cache, message); // Log the root cause var cause = e.GetBaseException(); if (cause != null && cause != e) { message.AppendLine("Root cause exception:"); message.Append(GetExceptionLog(cause, cache)); } } return(message.ToString()); }