获取 Mac OS X 上的当前堆栈跟踪

发布于 2024-07-08 17:26:37 字数 2131 浏览 7 评论 0原文

我正在尝试弄清楚如何在 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”(或“start”减去第一个字符)。

理想情况下,我想要行号以及此时的方法名称。 但我会为初学者选择正确的函数/方法名称。 也许在那之后拍摄行号,在 Linux 上我听说你必须为拥有自己的指令集的私有 ELF 块编写自己的解析器。 听起来很可怕。

无论如何,任何人都可以整理这段代码,使其获得正确的方法名称吗?

I'm trying to work out how to store and then print the current stack in my C++ apps on Mac OS X. The main problem seems to be getting dladdr to return the right symbol when given an address inside the main executable. I suspect that the issue is actually a compile option, but I'm not sure.

I have tried the backtrace code from Darwin/Leopard but it calls dladdr and has the same issue as my own code calling dladdr.

Original post:
Currently I'm capturing the stack with this code:

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;
}

Which seems to work ok. Then to print the stack I'm looking at using dladdr like this:

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);

Which almost works, some example output:

/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]

It's getting the method name right for the shared object but not for the main app. Those just map to "tart" (or "start" minus the first character).

Ideally I'd like line numbers as well as the method name at that point. But I'll settle for the correct function/method name for starters. Maybe shoot for line numbers after that, on Linux I hear you have to write your own parser for a private ELF block that has it's own instruction set. Sounds scary.

Anyway, can anyone sort this code out so it gets the method names right?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

睫毛溺水了 2024-07-15 17:26:37

您的目标是哪些版本的 OS X。 如果您在 Mac OS X 10.5 及更高版本上运行,则只需使用 backtrace() 和 backtrace_symbols() 库调用。 它们在 execinfo.h 中定义,并且有一个 手册页 包含一些示例代码。

编辑:

您在评论中提到您需要在 Tiger 上运行。 您可能只需在应用程序中包含 Libc 的实现即可。 该源代码可从 Apple 的开源网站获取。 这是相关 文件的链接

What releases of OS X are you targetting. If you are running on Mac OS X 10.5 and higher you can just use the backtrace() and backtrace_symbols() libraray calls. They are defined in execinfo.h, and there is a manpage with some sample code.

Edit:

You mentioned in the comments that you need to run on Tiger. You can probably just include the implementation from Libc in your app. The source is available from Apple's opensource site. Here is a link to the relevent file.

A君 2024-07-15 17:26:37

在 macOS 上,您可以使用atos从调试符号中查找源文件和行号。 atos 是一个命令行实用程序,因此您需要启动一个单独的进程。 像这样的事情会起作用:

#include <iostream>
#include <sstream>

#include <dlfcn.h>
#include <execinfo.h>
#include <stdio.h>

void print_backtrace() {
  constexpr int kBacktraceDepth = 15;
  void* backtrace_addrs[kBacktraceDepth];

  int trace_size = backtrace(backtrace_addrs, kBacktraceDepth);

  for (int i = 0; i < trace_size; ++i) {
    Dl_info info;
    dladdr(backtrace_addrs[i], &info);

    std::stringstream cmd(std::ios_base::out);
    cmd << "atos -o " << info.dli_fname << " -l " << std::hex
      << reinterpret_cast<uint64_t>(info.dli_fbase) << ' '
      << reinterpret_cast<uint64_t>(backtrace_addrs[i]);

    FILE* atos = popen(cmd.str().c_str(), "r");

    constexpr int kBufferSize = 200;
    char buffer[kBufferSize];

    fgets(buffer, kBufferSize, atos);
    pclose(atos);

    std::cout << buffer;
  }
  std::cout << std::flush;
}

On macOS, you can use atos to look up source file and line numbers from debug symbols. atos is a command line utility, so you need to launch a separate process. Something like this will work:

#include <iostream>
#include <sstream>

#include <dlfcn.h>
#include <execinfo.h>
#include <stdio.h>

void print_backtrace() {
  constexpr int kBacktraceDepth = 15;
  void* backtrace_addrs[kBacktraceDepth];

  int trace_size = backtrace(backtrace_addrs, kBacktraceDepth);

  for (int i = 0; i < trace_size; ++i) {
    Dl_info info;
    dladdr(backtrace_addrs[i], &info);

    std::stringstream cmd(std::ios_base::out);
    cmd << "atos -o " << info.dli_fname << " -l " << std::hex
      << reinterpret_cast<uint64_t>(info.dli_fbase) << ' '
      << reinterpret_cast<uint64_t>(backtrace_addrs[i]);

    FILE* atos = popen(cmd.str().c_str(), "r");

    constexpr int kBufferSize = 200;
    char buffer[kBufferSize];

    fgets(buffer, kBufferSize, atos);
    pclose(atos);

    std::cout << buffer;
  }
  std::cout << std::flush;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文