我正在尝试研究如何在Mac OS X上的C++应用程序中存储然后打印当前堆栈.主要问题似乎是在主要可执行文件中给出一个地址时让dladdr返回正确的符号.我怀疑这个问题实际上是一个编译选项,但我不确定.
我已经尝试过来自Darwin/Leopard的回溯代码,但它调用了dladdr并且与我自己的代码调用dladdr有相同的问题.
原帖:目前我用这段代码捕获堆栈:
int BackTrace(Addr *buffer, int max_frames)
{
void **frame = (void **)__builtin_frame_address(0);
void **bp = ( void **)(*frame);
void *ip = frame[1];
int i;
for ( i = 0; bp && ip && i < max_frames; i++ )
{
*(buffer++) = ip;
ip = bp[1];
bp = (void**)(bp[0]);
}
return i;
}
Run Code Online (Sandbox Code Playgroud)
这似乎工作正常.然后打印堆栈我正在使用这样的dladdr:
Dl_info dli;
if (dladdr(Ip, &dli))
{
ptrdiff_t offset;
int c = 0;
if (dli.dli_fname && dli.dli_fbase)
{
offset = (ptrdiff_t)Ip - (ptrdiff_t)dli.dli_fbase;
c …Run Code Online (Sandbox Code Playgroud) 我知道这个话题已经覆盖生厌这里,并在互联网上其他地方-但希望这个问题很简单,因为我试图让我的头周围装配...
因此,如果我理解正确,ebp(基指针)将指向堆栈的顶部,并且esp(堆栈指针)将指向底部 - 因为堆栈向下增长.因此,esp指向"当前位置".因此,在函数调用中,一旦将ebp保存在堆栈上,就会为函数插入一个新的堆栈帧.因此,对于下图,如果您从N-3开始,您将通过函数调用转到N-2.但是当你在N-2时 - 你的ebp == 25和esp == 24(至少最初,在任何数据被放入堆栈之前)?
这是正确的还是我在这里切线?
谢谢!
我正在使用ansi C调试应用程序,这是一个多线程程序.
有时,在主线程中会导致SIGSEGV故障.
(gdb) backtrace full
#0 0x0000000000000000 in ?? ()
No symbol table info available.
#1 0x0000000000000000 in ?? ()
No symbol table info available.
(gdb) info registers
rax 0x1 1
rbx 0x0 0
rcx 0x0 0
rdx 0x2 2
rsi 0x458e7aa0 1166965408
rdi 0x0 0
rbp 0x0 0x0
rsp 0x458e7b60 0x458e7b60
r8 0x458e7b20 1166965536
r9 0x0 0
r10 0x0 0
r11 0x206 518
r12 0x2aaaac400e70 46912522686064
r13 0x2aaaac514090 46912523813008
r14 0x1 1
r15 0x18505f10 407920400
rip 0x0 0 …Run Code Online (Sandbox Code Playgroud) 由于以下原因,来自核心文件的回溯信息剪切了有用的信息:
Backtrace已停止:没有足够的寄存器或内存可用于进一步展开。
为什么会出现此消息,我可以对此做些什么?
在手册页中,backtrace()Linux上的函数说:
请注意,"静态"函数的名称不会公开,并且在回溯中不可用.
然而,启用调试符号(-g),程序等addr2line,并gdb仍然可以得到的静态函数的名称.有没有办法从进程内部以编程方式获取静态函数的名称?
在我的应用程序中,我有设置信号处理程序来捕获Segfaults,并打印bactraces.进程启动时,我的应用程序会加载一些插件库.
如果我的应用程序崩溃了一个段错误,由于主可执行二进制文件中的错误,我可以用以下内容分析回溯:
addr2line -Cif -e ./myapplication 0x4...
Run Code Online (Sandbox Code Playgroud)
它准确地显示了函数和source_file:line_no
但是,如何分析是否由于插件中的错误而发生崩溃,如下面的回溯?
/opt/myapplication(_Z7sigsegvv+0x15)[0x504245]
/lib64/libpthread.so.0[0x3f1c40f500]
/opt/myapplication/modules/myplugin.so(_ZN11ICAPSection7processEP12CONNECTION_TP7Filebufi+0x6af)[0x7f5588fe4bbf]
/opt/myapplication/modules/myplugin.so(_Z11myplugin_reqmodP12CONNECTION_TP7Filebuf+0x68)[0x7f5588fe51e8]
/opt/myapplication(_ZN10Processors7ExecuteEiP12CONNECTION_TP7Filebuf+0x5b)[0x4e584b]
/opt/myapplication(_Z15process_requestP12CONNECTION_TP7Filebuf+0x462)[0x4efa92]
/opt/myapplication(_Z14handle_requestP12CONNECTION_T+0x1c6d)[0x4d4ded]
/opt/myapplication(_Z13process_entryP12CONNECTION_T+0x240)[0x4d79c0]
/lib64/libpthread.so.0[0x3f1c407851]
/lib64/libc.so.6(clone+0x6d)[0x3f1bce890d]
Run Code Online (Sandbox Code Playgroud)
我的应用程序和插件库都使用gcc编译并且未被删除.我的应用程序在执行时,使用dlopen加载plugin.so不幸的是,崩溃发生在我无法在gdb下运行应用程序的站点上.
谷歌疯狂地搜索答案,但所有讨论回溯和addr2line的网站排除了可能需要分析错误插件的情况.我希望一些善良的黑客知道这个困境的解决方案,并且可以分享一些见解.对于其他程序员来说,这将是非常宝贵的.
提前谢谢.
我正在查看我在GDB中调试的程序的以下回溯:
Thread 7 (Thread 3983):
#0 0xf7737430 in __kernel_vsyscall ()
#1 0x41b85412 in __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/i386/i486/lowlevellock.S:142
#2 0x41b80d6d in _L_lock_686 () from libpthread.so.0
#3 0xfbad8001 in ?? ()
#4 0x080eac80 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
Run Code Online (Sandbox Code Playgroud)
特别是我对0xfbad8001的帧地址及其含义感兴趣.
该平台基于x86,因此该未对齐地址无效.鉴于"坏"被编码为十六进制值,我猜这是一个神奇的数字,但到目前为止,我还无法确定是谁设置了这个值或为什么.我试图在谷歌和在线LXR数据库中搜索内核和glibc,但是没有发现任何实际设置此值的代码行.
如果我谷歌搜索"fbad8001",那么有许多点击在回溯和内存转储中显示此地址.所以这个特殊值似乎有一些意义,我假设它是某个地方的神奇数字,但到目前为止我还没能找到设置它的代码.
谁设定了这个值,它意味着什么?
内核基于Linux 3.4.10,glibc为2.15.
除了内核和glibc源代码外,我还通过gcc,gdb和binutils源代码,但仍然没有看到任何吸烟枪.我不知道还能在哪里看.
我正在编写C,C++和汇编的混合编程,我希望从代码的任何部分获得可靠的回溯.
这主要工作正常的C和C++代码,因为我可以生成调试与信息-g,这对于现代的x86编译器和平台生成DWARF调试信息,这意味着最终的二进制包括CFI(调用帧信息).此信息允许通过当前调用堆栈中的函数向后移动.它可以支持复杂的场景,例如没有基指针的函数以及rsp动态方式的变化.对于C和C++代码,我不必关心:即使对于优化的代码,编译器也会生成正确的CFI.
对于我在nasm中编写的x86汇编代码,CFI是个问题.nasm和语法兼容的竞争对手yasm对生成DWARF信息有一些有限的支持,但它主要涉及指向行映射的指令指针,并且不包括任何CFI 1(实际上,即使它集合也无法真正生成它因为程序集太低而无法明确表达调用/返回语义,所以需要.
我想为程序集生成的例程添加CFI,但我不需要CFI的完全灵活性来根据指令细化更改识别CFA rsp等等:我非常乐意设置标准框架-pointer in rbpfor each assembly function并保持整个函数的完整性.DWARF3是否通过一小段CFI信息支持这种情况,希望我能用汇编器宏生成一个?
1 yasm 的实验性"nextgen" 版本确实支持CFI指令,但该项目尚未在5年内更新.有一个未解决的问题是将此支持移回yasm的主线版本.
为了在运行时捕获诸如分段错误之类的致命错误,我编写了一个自定义 SignalHandler,它将打印堆栈跟踪到控制台并打印到日志文件中。
为了实现这一目标,我将backtrace()和backtrace_symbols()函数与 . 结合使用(在我之前有数百个) addr2line。
调用会backtrace_symbols()产生以下输出:
Obtained 8 stack frames.
./Mainboard_Software(+0xb1af5) [0x56184991baf5]
./Mainboard_Software(+0xb1a79) [0x56184991ba79]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x12dd0) [0x7fe72948bdd0]
./Mainboard_Software(causeSIGFPE+0x16) [0x561849918a10]
./Mainboard_Software(_Z13MainboardInit7QString+0xf3) [0x56184990e0df]
./Mainboard_Software(main+0x386) [0x5618499182a3]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7fe727fd909b]
./Mainboard_Software(_start+0x2a) [0x5618498ff0aa]
Run Code Online (Sandbox Code Playgroud)
我需要将偏移量传递给 addr2line 来获取我的模块名称和行号。
$ addr2line -C -a -s -f -p -e ./D098_Mainboard_Software 0xb1a79
0x00000000000b1a79: HandleBacktraceSignals at SignalModule.c:492
Run Code Online (Sandbox Code Playgroud)
但是,在某些模块(尤其是 cpp 模块)中,我将偏移量作为符号和十六进制的组合获得,例如_Z13MainboardInit7QString+0xf3
我可以通过调用将符号解析为十六进制nm:
$ nm Mainboard_Software | grep _Z13MainboardInit7QString
00000000000a3fec T _Z13MainboardInit7QString
Run Code Online (Sandbox Code Playgroud)
现在我可以添加这两个十六进制数字,将它们传递给 addr2line 并获取我的模块名称和行号,如果我愿意,甚至可以进行分解:
$ addr2line -C -a -s -f -p -e ./D098_Mainboard_Software 0xa40df …Run Code Online (Sandbox Code Playgroud) 在由 生成perf record --call-graph dwarf和打印的回溯中perf script,我一直得到可能有 5% 的调用堆栈的错误地址,即展开失败。一个例子是
my_bin 770395 705462.825887: 3560360 cycles:
7f0398b9b7e2 __vsnprintf_internal+0x12 (/usr/lib/x86_64-linux-gnu/libc-2.32.so)
7ffcdb6fbfdf [unknown] ([stack])
my_bin 770395 705462.827040: 3447195 cycles:
7f0398ba1624 __GI__IO_default_xsputn+0x104 (inlined)
7ffcdb6fb7af [unknown] ([stack])
Run Code Online (Sandbox Code Playgroud)
它是从此代码生成的(使用 编译g++ -O3 -g -fno-omit-frame-pointer my_bin.cpp -o my_bin):
#include <cstdio>
#include <iostream>
int __attribute__ ((noinline)) libc_string(int x) {
char buf[64] = {0};
// Some nonsense workload using libc
int res = 0;
for (int i = 0; i < x; ++i) {
res += …Run Code Online (Sandbox Code Playgroud)