在SetUnhandledExceptionFilter的处理程序如何打印stacktrace? [英] in the handler of SetUnhandledExceptionFilter how to print a stacktrace?

查看:415
本文介绍了在SetUnhandledExceptionFilter的处理程序如何打印stacktrace?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当未处理的异常发生时,我想打印一个stacktrace而不是刚刚终止。我试图这样做使用 SetUnhandledExceptionFilter

When unhandled exception occured i want to print a stacktrace instead of just terminating. I've tried to do that using SetUnhandledExceptionFilter:

SetUnhandledExceptionFilter(UnhandledException);
    ...
LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo)  
{  
    printf("An exception occurred which wasn't handled!\nCode: 0x%08X\nAddress: 0x%08X",   
        exceptionInfo->ExceptionRecord->ExceptionCode,  
        exceptionInfo->ExceptionRecord->ExceptionAddress);
    return EXCEPTION_EXECUTE_HANDLER;  
}

这个代码,我发现,工作正常。但是没有其他信息,因为 ExceptionCode ExceptionAddress 无论如何都打印在系统事件查看器中。
如果可以打印一个完整的堆栈跟踪,所以我可以确定发生异常的确切点。

This code, i've found, works fine. However there are no addition information because ExceptionCode and ExceptionAddress are printed in system "Event Viewer" anyway. If it is possible to print a full stack trace so I can determine the exact point where exception occured?

我发现这个代码 https://code.google.com/p /m0d-s0beit-sa/source/browse/src/main.cpp?r=9ceb4fec21d647b169c72851d7882bef2b9c5a8a 它部分解决了我的问题。仅打印发生异常的方法。但是不打印异常类型和行号。

I've found this code https://code.google.com/p/m0d-s0beit-sa/source/browse/src/main.cpp?r=9ceb4fec21d647b169c72851d7882bef2b9c5a8a It partly solves my problem. Only method where exception occured is printed. But type of exception and line number is not printed.

推荐答案

这里是几年前写的一些Windows的stack-walk代码。这是它产生的输出类型:

Here's some stack-walk code for Windows I wrote some years ago. Here's the kind of output it produces:

Walking stack.
0       DebugBreak
1       ThreadFunc2     e:\c\source\stackwalk2a.cpp(72)
2       ThreadFunc1     e:\c\source\stackwalk2a.cpp(79)
3       TargetThread    e:\c\source\stackwalk2a.cpp(86)
4       BaseThreadInitThunk
5       RtlUserThreadStart
End of stack walk.

缺少的主要是异常类型。如果你正在谈论一个本地结构化/向量异常,我很确定应该可以检索。检索C ++异常的类型可能有点困难(但我不确定 - 这可能很容易)。

The main thing that's missing is anything about the exception type. If you're talking about a native structured/vectored exception, I'm pretty sure that should be retrievable too. Retrieving types of C++ exceptions might be a little more difficult (but I'm not really sure -- it might be pretty easy).

#include <windows.h>
#include <winnt.h>

#include <string>
#include <vector>
#include <Psapi.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <iterator>

#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")

// Some versions of imagehlp.dll lack the proper packing directives themselves
// so we need to do it.
#pragma pack( push, before_imagehlp, 8 )
#include <imagehlp.h>
#pragma pack( pop, before_imagehlp )

struct module_data {
    std::string image_name;
    std::string module_name;
    void *base_address;
    DWORD load_size;
};
typedef std::vector<module_data> ModuleList;

HANDLE thread_ready;

bool show_stack(std::ostream &, HANDLE hThread, CONTEXT& c);
DWORD __stdcall TargetThread( void *arg );
void ThreadFunc1();
void ThreadFunc2();
DWORD Filter( EXCEPTION_POINTERS *ep );
void *load_modules_symbols( HANDLE hProcess, DWORD pid );

int main( void ) {
    DWORD thread_id;

    thread_ready = CreateEvent( NULL, false, false, NULL );

    HANDLE thread = CreateThread( NULL, 0, TargetThread, NULL, 0, &thread_id );

    WaitForSingleObject( thread_ready, INFINITE );
    CloseHandle(thread_ready);
    return 0;
}

// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack dump at the
// earliest opportunity, to avoid the interesting stack-frames being gone
// by the time you do the dump.
DWORD Filter(EXCEPTION_POINTERS *ep) {
    HANDLE thread;

    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
        GetCurrentProcess(), &thread, 0, false, DUPLICATE_SAME_ACCESS);
    std::cout << "Walking stack.";
    show_stack(std::cout, thread, *(ep->ContextRecord));
    std::cout << "\nEnd of stack walk.\n";
    CloseHandle(thread);

    return EXCEPTION_EXECUTE_HANDLER;
}

