首先,perf 会通过系统调用 sys_perf_event_open 在内核中注册一个监测“cycles”事件的性能计数器。内核根据 perf 提供的信息在 PMU 上初始化一个硬件性能计数器(PMC: Performance Monitoring Counter)。PMC 随着 CPU 周期的增加而自动累加。在 PMC 溢出时,PMU 触发一个 PMI (Performance Monitoring Interrupt)中断。内核在 PMI 中断的处理函数中保存 PMC 的计数值,触发中断时的指令地址(Register IP:Instruction Pointer),当前 时间戳以及当前进程的 PID,TID,comm 等信息。我们把这些信息统称为一个采 样(sample)。
内核会将收集到的 sample 放入用于跟用户空间通信的 Ring Buffer。 用户空间里的 perf 分析程序采用 mmap 机制从 ring buffer 中读入采样,并对其解 析。perf 根据 pid,comm 等信息可以找到对应的进程。
根据 IP 与 ELF 文件中的 符号表可以查到触发 PMI 中断的指令所在的函数。为了能够使 perf 读到函数名, 我们的目标程序必须具备符号表。如果读者在 perf 的分析结果中只看到一串地 址,而没有对应的函数名时,通常是由于在编译时利用 strip 删除了 ELF 文件中 的符号表。建议读者在性能分析阶段,保留程序中的 symbol table,debug info 等信息。 根据上述的 perf 采样原理可以得知,perf 假设两次采样之间,即两次相邻 的 PMI 中断之间系统执行的是同一个进程的同一个函数。这种假设会带来一定 的误差,当读者感觉 perf 给出的结果不准时,不妨提高采样频率,perf 会给出更 加精确的结果。