当前位置: 首页 > 编程日记 > 正文

关于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不同函数之中就可以进行调用栈打印。

相关文章:

泛型java 代码讲解_Java泛型详解

2516326-5475e88a458a09e4.png一&#xff0c;打破砂锅问到底泛型存在的意义&#xff1f;泛型类&#xff0c;泛型接口&#xff0c;泛型方法如何定义&#xff1f;如何限定类型变量&#xff1f;泛型中使用的约束和局限性有哪些&#xff1f;泛型类型的继承规则是什么&#xff1f;泛…

(转)金额转中文大写

public class RMB {//返回转换好的大写形式public static String numberToRMB(String money) {return cleanZero(splitNum(roundString(money)));}// 将小写金额转换成大写金额private static String splitNum(String s) {// 如果传入的是空串则继续返回空串if ("".e…

[IOS]UIWebView实现保存页面和读取服务器端json数据

如何通过viewView保存访问过的页面&#xff1f;和如何获取并解析服务器端发送过来的json数据&#xff1f;通过一个简单的Demo来学习一下吧&#xff01; 操作步骤&#xff1a; 1.创建SingleViewApplication应用&#xff0c;新建VIewController&#xff0c;并在xib试图中添加WebV…

struts2之配置文件struts.xml详解

struts配置文件 struts.xml配置参数详解 struts.xml中很大一部分配置默认配置就好了 但是有些还是需要做了解 以便于理解 和修改 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Str…

ceph bluestore 源码分析:刷缓存(trim)逻辑

环境 ceph版本&#xff1a;12.2.1 部署模式&#xff1a;ec 21 osd&#xff1a; 3个 且资源池已经有数据 执行命令&#xff1a;ceph daemon osd.0 flush_store_cache 进行刷缓存。即将dump_mempools内存池管理的bluestore cache中的无用数据进行释放 主要参数: bluestore_cac…

php中怎么过滤器_PHP 过滤器(Filter)

过滤多个输入表单通常由多个输入字段组成。为了避免对 filter_var 或 filter_input 重复调用&#xff0c;我们可以使用 filter_var_array 或 the filter_input_array 函数。在本例中&#xff0c;我们使用 filter_input_array() 函数来过滤三个 GET 变量。接收到的 GET 变量是一…

c++ Qt向PHP接口POST文件流

Qt调用PHP写的接口&#xff0c;向其传递图片文件&#xff0c;并保存在服务器。 二进制文件无法直接传递&#xff0c;Qt采用Base64进行编码发送&#xff0c;PHP解码保存为文件。 注意&#xff1a;PHP收到数据之后会将POST过来的数据中的加号()替换为空格&#xff0c;造成接收到的…

在网络通讯中应用Protobuf

Protobuf的设计非常适用于在网络通讯中的数据载体&#xff0c;它序列化出来的数据量少再加上以K-V的方式来存储数据&#xff0c;对消息的版本兼容性非常强&#xff1b;还有一个比较大的优点就是有着很多的语言平台支持。下面讲解一下如何在TCP通讯应用中集成Protobuf. Protobuf…

JS中Math函数的常用方法

Math 是数学函数&#xff0c;但又属于对象数据类型 typeof Math > ‘object’ console.dir(Math) 查看Math的所有函数方法。 1&#xff0c;Math.abs() 获取绝对值 Math.abs(-12) 12 2&#xff0c;Math.ceil() and Math.floor() 向上取整和向下取整 console.log(Math.ceil(1…

C++ 泛型编程 -- 函数模版

文章目录定义声明调用方式函数模版的重载函数模版的特点工作中一个同事写了测试demo&#xff0c;想要自己尝试使用发现调用老出错&#xff0c;请教的时候发现是函数模版&#xff0c;有自己的调用方式&#xff0c;并且发现核心代码中大量的函数模版和类模版。特此做一个函数模版…

bellman_ford寻找平均权值最小的回路

给定一个有向图&#xff0c;如果存在平均值最小的回路&#xff0c;输出平均值。 使用二分法求解&#xff0c;对于一个猜测值mid&#xff0c;判断是否存在平均值小于mid的回路 如果存在平均值小于mid的包含k条边的回路&#xff0c;那么有w1w2w3...wk < k * mid,即(w1-mid)(w2…

守护进程中创建的对象php,在PHP中生成守护进程(Daemon Process)

前两天看到一篇文章《如何使用PHP编写daemon process》&#xff0c;其中对核心代码却没有细说&#xff0c;我又查了一些资料&#xff0c;还看了一本《理解Unix进程》&#xff0c;才搞明白生成守护进程的时候发生了什么。这段代码是这个样子的&#xff1a;function run(){//第一…

log4j个人使用整理

Log4j介绍&#xff1a; 略过。 配置&#xff1a; Eclipse项目中添加log4j.jar到lib下。 在bin目录下新建log4j.properties&#xff0c;编辑好log4j配置文件。 样例分析&#xff1a; 1 log4j.rootLoggerWARN, stdout, file2 log4j.appender.stdoutorg.apache.log4j.ConsoleAppen…

完全理解 Python 迭代对象、迭代器、生成器(转)

完全理解 Python 迭代对象、迭代器、生成器 本文源自RQ作者的一篇博文&#xff0c;原文是Iterables vs. Iterators vs. Generators nvie.com&#xff0c;俺写的这篇文章是按照自己的理解做的参考翻译&#xff0c;算不上是原文的中译版本&#xff0c;推荐阅读原文&#xff0c;谢…

Rocksdb 与 TitanDb 原理分析 及 性能对比测试

文章目录前言Rocksdb的compaction机制compaction作用compaction分类level style compaction&#xff08;rocksdb 默认进行的compaction策略&#xff09;level 的文件存储结构compaction过程compaction中的level target sizeuniversal style compactionfifo style compactionTit…

经典SQL练习题

题目地址&#xff1a;http://blog.csdn.net/qaz13177_58_/article/details/5575711 1、 查询Student表中的所有记录的Sname、Ssex和Class列。select sname,ssex,class from STUDENT2、 查询教师所有的单位即不重复的Depart列。select depart from TEACHER group by departselec…

php url模式在哪修改,php如何修改url

php如何修改url2020-07-03 12:15:40php修改url的方法&#xff1a;1、通过配置文件修改URL规则&#xff1b;2、设置URL伪静态&#xff0c;即限制伪静态的后缀&#xff1b;3、在配置文件中开启路由支持&#xff0c;并配置路由&#xff1b;4、将URL进行重写即可。PHP对URL设置一、…

国外十大最流行PHP框架排名

以下为十个目前最流行的基于MVC设计模式的PHP框架。1. YiiYii是一个基于组件的高性能的PHP的框架&#xff0c;用于开发大规模Web应用。Yii采用严格的OOP编写&#xff0c;并有着完善的库引用以及全面的教程。从MVC&#xff0c;DAO/ActiveRecord&#xff0c;widgets&#xff0c;c…

python_web框架

一、web框架 web框架&#xff1a; 自己完成socket的web框架&#xff1a;如&#xff0c;Tornado等由WSGI完成socket的web框架&#xff1a;如&#xff0c;Django、flash等两种实现过程&#xff1a; 第二种WSGI方式的&#xff0c;由于自带socket所以可直接写后端代码。 python标准…

g-gdb 调试多线程

代码调试工具gdb是一个能够让我们在工作中高效排查代码异常根源的利器。 在此将gdb针对多线程的调试方式做一个笔记&#xff0c;也方便后续回顾以及分享大家。 本文采用的是一个简单的多线程代码示例&#xff0c;同时调试是在mac上进行的 mac安装gdb brew install gdb即可 基…

php数据库html文本,关于php,mysql,html的数字分页和文本_php

请勿盗版&#xff0c;转载请加上出处http://blog.csdn.net/yanlintao1请勿盗版&#xff0c;转载请加上出处http://blog.csdn.net/yanlintao1首先进行样式展示希望对大家有所帮助&#xff0c;也希望大家给出意见和建议&#xff1a;第一种&#xff1a;数字分页第二种&#xff1a;…

WinDbg加载不同版本CLR

WinDbg调试.net2.0和.net4.0程序有所不同&#xff0c;因为.net4.0使用新版本的CLR。例如&#xff1a; mscoree.dll 变为 mscoree.dll 和 mscoreei.dll&#xff0c; mscorwks.dll 变为 clr.dll&#xff0c; mscorjit.dll 变为 clrjit.dll。 因此&#xff0c;在.net2.0加载mscorj…

交换机***工具——Yersinia

Yersinia是国外一款专门针对交换机执行第二层***的***工具。目前的版本是0.7.1。目前支持的操作系统及版本号如表1所示。表1 Yerdinia支持的操作系统操作系统名称版本号OpenBSD3.4 (pcap库版本至少0.7.2以上)Linux2.4.x和2.6.xSolaris5.8 64bits SPARCMac OSX10.4 Tiger (Intel…

Rocksdb 写流程,读流程,WAL文件,MANIFEST文件,ColumnFamily,Memtable,SST文件原理详解

文章目录前言Rocksdb写流程图WAL 原理分析概述文件格式查看WAL的工具创建WAL清理WALMANIFEST原理分析概述查看MANIFEST的工具创建 及 清除 MANIFEST文件内容CcolumnFamily 详解概述API介绍核心数据结构创建以及删除MEMTABLE 实现概述实现Rocksdb写入逻辑概述实现总结关于写的一…

react 入门

首先安装node.js环境 下载地址 https://nodejs.org/en/download/检查安装版本 进入命令行npm -v~~3. 安装react命令环境 npm install - g react-native-cli ~~~ 初始化项目 FirstAppreact-native init FirstApp 转载于:https://www.cnblogs.com/liu-ya/p/10511537.html

将字符串打乱输出

将字符串打乱输出 Dim i,mm,Str,StrPosition,NewStrStr  "1234567890"For i1 To Len(Str)    StrPosition  GetRandomMath(1,Len(Replace(Str,mm,"")))    Str  Replace(Str,mm,"")    mm  Mid(str,StrPosition,1)   …

php帝国系统调出图片内空,帝国CMS图集字段的大图,小图,说明的调用方法

本文实例讲述了帝国CMS图集字段的大图,小图,说明的调用方法。分享给大家供大家参考。具体方法如下&#xff1a;复制代码代码如下:$arr array();$arr $navinfor[morepic];$newarr explode(egetzy(rn),$arr);$count count(explode(egetzy(rn),$navinfor[morepic]));//图集的图…

static和global的区别

1.global在整个页面起作用。2.static只在function和class内起作用。global和$GLOBALS使用基本相同&#xff0c;但在实际开发中大不相同。global在函数产生一个指向函数外部变量的别名变量&#xff0c;而不是真正的函数外部变量&#xff0c;一但改变了别名变量的指向地址&#x…

vue 之 nextTick 与$nextTick

VUE中Vue.nextTick()和this.$nextTick()怎么使用&#xff1f; 官方文档是这样解释的&#xff1a; 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法&#xff0c;获取更新后的 DOM。 虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考&#xff…

Linux创建线程时 内存分配的那些事

文章目录问题描述问题分析针对问题1 的猜测:针对问题2 的猜测:原理追踪总结问题描述 事情开始于一段内存问题&#xff0c;通过gperf工具抓取进程运行过程中的内存占用情况。 分析结果时发现一个有趣的事情&#xff0c;top看到的实际物理内存只有几兆&#xff0c;但是pprof统计…