news 2026/9/6 18:26:44

BOLT Address Translation(BAT)详解:在已优化二进制上采集 Profile 并反译回输入二进制的原理与实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BOLT Address Translation(BAT)详解:在已优化二进制上采集 Profile 并反译回输入二进制的原理与实践

BOLT Address Translation(BAT)详解:在已优化二进制上采集 Profile 并反译回输入二进制的原理与实践

【免费下载链接】llvm-projectThe LLVM Project is a collection of modular and reusable compiler and toolchain technologies.项目地址: https://gitcode.com/GitHub_Trending/ll/llvm-project

BOLT Address Translation(BAT)解决的是一个很实际的性能工程问题:如果只能在生产环境部署已经过 BOLT 优化的二进制,如何在其上采集采样 Profile,再把这些样本地址翻译回优化前的输入二进制,用于下一轮优化?读完本文,你将掌握 BAT 的端到端工作流(--enable-bat生成翻译表、perf2bolt反译 Profile)、.note.bolt_bat段的完整二进制编码格式(函数表、地址翻译表、次入口点表的 delta/LEB128 编码细节),以及BoltAddressTranslation类在 BOLT 源码中的构建、解析与查询链路。

1. 背景:为什么需要 BAT

BOLT 的常规 Profile 采集流程是:在未优化的输入二进制上插桩或用 perf 采样,得到基于输入二进制地址的 Profile,再交给 BOLT 做函数重排、基本块重排、冷热拆分等优化。但在真实部署场景中,往往只能部署已优化的二进制——也就是说采样只能发生在 BOLT 输出的二进制上。此时样本地址是"输出地址空间"中的地址,而 BOLT 又不支持直接对已优化二进制再做二次优化。

BAT 正是为此设计的:BOLT 在输出二进制中额外插入一个名为.note.bolt_bat的 note 段,其中保存地址翻译表函数冷热拆分(split functions)的关联信息。有了这个段,就可以把采集自优化二进制的 Profile 映射回原始输入二进制,从而避免为了单纯采集数据而部署非 BOLT 版本。

这一设计目标在头文件注释中写得很明确(见 BoltAddressTranslation.h 的类注释):

We do not support reoptimizing a binary already processed by BOLT, but we do support collecting samples in a binary processed by BOLT. We then translate samples back to addresses from the input (original) binary, one that can be optimized. The goal is to avoid special deployments of non-bolted binaries just for the purposes of data collection.

从源码结构看,内存中的表示以函数为单位组织:每个函数有一张自己的std::multimap<uint32_t, uint32_t>翻译表(MapTy),键为输出地址空间中区域的起点偏移,值为输入地址空间中的偏移;翻译时先在表中找到小于等于样本地址的最大键,再据此换算出输入地址。此外,只有会改变控制流的指令(分支、调用)才会作为"分支源"条目出现在表中,而基本块起始位置被记录下来是因为它们可能是跳转目标(LBR trace 中的 To 地址),也用于重建函数内基本块布局,以便根据 LBR 轨迹恢复 fall-through(隐式顺序执行)边。

2. 使用方式:从生成 BAT 段到反译 Profile

BAT 的完整使用流程分两步:

第一步:启用 BAT 并运行 BOLT 优化。--enable-bat控制是否生成 BAT 段,将采样的 Profile 与(不含 BAT 的)输入二进制一起交给llvm-bolt,并在参数中加上--enable-bat。该选项定义于 CommandLineOpts.cpp,默认值为false,描述为 "write BOLT Address Translation tables":

cl::opt<bool> EnableBAT("enable-bat", cl::desc("write BOLT Address Translation tables"), cl::init(false), cl::ZeroOrMore, cl::cat(BoltCategory));

第二步:在优化后的二进制上采集 Profile,并用perf2bolt反译。将采到的采样 Profile(如 perf 数据)连同包含 BAT 段的优化二进制一起传给perf2boltperf2bolt会读取.note.bolt_bat段,将其填充进BoltAddressTranslation对象,随后在样本处理过程中由DataAggregator查询该对象,把输出地址空间中的地址/偏移重建为输入二进制中的地址/偏移,最终产出一份针对原始输入二进制的 Profile。

在 DataAggregator.cpp 中可以观察到这一查询链路的具体调用点:BAT->translate(Func.getAddress(), ...)负责地址反译(普通样本与 LBR 样本各有一处)、BAT->fetchParentAddress(...)用于把拆分出的冷片段样本归并回其父热函数、BAT->getFallthroughsInTrace(...)用于从 LBR 轨迹推断被取到的 fall-through 边。仓库中的端到端测试 bolt-address-translation.test 演示了--reorder-blocks=normal --split-functions --enable-bat的组合用法,bolt-address-translation-yaml.test 则覆盖了--reorder-functions=cdsort --enable-bat --dyno-stats场景下的 Profile 输出;仓库还提供了 bat-dump 工具用于转储解析后的翻译表,方便人工检查。

