當程序出現某些問題而無法重現時,我們需要通過分析dump文件來查找程序崩潰的原因。本文將從多個方面對分析dump文件做詳細的闡述。
一、轉儲文件內容
如何查看dump文件的內容?我們可以使用某些工具或者通過編寫代碼實現。
以下是使用C++讀取dump文件並輸出其內容的示例:
#include <Windows.h> #include <Dbghelp.h> #include <iostream> const DWORD MaxDumpSize = 64 * 1024 * 1024;//最大處理文件大小,為64M。 void DumpFileContent(const char* dumpFilePath) { HANDLE hFile = CreateFileA(dumpFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//以只讀的方式打開dump文件 if (hFile == INVALID_HANDLE_VALUE) { std::cout << "Open dump file failed!" << std::endl; return; } HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);//創建文件映射 if (hMapping == NULL) { CloseHandle(hFile); std::cout << "CreateFileMapping failed!" << std::endl; return; } LPVOID mapped = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);//內存映射文件 if (mapped == NULL) { CloseHandle(hMapping); CloseHandle(hFile); std::cout << "MapViewOfFile failed!" << std::endl; return; } const DWORD dumpSize = GetFileSize(hFile, NULL); const DWORD viewSize = std::min(dumpSize, MaxDumpSize); std::cout << "file size: " << dumpSize << "; view size: " << viewSize << std::endl; for (DWORD i = 0; i < viewSize; i++) { const BYTE byte = *((BYTE*)mapped + i); std::cout << std::hex << static_cast<unsigned int>(byte) << " "; } std::cout << std::endl; UnmapViewOfFile(mapped); CloseHandle(hMapping); CloseHandle(hFile); } int main() { DumpFileContent("test.dmp"); return 0; }
運行上述代碼後可以將輸出為dump文件的16進制格式。這樣的輸出對於一些低級錯誤或者複雜的bug是有用的。
二、查找錯誤位置
當程序崩潰時,我們需要查找錯誤的位置。我們可以使用VS的調試器來查找錯誤位置,也可以使用WinDbg等工具進行分析。
以下是使用WinDbg分析dump文件並查找錯誤位置的示例:
!analyze -v
使用上述命令可以分析dump文件並輸出詳細的錯誤信息。例如:
FAULTING_IP: +0 000007fe`fafffa55 ?? ??? EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 000007fefafffa55 ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 00000000ffffffff Attempt to read from address ffffffff DEFAULT_BUCKET_ID: INVALID_POINTER_READ PROCESS_NAME: test.exe ERROR_CODE: (NTSTATUS) 0xc0000005 - EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - EXCEPTION_PARAMETER1: 0000000000000000 EXCEPTION_PARAMETER2: 00000000ffffffff READ_ADDRESS: ffffffff FOLLOWUP_IP: test!main+1d [c:\test.cpp @ 12] 00000001`400115dd c3 ret APP: test.exe FAULTING_THREAD: 00000000000005f8 BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ PRIMARY_PROBLEM_CLASS: APPLICATION_FAULT LAST_CONTROL_TRANSFER: from 000007fefa3365f1 to 000007fefafffa55 STACK_TEXT: 00000000`00eddc48 000007fe`fa3365f1 : 00000000`00e00c00 00000000`00000000 00000000`00000000 00000000`00000000 : 0x000007fe`fafffa55 00000000`00eddc50 00000001`400115dd : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : test!main+0x1d [c:\test.cpp @ 12] 00000000`00eddc80 00007ffc`c0d37d5d : 00000000`00eddeb8 00000000`77cf5961 00000000`00000000 00000000`00000000 : test!__tmainCRTStartup+0x159 [f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c @ 618] 00000000`00eddcf0 00007ffc`c2663034 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0xd 00000000`00eddd20 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
上述結果提供了引起錯誤的地址和模塊、錯誤類型、錯誤線程等信息,可以較快地定位到錯誤位置。
三、查看線程堆棧
線程堆棧信息可以告訴我們程序在出現錯誤時的執行狀態,可以使用VS的調試器來查看線程堆棧信息,也可以使用WinDbg等工具進行分析。
以下是使用WinDbg查看線程堆棧信息的示例:
~#
使用上述命令可以輸出線程列表,例如:
. 0 Id: 29a4.3e68 Suspend: 0 Teb: 00000000`00e4a000 Unfrozen # Child-SP RetAddr Call Site 00 00000000`02acf8d8 000007fe`fa3365f1 user32!NtUserGetMessage+0x14 01 00000000`02acf8e0 00000001`400115dd test!main+0x1d [c:\test.cpp @ 12] 02 00000000`02acf910 00007ffc`c0d37d5d test!__tmainCRTStartup+0x159 [f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c @ 618] 03 00000000`02acf980 00007ffc`c2663034 kernel32!BaseThreadInitThunk+0xd 04 00000000`02acfac0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
上述結果提供了線程的堆棧信息,方便我們查看程序在出現錯誤時的執行狀態。
四、查看堆內存分配狀態
如果程序崩潰時存在內存泄漏等問題,我們需要查看堆內存分配狀態以便找到內存泄漏的根源。我們可以使用VS的調試器來查看內存泄漏情況,也可以使用WinDbg等工具進行分析。
以下是使用WinDbg查看堆內存分配狀態的示例:
!heap -s
使用上述命令可以輸出堆信息和分配情況統計,例如:
**************************************************************************************************************** HEAP SUMMARY: **************************************************************************************************************** ProcessHeaps 20 ----> Heap 0000000000090000 **** 0000000000010220 - 000000000002d91c ( 2902) 48000 bytes ... **** 00000000ffbaffa0 - 00000000ffba0000 ( 144) 400 bytes ----> Heap 00000000099b0000 **** 00000000099b0240 - 0000000009a8a188 ( 82261) 1688000 bytes ... **** 00000000099ec6f0 - 00000000099ec720 ( 9) 36 bytes Large blocks (>= 512 KiB) detected: 1 Total heap size: 9555940 bytes Total committed: 8491820 bytes Total free: 23436 bytes Max free block: 23436 bytes Largest reserve: 9192960 bytes Commited range: 0000000000090000 - 0000000009a83ff8 Uncommited range: 0000000009a83ff8 - 0000000009c00000 Virtual query: 001b0000 - 001c0000 00001000 MEM_PRIVATE MEM_COMMIT **************************************************************************************************************** Statistical Analysis of the Heap Manager Stats available at Global Heap Stat **************************************************************************************************************** Heap Count Space(@) Space($) Map Space(@) Map Space($) Uncommitted($) settable size($) ------------------------------------------------------------------------------------------------------------------------ 0000000000090000 9662 4861988 $ 47490 2474836480 0 229376 00000000099b0000 49 1536 $ 23 0 0 9534976
上述結果列出了堆的使用情況和統計數據,方便我們查看堆內存分配狀態。
五、查看函數調用樹
當程序出現錯誤時,我們可以使用函數調用樹查看程序執行的路徑。我們可以通過調用棧來查看函數調用樹,也可以使用VS的調試器或者WinDbg等工具進行分析。
以下是使用VS的調試器查看函數調用樹的示例:
在調試器的調用堆棧窗口中進行查看,該窗口用於顯示所有停止的線程的調用堆棧,可在調用堆棧窗口中選擇函數調用樹,以了解程序執行的路徑和順序。
六、結語
本文介紹了如何從多個方面分析dump文件,包括轉儲文件內容、查找錯誤位置、查看線程堆棧、查看堆內存分配狀態和查看函數調用樹等。
dump文件分析還有許多內容,需要我們不斷努力學習和實踐。希望本文對讀者有所幫助。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/254299.html