Christo
Christo

Reputation: 1892

How to debug a StackOverflowException in .NET

Scenario:

I've just been on a roll and implemented a bunch of code, but when I execute it I get thrown a StackOverflowException? The StackOverflowException doesn't have a stacktrace so I'm stuck. I know why a stack overflow might occur, but to fix it I need to know where it's root is.

All I'm getting is:

An unhandled exception of type 'System.StackOverflowException' occurred in tag-you're-it.dll

Options:

  1. Scan through all the changes and try to pin point the problem. (could be slow)
  2. Use a debugger and step through till you find the problem. (probably better than 1.)
  3. Use a profile and look for the most called methods.
  4. ?

PS:

This is a hypothetical situation (although not too uncommon) and therefore no code is available.

Upvotes: 47

Views: 35456

Answers (11)

Andrey Stukalin
Andrey Stukalin

Reputation: 5939

The ProcDump utility has helped us to debug the issue as described in details here. Steps:

  1. Download the tool
  2. Run the process, note its ID
  3. Attach the debugger by running procdump -accepteula -e 1 -f C00000FD.STACK_OVERFLOW -g -ma <process ID> d:\home\DebugTools\Dumps (the directory must exist)
  4. Make the exception to happen and the procdump will make you a dump.
  5. Open the dump file in the Visual Studio. For my sample app right after opening the dump file the VS has highlighted the line on which the SO happened.

We can use the same technique on Azure by enabling the CrashDiagnoser extension, like described here. Basically it performs the same steps as above. The dump file it generates could be downloaded and opened within the Visual Studio.

Upvotes: 7

Jessie Lesbian
Jessie Lesbian

Reputation: 1486

During the course of the development of IKVM.MET, I figured out a trick to debug stack overflow bugs in both static and dynamic assemblies, since IKVM.NET makes extensive use of dynamic assemblies. I would run the executable in the Visual Studio debugger, and when a stack overflow occurs, the Visual Studio debugger would suspend the process, instead of terminating it. Then, I used dotnet-dump to obtain a core dump, and finally, I would terminate the process to free up RAM and analyze the core dump using dotnet-dump. Note that this only works on .NET core applications.

Command to create the core dump

dotnet-dump collect -p <Process ID here>

The process ID can be obtained using the Task Manager. Command to view the stack trace:

dotnet-dump analyze <file name of dump> -c clrstack -all

How to use dotnet-dump, if you want to do further debugging

Note: This debugging technique is risky, since the process could be corrupted, and may not work every single time.

Upvotes: 0

BatteryBackupUnit
BatteryBackupUnit

Reputation: 13233

WinDbg can get the job done, including even getting a sensible (clr) stack trace. You'll need to get WinDbg unless you've already installed it with Visual Studio or Windows SDK. Note: "WinDbg Preview" with the new GUI has worked fine for me.

I suggest to start your process from WinDbg, but of course you can also attach it to a running process if this suits you better.

