奥格对 CPython USDT 的应用很感兴趣,那我就详细说说。
核心是取到 PyFrameObject,能取到这玩意,就可以做 python 虚拟机级的栈回溯,也可以在 function__entry 取到入参:_PyInterpreterFrame(不过需要比较辛苦的解析工作)。
但是落脚点是怎么取到 PyFrameObject,因为 CPython 并没有直接传递这个指针给 USDT。
我们直接去看 function__entry 这个 USDT 所在的地方,图一:
1. 最后的 nop 在 attach 之后会变成 int3
2. 上一个指令是 call PyFrame_GetLineNumber
3. 简单看一眼 PyFrame_GetLineNumber 这个函数的声明: PyFrame_GetLineNumber(PyFrameObject *f) ,入参就是 PyFrameObject
4. 所以 nop 上面两行的指令 mov %rbx,%rdi 我们就可以知道 rbx == rdi == &PyFrameObject
所以至少在这个版本的 CPython 上,我们可以在 function__entry 这个 USDT 通过 rbx 取到 PyFrameObject。
当然这是需要打表的,比如 3.10 在 rbx,3.11 可能在其他寄存器。或者实时分析一下 ELF 也行。
当然这只是开始的开始,真要做到工业级的应用,还有很多工作要做。我只是想说,CPython USDT 虽然看起来简陋,但是我们已经可以做到很多很多的事情。
只可惜我要做的事情太多了,CPython 的 tracing tool 只能让少年们去做了。