2013-03-12 79 views
2

当发生未处理的异常时,我想打印堆栈跟踪而不是仅仅终止。我已经试过了使用SetUnhandledExceptionFilter做: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; 
} 

此代码,我已经找到了,工作正常。但是没有附加信息,因为无论如何,ExceptionCodeExceptionAddress都打印在系统“事件查看器”中。 如果可以打印完整的堆栈跟踪,那么我可以确定出现异常的确切位置?

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

+1

你总是可以让自己受到http://code.google.com/p/crashrpt/的启发,或者仅仅因为它方便的自由许可而使用它;)...我认为它正是你想要的。行号等等是您加载验尸转储并且拥有完整PDB时出现的顺序。最终用户可能不会有任何东西。 – 0xC0000022L 2013-03-12 22:04:06

+0

@ 0xC0000022L很高兴有没有第三方libriries的解决方案。我现在想要的是打印堆栈跟踪。而当我移植到Linux时,我会抛出一段时间。 – javapowered 2013-03-12 22:05:59

+0

如果你考虑甚至MS的libs作为第三方(它们是必需的,并且需要安装来解释符号,除非你阅读Sven Schreiber's * Undocumented Windows 2000 Secrets *),那么你运气不好。否则,你仍然可以重用这些东西的一部分,并简单地将它链接到你的二进制文件。 BSD许可证只需要归属。只有在内部使用时才需要关注的东西更少。 – 0xC0000022L 2013-03-12 22:08:57

回答

9

这是几年前我写的一些用于Windows的栈走码。这里是它产生的输出类型:

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++异常类型可能会更困难一些(但我不太确定 - 这可能很简单)。

#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; 
}