如何使用CaptureStackBackTrace来捕获异常堆栈,而不是调用堆栈
How can you use CaptureStackBackTrace to capture the exception stack, not the calling stack?
我标记了以下代码:
#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include <dbghelp.h>
using namespace std;
#define TRACE_MAX_STACK_FRAMES 1024
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
int printStackTrace()
{
void *stack[TRACE_MAX_STACK_FRAMES];
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
WORD numberOfFrames = CaptureStackBackTrace(0, TRACE_MAX_STACK_FRAMES, stack, NULL);
char buf[sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)];
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buf;
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
DWORD displacement;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (int i = 0; i < numberOfFrames; i++)
{
DWORD64 address = (DWORD64)(stack[i]);
SymFromAddr(process, address, NULL, symbol);
if (SymGetLineFromAddr64(process, address, &displacement, &line))
{
printf("tat %s in %s: line: %lu: address: 0x%0Xn", symbol->Name, line.FileName, line.LineNumber, symbol->Address);
}
else
{
printf("tSymGetLineFromAddr64 returned error code %lu.n", GetLastError());
printf("tat %s, address 0x%0X.n", symbol->Name, symbol->Address);
}
}
return 0;
}
void function2()
{
int a = 0;
int b = 0;
throw new exception;
}
void function1()
{
int a = 0;
function2();
}
void function0()
{
function1();
}
static void threadFunction(void *param)
{
try
{
function0();
}
catch (...)
{
printStackTrace();
}
}
int _tmain(int argc, _TCHAR* argv[])
{
_beginthread(threadFunction, 0, NULL);
printf("Press any key to exit.n");
cin.get();
return 0;
}
它所做的是记录堆栈跟踪,但问题是它记录的堆栈跟踪没有给我想要的行号。我想让它记录抛出异常的地方的行号,在调用堆栈上下,有点像c#。但它现在实际做的是输出以下内容:
at printStackTrace in c:users<yourusername>documentsvisual studio 2013pr
ojectsstacktracingstacktracingstacktracing.cpp: line: 17: address: 0x10485C0
at threadFunction in c:users<yourusername>documentsvisual studio 2013pro
jectsstacktracingstacktracingstacktracing.cpp: line: 68: address: 0x10457C0
SymGetLineFromAddr64 returned error code 487.
at beginthread, address 0xF9431E0.
SymGetLineFromAddr64 returned error code 487.
at endthread, address 0xF9433E0.
SymGetLineFromAddr64 returned error code 487.
at BaseThreadInitThunk, address 0x7590494F.
SymGetLineFromAddr64 returned error code 487.
at RtlInitializeExceptionChain, address 0x7713986A.
SymGetLineFromAddr64 returned error code 487.
at RtlInitializeExceptionChain, address 0x7713986A.
我面临的问题,再一次,是line: 68
在这个跟踪对应于调用方法printStackTrace();
的行,而我希望它给我第45行,对应于抛出异常的行:throw new exception;
,然后继续向上堆栈。
我如何才能实现这种行为,并打破这个线程,当它抛出这个异常,以获得适当的堆栈跟踪?
PS上面的代码是在Windows 8.1 x64机器上使用启用unicode的msvc++运行的控制台应用程序,该应用程序作为Win32应用程序在Debug模式下运行。
在Windows上,未处理的c++异常会自动生成SEH异常。SEH __except块允许附加一个过滤器,该过滤器接受_EXCEPTION_POINTERS结构体作为参数,该结构体包含在抛出异常时指向处理器上下文记录的指针。将此指针传递给StackWalk64函数,给出异常时刻的堆栈跟踪。因此,这个问题可以通过使用seh风格的异常处理而不是c++风格来解决。
示例代码:
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <tchar.h>
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"
using namespace std;
const int MaxNameLen = 256;
#pragma comment(lib,"Dbghelp.lib")
void printStack( CONTEXT* ctx ) //Prints stack trace based on context record
{
BOOL result;
HANDLE process;
HANDLE thread;
HMODULE hModule;
STACKFRAME64 stack;
ULONG frame;
DWORD64 displacement;
DWORD disp;
IMAGEHLP_LINE64 *line;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
char name[MaxNameLen];
char module[MaxNameLen];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
// On x64, StackWalk64 modifies the context record, that could
// cause crashes, so we create a copy to prevent it
CONTEXT ctxCopy;
memcpy(&ctxCopy, ctx, sizeof(CONTEXT));
memset( &stack, 0, sizeof( STACKFRAME64 ) );
process = GetCurrentProcess();
thread = GetCurrentThread();
displacement = 0;
#if !defined(_M_AMD64)
stack.AddrPC.Offset = (*ctx).Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = (*ctx).Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = (*ctx).Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
#endif
SymInitialize( process, NULL, TRUE ); //load symbols
for( frame = 0; ; frame++ )
{
//get next call from stack
result = StackWalk64
(
#if defined(_M_AMD64)
IMAGE_FILE_MACHINE_AMD64
#else
IMAGE_FILE_MACHINE_I386
#endif
,
process,
thread,
&stack,
&ctxCopy,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
);
if( !result ) break;
//get symbol name for address
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
SymFromAddr(process, ( ULONG64 )stack.AddrPC.Offset, &displacement, pSymbol);
line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
//try to get line
if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line))
{
printf("tat %s in %s: line: %lu: address: 0x%0Xn", pSymbol->Name, line->FileName, line->LineNumber, pSymbol->Address);
}
else
{
//failed to get line
printf("tat %s, address 0x%0X.n", pSymbol->Name, pSymbol->Address);
hModule = NULL;
lstrcpyA(module,"");
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)(stack.AddrPC.Offset), &hModule);
//at least print module name
if(hModule != NULL)GetModuleFileNameA(hModule,module,MaxNameLen);
printf ("in %sn",module);
}
free(line);
line = NULL;
}
}
//******************************************************************************
void function2()
{
int a = 0;
int b = 0;
throw exception();
}
void function1()
{
int a = 0;
function2();
}
void function0()
{
function1();
}
int seh_filter(_EXCEPTION_POINTERS* ex)
{
printf("*** Exception 0x%x occured ***nn",ex->ExceptionRecord->ExceptionCode);
printStack(ex->ContextRecord);
return EXCEPTION_EXECUTE_HANDLER;
}
static void threadFunction(void *param)
{
__try
{
function0();
}
__except(seh_filter(GetExceptionInformation()))
{
printf("Exception n");
}
}
int _tmain(int argc, _TCHAR* argv[])
{
_beginthread(threadFunction, 0, NULL);
printf("Press any key to exit.n");
cin.get();
return 0;
}
示例输出(前两项是噪声,但其余部分正确反映了导致异常的函数):
*** Exception 0xe06d7363 occured ***
at RaiseException, address 0xFD3F9E20.
in C:Windowssystem32KERNELBASE.dll
at CxxThrowException, address 0xDBB5A520.
in C:Windowssystem32MSVCR110D.dll
at function2 in c:workprojectstesttest.cpp: line: 146: address: 0x3F9C6C00
at function1 in c:workprojectstesttest.cpp: line: 153: address: 0x3F9C6CB0
at function0 in c:workprojectstesttest.cpp: line: 158: address: 0x3F9C6CE0
at threadFunction in c:workprojectstesttest.cpp: line: 174: address: 0x3F9C6D70
at beginthread, address 0xDBA66C60.
in C:Windowssystem32MSVCR110D.dll
at endthread, address 0xDBA66E90.
in C:Windowssystem32MSVCR110D.dll
at BaseThreadInitThunk, address 0x773C6520.
in C:Windowssystem32kernel32.dll
at RtlUserThreadStart, address 0x775FC520.
in C:WindowsSYSTEM32ntdll.dll
另一个选择是创建自定义异常类,它捕获构造函数中的上下文,并使用它(或派生类)抛出异常:
class MyException{
public:
CONTEXT Context;
MyException(){
RtlCaptureContext(&Context);
}
};
void function2()
{
throw MyException();
}
//...
try
{
function0();
}
catch (MyException& e)
{
printf("Exception n");
printStack(&e.Context);
}
如果您想捕获代码抛出异常点的堆栈回溯,您必须在异常对象的actor中捕获堆栈回溯,并将其存储在异常对象中。因此,调用CaptureStackBackTrace()的部分应该移动到异常对象的构造函数中,该构造函数还应该提供方法,以获取它作为地址向量或符号向量。这正是Java中的Throwable和c#中的Exception的操作方式。
最后,请不要写:
throw new exception;
在c++中,就像在c#或Java中一样。这是一种极好的方法,既会产生内存泄漏,又无法按类型捕获异常(因为您正在抛出指向这些类型的指针)。而使用:
throw exception();
我知道这是一个古老的问题,但人们(包括我自己)仍然在寻找它。
你想念下面的电话吗?syminialize (process, NULL, TRUE);SymSetOptions (SYMOPT_LOAD_LINES);
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- 为什么调用堆栈数组会导致内存泄漏
- 是否可以检查悬挂光纤的调用堆栈?
- MSVC __debugbreak() 与 openGL 错误回调一起使用时不会产生调用堆栈
- 了解使用堆栈实现队列的递归调用机制
- C++析构函数调用两次,堆栈分配的复合对象
- 以下代码如何工作以每次为唯一调用堆栈唯一实例化模板函数?
- OpenCV 3 Visual Studio 2017 调试,调用堆栈没有.pdb文件
- C/C++中全局调用堆栈的基础
- 是否可以访问代码中的调用堆栈?
- 使用在堆栈上创建的对象调用虚拟函数
- 为什么析构函数无休止地调用自己(导致堆栈溢出)?
- 调用堆栈显示 SIGBUS,这意味着什么
- 堆栈展开如何与析构函数调用有关?
- 打开C++故障转储不会在调用堆栈中显示正确的行
- 防止 CRTP 特征码在"pure virtual"调用中堆栈溢出
- 将参数推送到调用堆栈 (C++) 的可移植方法
- 从基类堆栈调用派生类实例
- 堆栈调用析构函数,即使遵循三规则
- 用于生成函数以从运行时堆栈调用带参数的函数的模板