Vishal Anand
Vishal Anand

Reputation: 1471

How to get full stack trace from exception object, when debugging in windbg?

I have a dump file which was dumped from a app pool crash in IIS. A thread contains System.NullReferenceException. I am able to locate the NullReferenceException in the dump but I am not able to view the _stackTrace

0:070> !wdo 00000009cea474b8

Address: 00000009cea474b8
Method Table/Token: 00007ffb53d2dc88/200011704 
Class Name: System.NullReferenceException
Size : 160
EEClass: 00007ffb53ea3e88
Instance Fields: 19
Static Fields: 0
Total Fields: 19
Heap/Generation: 10/0
Module: 00007ffb53d10000
Assembly: 0000000c5a934170
Domain: 00007ffb56324100
Assembly Name: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Inherits: System.SystemException System.Exception System.Object (00007FFB53D30660 00007FFB53DB5D20 00007FFB53DB5F88)
00007ffb53db5b70                                    System.String +0000                               _className 0000000751fec958 System.NullReferenceException
00007ffb53dbd6b8                     System.Reflection.MethodBase +0008                         _exceptionMethod 000000074ffc4608
00007ffb53db5b70                                    System.String +0010                   _exceptionMethodString 0000000000000000 (null)
00007ffb53db5b70                                    System.String +0018                                 _message 000000084c638270 オブジェクト参照がオブジェクト インスタンスに設定されていません。
00007ffb53d2de78                   System.Collections.IDictionary +0020                                    _data 0000000751fd4340
00007ffb53db5d20                                 System.Exception +0028                          _innerException 0000000000000000
00007ffb53db5b70                                    System.String +0030                                 _helpURL 0000000000000000 (null)
00007ffb53db5f88                                    System.Object +0038                              _stackTrace 000000074ff7ad00 0a 00 00 00 00 00 00 00 a0 3e 09 6c 0c 00 00 00 c5 44 70 f8 fa 7f 00 00 40 04 84 5b 0c 00 00 00  .........>.l.....Dp....@..[.... (...more...)
00007ffb53db5f88                                    System.Object +0040                           _watsonBuckets 000000074ff7aeb0 01 00 00 00 43 00 4c 00 52 00 32 00 30 00 72 00 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ....C.L.R.2.0.r.3............... (...more...)
00007ffb53db5b70                                    System.String +0048                        _stackTraceString 0000000000000000 (null)
00007ffb53db5b70                                    System.String +0050                  _remoteStackTraceString 0000000000000000 (null)
00007ffb53db5f88                                    System.Object +0058                          _dynamicMethods 0000000000000000
00007ffb53db5b70                                    System.String +0060                                  _source 0000000752028c20 System.Web
00007ffb53d495c8         System.Runtime.Serialization.SafeSeriali +0068                _safeSerializationManager 00000009cea47610
00007ffb53dbde60                                    System.IntPtr +0070                                   _xptrs 0 (0n0)
00007ffb53d99290                                   System.UIntPtr +0078                      _ipForWatsonBuckets 0 (0n0)
00007ffb53db80f8                                     System.Int32 +0080                        _remoteStackIndex 0 (0n0)
00007ffb53db80f8                                     System.Int32 +0084                                 _HResult 80004003 (0n-2147467261)
00007ffb53db80f8                                     System.Int32 +0088                                   _xcode e0434352 (0n-532462766)

How can I get the full stack in string from the _stackTrace? Also, what steps can I take to find out the origin of NullReferenceException?

Upvotes: 0

Views: 961

Answers (1)

Thomas Weller
Thomas Weller

Reputation: 59289

TLDR;

You can show the stack trace of a thrown exception using !pe. In probably more than 90% of the cases that's all you need.

Simple and complete example

Here's a minimal reproducible example:

using System;

namespace ConsoleNetFramework
{
    class Program
    {
        static void Main()
        {
            throw new ApplicationException("An exception");
        }
    }
}

and the debugging session:

0:000> .loadby sos clr
0:000> !pe
Exception object: 0335246c
Exception type:   System.ApplicationException
Message:          An exception
InnerException:   <none>
StackTrace (generated):
    SP       IP       Function
    012FEFB0 01720897 ConsoleNetFramework!ConsoleNetFramework.Program.Main()+0x4f

StackTraceString: <none>
HResult: 80131600

Complex example

Since I considered the answer to be too simple, I felt free to make it more interesting by adding more seldom cases to the example like so:

static void Main()
{
    var b = new BadImageFormatException("Bad exception");
    ApplicationException a;
    try
    {
        throw new ApplicationException("An exception");
    }
    catch (ApplicationException aex)
    {
        a = aex;
    }

    var t = new Thread(ThrowAnotherException);
    t.Start();
    throw new DataException("Data exception");

    Console.WriteLine(b.Message);
    Console.WriteLine(a.Message);
}

private static void ThrowAnotherException()
{
    throw new ConfigurationException("Config exception");
}

In the end we'll have 4 exception objects in memory:

  • A...Exception
  • B...Exception
  • C...Exception
  • D...Exception

and we can have a look at all of them.

The debugging session is:

0:000> .loadby sos clr
0:000> !pe
Exception object: 02eb2ca8
Exception type:   System.Data.DataException
Message:          Data exception
[...]

So far so normal, we stopped at the first exception that was thrown and we can have a look at that exception with !pe.

If we switch to another thread, we can see that the exception is indicated with a # in the list of threads, as long as it's not overwritten by the . (current thread).

0:000> ~1s
0:001> ~
#  0  Id: 28a8.501c Suspend: 1 Teb: 00cc6000 Unfrozen
.  1  Id: 28a8.1044 Suspend: 1 Teb: 00cc9000 Unfrozen
[...]

Here's an interesting part: let's freeze thread 0 and run the application so that a second exception is thrown.

0:001> ~0f
0:001> g
System 0: 1 of 7 threads are frozen
WARNING: Continuing a non-continuable exception
System 0: 1 of 8 threads were frozen
System 0: 1 of 8 threads are frozen
(28a8.4228): CLR exception - code e0434352 (!!! second chance !!!)
System 0: 1 of 8 threads were frozen
[...]

Now we have another exception that caused the debugger to stop.

0:006> ~1s
[...]
0:001> ~
   0  Id: 28a8.501c Suspend: 1 Teb: 00cc6000 Frozen  
.  1  Id: 28a8.1044 Suspend: 1 Teb: 00cc9000 Unfrozen
   2  Id: 28a8.1a2c Suspend: 1 Teb: 00ccc000 Unfrozen
   3  Id: 28a8.5cfc Suspend: 1 Teb: 00ccf000 Unfrozen
   4  Id: 28a8.490 Suspend: 1 Teb: 00cd2000 Unfrozen
   5  Id: 28a8.2b60 Suspend: 1 Teb: 00cd5000 Unfrozen
#  6  Id: 28a8.4228 Suspend: 1 Teb: 00cd8000 Unfrozen
[...]

That exception happened on thread 6. We can also see that we only have one # mark in that list, not two.

On the correct thread, we can print the exception:

0:006> !pe
Exception object: 02eb3ff4
Exception type:   System.Configuration.ConfigurationException
Message:          Config exception
[...]

On the wrong thread, we can't:

0:006> ~1s
[...]
0:001> !pe
The current thread is unmanaged

Lesson learned: the !pe command is thread sensitive.

Now, what about the other two exceptions, A... and B...? We can find them on the heap:

0:000> !dumpheap -type BadImageFormatException
 Address       MT     Size
02eb2a6c 6e3983c4       92   
[...]
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
[...]
6e342734  40002ab       20        System.Object  0 instance 00000000 _stackTrace
[...]

The exception that was not thrown at all, does not have a stack trace. It's null.

0:000> !dumpheap -type ApplicationException
 Address       MT     Size
02eb2ae4 6e388090       84     

Statistics:
      MT    Count    TotalSize Class Name
6e388090        1           84 System.ApplicationException
Total 1 objects
0:000> !DumpObj /d 02eb2ae4
Name:        System.ApplicationException
[...]
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
[...]
6e342734  40002ab       20        System.Object  0 instance 02eb2b7c _stackTrace
[...]

The exception that was thrown but caught, still has its stack trace.

0:000> !pe 02eb2ae4
Exception object: 02eb2ae4
Exception type:   System.ApplicationException
Message:          An exception
InnerException:   <none>
StackTrace (generated):
    SP       IP       Function
    00EFEEA0 052F08D2 ConsoleNetFramework!ConsoleNetFramework.Program.Main()+0x8a

StackTraceString: <none>
HResult: 80131600

Lesson learned: !pe can take an arbitrary exception as parameter and print the exception with its call stack.

Looking at the output of the managed !threads, both exceptions will be listed (unlike ~ which shows only one #):

0:001> !threads
ThreadCount:      3
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                         Lock  
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1 501c 00fb99c0     2a020 Preemptive  02EC0714:00000000 00f815a0 0     MTA System.Data.DataException 02eb2ca8
   5    2 2b60 00fc4728     2b220 Preemptive  00000000:00000000 00f815a0 0     MTA (Finalizer) 
   6    3 4228 00fec9a0     2b020 Preemptive  02EC5B70:00000000 00f815a0 0     MTA System.Configuration.ConfigurationException 02eb3ff4

what steps can I take to find out the origin of NullReferenceException?

With the call stack and symbols you should be able to identify a single line of code. Next step is to do a code review there. Figure out which variable is null. Write a unit test to reproduce the issue. Then fix the bug. Like in red/green refactoring.

Upvotes: 1

Related Questions