Wrapping Debug.Assert and skip wrap method in the stack trace
I'm trying to wrap Debug.Assert() into multiple functions to allow the following:
have 2 type of asserts (Release: always enabled, Dev: enabled in the editor and in the build when a specific flag is set)
set a default assert message containing the LOC related to the error using
CompilerServices. CallerFilePath
andCompilerServices.CallerLineNumber
So far, i have the following:
using System.Diagnostics;
public static class Asserts
{
/// <summary>Runtime and editor asserts. These will always be executed.</summary>
/// <param name="context">The object attached to this assert. Unused in web builds.</param>
public static void ReleaseAssert(bool condition, string message, UnityEngine.Object context = null)
{
#if UNITY_EDITOR
UnityEngine.Debug.Assert(condition, message, context);
#elif !UNITY_EDITOR && UNITY_WEBGL
UnityEngine.Debug.Assert(condition, message);
#else
UnityEngine.Debug.Assert(false, "RealseAssert case not supported.");
#endif
}
/// <summary>Asserts that are executed only for deveveloppers or when ALL_ASSERTS build flag is specified.</summary>
[Conditional("UNITY_EDITOR"), Conditional("ALL_ASSERTS")]
public static void Assert(bool condition, string message, UnityEngine.Object context)
{
UnityEngine.Debug.Assert(condition, message, context);
}
/// <summary>Asserts that are executed only for deveveloppers or when ALL_ASSERTS build flag is specified.</summary>
[Conditional("UNITY_EDITOR"), Conditional("ALL_ASSERTS")]
public static void Assert(bool condition, string message)
{
UnityEngine.Debug.Assert(condition, message);
}
}
But there is only one problem i need to resolve. By wrapping Debug.Assert
the message logged in the editor console does not redirect to the correct part of the code. It redirect to Asserts.RealaseAssert
or Asserts.Assert
but i would like it to redirect to the parent method that called my wrapper.
I found some interesting things like StackTrace.METHODS_TO_SKIP
or the StackTrace
constructor that allows you to give a number of StackFrame
(method) to skip. But I am not able to log the stack trace with a link to the code.
I also tried to inline my methods with AggressiveInlining
but it does not change anything.
Do you have any idea of how to fix this ?
I have been using this and it works :) https://stackoverflow.com/a/37605142/4623982
Could you share an example of using that approach? I still can't make it redirect to the expected line of code.
Answer by kevin-masson · Feb 04, 2020 at 08:16 AM
Utilities used to set a custom stack trace:
public static class ExceptionUtilities
{
private static readonly FieldInfo STACK_TRACE_STRING_FI = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly Type TRACE_FORMAT_TI = Type.GetType("System.Diagnostics.StackTrace").GetNestedType("TraceFormat", BindingFlags.NonPublic);
private static readonly MethodInfo TRACE_TO_STRING_MI = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TRACE_FORMAT_TI }, null);
public static Exception SetStackTrace(this Exception target, StackTrace stack)
{
var getStackTraceString = TRACE_TO_STRING_MI.Invoke(stack, new object[] { Enum.GetValues(TRACE_FORMAT_TI).GetValue(0) });
STACK_TRACE_STRING_FI.SetValue(target, getStackTraceString);
return target;
}
}
Using this to log an exception that skip the current method:
public static void SomeMethod()
{
// Create a stack trace that ignore the current method.
StackTrace trace = new StackTrace(1, true);
// Assign it to some exception.
var ex = new Exception();
ex.SetStackTrace(trace);
Debug.LogException(ex);
}
is it possible to modify this to use with just Debug.Log? Or Debug.LogWarning? I expect we'd need a different field info type in which case the variable name "_stackTraceString" would have to be different.
I need to erase the first two lines of the Debug.Log stack trace, I came close with your solution but its still an exception
Answer by Develax · Feb 04, 2020 at 11:53 AM
@kevin-masson thanks, well done!
I've wrapped it this way:
using System.Diagnostics;
public static class MyDebug
{
private const string _assertMessage = "The object of type '{0}' is `null`'.";
private const string _assertMessageWithName = "The instance of type '{0}' is `null` in the '{1}'.";
[Conditional("UNITY_ASSERTIONS")]
public static void Assert<T>(T obj, UnityEngine.Object context)
where T : class
{
if (obj != null)
return;
string message = string.Format(_assertMessageWithName, typeof(T), context);
LogError(message);
}
[Conditional("UNITY_ASSERTIONS")]
public static void Assert<T>(T obj)
where T : class
{
if (obj != null)
return;
string message = string.Format(_assertMessage, typeof(T));
LogError(message);
}
private static void LogError(string message)
{
StackTrace trace = new StackTrace(2, true);
var exc = new GameException(message).SetStackTrace(trace);
UnityEngine.Debug.LogException(exc);
}
}
then I use it as a normal Debug.Assert
:
private void Awake()
{
MyDebug.Assert(_image);
// or with context
MyDebug.Assert(_image, this);
}
Your answer
Follow this Question
Related Questions
Disable hashing of function names when printing stacktrace to file in unity build 0 Answers
How to properly display debug messages on UI 0 Answers
Problem with unity editor namesapce 1 Answer
Access an Array of class inside another Array of class? (EditorGUILayout) 1 Answer
I want to use a variable in the Unity editor source file to get it from a regular script. 0 Answers