需要注意的一个前提:llvm-bolt在写入 BAT 段时会记录每个基本块/指令的输入-输出地址映射(IO address map),BinaryFunction.cpp 中即有 "accurate IO address map later (used for BAT, ...)" 的相关逻辑;当二进制没有重定位(无 relocations)且函数体未被修改时,函数被视为"simple",无需翻译表(见 BoltAddressTranslation.cpp 中write的跳过条件)。

3. BAT 段的内容组织

.note.bolt_bat段整体组织如下:

Hot functions table (热函数表) └─ Address translation tables (各函数的地址翻译表) Cold functions table (冷函数表)

即:先是一张热函数表(每个函数附带自己的地址翻译表),后跟一张冷函数表。热/冷两个表共享同一套编码,差异在下方各小节单独标出。

从实现角度看,BoltAddressTranslation类由 BOLT linker 提供的地址翻译信息构建(write遍历BinaryContext中的每个BinaryFunction,通过writeEntriesForBB为每个基本块及其内部的控制流指令生成条目),然后以 ELF note 段的形式编码进输出二进制。反过来,perf2bolt处理 BAT 启用的二进制时,parse方法从 note 段重建内存表示,供DataAggregator在样本处理时查询。

4. 编码格式详解

编码的权威定义在 BoltAddressTranslation.h 与 BoltAddressTranslation.cpp 中。整体布局为:

Hot functions table Cold functions table Functions table: |------------------| | Function entry | | | | Address | | translation | | table | | | | Secondary entry | | points | |------------------|

4.1 函数表(Functions table)

热、冷函数表共享下述编码,差异在各条目最后一列标出。表头只有一个字段:

条目编码说明
NumFuncsULEB128该函数表中的函数数量

表头之后是NumFuncs个函数条目。两个关键的压缩策略:

  • 输出二进制地址采用 delta 编码:只存储与上一个输出地址的差值,地址隐含从 0 开始。输出地址在函数起始地址与函数内部偏移之间、以及热/冷片段之间都是"连续"的(continuous),这样差值更均匀,能更好地省空间。
  • 热函数索引(HotIndex)同样 delta 编码,隐含从 0 开始。

各条目定义如下:

条目编码说明热/冷
AddressContinuous, Delta, ULEB128函数在输出二进制中的地址两者
HotIndexDelta, ULEB128对应热函数在热函数表中的索引
FuncHash8b输入函数的哈希
NumBlocksULEB128原始函数中基本块的数量
NumSecEntryPointsULEB128原始函数中次入口点的数量
ColdInputSkewULEB128需要应用到所有输入偏移上的偏移量(skew)
NumEntriesULEB128该函数地址翻译表中的条目数两者
EqualElemsULEB128函数开头输入偏移等于输出偏移的条目数两者
BranchEntriesBitmask,alignTo(EqualElems, 8)个 bitEqualElems非零时,标记哪些条目带BRANCHENTRY位的位图两者

函数头部之后依次是含NumEntries个条目的地址翻译表,以及(仅热函数)含NumSecEntryPoints个条目的次入口点表

其中HotIndex的作用可以从 BoltAddressTranslation.h 的成员ColdPartSource("Links outlined cold blocks to their original function")印证:冷函数表条目通过HotIndex指向其父热函数,perf2bolt侧的fetchParentAddress查询正是利用这份关联把冷片段上的样本归并到父函数;FuncHashNumBlocks则对应内存结构FuncHashesTy/BBHashMapTy,用于在 BAT 模式下校验函数身份并重建输入二进制的基本块布局(DataAggregator写 YAML Profile 时会调用getBFHashgetNumBasicBlocksgetBBHashMap填充这些元数据)。

4.2 地址翻译表(Address translation table)

delta 编码的含义是:只编码与上一个对应条目的差值,输入偏移隐含从 0 开始。

条目编码说明分支/BB
OutputOffsetContinuous, Delta, ULEB128函数在输出二进制中的偏移两者
InputOffsetOptional, Delta, SLEB128输入二进制中函数的偏移,最低位(LSB)为BRANCHENTRY两者
BBHashOptional, 8b输入二进制中基本块的哈希BB
BBIdxOptional, Delta, ULEB128输入二进制中基本块的索引BB

