I’m trying to figure out why the stacktraces in my Development Builds aren’t giving me the results I expect. It seems that parts of the stacktrace are being optimized our, or just removed. Maybe someone understands why. Here’s an example. I’ve intentionally set up some code to cause an exception to be thrown. But the stacktrace in the exception doesn’t reflect the entire stack.
Here’s the code:
void Update()
if (Input.GetKeyDown(KeyCode.J) && Input.GetKey(KeyCode.RightShift) && Input.GetKey(KeyCode.LeftShift))
{
Sub1(); // This is line 1372
}
}
private void Sub1()
{
Sub2();
}
private void Sub2()
{
throw new Exception("This is a simple warning for testing.");
}
And here’s what ends up in the log file:
What you see in the log file is that the error appeared on line 1372. However, line 1372 is just a call to method “Sub1”. Then “Sub1” calls “Sub2”, and it’s “Sub2” that throws the exception. Is this some kind of code optimization that’s removing methods? Is there a way to opt-out of this behavior, since it definitely makes debugging errors more difficult.
That smells of some step of compilation turning Sub1 into an alias for Sub2… what happens if you create a Sub3() and call it from inside Sub1(), but after you call Sub2() for your exception? That should at least prohibit flattening Sub1, if that’s what’s going on. At least it might offer insight.
I think the simple example I provided here was too simple, and it just got optimized away. I added a single Debug.Log() statement to each of the “Sub” methods, and now the stacktrace looks like this:
So, I think it’s fine now. I was reacting to some “missing” lines in the stack trace that I was expecting. But I think now I can explain it. Most a false alarm I guess.
It did seem weird to me too… I had never noticed that optimization before… but I don’t think I had really ever done something as simplistic as your example, except maybe forwarding an interface endpoint or something.
Well, glad it works for you. Always to understand where the heck something came from!!
Oh yeah: what happens if you attach the debugger and throw a breakpoint right on the Sub2() call inside of Sub1()? Does it even let you? Does it break? Does it hollow out the breakpoint as if it’s not able to find the code?
So far, I haven’t been able to hit any breakpoints in the build. I have BuildOptions.AllowDebugging and BuildOptions.Development enabled, but any breakpoints show up as not hittable. I don’t know if I’m just not enabling the right settings for debugging, or if this is another case of the code being optimized in some way that prevents attaching the debugger.
This happens from time to time. Inside your project there is a Library/ folder, and under that is ScriptAssemblies/ that contains the built DLL code. Closing Unity, deleting that folder (ScriptAssemblies) and reopening Unity will trigger a full recompile, and this often fixes unhittable breakpoints.
Depending on third party plugins, the version of Unity, the version of debugger, the version of your operating system, and perhaps even the position of the Sun in Aquarius, this happens more or less frequently.
I worked on a project last year where it happened almost every 2 or 3 debugger sessions, so once every few minutes.
I made a shell script that did the delete and kept it handy in a bash window because I was doing it all day long every day just to be able to use the debugger.