之前已经问过这个问题,并且有特定于Windows的答案,但没有令人满意的gcc答案.我可以set_terminate()用来设置一个函数,terminate()当抛出未处理的异常时,该函数将被调用(代替).我知道如何使用backtrace库从程序中的给定点生成堆栈跟踪.但是,当我的终止替换被调用时,这将无济于事,因为此时堆栈已被解除.
然而,如果我只是允许程序abort(),它将产生一个核心转储,其中包含从抛出异常的点开始的完整堆栈信息.所以信息就在那里 - 但是有没有一种编程方式来获取它,例如它可以被记录,而不是必须检查核心文件?
c++ callstack exception-handling stack-trace unhandled-exception
今天,在我的C++多平台代码中,我对每个函数都进行了尝试.在每个catch块中,我将当前函数的名称添加到异常并再次抛出,以便在最上面的catch块(我最终打印异常的详细信息)中,我有完整的调用堆栈,这有助于我跟踪异常的原因.
这是一个好习惯,还是有更好的方法来获取异常的调用堆栈?
我正在为Android(仅限ARM)编写这个,但我相信通用Linux的原理也是如此.
我正在尝试从信号处理程序中捕获堆栈跟踪,以便我可以在应用程序崩溃时记录它.这就是我想出来的用法<unwind.h>.
初始化:
struct sigaction signalhandlerDescriptor;
memset(&signalhandlerDescriptor, 0, sizeof(signalhandlerDescriptor));
signalhandlerDescriptor.sa_flags = SA_SIGINFO;
signalhandlerDescriptor._u._sa_sigaction = signalHandler;
sigaction(SIGSEGV, &signalhandlerDescriptor, 0);
Run Code Online (Sandbox Code Playgroud)
代码本身:
struct BacktraceState
{
void** current;
void** end;
void* pc;
};
inline _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
BacktraceState* state = static_cast<BacktraceState*>(arg);
state->pc = (void*)_Unwind_GetIP(context);
if (state->pc)
{
if (state->current == state->end)
return _URC_END_OF_STACK;
else
*state->current++ = reinterpret_cast<void*>(state->pc);
}
return _URC_NO_REASON;
}
inline size_t captureBacktrace(void** addrs, size_t max, unsigned long pc)
{
BacktraceState state = {addrs, addrs + max, …Run Code Online (Sandbox Code Playgroud) 我希望堆栈跟踪不仅适用于我的例外,也适用于任何后代 std::exception
据我所知,由于堆栈展开(展开),捕获异常时堆栈跟踪完全丢失.
所以我看到抓住它的唯一方法是在std::exception构造函数调用的地方注入代码保存上下文信息(堆栈跟踪).我对吗?
如果是这种情况,请告诉我如何在C++中完成代码注入(如果可以).您的方法可能不完全安全,因为我只需要我的应用程序的调试版本.可能是我需要使用汇编程序?
我只对GCC的解决方案感兴趣.它可以使用c ++ 0x功能
我想知道如何在Ubuntu中生成核心转储文件.我使用的是Ubuntu 8.04.1和gcc编译器4.2.3.我编写了一个简单的C程序来生成核心转储.我已经将程序编译为 - gcc -g badpointer.c.当我运行程序时,它会给出分段错误,但不会生成核心转储.我还需要做些什么才能生成核心转储文件?
我希望我的异常处理程序和调试函数能够打印调用堆栈回溯,基本上就像glibc中的backtrace()库函数一样.不幸的是,我的C库(Newlib)没有提供这样的调用.
我有这样的事情:
#include <unwind.h> // GCC's internal unwinder, part of libgcc
_Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *d)
{
int *depth = (int*)d;
printf("\t#%d: program counter at %08x\n", *depth, _Unwind_GetIP(ctx));
(*depth)++;
return _URC_NO_REASON;
}
void print_backtrace_here()
{
int depth = 0;
_Unwind_Backtrace(&trace_fcn, &depth);
}
Run Code Online (Sandbox Code Playgroud)
这基本上有效,但结果痕迹并不总是完整的.例如,如果我这样做
int func3() { print_backtrace_here(); return 0; }
int func2() { return func3(); }
int func1() { return func2(); }
int main() { return func1(); }Run Code Online (Sandbox Code Playgroud)
backtrace只显示func3()和main().(这是一个玩具示例,但我已经检查了反汇编并确认这些功能全部都在这里,并没有优化或内联.)
更新:我在旧的ARM7系统上尝试了这个回溯代码但是使用相同(或至少,尽可能等同)的编译器选项和链接描述文件,它打印出正确的完整回溯(即func1和func2不会丢失)和实际上它甚至会将过去的主要内容转移到启动初始化代码中.所以可能问题不在于链接器脚本或编译器选项.(另外,通过反汇编确认在此ARM7测试中也没有使用帧指针).
代码使用-fomit-frame-pointer编译,但我的平台(裸机ARM Cortex M3)定义了一个不使用帧指针的ABI.(该系统的先前版本在ARM7上使用旧的APCS ABI,具有强制堆栈帧和帧指针,以及类似于此处的回溯,它完美地工作).
整个系统使用-fexception进行编译,这确保了_Unwind使用的必要元数据包含在ELF文件中.(_Unwind是为我认为的异常处理而设计的).
所以,我的问题是: 在使用GCC的嵌入式系统中,是否存在"标准",可接受的可靠回溯方式? …
我刚刚开始使用c ++异常并想要正确使用它.我想到的是在捕获异常时生成某种回溯信息.最初我有类似于C++异常的Call-stack的想法,但最终发现它并不是很好.
我还阅读了如何在我的gcc C++应用程序崩溃时生成堆栈跟踪但不想为当前项目添加更多复杂性.因为,我只需要在调试模式下进行回溯,我希望我可以将gdb用于此目的.
我的策略是在catch块中插入断点,然后通过调用堆栈来准确找出为什么首先抛出异常(或者是什么导致它)?不幸的是,我似乎无法做到这一点,因为当gdb到达断点时,它会清除调用堆栈,我只能看到main(这就是我捕获的地方).这应该发生还是我在这里做错了什么?
编辑: 我只想总结一下其他人的方法:
第一种方法(由paulsm4提供).设置一个捕获点,catch throw用于捕捉或catch catch抓住捕获!然后打电话backtrace
第二种方法(由aschepler设置)__cxa_throw然后设置断点backtrace
第三种方法(在Qt Creator中 - 如果你碰巧使用的话)你可以在throw或catch上轻松设置断点!
Edit_2:使用Qt Creator调试器,似乎设置断点__cxa_begin_catch也相当于catch catch:)
有没有人知道发现智能指针引起的内存泄漏的"技术"?我目前正在开发一个用C++编写的大型项目,该项目大量使用带引用计数的智能指针.显然我们有一些由智能指针引起的内存泄漏,它们仍然在代码中的某处被引用,因此它们的内存不会被释放.很难找到带有"不必要"引用的代码行,导致相应的对象不被释放(尽管它不再使用).
我在网上找到了一些建议,建议收集参考计数器的递增/递减操作的调用堆栈.这给了我一个很好的提示,哪一段代码导致参考计数器增加或减少.
但我需要的是某种算法将相应的"增加/减少调用堆栈"组合在一起.在删除这些调用堆栈之后,我希望(至少)剩下一个"增加调用堆栈",它向我显示带有"不必要"引用的代码段,这导致相应的对象不被释放.现在修复泄漏没什么大不了的!
但有人想知道进行分组的"算法"吗?
开发在Windows XP下进行.
(我希望有人理解,我试图解释......)
EDIt:我说的是循环引用造成的泄漏.
我想write在信号处理程序内使用(或任何异步安全函数)将数字打印到日志或终端中.我宁愿不使用缓冲I/O.
是否有一种简单而推荐的方法可以做到这一点?
例如代替printf,我更喜欢write(或任何asyn安全功能).
void signal_handler(int sig)
{
pid_t pid;
int stat;
int old_errno = errno;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
errno = old_errno;
return;
}
Run Code Online (Sandbox Code Playgroud)
打印字符串很容易.代替printf上面我可以使用(不打印pid):
write(STDOUT_FILENO, "child terminated", 16);
Run Code Online (Sandbox Code Playgroud) 问题
如果我从主机编译并运行,我能够设置并达到断点,但如果我从docker容器中执行此操作,gdb不会触及已设置的断点.
重现的步骤(所有片段都准备好复制粘贴)
创建一个docker文件:
cat << EOF > Dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get install -y build-essential gdb
EOF
Run Code Online (Sandbox Code Playgroud)
构建映像并在其中运行交互式会话:
docker build -t gdb_problem_testing . && docker run --rm -it gdb_problem_testing bash
Run Code Online (Sandbox Code Playgroud)
从容器内部创建小main.cpp,编译并运行gdb:
cat <<EOF > main.cpp && g++ -g main.cpp && gdb -ex 'break 5' -ex 'run' ./a.out
#include <iostream>
int main(int argc, const char *argv[])
{
std::cout << "hi\n";
return 0;
}
EOF
Run Code Online (Sandbox Code Playgroud)
观察gdb输出:
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
[Skipped gdb greeting]
Reading symbols from ./a.out...done. …Run Code Online (Sandbox Code Playgroud) c++ ×7
c ×3
arm ×2
callstack ×2
exception ×2
gcc ×2
gdb ×2
linux ×2
android-ndk ×1
async-safe ×1
backtrace ×1
c++11 ×1
coredump ×1
debugging ×1
docker ×1
memory-leaks ×1
newlib ×1
permissions ×1
pointers ×1
signals ×1
stack-trace ×1
try-catch ×1