void ThreadFunc2() {
    __try { DebugBreak(); }
    __except (Filter(GetExceptionInformation())) {  }
    SetEvent(thread_ready);
}

void ThreadFunc1(void (*f)()) {
    f();
}

// We'll do a few levels of calls from our thread function so 
//     there's something on the stack to walk...
//
DWORD __stdcall TargetThread(void *) {
    ThreadFunc1(ThreadFunc2);
    return 0;
}

class SymHandler { 
    HANDLE p;
public:
    SymHandler(HANDLE process, char const *path=NULL, bool intrude = false) : p(process) { 
        if (!SymInitialize(p, path, intrude)) 
            throw(std::logic_error("Unable to initialize symbol handler"));
    }
    ~SymHandler() { SymCleanup(p); }
};

#ifdef _M_X64
STACKFRAME64 init_stack_frame(CONTEXT c) {
    STACKFRAME64 s;
    s.AddrPC.Offset = c.Rip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Rsp;
    s.AddrStack.Mode = AddrModeFlat;    
    s.AddrFrame.Offset = c.Rbp;
    s.AddrFrame.Mode = AddrModeFlat;
    return s;
}
#else
STACKFRAME64 init_stack_frame(CONTEXT c) {
    STACKFRAME64 s;
    s.AddrPC.Offset = c.Eip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Esp;
    s.AddrStack.Mode = AddrModeFlat;    
    s.AddrFrame.Offset = c.Ebp;
    s.AddrFrame.Mode = AddrModeFlat;
    return s;
}
#endif

void sym_options(DWORD add, DWORD remove=0) {
    DWORD symOptions = SymGetOptions();
    symOptions |= add;
    symOptions &= ~remove;
    SymSetOptions(symOptions);
}

class symbol { 
    typedef IMAGEHLP_SYMBOL64 sym_type;
    sym_type *sym;
    static const int max_name_len = 1024;
public:
    symbol(HANDLE process, DWORD64 address) : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
        memset(sym, '\0', sizeof(*sym) + max_name_len);
        sym->SizeOfStruct = sizeof(*sym);
        sym->MaxNameLength = max_name_len;
        DWORD64 displacement;

        if (!SymGetSymFromAddr64(process, address, &displacement, sym))
            throw(std::logic_error("Bad symbol"));
    }

    std::string name() { return std::string(sym->Name); }
    std::string undecorated_name() { 
        std::vector<char> und_name(max_name_len);
        UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
        return std::string(&und_name[0], strlen(&und_name[0]));
    }
};

bool show_stack(std::ostream &os, HANDLE hThread, CONTEXT& c) {
    HANDLE process = GetCurrentProcess();
    int frame_number=0;
    DWORD offset_from_symbol=0;
    IMAGEHLP_LINE64 line = {0};

    SymHandler handler(process);

    sym_options(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);

    void *base = load_modules_symbols(process, GetCurrentProcessId());

    STACKFRAME64 s = init_stack_frame(c);

    line.SizeOfStruct = sizeof line;

    IMAGE_NT_HEADERS *h = ImageNtHeader(base);
    DWORD image_type = h->FileHeader.Machine;

    do {
        if (!StackWalk64(image_type, process, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
            return false;

        os << std::setw(3) << "\n" << frame_number << "\t";
        if ( s.AddrPC.Offset != 0 ) {
            std::cout << symbol(process, s.AddrPC.Offset).undecorated_name();

            if (SymGetLineFromAddr64( process, s.AddrPC.Offset, &offset_from_symbol, &line ) ) 
                    os << "\t" << line.FileName << "(" << line.LineNumber << ")";
        }
        else
            os << "(No Symbols: PC == 0)";
        ++frame_number;
    } while (s.AddrReturn.Offset != 0);
    return true;
}

class get_mod_info {
    HANDLE process;
    static const int buffer_length = 4096;
public:
    get_mod_info(HANDLE h) : process(h) {}

    module_data operator()(HMODULE module) { 
        module_data ret;
        char temp[buffer_length];
        MODULEINFO mi;

        GetModuleInformation(process, module, &mi, sizeof(mi));
        ret.base_address = mi.lpBaseOfDll;
        ret.load_size = mi.SizeOfImage;

        GetModuleFileNameEx(process, module, temp, sizeof(temp));
        ret.image_name = temp;
        GetModuleBaseName(process, module, temp, sizeof(temp));
        ret.module_name = temp;
        std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
        std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
        SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
        return ret;
    }
};

void *load_modules_symbols(HANDLE process, DWORD pid) {
    ModuleList modules;

    DWORD cbNeeded;
    std::vector<HMODULE> module_handles(1);

    EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
    module_handles.resize(cbNeeded/sizeof(HMODULE));
    EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);

    std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
    return modules[0].base_address;
}

这篇关于在SetUnhandledExceptionFilter的处理程序如何打印stacktrace?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