簡體   English   中英

NewHandler 和 UnhandledExceptionHandler 的不同堆棧跟蹤

[英]Different Stacktraces for NewHandler and UnhandledExceptionHandler

我有以下代碼:

#include <windows.h>
#include <minidumpapiset.h>
#include <strsafe.h>
#include <fileapi.h>
#include <iostream>
#include <signal.h>
#include <minwinbase.h>
#include <new.h>
#include "StackWalker.h"

int minidumpId = 0;

#ifndef _AddressOfReturnAddress

// Taken from: http://msdn.microsoft.com/en-us/library/s975zw7k(VS.71).aspx
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

// _ReturnAddress and _AddressOfReturnAddress should be prototyped before use
EXTERNC void* _AddressOfReturnAddress(void);
EXTERNC void* _ReturnAddress(void);
EXTERNC int __cdecl _purecall();

#endif

EXCEPTION_POINTERS ExceptionPointers;
EXCEPTION_RECORD ExceptionRecord;
CONTEXT ContextRecord;

void GetExceptionPointers(DWORD exceptionCode, EXCEPTION_POINTERS** exceptionPointers)
{
    // The following code was taken from VC++ 8.0 CRT (invarg.c: line 104)
    ZeroMemory(&ExceptionPointers, sizeof(EXCEPTION_POINTERS));
    ZeroMemory(&ExceptionRecord, sizeof(EXCEPTION_RECORD));
    ZeroMemory(&ContextRecord, sizeof(CONTEXT));

    // Looks like a workaround for some bug in RtlCaptureContext. But no description.
#ifdef _X86_

    __asm {
        mov dword ptr[ContextRecord.Eax], eax
        mov dword ptr[ContextRecord.Ecx], ecx
        mov dword ptr[ContextRecord.Edx], edx
        mov dword ptr[ContextRecord.Ebx], ebx
        mov dword ptr[ContextRecord.Esi], esi
        mov dword ptr[ContextRecord.Edi], edi
        mov word ptr[ContextRecord.SegSs], ss
        mov word ptr[ContextRecord.SegCs], cs
        mov word ptr[ContextRecord.SegDs], ds
        mov word ptr[ContextRecord.SegEs], es
        mov word ptr[ContextRecord.SegFs], fs
        mov word ptr[ContextRecord.SegGs], gs
        pushfd
        pop[ContextRecord.EFlags]
    }

    ContextRecord.ContextFlags = CONTEXT_CONTROL;
#pragma warning(push)
#pragma warning(disable : 4311)
    ContextRecord.Eip = (ULONG)_ReturnAddress();
    ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
#pragma warning(pop)
    ContextRecord.Ebp = *(static_cast<ULONG*>(_AddressOfReturnAddress()) - 1);


#elif defined(_IA64_) || defined(_AMD64_) || defined(_ARM_) || defined(_ARM64_)

    CaptureContext(&ContextRecord);

#else /* defined (_IA64_) || defined (_AMD64_) || defined(_ARM_) || defined(_ARM64_) */

    ZeroMemory(&ContextRecord, sizeof(ContextRecord));

#endif /* defined (_IA64_) || defined (_AMD64_) || defined(_ARM_) || defined(_ARM64_) */

    ExceptionRecord.ExceptionCode = exceptionCode;
    ExceptionRecord.ExceptionAddress = _ReturnAddress();
    ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;

    *exceptionPointers = &ExceptionPointers;
    (*exceptionPointers)->ExceptionRecord = &ExceptionRecord;
    (*exceptionPointers)->ContextRecord = &ContextRecord;
}


class DbgLibrary final
{
public:
    DbgLibrary()
    {
        dbgLibrary = LoadLibraryW(L"dbghelp.dll");
    }

    ~DbgLibrary()
    {
        FreeLibrary(dbgLibrary);
    }

    explicit operator bool() const
    {
        return dbgLibrary != NULL;
    }

