当集中精力看一个问题的时候,时间久了就会有这样一个状态,天空飘来五个字,那都不算事
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打印调用栈失败,只能退而求其次暂时来手动增加函数调用栈,后期会将该工具的使用详细列举补足该问题
这两个函数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
同样的方式,将以上函数封装到指定的类中,这里是为了不通类的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表达式,是可以实...