关于ceph源码 backtrace 打印函数调用栈
當集中精力看一個問題的時候,時間久了就會有這樣一個狀態,天空飄來五個字,那都不算事
ceph源碼龐大的體量以及復雜的設計讓很多人望而卻步,尤其是大量的純虛函數更是讓讀者迷失在代碼的海洋,這個時候函數調用棧是一個救命的東西,因為它節約了你大量的重復查找的時間
查看最終效果
如下為我想要查看bluestore在處理shareblob的釋放邏輯中對put函數的調用者查看
2019-07-11 20:11:30.408525 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(BlueStore::SharedBlob::print_stacktrace()+0x39) [0x7fe3dff055d9]
2019-07-11 20:11:30.408535 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(BlueStore::SharedBlob::put()+0x2c) [0x7fe3dff217bc]
2019-07-11 20:11:30.408536 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(std::_Rb_tree<boost::intrusive_ptr<BlueStore::SharedBlob>, boost::intrusive_ptr<BlueStore::SharedBlob>, std::_Identity<boost::intrusive_ptr<BlueStore::SharedBlob> >, std::less<boost::intrusive_ptr<BlueStore::SharedBlob> >, std::allocator<boost::intrusive_ptr<BlueStore::SharedBlob> > >::_M_erase(std::_Rb_tree_node<boost::intrusive_ptr<BlueStore::SharedBlob> >*)+0x39) [0x7fe3dff8d619]
2019-07-11 20:11:30.408537 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(BlueStore::_txc_finish(BlueStore::TransContext*)+0xbb) [0x7fe3dff2a55b]
2019-07-11 20:11:30.408537 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(BlueStore::_txc_state_proc(BlueStore::TransContext*)+0x216) [0x7fe3dff3c746]
2019-07-11 20:11:30.408538 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(BlueStore::_kv_finalize_thread()+0x630) [0x7fe3dff3e180]
2019-07-11 20:11:30.408538 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/root/ceph-osd(BlueStore::KVFinalizeThread::entry()+0xd) [0x7fe3dff95d2d]
2019-07-11 20:11:30.408539 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/lib64/libpthread.so.0(+0x7df3) [0x7fe3dce62df3]
2019-07-11 20:11:30.408539 7fe3cc5db700 0 bluestore.sharedblob(0x7fe3edad09a0) print_stacktrace/lib64/libc.so.6(clone+0x6d) [0x7fe3dbf573dd]
直接就是一目了然,非常直觀。
ps:由于對更高級的systemtap打印調用棧失敗,只能退而求其次暫時來手動增加函數調用棧,后期會將該工具的使用詳細列舉補足該問題
backtrace()和backtrace_symbols()函數實現調用棧
這兩個函數linux下使用命令
man backtrace
man backtrace_symbols
能夠查看到函數的具體用法
包含函數頭文件:#include <execinfo.h>
int backtrace(void **buffer, int size);buffer參數用來動態存儲調用當前函數的函數指針(地址),size參數則表示當前存儲函數指針的數組最大容量。所以這里需要注意將這兩個參數容量預估好char **backtrace_symbols(void *const *buffer, int size);該函數用來將以上獲取到的函數地址轉為對應容量的字符串數組
代碼如下:
void print_stacktrace()
{int size = 16,i=0;void * array[16];int stack_num = backtrace(array, size);char ** stacktrace = backtrace_symbols(array, stack_num);for (; i < stack_num; ++i){printf("%s\n", stacktrace[i]); }free(stacktrace);
}
查看測試代碼 print_func_stack.c
#include<stdio.h>
#include <execinfo.h>
void print_stacktrace()
{int size = 16,i=0;void * array[16];int stack_num = backtrace(array, size);char ** stacktrace = backtrace_symbols(array, stack_num);for (; i < stack_num; ++i){printf("%s\n", stacktrace[i]);
// ldout(coll->store->cct,0) << __func__ << stacktrace[i] << dendl; }free(stacktrace);
}
int func3(int a)
{print_stacktrace();return a*a;
}
int func2(int b)
{int c=func3(b);return c;
}
int func1(int c)
{return func2(c*c);
}
int main()
{printf("after print stack ,result is %d\n",func1(2));return 0;
}
編譯gcc print_func_stack.c -rdynamic -o print_func_stack ,這里需要加上-rdynamic編譯參數,它可以連接所有符號,否則打印出來僅僅為16進制函數地址
執行如下,可以看到函數調用棧已經清晰打印
[root@node1 ~]# ./print_func_stack
./print_func_stack(print_stacktrace+0x32) [0x400992]
./print_func_stack(func3+0x15) [0x400a04]
./print_func_stack(func2+0x15) [0x400a22]
./print_func_stack(func1+0x19) [0x400a43]
./print_func_stack(main+0xe) [0x400a53]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f7c766b2af5]
./print_func_stack() [0x400899]
after print stack ,result is 16
ps:
如果代碼是C++代碼,則如上編譯出來的二進制文件是被manle的(即源碼標識被c++編譯器轉為了編譯器標識符ABI,會出現我們認為的亂碼),這里需要將輸出轉為demangle(即我們認識的源碼標識)。
類似如下:/root/ceph-osd(_ZN9BlueStore10SharedBlob16print_stacktraceEv+0x2d) [0x7ff22948a5cd] /root/ceph-osd(_ZN9BlueStore10SharedBlob3putEv+0x2c) [0x7ff2294a61bc] /root/ceph-osd(_ZN9BlueStore6ExtentD1Ev+0xd1) [0x7ff229511bb1] /root/ceph-osd(_ZN9BlueStore5Onode3putEv+0x96) [0x7ff2295120f6] /root/ceph-osd(_ZN9BlueStore9TwoQCache5_trimEmm+0x365) [0x7ff2294b7b75] /root/ceph-osd(_ZN9BlueStore5Cache8trim_allEv+0x30) [0x7ff229489a80] /root/ceph-osd(_ZN9BlueStore12_flush_cacheEv+0x9f) [0x7ff2294b9d1f] /root/ceph-osd(_ZN9BlueStore6umountEv+0x128) [0x7ff2294ba0b8] /root/ceph-osd(_ZN3OSD8shutdownEv+0x1695) [0x7ff2290a48b5] /root/ceph-osd(_ZN3OSD13handle_signalEi+0x11f) [0x7ff2290a518f] /root/ceph-osd(_ZN13SignalHandler5entryEv+0x1d7) [0x7ff2295ebb17]可以執行如下命令進行過濾
./print_func_stack |c++filt
更具體一點,我們想要查看打印出來的函數具體在哪個程序的哪一行,需要使用如下編譯方式
gcc print_func_stack.c -rdynamic -g -o print_func_stack 再增加-g 的gdb調試參數
執行如下命令就可以看到具體函數位置
[root@node1 ~]# ./print_func_stack
./print_func_stack(print_stacktrace+0x32) [0x400992]
./print_func_stack(func3+0x15) [0x400a04]
./print_func_stack(func2+0x15) [0x400a22]
./print_func_stack(func1+0x19) [0x400a43]
./print_func_stack(main+0xe) [0x400a53]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f287597daf5]
./print_func_stack() [0x400899]
after print stack ,result is 16
[root@node1 ~]# addr2line -a 0x400a04 -e print_func_stack -f
0x0000000000400a04
func3
/root/print_func_stack.c:18
[root@node1 ~]# addr2line -a 0x400a04 -e print_func_stack -f -C #該-C參數是將源碼轉為demangle形式打印,防止看到的是mangle的ABI字符
0x0000000000400a04
func3
/root/print_func_stack.c:18
ceph源碼添加函數調用棧
同樣的方式,將以上函數封裝到指定的類中,這里是為了不通類的debug日志方式不通,所以沒有定義為全局變量。我這里是直接聲明在SharedBlob大類中,所有該類對象都可以調用。定義很簡單
1657 void BlueStore::SharedBlob::print_stacktrace() 1658 {1659 int size = 16;1660 void * array[16];1661 int stack_num = backtrace(array, size); 1662 char ** stacktrace = backtrace_symbols(array, stack_num); 1663 for (int i = 0; i < stack_num; ++i) 1664 {1665 // printf("%s\n", stacktrace[i]); 1666 ldout(coll->store->cct,0) << __func__ << stacktrace[i] << dendl; 1667 }1668 free(stacktrace); 1669 }
直接將該函數放入bluestore.cc不同函數之中就可以進行調用棧打印。
總結
以上是生活随笔為你收集整理的关于ceph源码 backtrace 打印函数调用栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “六年春二月”下一句是什么
- 下一篇: 伤感的个性签名女生短