我的日誌記錄代碼使用返回值backtrace()來確定當前的堆棧深度(用於漂亮的打印目的),但從分析中我可以看出這是一個相當昂貴的調用。有沒有比使用backtrace()更便宜的方法來查找調用堆棧的深度?
我不認爲有這樣做的更便宜的方法?請注意,我不關心幀地址,只是其中有多少。
編輯:這些日誌記錄函數全部用於大型代碼庫,因此手動跟蹤堆棧深度並不是真正的選擇。
我的日誌記錄代碼使用返回值backtrace()來確定當前的堆棧深度(用於漂亮的打印目的),但從分析中我可以看出這是一個相當昂貴的調用。有沒有比使用backtrace()更便宜的方法來查找調用堆棧的深度?
我不認爲有這樣做的更便宜的方法?請注意,我不關心幀地址,只是其中有多少。
編輯:這些日誌記錄函數全部用於大型代碼庫,因此手動跟蹤堆棧深度並不是真正的選擇。
自己走棧非常快 - 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()
的最開始處查看終止值。
如果您的漂亮打印函數被合理包含,則將縮進(或縮進大小)作爲參數傳遞,並在調用其他顯示函數時遞增。
難道你不能隨身攜帶一個TLS變量稱爲「深度」,並增加/減少它的每一個功能嗎?儘管你可以編寫自己的代碼來更快地執行堆棧,但它仍然會比只是隨身攜帶變量慢。
不,我認爲在每次函數調用時遞增/遞減TLS變量將會相當昂貴得多,這取決於您需要多久進行一次回溯。 – 2009-02-24 17:52:30
針對ARM架構:
register unsigned long *rfp asm("fp");
unsigned long *fp = rfp;
unsigned long depth = 0;
while(fp)
{
fp = (unsigned long *)(*(fp -3));
depth++;
}
return depth;
如果添加另一個參數,以你的函數的想法讓你害怕,你也可以使用一個靜態變量。在我看來,這通常不是一個好主意。這將和道格拉斯的解決方案一樣。 – Brian 2009-02-24 17:52:22