前言: 编译型语言,比如C,C++,Go编译出来的二进制,可以使用perf来分析性能。对于编译出来的elf格式,使用dwarf来分析symbol。 对于python这种解释型语言,就会比较麻烦。因为python进程的stack是Cpython的stack,并非对应的py的stack。 分析: 1,cProfile python的官方提供了profiling工具,https://docs.python.org/2/library/profile.html 用法上,需要修改代码,重新执行。对于线上业务,其实是不太友好的。另外就是如果父进程启动之后,启动子进程执行,就没法工作了。 相比这种方式,作者更倾向旁路的方式,对于一个running的python进程进行profiling,业务进程不需要修改,也无感知(允许一定范围内的性能下降)。像perf一样,不侵入进程的情况下进行性能分析,用起来更舒服一些。 2,cpython的stack 来一段测试代码:
打印的结果是:
python的内置的traceback的结果,也是我们预期的。 实际上,使用gdb得到的结果:
可见,python进程的stack,是解释器的二进制的stack。 3,py-bt python提供了gdb的能力,但是对gdb的版本有些要求。搭起来环境,发现gdb可以dump出来python的backtrace。 順藤摸瓜,分析libpython.py的代码发现: a,使用gdb来dump出来cpython的stack b,过滤掉非python的stack,方法就是判断frame的名字是不是“PyEval_EvalFrameEx”
c,在python的frame中保存了line number和file name。解释出来frame即可。 可见,gdb提供了一个很好的例子,使用旁路的方法,可以获取到python的backtrace。 那么,我们可以使用另外一个进程,每秒抓取1000次backtrace,哪个backtrace命中的更多,就说明CPU更加密集。好吧,这个动作的准确名词是“采样”,当然采样的频率可以调整才是。 4,pstack pstack和gdb的原理类似,都是使用kernel提供的ptrace这个syscall实现的。 相比于gdb,pstack更加合适。实际上,目前upstream上已经开始支持了python的trace。 改造pstack是一个不错的方法。 5,pyflame uber开源了pyflame工具,https://github.com/uber/pyflame 也是完全的旁路profiling工具。目前支持的功能基本满足需求,可以分析出来各个backtrace的命中次数,结合flame graph画出热点图。 值得一说的是,pyflame的实现上,和gdb的方式有很大差别: a,在cpython中,_PyThreadState_Current这个symbol保存着当前正在运行的thread。 b,在_PyThreadState_Current中保存着当前python栈的frame。 c,从当前的frame信息中得到行号和文件名,以及上一级frame的指针。 d,遍历frame得到所有的backtrace。 综上,pyflame是一个不错的旁路profiling工具。