[ Effective minidump ] windows C++ 如何生成dump/dmp文件

C++程序中dump文件生成方法详解 最近项目中新作成了一个动态链接库,长时间运行后,偶尔会崩溃。根据log分析,被调用的动态库函数最外层catch到了这个异常,但是不能定位哪里出了问题。另外虽然上层exe是有dump文件输出处理的,但是在C++中,如果异常被捕获并处理的情况下,系统就不会生成dump文件了。如果仍希望在try-catch块中捕获异常的同时生成dump文件,就必须在catch块中手动调用生成dump文件的函数。这样可以在异常被捕获后仍然生成dump文件以供后续分析。本文详细介绍下怎么生成dump文件。 阅读详情

Effective minidump

1. 简介

在过去几年里,崩溃转储(crash dump)成为了调试工作的一个重要部分。如果软件在客户现场或者测试实验室发生故障,最有价值的解决方式是能够创建一个故障瞬间的应用程序状态镜像,然后可以在开发者的机器上通过调试器进行分析。第一代的crash dump通常被称为“全用户转储(full user dump)”,它包含了进程的虚拟内存的全部内容。毫无疑问,这样的dump对于事后调试非常有价值。但是,这样的dump经常非常大,使得通过电子方式发送给开发者非常困难,甚至没法完成。另外,没用公共接口可以通过程序调用来创建dump,我们必须依赖于第三方工具(例如,Dr. Watson 或者Userdump)来创建他们。

随着Windows XP,微软发布了一组新的被称为“minidump”的崩溃转存技术。Minidump很容易定制。按照最常用的配置,一个minidump只包括了最必要的信息,用于恢复故障进程的所有线程的调用堆栈,以及查看故障时刻局部变量的值。这样的dump文件通常很小(只有几K字节)。所以,很容易通过电子方式发送给软件开发人员。一旦需要,minidump甚至可以包含比原来的crash dump更多的信息。例如,可以包含进程使用的内核对象的信息。另外,DbgHelp.dll提供了通过编程创建minidump的公开API。而且,它是可以重新发布的。我们可以不再依赖于外部工具。

minidump可以定制,给我们带来了一个问题-保存多少应用程序状态信息才能既保证调试有效,又能够尽量保证minidump文件尽可能小?尽管调试简单的异常访问只需要调用堆栈和局部变量的信息,但是解决更复杂的问题需要更多的信息。例如,我们可能需要查看全局变量的值、检查堆的完整性和分析进程虚拟内存的布局。同时,可执行程序的代码段往往是多余的,开发用的机器上可以很容易找到这些执行程序。

幸运的是我们可以通过DbgHelp函数组(MiniDumpWriteDump和MiniDumpCallback)来控制这些功能,甚至可以更复杂。在这篇文章里面,我们会解释怎么样使用这些函数来创建mindump,保证文件足够小但是又能有效调试。也会讲解minidump中应该包括那些数据,并且如何使用通用调试器(WinDbg和VS.NET)来看这些信息。

2. Minidump类型

先看一些代码。Figure 1是MiniDumpWriteDump的函数声明。Figure 2 显示如何使用这个函数创建简单的minidump。

Figure 1:

