有没有比使用 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() 更便宜的方法来查找调用堆栈的深度?的主要内容,如果未能解决你的问题,请参考以下文章