首页 > 关于ceph源码 backtrace 打印函数调用栈

关于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

  • 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
", stacktrace[i]); }free(stacktrace);
}

查看测试代码 print_func_stack.c

#include
#include 
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
", 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
",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
", stacktrace[i]);                     1666            ldout(coll->store->cct,0) << __func__ << stacktrace[i] << dendl;       1667       }1668       free(stacktrace);                                         1669 }

直接将该函数放入bluestore.cc不同函数之中就可以进行调用栈打印。

更多相关:

  • #coding:utf-8'''Created on 2017年10月25日@author: li.liu'''import pymysqldb=pymysql.connect('localhost','root','root','test',charset='utf8')m=db.cursor()'''try:#a=raw_inpu...

  • python数据类型:int、string、float、boolean 可变变量:list 不可变变量:string、元组tuple 1.list list就是列表、array、数组 列表根据下标(0123)取值,下标也叫索引、角标、编号 new_stus =['刘德华','刘嘉玲','孙俪','范冰冰'] 最前面一个元素下标是0,最...

  • from pathlib import Path srcPath = Path(‘../src/‘) [x for x in srcPath.iterdir() if srcPath.is_dir()] 列出指定目录及子目录下的所有文件 from pathlib import Path srcPath = Path(‘../tenso...

  • 我在使用OpenResty编写lua代码时,需要使用到lua的正则表达式,其中pattern是这样的, --热水器设置时间 local s = '12:33' local pattern = "(20|21|22|23|[01][0-9]):([0-5][0-9])" local matched = string.match(s, "...

  • 在分析ats的访问日志时,我经常会遇到将一些特殊字段对齐显示的需求,网上调研了一下,发现使用column -t就可以轻松搞定,比如 找到ATS的access.log中的200响应时间过长的日志 cat access.log | grep ' 200 ' | awk -F '"' '{print $3}' > taoyx.log co...

  • 有这样的题目: 已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈, 也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法? 类似如下: 已知栈的出栈序列为:3 2 5 4 1,判断该栈的出栈序列是否合法 过程如下: 第一次1-5顺序入栈:1,2,3 第二次 3 出栈:1,2 第三次 2 出...

  • 师弟总结的已经很好了 或者: 原工程需要导出的函数: extern "C" __declspec(dllexport) void Func(); 现工程导入这个函数: extern "C" __declspec(dllimport) void Func();...

  • 有使用过JS的朋友,相信都知道function。JS中的function是可以在里面在定义一个作为内部使用的。有时为了控制作用域,或者这种小函数只在这个函数体内会使用,所以就不希望在外部在作额外的定义。那C#中有没有这样类似的方式呢?答案是有的。 在C#中要实现,需要用到的是委托和lambda表达式。对于lambda表达式,是可以实...