BOOL MiniDumpWriteDump(
 HANDLE hProcess,
 DWORD ProcessId,
 HANDLE hFile,
 MINIDUMP_TYPE DumpType,
 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

Figure 2:

void CreateMiniDump(EXCEPTION_POINTERS* pep)
{
    // Open the file
    HANDLE hFile = CreateFile(_T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE,
        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
    {
        // Create the minidump
        MINIDUMP_EXCEPTION_INFORMATION mdei;
        mdei.ThreadId = GetCurrentThreadId();
        mdei.ExceptionPointers = pep;
        mdei.ClientPointers = FALSE;

        MINIDUMP_TYPE mdt = MiniDumpNormal;
        BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
            hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0);

        if (!rv)
            _tprintf(_T("MiniDumpWriteDump failed. Error: %u \n"), GetLastError());
        else
            _tprintf(_T("Minidump created.\n"));

        // Close the file
        CloseHandle(hFile);
    }
    else
    {
        _tprintf(_T("CreateFile failed. Error: %u \n"), GetLastError());
    }
}

在这个例子里面,我们如何指定minidump应该包括那些数据呢?主要取决于MiniDumpWriteDump的第四个参数MINIDUMP_TYPE。下表Figure 3是参数的定义。

Figure 3:

typedef enum _MINIDUMP_TYPE {

    MiniDumpNormal                          = 0x00000000,
    MiniDumpWithDataSegs                    = 0x00000001,
    MiniDumpWithFullMemory                  = 0x00000002,
    MiniDumpWithHandleData                  = 0x00000004,
    MiniDumpFilterMemory                    = 0x00000008,
    MiniDumpScanMemory                      = 0x00000010,
    MiniDumpWithUnloadedModules             = 0x00000020,
    MiniDumpWithIndirectlyReferencedMemory  = 0x00000040,
    MiniDumpFilterModulePaths               = 0x00000080,
    MiniDumpWithProcessThreadData           = 0x00000100,
    MiniDumpWithPrivateReadWriteMemory      = 0x00000200,
    MiniDumpWithoutOptionalData             = 0x00000400,
    MiniDumpWithFullMemoryInfo              = 0x00000800,
    MiniDumpWithThreadInfo                  = 0x00001000,
    MiniDumpWithCodeSegs                    = 0x00002000,
    MiniDumpWithoutManagedState             = 0x00004000,

} MINIDUMP_TYPE;

MINIDUMP_TYPE枚举是一些标志,允许我们来控制minidump包含哪些内容。我们来看一下这些值得内容,以及如何使用它们。

MiniDumpNormal

MiniDumpNormal是一个特别的标志。它的值是0,意味着这个值永远隐含存在,甚至不需要显示指定。因此,我们可以假定这个标记代表了minidump中永远存在的一组基础数据集合。通过指定用户自定义的回调函数,可以过滤这些值。

Figure 4的表格显示了数据基础数据集合中的数据类型。

Figure 4:

数据类型描述
系统信息关于操作系统和CPU的信息,包括:操作系统版本(包括服务包)处理器的数量和型号在WinDbg中,可以通过“vertarget” 和 “!cpuid”显示相应信息。
进程信息关于进程(Process)的信息,包括:进程ID进程时间(创建时间,用户态和核心态的执行时间)WinDbg通过| (Process Status)命令显示进程ID,“.time”显示进程时间。
模块(Module) 信息对于进程装载的所有可执行模块,显示如下信息:装载地址模块的大小文件名(包括路径)版本信息(VS_FIXEDFILEINFO structure)模块识别信息,帮助调试器定位相应的模块并且装载调试信息 (校验和,时间戳,调试信息记录)在WinDbg和 VS.NET中,可以在Modules窗口中看到这些信息。WinDbg的“lm”也可以看到这些信息。
线程信息对于进程中的任何一个线程,会包括这些信息:线程ID优先级线程上下文暂停计数(Suspend count)线程环境块(thread environment block ,TEB)的地址,但是不包括TEB的内容VS.NET中,Threads窗口中可以显示大多数这些信息。WinDbg中用 “~”命令显示线程信息。
线程栈对于每一个线程,minidump包含了栈内存的内容。允许我们得到所有线程的调用栈,查看函数参数和局部变量的值。
指令窗口对于每一线程,当前指令指针前后的256自己内存会保留下来。允许我们即使没有可执行模块,也可以获得故障时刻的线程代码的反编译信息。
异常信息可以通过MiniDumpWriteDump 函数的第5个参数(见Figure 2)把异常信息包含到minidump中。这种情况下minidump会包括如下异常信息:异常记录 (EXCEPTION_RECORD structure)异常发生时刻的线程上下文指令窗口(发生异常的指令地址附近的256字节)当VS.NET debugger 装载带有异常信息的minidump数据, debugger会自动显示异常时刻应用程序状态(包括调用堆栈、寄存器值、反汇编的指令和抛出异常的代码行)。WinDbg中,需要使用.ecxr命令切换到异常发生时刻的应用程序状态。

确实,MiniDumpNormal指定的基础信息集合非常有用。我们可以定位出现问题的指令,检查线程怎么样进入到这种状态。甚至可以产看到函数参数和局部变量的值。另外,这些信息也可以用来调试死锁,因为我们可以看到所有线程的调用栈,并且知道他们在等待什么。

同时,所有这些信息的代价非常小,minidump的大小通常不超过20KB。主要影响大小的因素的线程栈的大小-他们占用的内存越多,minidump的文件越大。

但是,如果需要调试的问题比较复杂,而不是像非法访问或者死锁这样的简单问题,我们就会发现MiniDumpNormal标记收集的信息还不够。我们有可能需要查看全局变量,但是里面没有。也有可能需要查看堆里面分配的结构体的内容,minidump也没有包括相应的堆信息。当我们需要更多的minidump数据时,就需要研究MINIDUMP_TYPE的其他成员了。

MiniDumpWithFullMemory

这可能是除了MiniDumpNormal以外使用最多的标志了。如果指定了这个标志,minidump会包含进程地址空间中所有可读页面的内容。我们可以看到应用程序分配的所有内存,这使我们有很多的调试方法。可以查看存储在栈上、堆上、模块数据段的所有数据。甚至还可以看到线程和进程环境块(Process Environment Block和Thread Environment Bolck, PEB和TEB)的数据。这些没有公开的数据结构可以给我们的调试提供无价的帮助。

使用这个标记的唯一问题是会使minidump变得很大,至少有几MByte。另外,minidump的内容里面包含了冗余信息,所有可执行模块的代码段都包含在了里面。但是很多时候,我们很容易从其他地方获得可执行代码。让我们一起来看看MINIDUMP_TYPE,是否能够找到更好的选项。

MiniDumpWithPrivateReadWriteMemory

如果指定这个标志,minidump会包括所有可读和可写的私有内存页的内容。这使我们可以察看栈、堆甚至TLS的数据。PEB和TEB也包括在里面。

这时候,minidump没有包括共享内存也的内容。也就是说,我们不能查看内存映射文件的内容。同样,可执行模块的代码和数据段也没有包括进来。不包括代码段意味着dump没有占用不需要的空间。但是,我们也没有办法查看全局变量的值。

无论如何,通过组合其他一些选项,MiniDumpWithPrivateReadWriteMemory是一个非常有用的选项。我们会在后面看到。

MiniDumpWithIndirectlyReferencedMemory

如果指定这个标志,MiniDumpWriteDump检查线程栈内存中的每一个指针。这些指针可能指向线程地址空间的其他可读内存页。一旦发现这样的指针,程序会读取指针附近1024字节的内容存到minidump中(指针前的256字节和指针后的768字节)。

Figure 5是一段例子代码.

Figure 5:

#include <stdio.h>

struct A
{
    int a;

    void Print()
    {
        printf("a: %d\n", a);
    }
};

struct B
{
    A* pA;
    B() : pA(0) {}
};

int main(int argc, char* argv[])
{
    B* pB = new B();

    pB->pA->Print();

    return 0;
}

在这个例子中,主程序试图通过null对象指针(pB->pA)调用A::Print。这会导致一个运行时非法访问。如果使用MiniDumpNormal产生的minidumo来调试,会发现没有办法看到指针pB指向的结构体的内容。这些内容存在堆上。我们只能猜测传给A::Print的对象指针是null。

如果我们指定了标志MiniDumpWithIndirectlyReferencedMemory,MiniDumpWriteDump会发现栈上有一个指针pB指向了堆上的其他区域。就会把pB指向地址附近的1024字节存到minidump中。因此,通过调试器就可以看到结构体B的内容,进而发现pA是null。

当然,MiniDumpWriteDump不能访问调试信息。因此,他没有办法区分真正的指针和另外一些值。这些值恰好可以被认为指向有效内存区域。Figure 6.解释了这种情况。

Figure 6:

#include <stdio.h>

void PrintSum(unsigned long sum)
{
    printf("sum: %x", sum);

    // access violation
    *(int*)0 = 1;
}

unsigned long Sum(unsigned long a, unsigned long b)
{
    unsigned long sum = a + b;

    PrintSum(sum);

    return sum;
}

int main()
{
    Sum(0x10000, 0x120);

    return 0;
}

当PrintSum导致非法访问的时候,0x10000和0x120的和保存在栈上。这个和(0x10120)不是指针。但是,MiniDumpWriteDump没有办法知道。如果0x10120恰好是可读内存页的有效地址,minidump会包括1024字节的内存(0x10020 – 0x10520)。

当搜索栈的时候,MiniDumpWriteDump会忽略指向可执行模块的数据段的指针。这就导致MiniDumpWithIndirectlyReferencedMemory没办法让我们看到全局变量的值。即使栈指向它们都不行。后面我们会看到,MINIDUMP_TYPE还包括其他标志可以完成这个功能。

加上MiniDumpWithIndirectlyReferencedMemory标记,minidump大小会增加。增加的数量取决于栈中指针的数量。

MiniDumpWithDataSegs

如果指定这个标志,minidump会包括进程装载的所有可执行模块的可写数据段。如果我们希望查看全局变量的值,有不希望被MiniDumpWithFullMemory困扰,就可以使用MiniDumpWithDataSegs。

这个标志对于minidump大小的影响完全取决于相关数据段的大小。系统DLL的数据段也包含在内,所以,即使一个简单的程序,也可能会增加几百KB。 例如,DbgHelp的.data段超过100K。如果我们只是为了使用MiniDumpWriteDump,这代价可能太大了。在文章的后半部分,会给大家演示,怎么样控制MiniDumpWriteDump来保证只包含真正需要的数据段。

MiniDumpWithCodeSegs

如果指定这个标志,mindump会包括所有进程装载的可执行模块的代码段。就像MiniDumpWithDataSegs,minidump大小会有明显增长。在文章的后半部分,我会演示增么样定制MiniDumpWriteDump,保证只包含必要的代码段。

MiniDumpWithHandleData

如果指定这个标志,minidump会包括故障时刻进程故障表里面的所有句柄。可以用WinDbg的!handle来显示这些信息。

这个标志对于minidump大小的影响取决于进程句柄表中的句柄数量。

MiniDumpWithThreadInfo

MiniDumpWithThreadInfo可以帮助收集进程中线程的附加信息。对于每一个线程,会提供下列信息:

  • 线程时间 (创建时间,执行用户代码和内核代码的时间)
  • 入口地址
  • 相关性

WinDbg中,可以通过.ttime命令查看线程时间。

MiniDumpWithProcessThreadData

有些时候我们需要查看线程和进程环境块的内容(PEB和TEB)。假设minidump包括了这些块占用的内存,就可以通过WinDbg的!peb和!teb命令来查看。这正是MiniDumpWithProcessThreadData所提供的数据。当使用这个标志时,minidump会包含PEB和TEB占据的内存页。同时,也包括了另外一些它们也用的内存页(例如,环境变量和进程参数保存的位置,通过TlsAlloc分配的TLS空间)。遗憾的是,有一些PEB和TEB引用的内存被忽略了,例如,通过__declspec(thread)分配的线程TLS数据。如果确实需要,就不得不使用MiniDumpWithFullMemory或者MiniDumpWithPrivateReadWriteMemory来获得。

MiniDumpWithFullMemoryInfo

如果希望检查整个继承的虚拟内存布局,我们可以使用MiniDumpWithFullMemoryInfo标志。如果指定它,mindump会包含进程虚拟内存布局的完整信息。可以通过WinDbg的!vadump和!vprot命令查看。这个标志对minidump大小的影响取决于虚拟内存布局-每个有相似特性的页面区域(参考VirtualQuery函数说明)会增加48字节。

MiniDumpWithoutOptionalData

我们已经看过的所有MINIDUMP_TYPE标记都是想minidump中添加一些数据。也有一些标志作用相反,它们从minidump中去除一些不必要的数据。MiniDumpWithoutOptionalData就是其中一个。他可以用来减小保存在dump中的内存的内容。当指定这个标志是,只有MiniDumpNormal指定的内存会被保存。其他内存相关的标志(MiniDumpWithFullMemory, MiniDumpWithPrivateReadWriteMemory, MiniDumpWithIndirectlyReferencedMemory)即使指定,也是无效的。同时,他不影响这些标志的行为:MiniDumpWithProcessThreadData, MiniDumpWithThreadInfo, MiniDumpWithHandleData, MiniDumpWithDataSegs, MiniDumpWithCodeSegs, MiniDumpWithFullMemoryInfo

MiniDumpFilterMemory

如果指定这个标志,栈内存的内容会在保存之前进行过滤。只有重建调用栈需要的数据才会被保留。其他数据会被写成0。也就是说,调用栈可以被重建,但是所有局部变量和函数参数的值都是0。

这个标志不影响minidump的大小。它只是没有改变保存的内存数量,只是把其中一部分用0覆盖了。同时,这个标志只影响线程栈占用内存的内容。其他内存(比如堆)不受影响。如果使用了MiniDumpWithFullMemory,这个标志就不起作用了。

MiniDumpFilterModulePaths

这个标志控制模块信息中是否包括模块路径(参考MiniDumpNormal的说明)。如果指定这个标记,模块路径会从dump中删除,只保留模块的名字。按照文档说明,它也可以帮助从minidump中删除可能涉及隐私的信息(例如有些时候模块的路径会包含用户名)。

由于模块路径数量不多,这个标志对minidump的大小影响不大。对调试的影响也不大。我们经常需要告诉调试器匹配的可执行程序保存的位置。

MiniDumpScanMemory

这个标志可以帮助我们节约minidump占用的空间。它会把调试不需要的可执行模块去掉。这个标志会和MiniDumpCallback函数紧密合作。因此,我们首先看一下这个函数,然后回头讨论MiniDumpScanMemory。

3. MiniDumpCallback函数

如果MINIDUMP_TYPE不能满足我们定制minidump内容的需要,我们可以使用MiniDumpCallback函数。这是一个用户定义的回调函数,MiniDumpWriteDump会调用它,让用户来决定是否把某些数据放到minidump中。通过这个函数,我们可以完成这些功能:

  • 从minidump的模块信息中移除一个可执行模块信息(部分或者全部)
  • 从minidump的线程信息中移除一个线程信息(部分或者全部)
  • 在minidump中添加一段用户指定范围的内存的内容

让我们先看一下MiniDumpCallback 的声明(见Figure 7):

Figure 7:

BOOL CALLBACK MiniDumpCallback(

 PVOID CallbackParam,
 const PMINIDUMP_CALLBACK_INPUT CallbackInput,
 PMINIDUMP_CALLBACK_OUTPUT CallbackOutput

);

这个函数有四个参数。第一个参数CallbackParam是一个用户为回调函数定义的数据结构(例如,一个指向C++对象的指针)。第二个参数CallbackInput是MiniDumpWriteDump传递给回调函数的数据。第三个参数CallbackOutput包含了回调函数返回给MiniDumpWriteDump的数据。这个数据通常就是指定关于那些数据应该包含在minidump中。

现在,让我们看一下MINIDUMP_CALLBACK_INPUT和MINIDUMP_CALLBACK_OUTPUT结构体的内容。

Figure 8:

typedef struct _MINIDUMP_CALLBACK_INPUT {

    ULONG ProcessId;
    HANDLE ProcessHandle;
    ULONG CallbackType;

    union {

        HRESULT Status;
        MINIDUMP_THREAD_CALLBACK Thread;
        MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
        MINIDUMP_MODULE_CALLBACK Module;
        MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
        MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
    };

} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT;


typedef struct _MINIDUMP_CALLBACK_OUTPUT {

    union {

        ULONG ModuleWriteFlags;
        ULONG ThreadWriteFlags;

        struct {

            ULONG64 MemoryBase;
            ULONG MemorySize;
        };

        struct {

            BOOL CheckCancel;
            BOOL Cancel;
        };

        HANDLE Handle;
    };

} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT;


typedef enum _MINIDUMP_CALLBACK_TYPE {

    ModuleCallback,
    ThreadCallback,
    ThreadExCallback,
    IncludeThreadCallback,
    IncludeModuleCallback,
    MemoryCallback,
    CancelCallback,
    WriteKernelMinidumpCallback,
    KernelMinidumpStatusCallback,

} MINIDUMP_CALLBACK_TYPE;

MINIDUMP_CALLBACK_INPUT结构体包含MiniDumpWriteDump对回调函数的请求。前两个成员意义很明显-创建minidump的进程的id和句柄。第三个成员CallbackType是请求的类型,通常叫做回调类型。所有CallbackType的可能的值定义在MINIDUMP_CALLBACK_TYPE枚举集合中(见Figure 8)。我们在后面会仔细看一下这些值。结构体的第四个参数是一个联合,它的意义依赖于CallbackType的值。这个联合包含了MiniDumpWriteDump请求的附加数据。

MINIDUMP_CALLBACK_OUTPUT结构体要简单一点。它有一个联合构成,联合的意义依赖于MINIDUMP_CALLBACK_INPUT的值。联合的CallbackType成员包含了回调对于MiniDumpWriteDump的反馈。

下面我们来过一下回调类型(callback type)对应的一些最终重要的请求,以及回调函数如何对他们做出响应。在开始之前,先看一下Figure 9。这个例子表示了怎么样告诉MiniDumpWriteDump有一个用户自定的回调函数需要调用。

Figure 9:

void CreateMiniDump(EXCEPTION_POINTERS* pep)
{
    // Open the file
    HANDLE hFile = CreateFile(_T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE,
        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
    {
        // Create the minidump
        MINIDUMP_EXCEPTION_INFORMATION mdei;
        mdei.ThreadId = GetCurrentThreadId();
        mdei.ExceptionPointers = pep;
        mdei.ClientPointers = FALSE;

        MINIDUMP_CALLBACK_INFORMATION mci;
        mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
        mci.CallbackParam = 0;   // this example does not use the context

        MINIDUMP_TYPE mdt = MiniDumpNormal;

        BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
            hFile, mdt, (pep != 0) ? &mdei : 0, 0, &mci);

        if (!rv)
            _tprintf(_T("MiniDumpWriteDump failed. Error: %u \n"), GetLastError());
        else
            _tprintf(_T("Minidump created.\n"));

        // Close the file
        CloseHandle(hFile);
    }
    else
    {
        _tprintf(_T("CreateFile failed. Error: %u \n"), GetLastError());
    }
}

BOOL CALLBACK MyMiniDumpCallback(

    PVOID              pParam,
    const PMINIDUMP_CALLBACK_INPUT  pInput,
    PMINIDUMP_CALLBACK_OUTPUT    pOutput
)

{
    // Callback implementation
    …
}

IncludeModuleCallback

当回调类型被设成IncludeModuleCallback,MiniDumpWriteDump询问回调函数是否要把特定可执行模块的信息存到minidump中。回调函数根据MINIDUMP_CALLBACK_INPUT的内容做出决定。此时,联合成员应该是MINIDUMP_INCLUDE_MODULE_CALLBACK:

typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK {

  ULONG64 BaseOfImage;

} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK;

这里,BaseOfImage是模块在内存中的基地址。利用这个地址,可以获得模块更多的信息,以便决定是否需要存到minidump中。

回调函数利用返回值来把决定返回给MiniDumpWriteDump。如果回调返回值是TRUE,关于模块的信息会被包含进minidump中。通过后续的回调调用可以更精确的定义那些信息需要保存。如果返回值是FALSE,模块的所有信息会被丢弃。Minidump中看不到任何模块存在的痕迹。

对于这个回调类型,MINIDUMP_CALLBACK_OUTPUT没有用处。

ModuleCallback

一个模块通过了IncludeModuleCallback的测试之后,它会面临在通往minidump之路上的另外一个障碍。这个障碍是ModuleCallback。这个回调函数会决定关于这个模块的哪些信息需要保存。

这一次回调函数必须返回TRUE,来保证MiniDumpWriteDump继续工作。回调函数使用MINIDUMP_CALLBACK_OUTPUT结构体通知MiniDumpWriteDump的关于数据的决定。这个结构体中的联合包括一个ModuleWriteFlags成员。MiniDumpWriteDump会初始化它的值。它的值代表了可以保存在minidump中的各种模块信息。MODULE_WRITE_FLAGS枚举包含了所有可用的标志。

Figure 10:

typedef enum _MODULE_WRITE_FLAGS {

    ModuleWriteModule           = 0x0001,
    ModuleWriteDataSeg          = 0x0002,
    ModuleWriteMiscRecord       = 0x0004,
    ModuleWriteCvRecord         = 0x0008,
    ModuleReferencedByMemory    = 0x0010,
    ModuleWriteTlsData          = 0x0020,
    ModuleWriteCodeSegs         = 0x0040,

} MODULE_WRITE_FLAGS;

当MiniDumpWriteDump带着ModuleCallback参数调用回调函数,它会设置一些标志,告诉回调函数哪些模块信息可以包含在minidump中。回调函数可以分析这些标志,然后决定清除其中的一部分和还是全部。这样就可以告诉MiniDumpWriteDump哪些信息不需要保存。Figure 11中的表格列出了目前可用的所有标志,并且解释了他们所代表的信息。

Figure 11:

标志描述
ModuleWriteModule这个标志允许从minidump中排除模块的所有信息。如果回调函数清除了这个标志,minidump中就不会包含这个模块的任何信息。 默认条件下,这个标志总是被设置的。
ModuleWriteCvRecord, ModuleWriteMiscRecord这些标志可以用来从minidump中排除模块的调试信息记录。如果清除这个标志,只有在开发机器是有这个模块的时候,调试器才能装载模块的调试信息。 通常,只有在模块包含调试信息记录的时候,这些标志才会被设置,也就是,模块是带着调试信息进行编译的时候。 可以在这里面找到关于调试信息的更详细说明,http://www.debuginfo.com/articles/debuginfomatch.html
ModuleWriteDataSeg这个标志可以用来从minidump中排除模块的数据段的内容。如果我们在MiniDumpWriteDump使用了MiniDumpWithDataSegs 标志,又希望选择哪些模块的数据段需要包含进来,这个标记就非常有用了。通常,我们希望看到所有我们自己模块的数据段(以便在调试器中看到全局变量),以及一小部分系统模块(比如,ntdll.dll)。其他第三方模块或者系统模块的数据段没有用处。由于可执行模块的数据段在minidump中占用了很大的空间。这个标记给我们提供一个很好的优化文件尺寸的机会。 只有MiniDumpWithDataSegs 标志被传给MiniDumpWriteDump 的时候,这个标志才会被设置。
ModuleWriteCodeSegs这个标记可以用来从minidump中排除模块的代码段。只有MiniDumpWithCodeSegs 传给MiniDumpWriteDump 函数的时候,这个标志才可用。这个标志可以用来选择哪些模块的代码段可以包含在minidump中。一定不要包含所有模块的代码段,这会显著增加minidump的大小。
ModuleReferencedByMemory这个标志需要和MINIDUMP_TYPE中的MiniDumpScanMemory一起使用。如果MiniDumpScanMemory被传给MiniDumpWriteDump,函数会遍历进程中的所有线程栈,查找指向可执行模块的地址空间的所有指针。搜索完成后,MiniDumpWriteDump就知道了哪些模块被引用了,哪些模块没有被引用。 如果一个模块没有被任何一个线程栈引用,那么重建调用栈可能不会用到这个模块。Minidump就可以不包括这个模块,来节省空间。为了让回调函数作最终决定,如果一个模块被栈引用了,MiniDumpWriteDump会设置ModuleReferencedByMemory标志,没有被引用的模块的标志会被清除。. 接着,回调函数可以检查这个模块是否被引用过。然后,可以通过清除ModuleWriteModule标志,来把模块排除到minidump之外。
ModuleWriteTlsData这个标志可能是用来控制模块的TLS数据(通过__declspec(thread)分配)是否要包括在mindump中。但是,到写这篇文章为止,还不能工作。

注意ModuleCallback只允许我们排除一些模块信息,但是不允许添加新的数据。这意味着,如果MiniDumpWriteDump没有设置相应的标志,在回调函数中设置相应的标志没有用处。例如,如果没有给MiniDumpWriteDump设置MiniDumpWithDataSegs标志,MiniDumpWriteDump函数就不会给任何模块设置ModuleWriteDataSeg标志。进一步,即使回调函数设置一个模块的ModuleWriteDataSeg标志,minidump中也不会真的包含模块数据段的内容。

在讨论很长时间MINIDUMP_CALLBACK_OUTPUT结构体之后,我们回头来看MINIDUMP_CALLBACK_INPUT结构体。这时候,这个联合会被解析成MINIDUMP_MODULE_CALLBACK结构体(Figure 12)。它里面包括了关于模块的丰富的信息,例如,名称和路径、大小、版本信息。

Figure 12:

typedef struct _MINIDUMP_MODULE_CALLBACK {

    PWCHAR FullPath;
    ULONG64 BaseOfImage;
    ULONG SizeOfImage;
    ULONG CheckSum;
    ULONG TimeDateStamp;
    VS_FIXEDFILEINFO VersionInfo;
    PVOID CvRecord;
    ULONG SizeOfCvRecord;
    PVOID MiscRecord;
    ULONG SizeOfMiscRecord;

} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK;

IncludeThreadCallback

这个回调类型对于对于线程的作用,和IncludeModuleCallback对于模块的作用一样。这给我们一个机会来决定一个线程的哪些信息需要保存到minidump中。就像IncludeModuleCallback,回调函数返回TRUE表示要把线程信息保存到mindump,返回FASLE表示完全放弃这些信息。可以通过存储在MINIDUMP_CALLBACK_INPUT的ID来区分线程。

typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK {

  ULONG ThreadId;

} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK;

MINIDUMP_CALLBACK_OUTPUT structure is not used.

ThreadCallback

这个回调类型的目的和ModuleCallback 对于模块的作用一样。回调类型的基本原则也一样。MINIDUMP_CALLBACK_OUTPUT中的联合包括了一系列的标志(ThreadWriteFlags),回调函数可以清除部分或者全部标记,来从minidump清除相应的线程信息。

MINIDUMP_CALLBACK_INPUT提供了很多种关于线程的信息。这里面的联合可以解释成MINIDUMP_THREAD_CALLBACK (Figure 13)。包括了线程ID和句柄、线程上下文、线程栈的边界。为了保证MiniDumpWriteDump继续运行,回调函数必须返回TRUE.

Figure 13:

typedef struct _MINIDUMP_THREAD_CALLBACK {

    ULONG ThreadId;
    HANDLE ThreadHandle;
    CONTEXT Context;
    ULONG SizeOfContext;
    ULONG64 StackBase;
    ULONG64 StackEnd;

} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK;

Figure 14种表格列出了所有常用标志,以及他们所代表的信息。

Figure 14:

FlagDescription
ThreadWriteThread通过这个标志可以从minidump中清除一个线程的所有信息。如果回调函数清除了这个标志,所有其他的标志都会被忽略。Minidump就不保存任何关于这个线程的信息了。 通常,这个标志总是被设置的。
ThreadWriteStack这个标志允许从minidump中清除线程栈的内容。因此,如果回调函数清除了这个标志,调试器就没办法看到线程的调用栈了。线程栈通常有几KB ,极少数情况可以达到几MB。因此这个标志会影响minidump的大小。 通常,这个标志总是被设置的。
ThreadWriteContext通过这个标志可以清除线程上下文的内容(定义在winnt.h中的CONTEXT结构体)。如果回调清除了这个标志,调试器就不能看到线程上下文和调用栈,所有寄存器会被置成0。 通常线程上下文不会占据很大的minidump 空间(X86环境下是716字节),因此对于minidump大小的影响很小。 通常,这个标志总是被设置的。
ThreadWriteInstructionWindow通过这个标志可以清除线程指令窗口(当前执行指针附近的256字节)。如果清除这个标志,就没有办法直接看到出故障时的反汇编代码。如果想看到,就必须在开发者的计算机上装载相应的模块。
ThreadWriteThreadInfo只有给MiniDumpWriteDump 传递了MiniDumpWithThreadInfo 参数时,这个标志才被设置。通过这个标志,可以清除minidump中的额外线程信息。(参考本文中关于MiniDumpWithThreadInfo的解释)
ThreadWriteThreadData只有给MiniDumpWriteDump 传递了MiniDumpWithProcessThreadData参数时,这个标志才被设置。通过这个标志可以从minidump中清除线程的特别信息(TEB的内容、TLS存储和一些附加信息)

MemoryCallback

有些时候,我们肯能希望在minidump中添加一些额外内存区域的内容。例如,我们可能在堆上分配了一些数据(也可能是通过VirtualAlloc),希望在调试minidump的时候能够看到这些数据。我们可以通过MemoryCallback来完成这个功能。MiniDumpWriteDump会在通过回调调用处理完线程和模块之后调用这个回调函数。

当使用MemoryCallback 作为回调函数的回调参数时,MINIDUMP_CALLBACK_OUTPUT 中的联合会被解析成:

struct {    ULONG64 MemoryBase;    ULONG MemorySize;};

如果回调函数在这个结构体中写入可读内存块的资质和大小,并且返回TRUE,这个内存块的内容就会被放到minidump中。我们可以添加多个内存块。当回调函数返回TRUE的时候,这个回调会被再次调用。MiniDumpWriteDump会一直等到返回FALSE才停止调用这个回调函数。

CancelCallback

MiniDumpWriteDump会定期调用这个回调类型。这个回调类型允许终止创建minidump的过程,这对于GUI应用程序很有用。MINIDUMP_CALLBACK_OUTPUT结构体被解析成两个值,Cancel和 CheckCancel:

struct {

    BOOL CheckCancel;
    BOOL Cancel;
};

如果我们希望彻底取消创建minidump,我们应该把Cancel设成TRUE。如果我们不想取消minidump,而只是不想再接收CancelCallback的回调,就把CheckCancel设成TRUE。如果两个成员都设置成FALSE,MiniDumpWriteDump就不再使用CancelCallback调用回调函数。

回调函数应该返回TRUE来确认MINIDUMP_CALLBACK_OUTPUT 的值被设置了。

回调的顺序

在讨论完回调的类型之后,我们可能会关心这些回调类型的顺序。调用的顺序如下:

  • IncludeThreadCallback – 进程中的每一个线程一次
  • IncludeModuleCallback –进程中每一个可执行模块一次
  • ModuleCallback – 没有被IncludeModuleCallback排除的模块,每个调用一次
  • ThreadCallback –没有被IncludeThreadCallback排除的线程,每个调用一次
  • MemoryCallback会调用一次或者多次,一直到回调函数返回FALSE

另外,CancelCallback 会在其他回调类型之间定期调用。这样,保证在需要的时候可以中断minidump的创建过程。

这个例子程序(http://www.debuginfo.com/examples/src/effminidumps/CallbackOrder.cpp)会显示实际的调用顺序。你也可以使用MiniDump Wizard来测试各种回调(http://www.debuginfo.com/tools/minidumpwizard.html)。

MiniDump Wizard

你可以使用MiniDump Wizard 来试验各种minidump的选项并且看到他们会怎么影响minidump的大小和内容。MiniDump Wizard可以创建任意进程的minidump。它也可以模拟异常来创建自己的mindump文件。你可以选择把哪些类型标志 传递给MiniDumpWriteDump ,然后通过一系列的对话框对回调请求做出响应。

当创建完minidump,可以在一个调试器中装载它,然后查看包括了哪些信息。也可以使用MinDumpView(http://www.debuginfo.com/tools/minidumpview.html)应用来得到minidump中内容的清单。

4. 用户数据流

除了MiniDumpWriteDump已经捕获的成功调试需要的所有应用程序状态之外,我们经常需要程序运行环境的一些额外信息。例如,如果可以查看配置文件的内容或者应用程序相关的注册表设置会很有帮助。Minidump允许把这些信息作为额外数据流添加进来。

这个例子程序显示了如何做到这一点(http://www.debuginfo.com/examples/src/effminidumps/WriteUserStream.cpp)。我们需要声明一个MINIDUMP_USER_STREAM_INFORMATION变量,在里面填充流的数量和用户数据流的指针数组。每个用户数据流用一个MINIDUMP_USER_STREAM结构体表示。结构体里面包括流的类型、大小、以及一个指向流数据的指针。流类型是识别流的一个唯一标志,必须是一个比LastReservedStream大的常数。

Figure 14:

typedef struct _MINIDUMP_USER_STREAM_INFORMATION {

    ULONG UserStreamCount;
    PMINIDUMP_USER_STREAM UserStreamArray;

} MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;


typedef struct _MINIDUMP_USER_STREAM {

    ULONG32 Type;
    ULONG BufferSize;
    PVOID Buffer;

} MINIDUMP_USER_STREAM, *PMINIDUMP_USER_STREAM;

当我们向一个minidump 添加了用户数据流,我们可以通过MiniDumpReadDumpStream 函数来读出这些信息。这个例子程序(http://www.debuginfo.com/examples/src/effminidumps/WriteUserStream.cpp)显示了如何从前一个例子(http://www.debuginfo.com/examples/src/effminidumps/WriteUserStream.cpp)写入的例子数据。

5. 策略

MiniDumpWriteDump有丰富功能和大量的可用选项。这使得找到一个所有应用都适用的策略会很困难。对于每一个特定的情况,应用程序的开发者必须决定哪些选项对他们的调试工作有用。在这我会试着描述一些基本策略,用来解释如何把MiniDumpWriteDump的配置选项应用到真实场景中。我们会看到四种不同的MiniDumpWriteDump收集数据的策略。并且来了解他们会对minidump的大小和调试的可能性发生什么影响。

TinyDump

这不是一个真实世界的场景。这个方法显示了怎么样来创建一个最小可能数据集的minidump,来使它有一点用途。Figure 15总结了这种MiniDumpWriteDump配置选项。

Figure 15:

MINIDUMP_TYPE标志MiniDumpNormal
MiniDumpCallbackIncludeThreadCallback – exclude all threads IncludeModuleCallback – exclude all modules

实现这种方式的例子程序在这个地址http://www.debuginfo.com/examples/src/effminidumps/TinyDump.cpp。

结果minidump非常小,在我的系统上非常小。并不令人惊讶,我们去掉了所有线程和模块的信息。如果你试着用WinDbg or VS.NET debugger来装载,你会发现调试器没有办法装载它。

但是,这个minidump还包含了异常的信息,所以不是完全无用,我们可以手工读取这些信息(使用MiniDumpReadDumpStream函数),可以看到异常发生的地址、异常时刻的线程上下文、异常代码甚至反汇编。你可以使用MinDumpView工具(http://www.debuginfo.com/tools/minidumpview.html)来查看其中的信息。为了保持工具简单,没有提供返汇编。

MiniDump

不像TinyDump,这种方式对于真实世界场景是有用的。它收集了足够的调试信息同时又保持minidump足够小。Figure 16中的表格描述了相应的MiniDumpWriteDump配置项。

Figure 16:

MINIDUMP_TYPEMiniDumpWithIndirectlyReferencedMemory, MiniDumpScanMemory
MiniDumpCallbackIncludeThreadCallback – 包括所有线程 IncludeModuleCallback – 包括所有模块 ThreadCallback – 包括所有线程 ModuleCallback – 检查ModuleWriteFlags ,把所有ModuleReferencedByMemory 没有设置的模块排除掉(清除这些模块的ModuleWriteModule标志)

可以在这找到例子程序(http://www.debuginfo.com/examples/src/effminidumps/MiniDump.cpp)

结果的mindump文件仍然很小(在我的系统上大约40-50KB)。他比mindump的标准方式(MiniDumpNormal + no MiniDumpCallback))包含了更多的信息量。他允许查看栈上的引用的数据。为了优化大小,我们把所有线程栈没有引用的模块从minidump中去掉了(在我的系统上,advapi32.dll 和rpcrt4.dll被去掉了)。

但是,这个minidump还缺少一些重要的信息。例如,我们看不到全局标量的值,不能查看堆和TLS中分配的数据(除非他们被线程栈引用了)。

MidiDump

下一个方式会产生一个信息量充足的minidump,同时保证文件不会过大。Figure 17的表格描述了配置。

Figure 17:

MINIDUMP_TYPE flagsMiniDumpWithPrivateReadWriteMemory, MiniDumpWithDataSegs, MiniDumpWithHandleData, MiniDumpWithFullMemoryInfo, MiniDumpWithThreadInfo, MiniDumpWithUnloadedModules
MiniDumpCallbackIncludeThreadCallback –包括所有线程 IncludeModuleCallback – 包括所有模块 ThreadCallback – 包括所有线程 ModuleCallback – 只保留主模块和ntdll.dll的数据区

例子程序可以在这看到(http://www.debuginfo.com/examples/src/effminidumps/MidiDump.cpp)。minidump的大小在我的系统上大约1350KB。当在调试器中装载的时候,我们可以得到应用程序的几乎所有信息,包括全局变量的值、堆和TLS的内容、PEB、TEB。我们甚至可以得到句柄信息以及虚拟内存布局。这是一个非常有用的dump,并且不是很大。下面的信息没有包括在mindump中:

  • 所有模块的代码区(如果我们可以得到这些模块,就不需要他们)
  • 某些模块的数据区(我们只包括了我们希望看到全局变量的模块的数据区)

MaxiDump

最后一个例子显示了如何创建一个包含所有可能数据的minidump。Figure 18的表格显示了如何做到这一点。

Figure 18:

MINIDUMP_TYPE flagsMiniDumpWithFullMemory, MiniDumpWithFullMemoryInfo, MiniDumpWithHandleData, MiniDumpWithThreadInfo, MiniDumpWithUnloadedModules
MiniDumpCallbackNot used

例子程序可以在这找到http://www.debuginfo.com/examples/src/effminidumps/MaxiDump.cpp。

这个minidump对于这样一个简单程序来说已经很大了(在我的系统上有8MB)。但是,它给了我们在一个mindump中包含所有信息的可能。

对比

Figure 19的表格比较和四种方式创建的minidump的大小。除了这个例子程序的数据之外(它和真实程序会有一定差距),我还添加了一个真实程序的数据。同样也使用这四种不同的minidump。

Figure 19:

TinyDumpMiniDumpMidiDumpMaxiDump
例子程序2 KB40-50 KB1,35 MB8 MB
真实程序2 KB200 KB14 MB35 MB

6. 补充

关于64位系统

这篇文章没有讨论MiniDumpWriteDump 中关于64位系统的选项。我的实验室里面没有64位的机器,我没有办法提供关于他们的更有效信息。

关于 DbgHelp版本

DbgHelp.dll一直在不断改进。新的特性会随着Debugging Tools for Windows工具包的新版本推出。在写这篇文章的时候,使用的版本是DbgHelp.dll 6.3。

例子程序

这篇文上涉及的所有例子程序(包括编译指令)可以在这找到。(http://www.debuginfo.com/examples/effmdmpexamples.html)

联系方式

Have questions or comments? Free free to contact Oleg Starodumov at firstname@debuginfo.com.

这份文件翻译自
http://www.debuginfo.com/articles/effminidumps.html

http://www.debuginfo.com/articles/effminidumps2.html

[补充]

如果希望找到一个完整的实现,可以使用XCrashReport
http://www.codeproject.com/Articles/5257/XCrashReport-Exception-Handling-and-Crash-Reportin

原文摘自 http://www.debuginfo.com/articles/effminidumps.html

本文转载自:https://blog.youkuaiyun.com/pkrobbie/article/details/6636310
https://blog.youkuaiyun.com/pkrobbie/article/details/6641081

生成C++程序的dump文件并使用dump分析 在C++代码调试和优化过程中,dump文件是一种非常有用的工具。本文将介绍如何在C++程序中生成dump文件,并使用dump分析工具WinDbg对其进行分析。首先,我们需要为我们的C++程序设置一个 SEH(Structured Exception Handling,结构化异常处理)的异常处理函数,以便在程序崩溃时捕获相关信息。总的来说,生成C++程序的dump文件并使用dump分析工具WinDbg对其进行分析可以帮助我们更快速地找到程序的问题。函数,将生成dump文件的操作放在了异常处理函数中。 阅读详情

相关推荐

Jupyter Lab 无法启动 Kernel 问题排查与解决总结

使用 curl,telnet,或 Python websocket-client 库可以本地模拟 WebSocket 握手行为,帮助分析服务端响应状态。避免使用 http:// + ws:// 组合方式部署 Jupyter Lab,特别是在远程或跨域访问场景。建议统一采用 https:// + wss:// 方式部署,确保浏览器信任并兼容。确认 WebSocket 已升级为 wss://,内核可正常启动。但在远程访问时,浏览器出于安全策略,默认拒绝 ws:// 协议。

weixin_43866043的博客 1859

minidump详细介绍

Effective minidump  简介 在过去几年里,崩溃转储(crash dump)成为了调试工作的一个重要部分。如果软件在客户现场或者测试实验室发生故障,最有价值的解决方式是能够创建一个故障瞬间的应用程序状态镜像,然后可以在开发者的机器上通过调试器进行分析。第一代的crash dump通常被称为“全用户转储(full user dump)”,它包含了进程的虚拟内存的全

chentao1206的专栏 4683

高德地图轨迹回放,消息框内展示车辆速度且随车辆移动-升级支持调整速度、回放进度

使用高德地图回放车辆GPS轨迹,并添加速度和回放进度条,并支持调整,效果如下: 新增的功能: 在基础版本,有如下升级 速度条:可以拖动以调整车辆回放速度,且从当前位置继续回放 进度条:进度条表示车辆已回放的百分比,且拖动进度条车辆定位到相应的位置并继续回放 ###思路分析 高德和marker回放相关的API //主要靠他回放 moveAlong(path:Array,speed:Number,f:Function,circlable:Boolean) //点移动到另一个位置,移动进度条车辆跟随移动

博客 1万+

【小沐学C++】C++ 捕获程序异常奔溃minidump

并不是所有的bug都可以在发布前找到,也就是说并不是所有的抛出异常的bug都可以在发布前找到。幸运的是,微软在 Platform SDK 中包含了一个功能,可以帮助开发人员收集用户发现的异常信息。MiniDumpWriteDump函数将必要的故障转储信息写入文件,而不节省整个进程空间。此故障转储信息文件称为小型转储。这篇技术文章提供了有关如何编写和使用小型转储的信息。╮( ̄▽ ̄)╭如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???.........

爱看书的小沐 5828

第一章、isaacsim 及 isaaclab 安装教程

本专栏所有内容将根据上述资源地址展开,请结合食用~

m0_47719040的博客 4782

告别‘抓瞎’:CANoe的Trace、Graphics和Logging窗口实战技巧,高效分析总线故障

本文深入解析CANoe的Trace、Graphics和Logging窗口实战技巧,帮助工程师高效分析总线故障。通过智能过滤、可视化监控和智能日志管理,构建系统级问题定位工作流,显著提升车载网络故障排查效率。

weixin_42709044的博客 294

Dump文件分析整理

1. dump介绍 Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。Dump文件是用来给驱动程序编写人员调试驱动程序用的,这种文件必须用专用工具软件打开,比如使用WinDbg、VS打开。 Windows下Dump文件分为两大类,内核模式Dump和用户模式Dump。内核模式Dump是操作系统创建的崩溃转储,最经典的就是系统蓝屏,这时候会自动创建内核模式的Dump...

NewNicholas的博客 9100

15 君正IPC库开发总结

在前面的章节中,我们介绍了君正ISP、视频采集、音频采集、音频播放、云台电机、移动侦测算法、人形侦测算法、移动跟踪算法等的封装。那么,应用层如何使用这些封装好的接口呢?根据设计模式中的依赖倒置原则:高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象。因此,应用层应当只能感知到我们封装的接口类,而不应当感知到其对应的具体实现类。

hope_wisdom的技术博客 1662

VS生成dump文件和调试dump文件

在程序崩溃时,需要对异常的情况进行记录,Windows平台下的C++程序异常通常可分为两种:结构化异常(Structured Exception,可以理解为与操作系统相关的异常)和C++异常。对于结构化异常处理(SEH),可以找到很多资料,在此不细说。对于crash错 误,一般由未被正常捕获的异常引起,Windows操作系统提供了一个API函数可以在程序crash之前有机会处理这些异常,就是 SetUnhandleExceptionFilter函数。(C++也有一个类似函数set_terminate可以处理

hanxiaoyong_的博客 7855

2026年OPPO嵌入式岗位面试题带详细答案

本文总结了OPPO嵌入式岗位面试的核心考点,涵盖硬件驱动、系统启动、Linux内核开发等7大模块共34道典型题目。重点解析了SPI通信协议、中断处理、U-Boot启动流程、Linux驱动开发等关键技术,突出硬件调试与工程实践能力考核。题目设置贴合实际岗位需求,适合校招与社招驱动开发岗位应聘者系统复习,帮助掌握嵌入式开发的核心知识与面试要点。

Open 491

Windows2008使用-解决漏洞-CVE-2010-2730

随着硬件的升级,操作系统也逐渐进行了版本更新。目前阶段,Windows2016Server、Windows2022Server逐渐成为了以Windows系列的服务器平台系统。对于有些应用而言,由于其业务的固定,可能仍只能运行在Windows2008Server或者Windows2012Server上。有时候我们为安全需要,需要将相关的操作系统涉及的漏洞做修复。最近碰到在一个Windows2008R2 的操作系统漏洞CVE-2010-2730,一顿操作,终于找到了合适的方案,这里分享给大家。

mystonelxj的博客 2314

Windows上使用dump文件调试

dump文件记录当前程序运行某一时刻的信息,包括内存,线程,线程栈,变量等等,相当于调试程序时运行到某个断点上,把程序运行的信息记录下来。可以通过Windbg打开dump,查看程序运行的变量等,来调试程序。

Wlk1229 4644

YOLOv8与Swin Transformer的完美结合:如何提升目标检测的精度与速度

Swin Transformer(Shifted Window Transformer)是一种新型的视觉Transformer架构,采用了局部窗口(Window)和局部窗口偏移(Shifted Window)的策略,极大减少了计算复杂度,同时保持了全局感知能力。局部窗口注意力:每个窗口内的元素进行自注意力计算,减少计算量。窗口偏移:通过在不同层次引入窗口偏移,增强了全局信息的交互。层级设计:通过堆叠多个层次的模块,逐渐捕捉不同尺度的特征。

shrgegrb的博客 2151

.NET -- 使用Dump文件分析异常

Dump文件是进程的内存镜像。主要是用来在系统中出现异常或者崩溃的时候来生成dump文件,然后用调试器进行调试,这样就可以快速定位到程序崩溃位置,对问题进行排查。若将Dump文件拷贝到另一台PC上调试时,则需要将以下3个文件都拷下来,放在同一目录下,必须保证pdb与出问题的exe是同一时间生成的。在代码中断时,调试 --> 将转储另存为 --> 设置文件路径&文件名。打开任务管理器 --> 找到对应进程 --> 右击选择创建转储文件。运行DumpTest.exe,程序崩溃后,自动生成Dump文件。

哈怂的博客 5736

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Windows C++程序运行过程中生成dump文件

开发Windows C++程序跟踪异常是比较重要的功能,一般情况都需要进行全局异常捕获,并且生成dump文件。而且C++没有运行时直接获取堆栈信息的方法,返回错误或者异常处理时无法记录到堆栈信息,如果这个时候能够生成dump,对于bug分析是非常有利的。

codeofcc的博客 3926

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Windows下的Dump文件

一、    生成Dump文件方式 1.1任务管理器 在程序崩溃后,先不关闭程序,在任务管理器中找到该程序对应的进程。右键—>创建转储文件。 此时会在默认的目录下创建出一个dump文件。 可以看出,此种方法只使用与程序崩溃但没有立即自行退出的情况。倘若程序故障或自行退出,则此方法就难以应用。不过,我们可以在注册表中添加如下信息已确保系统在程序崩溃后自行保存一个dump文件

xiaoluo91的专栏 1万+

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Windows下用crashRpt让C++程序崩溃自动生成dump

windows c++程序崩溃自动收集dump,用于复现BUG

凝视阳光呦 3170

Windows下生成dump文件的三种方式

windows平台下dump文件的生成方式

qq_58397358的博客 2万+

windows系统下使用crashpad为vs2019项目在崩溃时生成dump文件(步骤超详细)

windows系统下使用crashpad为vs2019项目在崩溃时生成dump文件(步骤超详细)

ml29895063的博客 8874

windows环境C++抓取dump信息

Minidump文件生成 1、Minidump概念 minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在系统属性->高级->启动和故障恢复->设置->写入调试信息中选择“小内存转储(64 KB)”的话,当系统意外停止时都会在C:\Windows\Minidump\路径下生...

qq_32619837的博客 2217

C++ 记录Windows程序崩溃时的dumpfile

【原理】       windows程序当遇到异常,没有try-catch或者try-catch也无法捕获到的异常时,程序就会自动退出,如果这时候没有dump文件的话,我们是没有得到任何程序退出的信息。在windows程序异常退出之前,会预先调用一个在程序中注册的异常处理回调函数(默认是没有设置),只要我们在这个回调函数中调用MiniDumpWriteDump函数就可以产生我们想要的dump文件

郎涯技术 5829

跨平台 C++ 程序崩溃调试与 Dump 文件分析

本篇博客将介绍如何在代码中自动生成Dump文件,并详细解析Dump相关参数,帮助开发者快速定位Bug。

Darker的博客 2450

Minidump方式保留程序崩溃现场

介绍部分转自https://www.cnblogs.com/lisuyun/p/5245609.html 程序部分为原创。   Minidump方式保留程序崩溃现场 在Windows平台下用C++开发应用程序,最不想见到的情况恐怕就是程序崩溃,而要想解决引起问题的bug,最困难的应该就是调试release版本了。因为release版本来就少了很多调试信息,更何况一般都是发布出去由用户使用,...

wqfhenanxc的专栏 5028

C++开发:异常崩溃生成dump文件

在 C++ 程序开发中,尤其是在 Windows 平台上,程序发生异常崩溃时生成 Dump 文件(.dmp) 是一种非常重要的调试手段。Dump 文件记录了程序崩溃时的内存状态、线程信息、堆栈调用等数据,便于后续通过调试器(如 Visual Studio 或 WinDbg)分析崩溃原因。

倔强老吕的博客 1032
上一篇: GitLab API使用及如何删除某项目的Pipelines流水线和jobs
下一篇: linux下生成core dump文件方法及设置
EchoCoder
博客等级 码龄16年 25粉丝 50原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值