Geeks With Blogs
.NET Corner Jeans, .NET and Physics (eka The Quantum Boy)

For a long tome I kept myself away from this wonderful but ugly looking tool as I was the die-hard fan of a Compuware(Numega then) masterpiece - SoftIce. Hey I still have it installed inside of a Win98 Virtual Box in my XP host. Though SoftIce has got its new incarnation - DriverStudio, not the WinDbg. If you haven't downloaded it, get it Debugging tools for Windows. But I'm not going to give basics of WinDbg usage here.  Nop. But quality articles are galore. One thing I must mention that if your technology stack contains anything .NET, there is a nice WinDbg extension DLL(always keep an eye on software extensibility model) exists at the .NET framework installation folder. It's sos.dll(Son Of Strike). This guy will help to inspect virually anything in .NET world from method table to managed stack

This encounter with Windbg is actually initiated from requirements at my office. I was analyzing some crash dump generated by our driver library. Boy they are fatty. But still they are smaller than the earlier days. Actually these beasts are tagged as Mini Dump which is much light-weight than the full fledged dumps often called Core Dump. Mini Dumps are very different than their older counterparts in the following way

  • Instead of saving the entire process space, only certain sections are saved. There is no point in saving copies of modules such as Kernel32.dll; if the version number is included, it is easy to get a copy from a Windows CD. The actual memory heap of the application is by default not saved in a minidump; it is not required to debug a surprisingly high percentage of crashes. You can save the heap if you need to, however.
  • The minidump save code works to get accurate and full information for modules, including their names, paths, version information, and internal timestamps.
  • The minidump save code also gets the list of threads, their contexts (that is, register sets), and the memory behind their stacks.
  • The whole file is compressed, further reducing its size. A minidump for Notepad is around 6K on Windows XP, almost 300 times smaller than the previous crash dump of the same process.

Now when do we generates dump? Certainly not always. Instead one of these scary moments when your software "sees" end of days at customer premises. Most of the softwares now-a-days employs structure exception handling(SEH) and displays custom user-friendly UI if a fault occurs. But sometimes it is necessary for the developers to analyze what's exactly happened when the fault occurred. Handling the exception is certainly not the answer. Instead with some configuration changes(may be application configuration or infamous Windows Registry backdoor) the "on site" guy enables the dump generation facility and reproduces the fault once more. Following is a vanilla piece of code found somewhere in this context.

[C#]
try
{
     //where fault occurs...
}
catch(Exception e)
{
   if (TraceLevel == TraceLevel.GenerateDump)
   {
          DumpGenerator.WriteCrashDump();
   }
}

Couple of pointers here. First the check inside the exception handler is very important..I repeat..very very important. Here dump is generated as an elevated level of application(or driver) tracing configuration(again done by our beloved "on site" guy). Not always your application(or driver) should inflate the customer disks with fatty DMP files. Another subtle point - I didn't pass the exception object as a parameter to the dump generator function. As this is managed code the exception object will only contain the exception information pertaining to the managed stack. But we need an unmanaged Exception_Pointers struct to pass around. That could be generated within the function with the help of  GetLastError() API. Still there is a catch - if you P/Invoke the GetLastError call from C#, always remember that there is chance that CLR will invalidate the error while doing its own stuff when control returns from unmanaged world(where error occurred) to managed world by Interop Marshallar. Look at this entry of Adam Nathan's blog for more detail.

The most important parameter to the MiniDumpWrite function is the first and second.

typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
  DWORD ThreadId;
  PEXCEPTION_POINTERS ExceptionPointers;
  BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;


Naturally the exception pointers parameter is what I was talking about a liitle bit ago and its retrieval by GetLastError or Marshal.GetLastWin32Error. The first parameter identifies the thread which throws the exception.

Now from here the fun begins. I successfully able to analyze all but one DMP file by inspecting managed and unmanaged stack. But whenever I tried to open the offending DMP file, my WinDbg crashed. Oh God! Who will analyze the dump that WinDbg will generate? Purplexing !
Just before crash I saw a strange output at the WinDbg's console. Something related to invalid thraed id.

ERROR: Unable to find system thread 2
ERROR: The thread being debugged has either exited or cannot be accessed
ERROR: Many commands will not work properly

Now I was performing a suite of test cases while dump was generated. It was utmost important to me to know which process actually generated the dump so that I could narrow down the troubled section. Fortunately there is a tool in the same folder where WinDbg usually resides - dumpchk.exe. With

>>  dumpchk -e "offending_dump_file.dmp"

I was instantly presented with enough information to determine the offending test case. But that doesn't solve the problem of WinDbg's unusal crash. Later I found out(through debugging - without dumpchk that even would have been impossible)  that when ExceptionPointers parameter is not null(means exception actually happened at unmanaged layer of our code) we are pushing the "passed" thread id to this structure before calling the write dump function.Although if you pass null or 0 to this ExceptionPointers parameter dump will be generated but its unmanaged stack will be ignored. So to track back how we were passing the thread id to this function, I discovered following code

#ifdef  DOTNET20
     threadId = System.Threading.Thread.ManagedThreadId();
#else
    threadId = AppDomain.GetCurrentThreadId();
#endif

Surely the Appdomain.GetCurrentThreadId API has been deprecated but It does something the newer API doesn't and neither supposed to that.
Managed threads has nothing to do with unmanaged threads. The mapping between managed thread to unmanage thread is tricky. The newer API is just a hash  value used to determine a managed threads uniquely. It doesn't corresponds to the unmanaged thread. The mapping between these thraeds of two worlds could be controlled by a CLR host or could be changed by future versions of .NET Fx. Due to this unstability Microsoft recommends that you are not supposed to assume 1:1 correspondence between manage and unmanaged thread while using AppDomain.GetCurrentThreadId() in your code. Although in 2.0 it happens to return the unmanaged id but who knows. Sonner or later this code will definitely break. Check at this blogs about manage and unmanaged threads - 1, 2

With "not-so-offending" dump files your "!analyze -v" session will be peaceful. It's 3 AM here at India. I'm tired..See you guys

Posted on Saturday, November 8, 2008 3:56 AM .NET Core | Back to top


Comments on this post: WinDbg , Crash Dump .. Associated Headches - Part 1

# re: WinDbg , Crash Dump .. Associated Headches - Part 1
Requesting Gravatar...
I have to start this blog with a confession: I’m an inveterate system tinkerer, and am always looking for something better for my system (if not for something rated as the best of its kind). For example, this approach has led me to skip using a good all-around security suite in favor of picking the best elements of each kind by itself (anti-virus, anti-spyware, firewall, anti-spam, rootkit detector, intrusion detection/prevention, system file and state monitoring, and so forth).
Left by Free casino slot machine on Jul 25, 2009 5:20 AM

Your comment:
 (will show your gravatar)


Copyright © dbose | Powered by: GeeksWithBlogs.net