https://blog.csdn.net/Z_Stand/article/details/108137659
Systemtap 工具
Systemtap 工具是一种可以通过脚本进行自由扩展的动态追踪技术,但是因为长时间游离于内核之外,所以在RHEL系统中是比较稳定,而其他系统则容易出现异常。
反过来说,在3.x 等旧版本内核中,systemtap 相比于eBPF 是一个巨大的优势。
首先通过systemtap 来获取我们rocksdb内部子流程上的关键函数调用栈,从而帮助大家更好的分析。
如果没有stap命令,则可以通过sudo yum install system tap -y来安装systemtap工具。
比如我们抓取写WAL 函数的调用栈,编写call_trace.stp 如下
#!/bin/stap
probe process("/home/test_binary").function("rocksdb::DBImpl::WriteToWAL")
print("------------------------------------\n")
print_ubacktrace()
print("------------------------------------\n")
}
1
2
3
4
5
6
7
通过sudo stap call_trace.stp | c++filt 将每次调用WriteToWAL函数的调用栈打印出来。这里如果编译rocksdb的时候采用demangle的方式,那么就不需要c++filt了, 否则会出现一些乱码,这里使用c++filt进行过滤。
最终可以看到很多wal相关的调用栈信息如下
通过调用栈,我们就大概知道了从rocksdb的IO链路到上层应用链路的调用关系, 取到了这条路径上的函数,再逐层从下向上结合后面的stap脚本进行耗时统计。
以下是一个案例,可以在binary中增加多个探针,来打印对应函数的耗时情况。
!#/bin/stap
global times
probe process("/home/test_binary").function("rocksdb::DBImpl::WriteImpl").return,
process("/home/test_binary").function("rocksdb::DBImpl::WriteToWAL").return,
process("/home/test_binary").function("rocksdb::WriteBatchInternal::InsertInto").return,
process("/home/test_binary").function("rocksdb::WriteBatchInternal::Iterate").return,
process("/home/test_binary").function("rocksdb::MemTable::Add").return {
times[pid(), ppfunc()] += gettimeofday_us() - @entry(gettimeofday_us()) #耗时及耗时信息放在times 数组之中
}
probe timer.s(10) { #每隔十秒打印一次
println("========%s", execname())
foreach([pid, pp] in times - limit 10) {
printf("pid:%5d %50s %10ld(us)\n", pid, pp, times[pid, pp])
}
delete times
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
最终结果如下(这个结果显然偏高了):
========%sswapper/42
pid:98097 rocksdb::DBImpl::WriteImpl 1510686(us)
pid:98097 rocksdb::DBImpl::WriteToWAL 1153604(us)
pid:98097 rocksdb::WriteBatchInternal::InsertInto 574674(us)
pid:98097 rocksdb::WriteBatchInternal::Iterate 437807(us)
pid:98097 rocksdb::MemTable::Add 257805(us)
1
2
3
4
5
6
一般不建议使用这种多个探针方式进行探测,这样会对应用程序性能有比较大的影响,systemtap的执行方式是 先转换成C代码,编译成内核模块,加载到内核中,对指定的用户程序添加探针,根据指定的行为做对应的返回。如果同时有过多的探针,那肯定会对性能有比较大的影响。
所以这里抓取 的 耗时能够和rocksdb内部统计的耗时数据核对上之后(比如writeToWAL函数的消耗),再进行逐层向上抓取,当然也可以向下抓取。
比如writeToWAL之下会调用AddRecord函数进行log文件的写入,再之下会通过Flush函数进行数据的写入,通过PosixWritableFile::Append函数 调用 PosixWrite函数,最终执行到文件系统的write系统调用之上。详细的写WAL的实现可以参考Rocksdb Wal机制 底层探索。
System tap 这个工具本身还是有很多可以研究的地方,能够极大得节省内核的调试效率(本身的执行方式就是编译成对应的内核模块,加载到系统中执行的),但是在调试用户态应用过程中除了会对应用本身性能有影响之外,其他功能方面的影响还好。
————————————————
版权声明:本文为CSDN博主「z_stand」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Z_Stand/article/details/108137659