动态插桩——不重编译,也能给算子"装监控"
系列三 · 第 11 篇
上篇埋了个钩子:工具在 rtKernelLaunch 这个口子、算子真正开跑前,做了一件事。这件事,就是本篇的主角——动态二进制插桩(DBI)。
先回到一个矛盾。第 2 篇说过,要检测就得在编译时插桩,因为那些记录事件的"探针"是编译期埋进去的。可总有些场景没法重编译——算子二进制是别人给的,或者重编一次的代价太大。怎么办?答案是:不改源码、不重编译,直接对着已经编译好的算子二进制动手,在它即将运行的那一瞬间,现场给它安上探针。
这套"现场改装"分四步,全发生在 kernel 启动前的一小段时间里。
第一步,导出。工具把即将执行的算子二进制从设备的注册信息里 dump 出来,拿到一个二进制的"坯件"。
第二步,选桩。工具内部有一张巨大的对照表——就是探针绑定注册表里那数百条记录——写着"哪种昇腾指令,该配哪个探针函数"。它从表里挑出这个算子用到的指令所对应的探针,再从自己的插件库里把对应的探针实现段 dump 出来。
第三步,链接。把探针和算子坯件交给链接器,这里用的是 LLVM 家的 ld.lld——跟编译器同出一系。链接器把它们拼成一个可运行的整体,探针从此"长"在了算子的指令序列里。
第四步,调优。链接完还没完,得用昇腾的调优工具(bisheng-tune)再加工一遍,处理好跳转、生成最终二进制,重新注册回设备,这才真正执行。
于是,一份从没为检测编译过的算子,就在启动的瞬间被"改装"成了带监控的版本。代价是明显的:相比编译期静态插桩,它拿不到源码调试信息,报告里给不出"第几行代码"这种精确调用栈,只能说清"哪条指令、哪个核、碰了什么内存"。
说句公道话:这四步里,探针实现和那张对照表在本仓库(mssanitizer);而"导出二进制、调链接器、重新注册"的真正执行逻辑,放在另一个公共组件仓库(msopscommon)里。这也解释了为什么我们说它"玩操作"——它是整个工具里最贴近底层、也最依赖昇腾编译工具链的一环。
下一篇,我们顺着"探针"往下钻,看看一条昇腾指令,工具是怎么"读懂"它到底碰了哪块内存的。
- 点赞
- 收藏
- 关注作者
评论(0)