    bool WriteMinidump(HANDLE file, EXCEPTION_POINTERS* exceptionPointers) const
    {
        MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
        exceptionInformation.ThreadId = GetCurrentThreadId();
        exceptionInformation.ExceptionPointers = exceptionPointers;
        exceptionInformation.ClientPointers = FALSE;

        MINIDUMP_CALLBACK_INFORMATION callbackInformation;
        callbackInformation.CallbackRoutine = NULL;
        callbackInformation.CallbackParam = NULL;

        typedef BOOL(WINAPI* LPMINIDUMPWRITEDUMP)(HANDLE processHandle, DWORD ProcessId, HANDLE fileHandle,
            MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
            CONST PMINIDUMP_USER_STREAM_INFORMATION UserEncoderParam,
            CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);

        LPMINIDUMPWRITEDUMP pfnMiniDumpWriteDump =
            (LPMINIDUMPWRITEDUMP)GetProcAddress(dbgLibrary, "MiniDumpWriteDump");
        if (NULL == pfnMiniDumpWriteDump)
        {
            return false;
        }

        BOOL isWriteSucceed = pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal,
            &exceptionInformation, NULL, &callbackInformation);
        return isWriteSucceed;
    }

private:
    HMODULE dbgLibrary;
};

inline HANDLE CreateNativeFile(const wchar_t* filePath)
{
    HANDLE file = NULL;
    file = CreateFileW(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    return file;
}

void CreateMiniDump(PEXCEPTION_POINTERS exceptionPointers)
{
    const DbgLibrary dbgLibrary;
    if (dbgLibrary)
    {
        wchar_t FILE_PATH[4096];
        // Write `exceptionPointers` to the minidump file
        StringCbPrintfW(FILE_PATH, sizeof(FILE_PATH), L"%ls\\%ls_%ld.dmp", ".",
            L"minidump", minidumpId++);
        HANDLE hMinidump = CreateNativeFile(FILE_PATH);
        if (hMinidump != INVALID_HANDLE_VALUE)
        {
            dbgLibrary.WriteMinidump(hMinidump, exceptionPointers);
            CloseHandle(hMinidump);
        }
    }
}

LONG WINAPI SehHandler(PEXCEPTION_POINTERS exceptionPointers)
{
    std::cerr << "SehHandler\n";
    CreateMiniDump(exceptionPointers);
    return EXCEPTION_EXECUTE_HANDLER;
}

void SigsegvHandler(int)
{
    std::cerr << "SigsegvHandler\n";
    PEXCEPTION_POINTERS exceptionPointers = static_cast<PEXCEPTION_POINTERS>(_pxcptinfoptrs);

    // Write minidump file
    CreateMiniDump(exceptionPointers);
}

int __cdecl NewHandler(size_t size)
{
    std::cerr << "NewHandler\n";
    // 'new' operator memory allocation exception
    PEXCEPTION_POINTERS exceptionPointers;
    GetExceptionPointers(STATUS_NO_MEMORY, &exceptionPointers);

    CreateMiniDump(exceptionPointers);

    return 0;
}

struct A5 {
    void F()
    {
        while (true)
        {
            int* a = new int[50000000];
        }
    }
};

struct A4 {
    A5 a;
    void F()
    {
        a.F();
    }
};

struct A3 {
    A4 a;
    void F()
    {
        a.F();
    }
};

struct A2 {
    A3 a;
    void F()
    {
        a.F();
    }
};

struct A1 {
    A2 a;
    void F()
    {
        a.F();
    }
};

int main()
{
    SetUnhandledExceptionFilter(SehHandler);
    signal(SIGSEGV, SigsegvHandler);
    _set_new_handler(NewHandler);
    A1().F();
    return 0;
}

這里將調用兩個處理程序:NewHandler 和 SehHandler。 第一個是因為 operator new[] 中的 bad_alloc,第二個是因為未處理的異常。 在這兩個處理程序中,我都創建了帶有崩潰信息的小型轉儲。

  • 新處理程序:
Thread 0 (crashed)
 0  StackWalker_VC2017.exe!_callnewh [new_handler.cpp : 79 + 0x2]
    eip = 0x0040a636   esp = 0x0019fefc   ebp = 0x0019ff08   ebx = 0x00311000
    esi = 0x00401d10   edi = 0x00655368   eax = 0x0042eed0   ecx = 0x00000000
    edx = 0x00655368   efl = 0x00000202
    Found by: given as instruction pointer in context
 1  StackWalker_VC2017.exe!operator new(unsigned int) [new_scalar.cpp : 40 + 0x8]
    eip = 0x00404a05   esp = 0x0019ff10   ebp = 0x0019ff14
    Found by: call frame info
 2  StackWalker_VC2017.exe!A5::F() [main.cpp : 197 + 0xa]
    eip = 0x00401d0a   esp = 0x0019ff1c   ebp = 0x0019ff28
    Found by: call frame info
 3  StackWalker_VC2017.exe!main [main.cpp : 239 + 0x8]
    eip = 0x00402500   esp = 0x0019ff24   ebp = 0x0019ff28
    Found by: call frame info
 4  StackWalker_VC2017.exe!static int __scrt_common_main_seh() [exe_common.inl : 288 + 0x1c]
    eip = 0x00404c5d   esp = 0x0019ff30   ebp = 0x0019ff70
    Found by: call frame info
 5  kernel32.dll + 0x1fa29
    eip = 0x7712fa29   esp = 0x0019ff78   ebp = 0x0019ff80
    Found by: call frame info
 6  ntdll.dll + 0x67a9e
    eip = 0x77c97a9e   esp = 0x0019ff88   ebp = 0x0019ffdc
    Found by: previous frame's frame pointer
 7  ntdll.dll + 0x67a6e
    eip = 0x77c97a6e   esp = 0x0019ffe4   ebp = 0x0019ffec
    Found by: previous frame's frame pointer
  • 處理程序:
Thread 0 (crashed)
 0  KERNELBASE.dll + 0x12b812
    eip = 0x76ddb812   esp = 0x0019fe68   ebp = 0x0019fec4   ebx = 0x19930520
    esi = 0x00645a90   edi = 0x0042c754   eax = 0x0019fe68   ecx = 0x00000003
    edx = 0x00000000   efl = 0x00000212
    Found by: given as instruction pointer in context
 1  StackWalker_VC2017.exe!_CxxThrowException [throw.cpp : 74 + 0x19]
    eip = 0x00405a98   esp = 0x0019fecc   ebp = 0x0019fef4
    Found by: previous frame's frame pointer
 2  StackWalker_VC2017.exe!__scrt_throw_std_bad_alloc() [throw_bad_alloc.cpp : 35 + 0x16]
    eip = 0x0040509c   esp = 0x0019fefc   ebp = 0x0019ff10
    Found by: call frame info
 3  StackWalker_VC2017.exe!main [main.cpp : 239 + 0x8]
    eip = 0x00402500   esp = 0x0019ff24   ebp = 0x0019ff14
    Found by: call frame info with scanning

使用 breakpad minidump_stackwalk 提取堆棧:問題是為什么 SehHandler stacktrace 沒有所有 function 調用? 主要問題是在項目中我使用崩潰處理程序在轉儲中記錄信息。 但是在每次調用 NewHandler 時創建 minidump 並不是不合適的解決方案,因為有時 bad_alloc 可以被修復並在 try/catch 塊中拋出異常,這意味着這是預期的行為。 所以想在unhandled exception handler中處理bad_alloc,這樣肯定會crash。 此外,問題僅發生在發布版本中。

https://developercommunity.visualstudio.com/t/stdbad-alloc-failures-are-undebuggable/542559?viewtype=solutions 中所述,它是 msvc 中的錯誤。 不幸的是,發布版本沒有好的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM