Get stack trace from dump file

In the last several posts, I attached URLs of a serials of blog posts that describe how to automate dump file analysis. One of the posts gives out a solution on how to dump stack trace. Even though, it’s pity that author did not dump out stack trace for a crash context. After reading documentation, and overcome those damn debugger concepts, I comes to the following code excerpt.
 
Two functions need to be highlighted: IDebugAdvanced2::Request, IDebugControl4::GetContextStackTrace. Use Request to get register context information of the last event stored in dump file, where last event is usually an exception that we are of interest; Use GetContextStackTrace to get stack trace related to the previous register context.
 

HRESULT DumpStackEx(IDebugSymbols *symbols, IDebugAdvanced2 *advanced2, IDebugControl4 *control4)

{

          HRESULT hr = S_OK;

 

          CONTEXT _context = { 0 };

          ULONG _uOutSize = 0;

 

          hr = advanced2->Request(DEBUG_REQUEST_TARGET_EXCEPTION_CONTEXT,

                   NULL, 0, &_context, sizeof(CONTEXT), &_uOutSize);

          if(FAILED(hr))

          {

                   goto cleanup;

          }

 

          DEBUG_STACK_FRAME _stackFrames[256] = { 0 };

          CONTEXT _frameContexts[256] = { 0 };

          ULONG _uFramesFilled = 0;

          hr = control4->GetContextStackTrace(&_context, sizeof(_context), _stackFrames, ARRAYSIZE(_stackFrames),

                   _frameContexts, 256 * sizeof(CONTEXT), sizeof(CONTEXT), &_uFramesFilled);

          if(FAILED(hr))

          {

                   goto cleanup;

          }

         

          printf("Stack Trace:\n");

 

          for(ULONG _uFrame = 0; _uFrame < _uFramesFilled; _uFrame++)

          {

                   HRESULT symhr;

                   char _name[512];

                   unsigned __int64 offset = 0;

                   ULONG _uLineNo = 0;

 

                   ZeroMemory(_name, ARRAYSIZE(_name));

                   symhr = symbols->GetNameByOffset(_stackFrames[_uFrame].InstructionOffset,

                             _name, ARRAYSIZE(_name) – 1, NULL, &offset);

 

                   if(SUCCEEDED(symhr))

                   {

                             printf("%s+0x%I64X", _name, offset);

                   }

                   else

                   {

                             printf("0x%08I64X", _stackFrames[_uFrame].InstructionOffset);

                   }

 

                   ZeroMemory(_name, ARRAYSIZE(_name));

                   symhr = symbols->GetLineByOffset(_stackFrames[_uFrame].InstructionOffset,

                             &_uLineNo, _name, ARRAYSIZE(_name) – 1, NULL, NULL);

 

                   if(SUCCEEDED(symhr))

                   {

                             printf(" [%s(%u)]", _name, _uLineNo);

                   }

                   printf("\n");

          }

 

cleanup:

 

          return hr;

}

Austin.D @ Autodesk

Get modules’ info from dump file

Code snippet for programmatically abtaining modules’ info from dump file. Some useful information, such as Timestamp and ImageSize.
 
1. Get interface pointers necessarily when accessing functionalities available from dbgeng.dll
 

hr = client->QueryInterface(__uuidof(IDebugControl), (LPVOID*)&control);

hr = client->QueryInterface(__uuidof(IDebugSymbols), (LPVOID*)&symbols);

hr = client->QueryInterface(__uuidof(IDebugSymbols2), (LPVOID*)&symbols2);

2. Set Symbol Search Path, and Image Search Path. Then open dump file
 

symbols->SetSymbolPath("D:\\Dotfuscated\\old_version;symsrv*symsrv.dll*C:\\sc\\ms*http://msdl.microsoft.com/download/symbols");

symbols->SetImagePath("D:\\Dotfuscated\\old_version");hr = client->OpenDumpFile("D:\\Dotfuscated\\old_version\\vb_filter_wrapper.dmp");

 
3. Dump all loaded and unloaded modules’ information
 

HRESULT DumpModule(IDebugControl *control, IDebugSymbols *symbols, IDebugSymbols2 *symbols2)

