From 24b225412940ffdc0a1eeafab4dde3d8de015baf Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Nov 2024 03:45:23 +0000 Subject: [PATCH] Revert "removal of unused preload patcher" This reverts commit 1726272f79b7a8b1761ba46d9d94f1123bb0eaeb. --- .../PreloadPatcherProcessor.cs | 1 + .../Patches/Preloader/FunctionCallTrace.cs | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 UniTAS/Patcher/Patches/Preloader/FunctionCallTrace.cs diff --git a/UniTAS/Patcher/Implementations/PreloadPatcherProcessor.cs b/UniTAS/Patcher/Implementations/PreloadPatcherProcessor.cs index a722722c..6a24c8b7 100644 --- a/UniTAS/Patcher/Implementations/PreloadPatcherProcessor.cs +++ b/UniTAS/Patcher/Implementations/PreloadPatcherProcessor.cs @@ -11,6 +11,7 @@ public class PreloadPatcherProcessor new StaticCtorHeaders(), new UnityInitInvoke(), new FinalizeSuppressionPatch(), + new FunctionCallTrace(), new SteamAPIPatch(), new SerializationCallbackPatch() ]; diff --git a/UniTAS/Patcher/Patches/Preloader/FunctionCallTrace.cs b/UniTAS/Patcher/Patches/Preloader/FunctionCallTrace.cs new file mode 100644 index 00000000..7c538b21 --- /dev/null +++ b/UniTAS/Patcher/Patches/Preloader/FunctionCallTrace.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using BepInEx.Logging; +using HarmonyLib; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using UniTAS.Patcher.Extensions; +using UniTAS.Patcher.Implementations.Customization; +using UniTAS.Patcher.Interfaces; +using UniTAS.Patcher.Utils; + +namespace UniTAS.Patcher.Patches.Preloader; + +public class FunctionCallTrace : PreloadPatcher +{ + private static readonly ManualLogSource LOG = Logger.CreateLogSource("FunctionCallTrace"); + + public override void Patch(ref AssemblyDefinition assembly) + { + if (!File.Exists(UniTASPaths.ConfigBepInEx)) return; + + // check config if loading + var unitasConfig = File.ReadAllText(UniTASPaths.ConfigBepInEx); + + var funcTraceEnable = ConfigUtils.GetEntryKey(unitasConfig, Config.Sections.Debug.FunctionCallTrace.SECTION_NAME, + Config.Sections.Debug.FunctionCallTrace.ENABLE); + + if (funcTraceEnable != "true") return; + + StaticLogger.LogDebug("Hooking functions for trace logging"); + + var methodInvokeTrace = AccessTools.Method(typeof(FunctionCallTrace), nameof(MethodInvokeTrace)); + var methodInvokeTraceRef = assembly.MainModule.ImportReference(methodInvokeTrace); + + var types = assembly.Modules.SelectMany(module => module.GetAllTypes()); + var matchingTypesConfig = ConfigUtils.GetEntryKey(unitasConfig, Config.Sections.Debug.FunctionCallTrace.SECTION_NAME, Config.Sections.Debug.FunctionCallTrace.MATCHING_TYPES); + + if (matchingTypesConfig != null && matchingTypesConfig.Trim() != "*") + { + var matchingTypes = matchingTypesConfig.Split([','], System.StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); + + types = types.Where(t => matchingTypes.Any(x => t.FullName.Like(x))); + } + + foreach (var type in types) + { + if (type.IsEnum || type.IsInterface) continue; + + StaticLogger.LogDebug($"hooking on class {type.FullName}"); + + foreach (var method in type.Methods) + { + if (!method.HasBody) continue; + + StaticLogger.LogDebug($"hooking on method {method.Name}"); + + var body = method.Body; + body.SimplifyMacros(); + + var il = body.GetILProcessor(); + + var methodInvokeTraceInstruction = il.Create(OpCodes.Call, methodInvokeTraceRef); + + il.InsertBefore(body.Instructions.First(), methodInvokeTraceInstruction); + + body.OptimizeMacros(); + } + } + } + + public static void MethodInvokeTrace() + { + var stack = new StackTrace(); + var frame = stack.GetFrame(1); + + if (frame == null) + { + LOG.LogWarning("trace data is empty, cannot trace call"); + return; + } + + var space = stack.FrameCount - 2; + var func = frame.GetMethod(); + + var funcNameFull = $"{func.DeclaringType.SaneFullName()}.{func.Name}"; + + LOG.LogInfo($"{new string(' ', space)}{funcNameFull}"); + } +}