有没有比使用 backtrace() 更便宜的方法来查找调用堆栈的深度?

Posted

技术标签:

【中文标题】有没有比使用 backtrace() 更便宜的方法来查找调用堆栈的深度?【英文标题】:Is there a cheaper way to find the depth of the call stack than using backtrace()? 【发布时间】:2010-10-09 14:54:00 【问题描述】:

我的日志记录代码使用backtrace() 的返回值来确定当前的堆栈深度(用于漂亮的打印目的),但我可以从分析中看出这是一个非常昂贵的调用。

我认为没有更便宜的方法可以做到这一点?请注意,我不关心框架地址,只关心它们有多少。

编辑:这些日志记录功能在整个大型代码库中都使用,因此手动跟踪堆栈深度并不是一个真正的选择。

【问题讨论】:

【参考方案1】:

对于手臂架构:

register unsigned long *rfp asm("fp");
unsigned long *fp = rfp;
unsigned long depth = 0;

while(fp)

    fp = (unsigned long *)(*(fp -3));
    depth++;


return depth;

【讨论】:

【参考方案2】:

自己遍历堆栈非常快 - backtrace() 中的大部分缓慢来自于查找符号名称。在 x86 上,您可以执行以下操作:

inline uint32_t get_ebp(void)

    __asm__ __volatile__("mov %%ebp, %%eax");


int get_stack_depth(void)

    uint32_t ebp = get_ebp();
    int stack_depth = 0;
    while(ebp != 0)
    
        ebp = *(uint32_t *)ebp;
        stack_depth++;
    
    return stack_depth;

这将遍历ebp 指针链。请记住,这是非常不便携的。另请注意,这不会计算任何已内联或尾调用优化的函数(当然,backtrace() 也有同样的问题)。

另一个重要的问题是终止条件——一旦你回溯到main(),通常不能保证你会在堆栈中找到什么。因此,如果 libc 没有放置空帧指针,您很可能会出现段错误。可以通过main()的开头查看终止值。

【讨论】:

【参考方案3】:

您不能随身携带一个名为“深度”的TLS 变量并在每个函数中递增/递减它吗?虽然您可以编写自己的代码来更快地遍历堆栈,但它仍然比随身携带变量要慢。

【讨论】:

不,我认为每次函数调用递增/递减 TLS 变量的成本会高得多,具体取决于您需要执行回溯的频率。 他在进行回溯每个调用 - 整数 inc/dec 不可能更慢【参考方案4】:

如果您的漂亮打印函数被合理地包含,则将缩进(或缩进大小)作为参数传递,并在调用其他显示函数时将其递增。

【讨论】:

如果向函数添加另一个参数的想法让您感到害怕,您也可以使用静态变量。在我看来,这通常不是一个好主意。这与道格拉斯的解决方案的工作方式相同。

以上是关于有没有比使用 backtrace() 更便宜的方法来查找调用堆栈的深度?的主要内容,如果未能解决你的问题,请参考以下文章

始终分配 CPU 的云运行比仅在请求处理期间分配更便宜。如何?

为什么自建深度学习机器?因为比AWS便宜10倍啊!

没有确切的答案:哪个Java Map最便宜?

iPhone贵但使用成本低,安卓手机便宜其实使用成本高

[无聊测试赛] T8 佳佳的魔法药水

PHP使用debug_backtrace方法跟踪代码调用