C++ 在SetUnhandledExceptionFilter的处理程序中,如何打印stacktrace?
当发生未处理的异常时,我希望打印stacktrace,而不仅仅是终止。我已尝试使用C++ 在SetUnhandledExceptionFilter的处理程序中,如何打印stacktrace?,c++,winapi,C++,Winapi,当发生未处理的异常时,我希望打印stacktrace,而不仅仅是终止。我已尝试使用SetUnhandledExceptionFilter来完成此操作: SetUnhandledExceptionFilter(UnhandledException); ... LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo) { printf("An exception occurred which wasn
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
,因此没有添加信息。
是否可以打印完整的堆栈跟踪,以便确定异常发生的确切点
我发现这段代码部分解决了我的问题。仅打印发生异常的方法。但是异常类型和行号并没有打印出来。这里是我几年前为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++异常类型可能会有点困难(但我不确定——可能很简单)。
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#pragma注释(lib,“psapi.lib”)
#pragma注释(lib,“dbghelp.lib”)
//imagehlp.dll的某些版本本身缺乏正确的打包指令
//所以我们必须这样做。
#pragma包(推送,在图像HLP之前,8)
#包括
#pragma包(pop,在图像HLP之前)
结构模块数据{
std::字符串图像名称;
std::字符串模块名称;
void*基址;
DWORD负载大小;
};
typedef std::向量模块列表;
准备好手柄;
boolshow_堆栈(std::ostream&,handlehthread,CONTEXT&c);
DWORD uu stdcall TargetThread(void*arg);
void ThreadFunc1();
void ThreadFunc2();
DWORD过滤器(异常指针*ep);
void*加载模块符号(HANDLE hProcess,DWORD pid);
内部主(空){
德沃德螺纹;
thread_ready=CreateEvent(NULL,false,false,NULL);
HANDLE thread=CreateThread(NULL,0,TargetThread,NULL,0,&thread\u id);
WaitForSingleObject(线程就绪,无限);
闭合手柄(螺纹准备就绪);
返回0;
}
//如果使用C++异常处理:安装翻译器函数
//使用set_se_translator()。在该函数的上下文中(但*不是*
//之后),您可以执行堆栈转储,也可以保存上下文
//记录为本地副本。请注意,您必须在
//最早的机会,以避免有趣的堆栈帧消失
//等你倒垃圾的时候。
DWORD筛选器(异常指针*ep){
手柄螺纹;
DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),
GetCurrentProcess(),&线程,0,false,重复(相同访问);
std::cout ContextRecord);
标准::cout SizeOfStruct=sizeof(*sym);
sym->MaxNameLength=最大名称长度;
DWORD64位移;
if(!SymGetSymFromAddr64(进程、地址和置换,sym))
抛出(标准::逻辑错误(“坏符号”);
}
std::string name(){返回std::string(sym->name);}
std::字符串未修饰的_name(){
标准::矢量和名称(最大名称);
取消装饰符号名称(符号->名称,&und\u名称[0],最大名称,取消名称完成);
返回std::string(&und_name[0],strlen(&und_name[0]);
}
};
boolshow_堆栈(std::ostream&os、handlehthread、CONTEXT&c){
HANDLE process=GetCurrentProcess();
int frame_number=0;
从符号开始的双字偏移量=0;
IMAGEHLP_LINE64 line={0};
SymHandler(进程);
sym_选项(SYMOPT_载重线| SYMOPT_UNDDNAME);
void*base=加载模块符号(进程,GetCurrentProcessId());
STACKFRAME64 s=初始堆栈帧(c);
line.SizeOfStruct=行的大小;
图像标题*h=图像标题(基本);
DWORD image_type=h->FileHeader.Machine;
做{
if(!StackWalk64(映像类型、进程、hThread、&s、&c、NULL、SymFunctionTableAccess64、SymGetModuleBase64、NULL))
返回false;
你可以随时让自己从操作系统中得到启发,或者仅仅为了它方便的自由许可而使用它;)…我认为它正是你想要的。行号等是当你加载一个事后转储并拥有完整的PDB时附带的东西。这可能是最终用户无论如何都不会拥有的。@0xC000002L没有第三部分的解决方案很好我现在想要的是打印一个堆栈跟踪。当我移植到Linux.if时,我会把它扔掉一段时间,你甚至认为MS的LIBs是第三方的(它们是必需的,需要安装来解释这些符号,除非你阅读Sven Schreiber的无文件的Windows 2000秘密)。你运气不好。否则你仍然可以重用这些东西的一部分,只需将其链接到你的二进制文件中。BSD许可证只需要归属。当它仅用于内部使用时,你就更不必关心它了。
#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;
}