乱写了一下 cpython3.10 的通用化 perf -g,不是很难,倒是找 go perf lib 找了半天都想自己写了: https://github.com/jschwinger233/perf-examples/blob/main/cpython310_backtrace/bpf.c
思路是在栈上搜索 PyFrameObject 指针,因为有明显特征所以很容易精准匹配。找到 PyFrameObject 之后就能在 Python 虚拟机上栈回溯了,这在之前讨论过。搜索栈是我对比好几个方案之后的选择,因为不用解析 elf、内存映射、烦人的 pie 和动态链接,通用性极佳,就是会多消耗一点 CPU。。
相比 cpython3.12 的 trampoline 方案,它好就好在灵活通用,比如可以搜集函数调用的参数什么的,过滤条件也可编程。相比 py-spy 等 ptrace 方案,性能应该更好(吧?),py-spy 根本就不是用 linux perf 基建 (PERF_COUNT_SW_CPU_CLOCK/...) 而是用户态 timer 定时抠内存,这导致 py-spy 的结果和 perf 有本质不同,后者保证 on-cpu,且可以有更复杂的使用场景(如 off-cpu sampling)。
ebpf + perf (BPF_PROG_TYPE_PERF_EVENT) 是超级好的东西,本质上是实现了采样可编程化,威力无穷,用途极广,我对 page fault、context switch、cpu migration、和各种 hw 事件充满了奇思妙想。
思路是在栈上搜索 PyFrameObject 指针,因为有明显特征所以很容易精准匹配。找到 PyFrameObject 之后就能在 Python 虚拟机上栈回溯了,这在之前讨论过。搜索栈是我对比好几个方案之后的选择,因为不用解析 elf、内存映射、烦人的 pie 和动态链接,通用性极佳,就是会多消耗一点 CPU。。
相比 cpython3.12 的 trampoline 方案,它好就好在灵活通用,比如可以搜集函数调用的参数什么的,过滤条件也可编程。相比 py-spy 等 ptrace 方案,性能应该更好(吧?),py-spy 根本就不是用 linux perf 基建 (PERF_COUNT_SW_CPU_CLOCK/...) 而是用户态 timer 定时抠内存,这导致 py-spy 的结果和 perf 有本质不同,后者保证 on-cpu,且可以有更复杂的使用场景(如 off-cpu sampling)。
ebpf + perf (BPF_PROG_TYPE_PERF_EVENT) 是超级好的东西,本质上是实现了采样可编程化,威力无穷,用途极广,我对 page fault、context switch、cpu migration、和各种 hw 事件充满了奇思妙想。