Hello!
2012/10/13 Wenhua Zhang:
> Hi 章老师,
> 您好,
> 之前在微博上看到有些您用flamegraph画的“火焰图”,例如:http://agentzh.org/misc/nginx/lua-resty-redis-flamegraph.svg
> 目前也想用systemtap进行nginx的时间消耗分析,并用flameGraph画出相应的火焰图。
> 能否提供一下您之前画图使用的systemtap脚本作为参考?谢谢
>
我生成纯用户态的火焰图所使用的 systemtap 脚本 a.stp 的源码如下:
global s;
global quit = 0;
probe timer.profile {
if (pid() == target()) {
if (quit) {
foreach (i in s-) {
print_ustack(i);
printf("\t%d\n", @count(s[i]));
}
exit()
} else {
s[ubacktrace()] <<< 1;
}
}
}
probe timer.s(20) {
quit = 1
}
运行这个脚本的一条典型命令行是:
stap --ldd -d /path/to/nginx/sbin/nginx \
-d /path/to/luajit/lib/libluajit-5.1.so.2.0.0 \
--all-modules -D MAXMAPENTRIES=20240 \
-D MAXACTION=20000 \
-D MAXTRACE=100 \
-D MAXSTRINGLEN=4096 \
-D MAXBACKTRACE=100 -x 5857 a.stp > a.out
假设这里测量的 nginx worker 进程的 pid 是 5857. 大约过 20 多秒之后,该命令会退出并生成 a.out
输出文件,然后从此文件生成火焰图文件 a.svg:
perl stackcollapse-stap.pl a.out > a.out2
perl flamegraph.pl a.out2 > a.svg
这里使用的两个 perl 脚本(.pl 文件)来自 Brendan Gregg 放在 GitHub 上的 flamegraph 项目:
https://github.com/brendangregg/FlameGraph
值得提醒的是,在运行 systemtap 脚本期间,被采样的 nginx worker 进程(或者其他任意进程)须是足够繁忙的,否则
systemtap 脚本可能会长时间不退出。一般我在上面第一步运行 stap 之前会先用 ab 工具对 nginx
服务器保持压力,至少得持续到 stap 命令自动退出以后。
当然,利用这种方法也可以绘制核心态 + 用户态的更为完整的火焰图,例如这个例子:
http://agentzh.org/misc/nginx/lua-resty-redis-flamegraph4.svg
这样可以把 kernel 空间中的调用栈也给一起采样了,从而可以得到整个“软件栈”(software stack)的全景图。
此时需要换用类似下面这样的 systemtap 脚本:
global bt;
global quit = 0
probe timer.profile {
if (pid() == target()) {
if (!quit) {
bt[backtrace(), ubacktrace()] <<< 1
} else {
foreach ([sys, usr] in bt- limit 1000) {
print_stack(sys)
print_ustack(usr)
printf("\t%d\n", @count(bt[sys, usr]))
}
exit()
}
}
}
probe timer.s(20) {
quit = 1
}
值得一提的是,建议使用最新的 SystemTap 2.0 发布:
http://sourceware.org/systemtap/ftp/releases/
或者干脆直接使用 systemtap 的 git master HEAD:
git clone git://sourceware.org/git/systemtap.git
从源码编译 systemtap 是推荐的做法。我一般使用类似下面这样的命令构造之:
./configure --prefix=/opt/systemtap --disable-docs
--disable-publican --disable-refdocs && make -j8
sudo make install
PATH=/opt/systemtap/bin:$PATH
export PATH
>
> 目前对Brendan Gregg在博客上写的systemtap的参数不是很理解:
> stap -s 32 -D MAXTRACE=100 -D MAXSTRINGLEN=4096 -D MAXMAPENTRIES=10240 \
> -D MAXACTION=10000 -D STP_OVERLOAD_THRESHOLD=5000000000 --all-modules \
> -ve 'global s; probe timer.profile { s[backtrace()] <<< 1; }
> probe end { foreach (i in s+) { print_stack(i);
> printf("\t%d\n", @count(s[i])); } } probe timer.s(60) { exit(); }' \
> > out.stap-stacks
Brendan 使用的各个 stap 命令行参数的具体含义可以参见 stap -h 的输出。
特别地,其中 -D 选项定义的那些宏的值是为了放宽 systemtap 内部的很多限制。例如,MAXTRACE
宏用于控制调用栈的深度,默认只有 20. 所以当实际的调用栈深度超过 20
时(事实上,经常会超时),但会发生调用栈信息的自动截断,从而影响火焰图的绘制。
其他宏的具体含义可以参考 systemtap 邮件列表中的相关讨论以及 systemtap 自身的源码实现。
Brendan 在这里给出的 systemtap one-liner 是单独对 Linux kernel 进行采样,即得到纯核心态的火焰图 :)
Best regards,
-agentzh
P.S. 同时抄送给 openresty 中文邮件列表:https://groups.google.com/group/openresty
这样其他朋友也可以从我们这里的讨论中获益 :)