Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions RuntimeUnityEditor.Core/Windows/Breakpoints/BreakpointHit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@ public sealed class BreakpointHit
/// <summary>
/// Create a new breakpoint hit.
/// </summary>
public BreakpointHit(BreakpointPatchInfo origin, object instance, object[] args, object result, StackTrace trace)
public BreakpointHit(BreakpointPatchInfo origin, object instance, StackTrace trace)
{
Origin = origin;
Instance = instance;
Args = args;
Result = result;
Trace = trace;
TraceString = trace.ToString();
Time = DateTime.UtcNow;
_sw = Stopwatch.StartNew();
}

internal void Finalize(object[] args, object result)
{
ElapsedMilliseconds = _sw.ElapsedMilliseconds;
Args = args;
Result = result;
}

/// <summary>
Expand All @@ -34,11 +40,11 @@ public BreakpointHit(BreakpointPatchInfo origin, object instance, object[] args,
/// <summary>
/// The arguments that were passed to the method.
/// </summary>
public readonly object[] Args;
public object[] Args { get; private set; }
/// <summary>
/// The result of the method call.
/// </summary>
public readonly object Result;
public object Result { get; private set; }
/// <summary>
/// The stack trace at the time of the hit.
/// </summary>
Expand All @@ -49,6 +55,12 @@ public BreakpointHit(BreakpointPatchInfo origin, object instance, object[] args,
/// </summary>
public readonly DateTime Time;

private readonly Stopwatch _sw;
/// <summary>
/// Gets the total time the target method ran for, in milliseconds.
/// </summary>
public long ElapsedMilliseconds { get; private set; }

private string _toStr, _searchStr;
/// <summary>
/// Returns a string that can be used to search for this breakpoint hit.
Expand Down
41 changes: 28 additions & 13 deletions RuntimeUnityEditor.Core/Windows/Breakpoints/Breakpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ namespace RuntimeUnityEditor.Core.Breakpoints
public static class Breakpoints
{
private static readonly Harmony _harmony = new Harmony("RuntimeUnityEditor.Core.Breakpoints");
private static readonly HarmonyMethod _handlerMethodRet = new HarmonyMethod(typeof(Hooks), nameof(Hooks.BreakpointHandlerReturn));
private static readonly HarmonyMethod _handlerMethodNoRet = new HarmonyMethod(typeof(Hooks), nameof(Hooks.BreakpointHandlerNoReturn));
private static readonly HarmonyMethod _handlerMethodPrefix = new HarmonyMethod(typeof(Hooks), nameof(Hooks.BreakpointHandlerPrefix)) { priority = Priority.First }; // wrap around other patches
private static readonly HarmonyMethod _handlerMethodRet = new HarmonyMethod(typeof(Hooks), nameof(Hooks.BreakpointHandlerReturn)) { priority = Priority.Last };
private static readonly HarmonyMethod _handlerMethodNoRet = new HarmonyMethod(typeof(Hooks), nameof(Hooks.BreakpointHandlerNoReturn)) { priority = Priority.Last };
private static readonly Dictionary<MethodBase, BreakpointPatchInfo> _appliedPatches = new Dictionary<MethodBase, BreakpointPatchInfo>();
/// <summary>
/// A collection of all applied patches.
Expand Down Expand Up @@ -53,7 +54,7 @@ public static bool AttachBreakpoint(MethodBase target, object instance)
}

var hasReturn = target is MethodInfo mi && mi.ReturnType != typeof(void);
var patch = _harmony.Patch(target, postfix: hasReturn ? _handlerMethodRet : _handlerMethodNoRet);
var patch = _harmony.Patch(target, prefix: _handlerMethodPrefix, postfix: hasReturn ? _handlerMethodRet : _handlerMethodNoRet);
if (patch != null)
{
_appliedPatches[target] = new BreakpointPatchInfo(target, patch, instance);
Expand Down Expand Up @@ -111,37 +112,51 @@ public static void DetachAll()
_appliedPatches.Clear();
}

private static void AddHit(object __instance, MethodBase __originalMethod, object[] __args, object __result)
private static BreakpointHit BeginHit(object __instance, MethodBase __originalMethod)
{
if (!Enabled) return;
if (!Enabled) return null;

if (!_appliedPatches.TryGetValue(__originalMethod, out var pi)) return;
if (!_appliedPatches.TryGetValue(__originalMethod, out var pi)) return null;

if (pi.InstanceFilters.Count > 0 && !pi.InstanceFilters.Contains(__instance)) return;
if (pi.InstanceFilters.Count > 0 && !pi.InstanceFilters.Contains(__instance)) return null;

return new BreakpointHit(pi, __instance, new StackTrace(2, true));
}

private static void EndHit(BreakpointHit hit, object[] __args, object __result)
{
hit.Finalize(__args, __result);

if (DebuggerBreaking == DebuggerBreakType.ThrowCatch)
{
try { throw new BreakpointHitException(pi.Target.Name); }
try { throw new BreakpointHitException(hit.Origin.Target.Name); }
catch (BreakpointHitException) { }
}
else if (DebuggerBreaking == DebuggerBreakType.DebuggerBreak)
{
Debugger.Break();
}

OnBreakpointHit?.Invoke(new BreakpointHit(pi, __instance, __args, __result, new StackTrace(2, true)));
OnBreakpointHit?.Invoke(hit);
}

private static class Hooks
{
public static void BreakpointHandlerReturn(object __instance, MethodBase __originalMethod, object[] __args, object __result)
public static void BreakpointHandlerPrefix(object __instance, MethodBase __originalMethod, ref BreakpointHit __state)
{
__state = BeginHit(__instance, __originalMethod);
}

public static void BreakpointHandlerReturn(object[] __args, object __result, BreakpointHit __state)
{
AddHit(__instance, __originalMethod, __args, __result);
if (__state != null)
EndHit(__state, __args, __result);
}

public static void BreakpointHandlerNoReturn(object __instance, MethodBase __originalMethod, object[] __args)
public static void BreakpointHandlerNoReturn(object[] __args, BreakpointHit __state)
{
AddHit(__instance, __originalMethod, __args, null);
if (__state != null)
EndHit(__state, __args, null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ private void DrawHits()

GUILayout.BeginHorizontal(GUI.skin.box);
{
GUILayout.Label($"{hit.Time:HH:mm:ss.fff}", GUILayout.Width(85));
GUILayout.Label($"{hit.Time:HH:mm:ss.fff}\nTook {hit.ElapsedMilliseconds}ms", GUILayout.Width(85));

DrawHitOriginButton(hit.Origin);

Expand Down