Function names for managed callstack under SOS debugging of .NET 2.0 app
- From: "Daniel Bowen" <DanielBowenWl@xxxxxxxxxxxxxxxxx>
- Date: Tue, 11 Jul 2006 14:02:08 -0600
(I posted this earlier on microsoft.public.windbg, but they wanted me to
post it elsewhere).
We have an application that is a mix of .NET managed code and native C++
code. The main EXE is a .NET 2.0 application. We use a native DLL that we
wrote that sets the unhandled exception filter
(SetUnhandledExceptionFilter), and generates a mini-dump if an unhandled
exception makes it to it (with the help of the 6.6.3.5 version of
dbghelp.dll). With .NET 2.0, unhandled exceptions on managed threads no
longer get swallowed like they did in 1.1, and make it all the way to our
unhandled exception filter, and we generate a mini-dump. So we can then
take this mini-dump file, drop it into a directory with copies of our images
and symbol files for that build and open it in WinDbg. If the unhandled
exception happened on a .NET thread, we load up SOS, and can get callstack
information. For example:
..ecxr
..loadby sos mscorwks (or .load
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos)
!pe -nested
(etc.)
However, we have a little problem. Let's say I have a divide-by-zero that I
force to happen on a background worker thread. Doing !pe (or !clrstack,
etc.) gives us something like this:
0:021> !pe
Exception object: 01420e44
Exception type: System.DivideByZeroException
Message: Attempted to divide by zero.
InnerException: <none>
StackTrace (generated):
SP IP Function
06A0EF2C 114979A0 MyApp.exe!Unknown
06A0EF34 793D7A7B
System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
06A0EF3C 793683DD
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
06A0EF54 793D7B5C System.Threading.ThreadHelper.ThreadStart()
Notice how we have "MyApp.exe!Unknown" instead of
"MyApp.exe!MyNamespace.MyClass.FunctionToForceException()".
My first thought was that we needed to do "ngen install MyApp.exe /debug"
instead of just "ngen install MyApp.exe", but that didn't help.
So then I turned to our MINIDUMP_TYPE options. The callstack above happens
when we use MiniDumpNormal when calling MiniDumpWriteDump (when using the
6.6.3.5 version of dbghelp.dll as I mentioned). So then I experimented with
different MINIDUMP_TYPE options to see what would give us the function name
on the callstack. Of the "smaller size" dump options, it looks like
including MiniDumpWithCodeSegs will give us the function names in our code
in the callstack. However, that turns our ~300KB mini-dump into a ~66MB
mini-dump.
So then I experimented with using the MINIDUMP_CALLBACK_INFORMATION with our
own MiniDumpCallback function to see if I could maybe only capture the code
segment information for the modules I need to. It looks like on a deployed
system where we've done "ngen install MyApp.exe" on our assembly, having a
callback routine like this (for the divide-by-zero example), also gets us
..NET function names:
BOOL CALLBACK MiniDumpCallback_ClrDebuggingInfo(PVOID /*callbackParam*/,
const PMINIDUMP_CALLBACK_INPUT callbackInput,
PMINIDUMP_CALLBACK_OUTPUT callbackOutput)
{
if(callbackInput == NULL || callbackOutput == NULL)
{
return FALSE;
}
// We've already specified the options we want in the MINIDUMP_TYPE,
// but just want to filter out a select number of things.
switch(callbackInput->CallbackType)
{
case ModuleCallback:
{
if( (callbackOutput->ModuleWriteFlags & ModuleWriteCodeSegs) ==
ModuleWriteCodeSegs )
{
// Filter out code segment information from all but selected
modules.
bool include = false;
LPCWSTR fileName =
::PathFindFileNameW(callbackInput->Module.FullPath);
if(fileName != NULL)
{
// Include the code segments for the fewest modules we
need
// to still get function names under SOS debugging
if(::wcscmp(fileName, L"mscorwks.dll") == 0)
include = true;
else if(::wcscmp(fileName, L"MyApp.ni.exe") == 0)
include = true;
}
if(include)
{
// Leave ModuleWriteCodeSegs in there. Change any other
flags?
}
else
{
callbackOutput->ModuleWriteFlags &=
~ModuleWriteCodeSegs;
}
}
}
break;
}
return TRUE;
}
If ngen install hasn't been done, then using "MyApp.exe" instead of
"MyApp.ni.exe" works. However, the mini-dump is ~10MB, which still seems
larger than it has to be. The other complication is not hard-coding the
ngen'ed assembly name in the list of modules to include. Also, we do have
some other managed .NET code in managed assemblies that are DLLs, and I
imagine we'd need to include the modules (ngen'ed or not) for all of our own
stuff at the least. It'd be nice to not have a hard-coded list of module
names too (other than system ones), so being able to at least check if a
module is managed would be nice (given callbackInput->Module.FullPath).
What we'd like to do is to capture enough CLR state so that we get at least
the function names for everything in the callstack. But we'd also like the
mini-dump files to be small enough for customers to send in an e-mail. Any
suggestions?
Thanks!
-Daniel
.
- Follow-Ups:
- RE: Function names for managed callstack under SOS debugging of .NET 2.0 app
- From: "Jeffrey Tan[MSFT]"
- RE: Function names for managed callstack under SOS debugging of .NET 2.0 app
- Prev by Date: Re: Image search path for VC7.1, VC8.0, and WinDbg
- Next by Date: RE: Function names for managed callstack under SOS debugging of .NET 2.0 app
- Previous by thread: Got a wacky solution for slow debugging in VS 2005!
- Next by thread: RE: Function names for managed callstack under SOS debugging of .NET 2.0 app
- Index(es):
Relevant Pages
|