{

          HRESULT hr = S_OK;

 

          ULONG uloadedModules = 0;

          ULONG uUnloadedModules = 0;

 

          // Get number of both loaded and unloaded modules.

          hr = symbols->GetNumberModules(&uloadedModules, &uUnloadedModules);

          if (FAILED(hr))

          {

                   goto cleanup;

          }

 

          ULONG uTotal = uloadedModules + uUnloadedModules;

 

          DEBUG_MODULE_PARAMETERS* pDebugModParam =

                   (DEBUG_MODULE_PARAMETERS*)_malloca(uTotal * sizeof(DEBUG_MODULE_PARAMETERS));

 

          hr = symbols->GetModuleParameters(uTotal, NULL, 0, pDebugModParam);

          if (FAILED(hr))

          {

                   goto cleanup;

          }

 

          for (int i = 0; i < uTotal; i++, pDebugModParam++)

          {

                   char buffer[MAX_PATH] = { 0 };

                   hr = symbols2->GetModuleNameString(DEBUG_MODNAME_IMAGE, i, NULL, buffer, MAX_PATH, NULL);

                   if (FAILED(hr))

                   {

                             continue;

                   }

                   printf("ImageName: %s\n", buffer);

                   printf("-Base: 0x%I64X\tImageSize: 0x%I32X\tTimestamp: 0x%I32X\n",

                            pDebugModParam->Base, pDebugModParam->Size, pDebugModParam->TimeDateStamp);

          }

 

cleanup:

          if (pDebugModParam != NULL)

          {

                   _freea(pDebugModParam);

          }

         

          return hr;}

 

Some tips on dbgeng.dll

It has been long that CER Long Term support project staying in no more progress. Stephen and I still keep in investigating, and luckily, we did really find something (dbgeng.dll) that might help.

 

Dbgeng.dll is a Debug Engine, which those known debuggers such as Windbg, CDB, KD and so forth are based on. It can be found in the System32 directory, and the latest version of it is distributed along with Debugging Tools for Windows.

 

As a known issue you may know, the built-in CLR debugging support for Windbg is only available in version 6.7.5.0, which says you can use ‘k’ command to get managed stacks directly without Son of Strike. I had been confused on that, because when using StackWalk64 API exposed by dbghelp.dll in both version 6.7.5.0 and the latest one, only native stacks can be obtained. In a nutshell, StackWalk64 behaves consistently through those several versions.

 

Yesterday, I made an experiment, after so many trial and error, I happened to find that it is dbgeng.dll that whose version 6.7.5.0 did some tricky that allows mixed callstacks to be get. Quickly setting up the 6.9.3.133 Windbg to debug 6.7.5.0 Windbg so that I can trace what occurs inside dbgeng.dll. I set the breakpoint at entry point of StackWalk64, and got hit it as soon as I keyed in ‘k’ command. From the callstacks, it shows that its dbgeng!DoStackTrace implements the logic of ‘k’ command. This function further loopy calls dbgeng!TargetInfo::GetTargetStackFrames. With using “wt –l 3” to dump the call graph, I found besides StackWalk64 API, it also calls those undocumented methods and structures, such as IsClrMethod, DumpClrStack… which step in deeply into another debug help component mscordacwks.dll.

 

If you familiar with Clr debugging, you should be familiar with this dll, which is used to access Clr data structure. We usually use “.cordll” command to check if this dll is loaded correctly. At that point, I immediately comes to realize that the reason 6.7.5.0 can dump mixed callstacks is for the sake of mscordacwks.dll. Unfortunately, I can’t compare the assembly for 6.9.3.133, it’s the hell that Microsoft does not provided debug symbol information for dbgeng.dll except for the special version 6.7.5.0.

 

But, anyway, this provided a good clue that those COM interface API available in dbgeng.dll is worthy of trying. After reading through the help documentation, you will find how powerful this dll is. You can write your own code to do whatever Windbg can.

 

At the end of this blog post, I want to mention a special usage scenario that shows off the power of dbgeng.dll. With the help of dbgeng.dll, we can easily open up a dump file, dump stack trace, get exception code, which make automation dump file parsing/analysis possible. I got a great post in MSDN blog (it is pity that no one cares these serials of blog posts):

Automating Crash Dump Analysis: Part 0, Part 1, Part 2, and Part 3.

 

Austin.D