程序发布后,针对在用户使用过程中出现的一些问题进行调试,这个过程可以称为是事后调试。在程序Crash时转储MiniDump文件供软件开发工程师分析是一种比较常用的方法。
1. MiniDumpWriteDump
MiniDumpWriteDump是windows DbgHelp.dll提供的一个转储进程MiniDump的API,可以将其导入到C#程序中。
class="code_img_closed" src="/Upload/Images/2013102922/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('5df8cd5f-dace-4e8d-8493-e7d5d81baa5d',event)" src="/Upload/Images/2013102922/2B1B950FA3DF188F.gif" alt="" />1 using System; 2 using System.Diagnostics; 3 using System.IO; 4 using System.Runtime.InteropServices; 5 using System.Threading; 6 7 public class MiniDumpUtil 8 { 9 [DllImport("kernel32.dll")] 10 private static extern int GetCurrentThreadId(); 11 12 [DllImport("DbgHelp.dll")] 13 private static extern bool MiniDumpWriteDump(IntPtr hProcess, int processId, IntPtr fileHandle, MiniDumpType dumpType,ref MiniDumpExceptionInfo excepInfo, IntPtr userInfo, IntPtr extInfo); 14 15 [DllImport("DbgHelp.dll")] 16 private static extern bool MiniDumpWriteDump(IntPtr hProcess, int processId, IntPtr fileHandle, MiniDumpType dumpType, IntPtr excepParam, IntPtr userInfo, IntPtr extInfo); 17 18 public static bool TryWriteMiniDump(string dmpFileName, MiniDumpType dmpType) 19 { 20 using (FileStream stream = new FileStream(dmpFileName, FileMode.OpenOrCreate)) 21 { 22 Process process = Process.GetCurrentProcess(); 23 MiniDumpExceptionInfo exceptionInfo = new MiniDumpExceptionInfo(); 24 exceptionInfo.ThreadId = GetCurrentThreadId(); 25 exceptionInfo.ExceptionPointers = Marshal.GetExceptionPointers(); 26 exceptionInfo.ClientPointers = true; 27 if (exceptionInfo.ExceptionPointers == IntPtr.Zero) 28 { 29 return MiniDumpWriteDump(process.Handle, process.Id, stream.SafeFileHandle.DangerousGetHandle(), dmpType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); 30 } 31 else 32 { 33 return MiniDumpWriteDump(process.Handle, process.Id, stream.SafeFileHandle.DangerousGetHandle(), dmpType, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero); 34 } 35 } 36 } 37 } 38 39 public enum MiniDumpType 40 { 41 MiniDumpNormal = 0x00000000, 42 MiniDumpWithDataSegs = 0x00000001, 43 MiniDumpWithFullMemory = 0x00000002, 44 MiniDumpWithHandleData = 0x00000004, 45 MiniDumpFilterMemory = 0x00000008, 46 MiniDumpScanMemory = 0x00000010, 47 MiniDumpWithUnloadedModules = 0x00000020, 48 MiniDumpWithIndirectlyReferencedMemory = 0x00000040, 49 MiniDumpFilterModulePaths = 0x00000080, 50 MiniDumpWithProcessThreadData = 0x00000100, 51 MiniDumpWithPrivateReadWriteMemory = 0x00000200, 52 MiniDumpWithoutOptionalData = 0x00000400, 53 MiniDumpWithFullMemoryInfo = 0x00000800, 54 MiniDumpWithThreadInfo = 0x00001000, 55 MiniDumpWithCodeSegs = 0x00002000, 56 MiniDumpWithoutAuxiliaryState = 0x00004000, 57 MiniDumpWithFullAuxiliaryState = 0x00008000, 58 MiniDumpWithPrivateWriteCopyMemory = 0x00010000, 59 MiniDumpIgnoreInaccessibleMemory = 0x00020000, 60 MiniDumpWithTokenInformation = 0x00040000, 61 MiniDumpWithModuleHeaders = 0x00080000, 62 MiniDumpFilterTriage = 0x00100000, 63 MiniDumpValidTypeFlags = 0x001fffff 64 } 65 66 public struct MiniDumpExceptionInfo 67 { 68 public int ThreadId; 69 public IntPtr ExceptionPointers; 70 public bool ClientPointers; 71 }MiniDumpUtil
这里要注意的一点是当前线程的ID,不能用当前线程的托管线程ID(即Thread.CurrentThread.ManagedThreadId),而是用当前线程的操作系统ID(即OSID),否则用windbg加载dump文件时可能会出现一些错误信息:
ERROR: Unable to find system thread 1
ERROR: The thread being debugged has either exited or cannot be accessed
ERROR: Many commands will not work properly
当然,这似乎对调试影响并不是很大,切换线程很容易通过windbg命令完成。
可以简单地在未处理异常中转储进程的MiniDump
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 6 7 TestMethod(-1); 8 } 9 10 static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 11 { 12 MiniDumpUtil.TryWriteMiniDump("E:\\dump\\test.dmp", MiniDumpType.MiniDumpWithFullMemory); 13 } 14 15 static void TestMethod(int num) 16 { 17 if (num < 0) 18 { 19 throw new ArgumentException("The argument 'num' is less than zero!"); 20 } 21 } 22 }View Code
运行程序后,会生成test.dmp文件,用windbg加载该dump文件,设置pdb路径,可以分析异常信息。以下是采用windbg分析输出的一些信息:
Loading Dump File [E:\dump\test.dmp]
User Mini Dump File with Full Memory: Only application data is available
Symbol search path is: srv*
Executable search path is:
Windows 7 Version 7600 MP (4 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS
Machine Name:
Debug session time: Tue Oct 29 21:42:14.000 2013 (UTC + 8:00)
System Uptime: 0 days 2:42:59.907
Process Uptime: not available
...............................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(168.1474): CLR exception - code e0434352 (first/second chance not available)
eax=fffffffd ebx=038b0d78 ecx=0013d7f8 edx=77886194 esi=038b0d38 edi=0013d898
eip=77886194 esp=0013d558 ebp=0013d568 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
77886194 c3 ret
0:000> .sympath+ E:\dump
Symbol search path is: srv*;E:\dump
Expanded Symbol search path is: cache*;SRV*http://msdl.microsoft.com/download/symbols;e:\dump
0:000> .loadby sos clr
0:000> !pe
Exception object: 017eb58c
Exception type: System.ArgumentException
Message: The argument 'num' is less than zero!
InnerException: <none>
StackTrace (generated):
SP IP Function
0013EC98 002B00E6 DumpUtilDemo!DumpUtilDemo.Program.Main(System.String[])+0x76
StackTraceString: <none>
HResult: 80070057