Dump Diving into Cert Failure: How to Get a Good Callstack from TriageDump.dmp
As of this writing, I am currently working on a support case where the developer's app has been failing certification for a FileNotFoundException in his application that has been submitted to the Windows Store. He has been trying to understand exactly where in his source code the application is failing. To do this, he's been using Visual Studio to open up the TriageDump.dmp file which was sent as part of the certification failure report. The developer reported that he was not able to get a good callstack from the dump file, so I took a crack at it. The following describes the process by which I was able to get to the callstack where the exception occurred.
I want to note that I used WinDbg, a free downloadable tool from Microsoft. It is a standalone, text-driven debugging engine which is widely used by Microsoft employees for most debugging scenarios. The same debugging engine is included with Visual Studio, and can be used if you go to Open > Dump File.
*Note: name of the app has been changed for privacy purposes.
In case you're not familiar with it, the Windows Store Certification report contains a Report.cab file. This is simply a zip archive file, and files can be extracted from the .cab exact like a zip. The cab file contains a file called triagedump.dmp, which is a snapshot of the app at the exact time that the app crashed due to the exception that occurred.
In the interest of keeping this blog post manageable, I will show the process and commands I used to get to the usable callstack, and explain what they do, rather than explain the intricacies of why. You can do this using your own dumps and apps.
Open the triagedump.dmp file in WinDbg. The command line at the bottom of the page:
0:019>
indicates the current thread context. WinDbg typically opens to the thread where the exception occurred.
FYI: The first zero refers to the process, which can change if you were debugging the kernel. It is always 0 for a crash dump. The 019 refer to the number of the current thread context inside the process.
Set the symbols path:
0:019> !symfix
Symbols are files that help the debugger translate the binary information inside the dump to human-readable information. This particular command sets a default symbols path. For WinDbg, it downloads from the public Microsoft symbols server: : https://msdl.microsoft.com/download/symbols
Make it so you can tell where the symbols are being loaded from.
0:019> !sym noisy
This is important so we can tell where to place our app-specific symbols and images so that WinDbg can find them.
We have tell WinDbg to reload the symbols and images since we have a new symbols path:
0:019>.reload
WinDbg will perform an automated analysis on the dump:
0:019> !analyze –v
This will take a long time and you will see a bunch of debugging spew. Eventually you will get some interesting information, including a callstack and an exception object stack, but it won't be helpful yet. In this case, the exception object stack actually had the function call that caused the exception:
EXCEPTION_OBJECT: !pe 25afc54
Exception object: 025afc54
Exception type: System.IO.FileNotFoundException
Message: <Invalid Object>
InnerException: <none>
StackTrace (generated):SP IP Function
04F0F364 6F00C946 mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)+0x5e
04F0F374 6F00C8D9 mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)+0x35
04F0F380 6EBB8498 APPNAME_ni!UNKNOWN+0x270
04F0F4BC 6F79E6FF mscorlib_ni!System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__0(System.Object)+0x33
04F0F4C4 6D2E13D4 System_Runtime_WindowsRuntime_ni!System.Threading.WinRTSynchronizationContext+Invoker.InvokeCore()+0x24
0A25F4B4 6F6A2607 mscorlib_ni!System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()+0x17
0A25F4BC 6D30E728 System_Runtime_WindowsRuntime_ni!System.Threading.WinRTSynchronizationContext+Invoker.<InvokeCore>b__0(System.Object)+0x28
0A25F4C0 6EFACB6A mscorlib_ni!System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)+0x3e
0A25F4C8 6EFC2407 mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0xa7
0A25F534 6EFC2346 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,System.Threading.ContextCallback, System.Object, Boolean)+0x16
0A25F548 6EFCDE60 mscorlib_ni!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()+0x60
0A25F55C 6EFCD609 mscorlib_ni!System.Threading.ThreadPoolWorkQueue.Dispatch()+0x149
0A25F5AC 6EFCD4A5 mscorlib_ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()+0x5The highlighted line is where the app in question threw the exception. The problem is that there's no information that says the specific method inside the app where the problem occurred. That's why we needed the "!sym noisy" command. We need to figure out what more information is needed for the debugger to give us more information. Let's find this information in the rest of the spew:
C:\ProgramData\dbg\sym\APPNAME.exe\51D597BF64000\APPNAME.exe not found
What you want to do is create the path to this location, then put the exact executable that matches to this dump inside that path. You can ensure that the correct .exe matches up by checking the green number in the path with the timestamp of the .exe. You can check the timestamp of the .exe that you are using by doing this:
Open a new WinDbg instance.
File > Open Crash Dump…
Change “Crash Dump Files” to “All Files”
Navigate to the “.exe” file
> OKAt the command line:
0x000> lmv
You will see this:
start end module name
00400000 00464000 APPNAMEC (no symbols)
Loaded symbol image file: APPNAME.exe
Mapped memory image file: C:\ProgramData\dbg\sym\APPNAME.exe\51D597BF64000\APPNAME.exe
Image path: C:\ProgramData\dbg\sym\APPNAME.exe\51D597BF64000\APPNAME.exe
Image name:APPNAME.exeUsing CLR debugging support for all symbols
Has CLR image header, track-debug-data flag not setTimestamp: Thu Jul 04 11:41:51 2013 (51D597BF)
CheckSum: 00000000
ImageSize: 00064000
File version: 1.0.0.0
Product version: 1.0.0.0
File flags: 0 (Mask 3F)
File OS: 4 Unknown Win32
File type: 1.0 App
File date: 00000000.00000000
Translations: 0000.04b0
CompanyName: Windows Store App Developer
ProductName: APPNAME
InternalName: APPNAME.exe
OriginalFilename: APPNAME.exe
ProductVersion: 1.0.0.0
FileVersion: 1.0.0.0
FileDescription: APPNAME
LegalCopyright: Copyright © 2013
Comments: App on Windows 8The green numbers put together should match up to the location where the .exe is being looked for.
You can get the exact image (.exe) from the .appx package. Rename .appx to .zip and unzip the content. You will see the .exe inside that package under the .appx folder.
After you’ve put the exact image in that folder location, run
0x019> .reload
To pull in the new image.
Run this to get a refreshed callstack
0x019> !analyze –vNow you should get much better debugging information and a good callstack:
BUGCHECK_STR: APPLICATION_FAULT_CLR_EXCEPTION__SYSTEM.IO.FILENOTFOUNDEXCEPTION__SYSTEM.IO.FILENOTFOUNDEXCEPTION__SYSTEM.IO.FILENOTFOUNDEXCEPTION
PRIMARY_PROBLEM_CLASS: CLR_EXCEPTION_SYSTEM.IO.FILENOTFOUNDEXCEPTION
DEFAULT_BUCKET_ID: CLR_EXCEPTION_SYSTEM.IO.FILENOTFOUNDEXCEPTIONSTACK_TEXT:
04f0f364 6f00c946 mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess+0x5e
04f0f374 6f00c8d9 mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification+0x35
04f0f380 6ebb8498 APPNAME_ni!APPNAME.Controls.Layouts.PageLayouts+_LoadLiveOverlays_d__0.MoveNext+0x270
04f0f4bc 6f79e6ff mscorlib_ni!System.Runtime.CompilerServices.AsyncMethodBuilderCore._ThrowAsync_b__0+0x33
04f0f4c4 6d2e13d4 system_runtime_windowsruntime_ni!System.Threading.WinRTSynchronizationContext+Invoker.InvokeCore+0x24
0a25f4b4 6f6a2607 mscorlib_ni!System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw+0x17
0a25f4bc 6d30e728 system_runtime_windowsruntime_ni!System.Threading.WinRTSynchronizationContext+Invoker._InvokeCore_b__0+0x28
0a25f4c0 6efacb6a mscorlib_ni!System.Threading.QueueUserWorkItemCallback.WaitCallback_Context+0x3e
0a25f4c8 6efc2407 mscorlib_ni!System.Threading.ExecutionContext.RunInternal+0xa7
0a25f534 6efc2346 mscorlib_ni!System.Threading.ExecutionContext.Run+0x16
0a25f548 6efcde60 mscorlib_ni!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem+0x60
0a25f55c 6efcd609 mscorlib_ni!System.Threading.ThreadPoolWorkQueue.Dispatch+0x149
0a25f5ac 6efcd4a5 mscorlib_ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback+0x5Notice that the highlighted line gives far more information that it did previously. You can now look at the specific method in the application to determine why the application is crashing.
I hope that this process helps you understand how you can get useful information from the dumps that are provided by the certification team. Please leave questions in the comments below.
Special thanks to Jeff Sanders for providing lots of valuable input on the debugging process!
Be sure to follow me on Twitter: @WinDevMatt