Note: Right after starting the process, the CLR is not loaded, and .loadby SOS.dll clr will fail ("unable to find module 'clr'). You have to wait for the CLR to be loaded. To stop execution once that happens perform:

  • sxe ld clr

Once the CLR is loaded you'll have to perform the following steps to break on StackOverflowException (enter in the command window / line):

  • .loadby SOS.dll clr (not .loadby sos clr—this can lead to the extension being loaded twice)
  • !stoponexception -create System.StackOverflowException
  • g (continues debugging)

trigger the StackOverflowException / wait for it to happen

  • !clrstack (will print the stacktrace)

Notable sources:

Upvotes: 32

Jas
Jas

Reputation: 71

I suspect if the stack size of the thread that caused the stack overflow is larger than some threshold that Visual Studio debugger can track, then the call stack is unavailable.

A workaround is to spawn a thread who's stack size is smaller than the default stack size, thus Visual Studio debugger can track the call stack.

        (new Thread(delegate ()
        {
            ProduceAStackOverFlowHere() ;
        }, 256 * 1024)).Start();//Default size for 32 bit process is 1MB, 64 bit process is 4MB. So I'll set the size at 256KB.

Upvotes: 3

jazza1000
jazza1000

Reputation: 4247

Just wanted to add to the answer about using WinDbg, with what I found for debugging a dotnet core application

  1. make sure you have windbg installed
  2. start your application via command line with dotnet run
  3. attach to the running process with windbg
  4. from the command line enter .loadby sos coreclr (this should detect the version of .net core that you are using, but if not you can use .load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.5\sos where 2.05 is the version of .netcore you are using
  5. commands are now available by entering !help
  6. Use !dso to get a dump of the stack

In my case that told me exactly where the stackoverflow exception was occurring

Upvotes: 1

Matthew Czarnek
Matthew Czarnek

Reputation: 351

Personally, I like to narrow it down as much as possible to a certain section of code. For example, I just had one. The odd thing was it was only happening on the machine I couldn't directly debug.

I had two threads running in parallel, so I stopped one from running(or you could unparallelize it).

Then I went through my functions and added like print out functions, such as: Just as function starts:

Console.WriteLine("<Enter method: {0}", DebuggingHelper.GetCurrentMethod());

Just before function returns:

Console.WriteLine(">Exit method: {0}", DebuggingHelper.GetCurrentMethod());

Where GetCurrentMethod is defined as:

[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCurrentMethod()
{
    StackTrace st = new StackTrace();
    StackFrame sf = st.GetFrame(1);
    return sf.GetMethod().Name;
}

Then I run it, maybe I don't add to all functions, but enough to narrow down where in the code it's happening. Then within that section, add more.

You could also add checkpoint as it runs through certain methods.

Then run it again, what you'll find is that the StackOverFlow exception will occur between those statements. Keep narrowing down till you find it.

Fairly simply and quick to find where it's happening this way.

Upvotes: 1

PPC-Coder
PPC-Coder

Reputation: 3632

If you have the code and are able to run your program from Visual Studio it should break in the debugger (if first-chance exceptions are enabled) when it encounters the System.StackOverflowException. From there you can examine the call stack and see which calls are blowing the stack. enter image description here

I've confirmed that this works for Visual Studio 2010 and Visual C# 2010 Express.

Upvotes: 0

Johan
Johan

Reputation: 115

Go to Debug, exceptions and check the thrown checkbox at 'Common Language Runtime Exceptions'. Now when you cause the stackoverflow exception, the debugger will stop (eventually) and show you the call stack.

Upvotes: 7

Neil Barnwell
Neil Barnwell

Reputation: 42125

This is almost always due to recursion. Either a method calling itself, or a method calling a method that calls it back and so on.

To find it:

  • UPDATED: I didn't realise, but apparently you can't get the stack trace for a StackOverflowException (I guess something to do with not being able to catch one, either). However there are ways to get a dump as mentioned here.
  • ReSharper will show methods that call themselves (it puts a little green circle in the sidebar for recursive calls) though it won't catch recursion where two or more methods are involved.
  • Use a tool like ANTS Profiler to see which methods are called the most times.
  • Keep an eye out for events that fire that might call code that means the same event fires again, causing a loop.

Occasionally you'll get typos like this, too:

private string name;

public string Name
{
    get { return Name; } // Ooops! This is recursive, all because of a typo...
}

Which is one reason why I personally now prefer to use automatic properties.

Upvotes: 17

Borja
Borja

Reputation: 2198

You can execute the program on debug mode and pause it. On the current callstack you can see that there are a method or a group of method that appears several times, these are the problematic methods. Put a break point on this method and look what its calling itself all the time.

Upvotes: 5

Daniel Earwicker
Daniel Earwicker

Reputation: 116674

At the method that is the "entry point" to the operation that fails, put a breakpoint. Step through the code and watch for occurrences of the same sequence of method calls happening over and over in an identical pattern so the call stack gets deeper and deeper.

As soon as you notice that, put a breakpoint at the current location, wherever that is. Continue execution (F5 in Visual Studio) - if you're on the right track then the debugger will stop very quickly at the same location, and the call stack will be even deeper.

Now you have a "live" stack frame you can examine, in order to figure out how to ensure that this recursion will properly terminate.

Upvotes: 1

Related Questions