您的位置:

如何进行dump文件分析

当程序出现某些问题而无法重现时,我们需要通过分析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文件分析还有许多内容,需要我们不断努力学习和实践。希望本文对读者有所帮助。