我正在尝试研究如何在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; }
这似乎工作正常.然后打印堆栈我正在使用这样的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 = snprintf(buf, buflen, "%s+0x%x", dli.dli_fname, offset ); } if (dli.dli_sname && dli.dli_saddr) { offset = (ptrdiff_t)Ip - (ptrdiff_t)dli.dli_saddr; c += snprintf(buf+c, buflen-c, "(%s+0x%x)", dli.dli_sname, offset ); } if (c > 0) snprintf(buf+c, buflen-c, " [%p]", Ip);
这几乎可行,一些示例输出:
/Users/matthew/Library/Frameworks/Lgi.framework/Versions/A/Lgi+0x2473d(LgiStackTrace+0x5d) [0x102c73d] /Users/matthew/Code/Lgi/LgiRes/build/Debug/LgiRes.app/Contents/MacOS/LgiRes+0x2a006(tart+0x28e72) [0x2b006] /Users/matthew/Code/Lgi/LgiRes/build/Debug/LgiRes.app/Contents/MacOS/LgiRes+0x2f438(tart+0x2e2a4) [0x30438] /Users/matthew/Code/Lgi/LgiRes/build/Debug/LgiRes.app/Contents/MacOS/LgiRes+0x35e9c(tart+0x34d08) [0x36e9c] /Users/matthew/Code/Lgi/LgiRes/build/Debug/LgiRes.app/Contents/MacOS/LgiRes+0x1296(tart+0x102) [0x2296] /Users/matthew/Code/Lgi/LgiRes/build/Debug/LgiRes.app/Contents/MacOS/LgiRes+0x11bd(tart+0x29) [0x21bd]
它使方法名称适用于共享对象,但不适用于主应用程序.那些只是映射到"tart"(或"开始"减去第一个字符).
理想情况下,我想要行号以及此时的方法名称.但我会为初学者找到正确的功能/方法名称.也许在那之后拍摄行号,在Linux上我听说你必须为拥有它自己的指令集的私有ELF块编写你自己的解析器.听起来很吓人.
无论如何,任何人都可以对这些代码进行排序,以便获得方法名称吗?
你正在瞄准什么版本的OS X. 如果您在Mac OS X 10.5及更高版本上运行,则可以使用backtrace()和backtrace_symbols()libraray调用.它们在execinfo.h中定义,并且有一个包含一些示例代码的联机帮助页.
编辑:
你在评论中提到你需要在Tiger上运行.您可以在应用程序中包含Libc的实现.该来源可从Apple的开源网站获得.这是相关文件的链接.