格式上的三个要点:

  1. 等偏移省略:表会省略函数开头EqualElems个"输入偏移等于输出偏移"的条目——因为这些位置本身就不需要翻译。
  2. BRANCHENTRY位的语义:该位标记该偏移对是否为控制流源(分支或调用指令);未置位则表示控制流目标(基本块偏移)。这与 BoltAddressTranslation.h 中const static uint32_t BRANCHENTRY = 0x1;一致,也解释了为何InputOffset用 SLEB128 且值域左移一位。
  3. 等偏移条目的输入地址省略:当输入、输出函数中偏移相等时,InputOffset字段整体省略,此时BRANCHENTRY位单独编码在BranchEntries位向量中(即上表 4.1 中的位图字段)。

在 BoltAddressTranslation.cpp 的writeEntriesForBB中可以看到条目生成的实际规则:每个输出基本块至少产生一条 BB 起始条目(Map.emplace(BBOutputOffset, BBInputOffset << 1),左移一位即为BRANCHENTRY位留空);随后遍历块内记录的位置符号,为每个控制流指令追加一条(InputOffset << 1) | BRANCHENTRY条目。对于被优化删除的基本块,编码上输出其OutputOffset等于函数大小——这类条目不影响地址翻译,只参与输入基本块映射(DataAggregatorisInputBlock的判断正依赖完整的输入块映射)。

值得注意的工程细节:当多个输入块映射到同一输出地址时(例如只含 NOP 或跳转的块被删除,与后继块共享地址),编码策略是"后写者胜出",优先保留后继块(successor)的映射,保证样本不会错误落到已删除块上。

4.3 次入口点表(Secondary Entry Points table)

该表仅对热片段输出,包含NumSecEntryPoints个偏移,表示次入口点(函数体内允许从外部直接跳转进入的位置,如跳表、尾调用目标等),采用 delta 编码、隐含从 0 开始:

条目编码说明
SecEntryPointDelta, ULEB128次入口点偏移

write流程通过Function.forEachEntryPoint收集这些偏移并缓存到SecondaryEntryPointsMap;查询侧的translateSymbol/getSecondaryEntryPointId则用于把优化二进制的符号引用翻译回父函数与对应入口点(YAMLProfileWriter.cpp 在写被调方地址时即调用translateSymbol)。

5. 小结:BAT 在 BOLT 工作流中的位置

把前述内容串起来,BAT 支撑的完整闭环是:

  1. llvm-bolt --enable-bat ...优化输入二进制,输出二进制中带有.note.bolt_bat段(热函数表 + 翻译表 + 次入口点表;冷函数表带HotIndex关联);
  2. 该优化二进制上线运行,perf 采样得到基于输出地址的 Profile;
  3. perf2bolt将采样数据与优化二进制一起处理:BoltAddressTranslation::parse从 note 段重建翻译表,DataAggregator在处理每个样本时调用translate/fetchParentAddress/getFallthroughsInTrace把地址反译、冷片段归并、fall-through 边补全,产出输入二进制地址空间的 Profile;
  4. 该 Profile 回到步骤 1,驱动下一轮 BOLT 优化。

对使用者而言需要记住的约束:BAT 面向"采集"而非"二次优化"——已 BOLT 过的二进制本身不能作为 BOLT 的输入再优化;翻译表只覆盖会出现在 LBR 中的控制流点与基本块位置,普通指令不占条目,这正是编码能够保持紧凑的原因。

【免费下载链接】llvm-projectThe LLVM Project is a collection of modular and reusable compiler and toolchain technologies.项目地址: https://gitcode.com/GitHub_Trending/ll/llvm-project

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 18:22:32

概率论与数理统计学习指南:川大数学学院PDF讲义深度解析与实战应用

简介&#xff1a;由四川大学数学学院编写的《概率论与数理统计》PDF&#xff0c;面向理工科非数学专业本科生及考研学生&#xff0c;系统覆盖随机事件与概率、离散型与连续型随机变量、数字特征、大数定律、数理统计基础、参数估计和假设检验等核心模块&#xff0c;既能作为课程…

作者头像 李华
网站建设 2026/9/6 18:21:44

FreeTube 护眼设置完整指南:5 个真实可用的步骤打造舒适夜览界面

FreeTube 护眼设置完整指南&#xff1a;5 个真实可用的步骤打造舒适夜览界面 【免费下载链接】FreeTube An Open Source YouTube app for privacy 项目地址: https://gitcode.com/GitHub_Trending/fr/FreeTube 深夜追番、关灯刷视频&#xff0c;刺眼的纯白背景十分钟就能…

作者头像 李华
网站建设 2026/9/6 18:21:32

铝型材加工工厂课程设计完整指南:工艺流程与设备选型决策

简介&#xff1a;《铝型材加工工厂课程设计说明书》是一份面向材料成型、机械及建筑工程相关专业学生的完整设计文档&#xff0c;聚焦年产量五千吨以上铝型材挤压车间的规划难题&#xff0c;涵盖材料选型、模具设计、挤压机选择、能耗与设备投资等核心环节。文档以6063/LD31铝合…

作者头像 李华