perf report 有时候会解析不了用户态符号,潜在的原因有很多,比如目标进程已死、mnt namespace、奇怪的 buildid cache,总之 perf report 输出是下面这样的:
100.00% 0.00% go_10cpu go_10cpu [.] 0x000000000046f641
|
---0x46f641
0x43a2cb
0x4a2226
我固然精通符号 但在玩了几个小时的 perf buildid-cache、 perf report --symfs、namespace 魔术且听 AI 瞎指挥之后依然无法成功,恼羞成怒,想到一个好😤的解决方法。
我们直接看 perf record 尝试从哪里去读 elf 来解析符号
strace -e trace=file -fTtt -- perf report -g --stdio
过滤一下结果里 buildid / build-id / 二进制名字,比如我本地 ubuntu 2404 能找到
16:43:51.422168 newfstatat(AT_FDCWD, "/root/.debug/.build-id/e8/bdccd3a32295d2545a52e78648834e3f0e3c34", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = ENOENT (No such file or directory)
在 eks 节点 6.1.159-181.297.amzn2023.x86_64 上能看到
08:45:02.012542 newfstatat(AT_FDCWD, "/usr/lib/debug/go_10cpu.debug", {st_mode=S_IFREG|0755, st_size=2411431, ...}, 0) = ENOENT (No such file or directory)
那就把二进制文件(从容器里、镜像里、 /proc/$PID/root/ 里)拷贝过去,比如对于我上述的 eks 节点就是
cp /proc/$(pidof go_10cpu)/root/go_10cpu /usr/lib/debug/go_10cpu.debug
然后 perf report 就正常了
100.00% 100.00% go_10cpu go_10cpu [.] main.burnFor
|
---runtime.goexit.abi0
runtime.main
main.main
main.burnFor
测试发现对 static linked / static-pie linked 都没问题,目标进程死后也能解析。动态链接没测过,但我目测找到 perf-report 要从那里读 libc.so 再手动考虑过去就行了。
这套方法也可以用来做符号分离,比如在生产运行 stripped elf,perf record 不可能解析出符号;但是发版时同步编译了 debug 版本的 elf,那把 .debug 放到 perf report 要读的目录下就可以了。
100.00% 0.00% go_10cpu go_10cpu [.] 0x000000000046f641
|
---0x46f641
0x43a2cb
0x4a2226
我固然精通符号 但在玩了几个小时的 perf buildid-cache、 perf report --symfs、namespace 魔术且听 AI 瞎指挥之后依然无法成功,恼羞成怒,想到一个好😤的解决方法。
我们直接看 perf record 尝试从哪里去读 elf 来解析符号
strace -e trace=file -fTtt -- perf report -g --stdio
过滤一下结果里 buildid / build-id / 二进制名字,比如我本地 ubuntu 2404 能找到
16:43:51.422168 newfstatat(AT_FDCWD, "/root/.debug/.build-id/e8/bdccd3a32295d2545a52e78648834e3f0e3c34", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = ENOENT (No such file or directory)
在 eks 节点 6.1.159-181.297.amzn2023.x86_64 上能看到
08:45:02.012542 newfstatat(AT_FDCWD, "/usr/lib/debug/go_10cpu.debug", {st_mode=S_IFREG|0755, st_size=2411431, ...}, 0) = ENOENT (No such file or directory)
那就把二进制文件(从容器里、镜像里、 /proc/$PID/root/ 里)拷贝过去,比如对于我上述的 eks 节点就是
cp /proc/$(pidof go_10cpu)/root/go_10cpu /usr/lib/debug/go_10cpu.debug
然后 perf report 就正常了
100.00% 100.00% go_10cpu go_10cpu [.] main.burnFor
|
---runtime.goexit.abi0
runtime.main
main.main
main.burnFor
测试发现对 static linked / static-pie linked 都没问题,目标进程死后也能解析。动态链接没测过,但我目测找到 perf-report 要从那里读 libc.so 再手动考虑过去就行了。
这套方法也可以用来做符号分离,比如在生产运行 stripped elf,perf record 不可能解析出符号;但是发版时同步编译了 debug 版本的 elf,那把 .debug 放到 perf report 要读的目录下就可以了。