Skip to content

默认 DebugOptions

本页所有地址、结构体偏移和字段编号映射均适用于 libtpu 0.0.40(cp314 manylinux 构建,libtpu_lts_20260413_b_RC00libtpu.so build-id 89edbbe81c5b328a958fe628a9f2207d)。其他版本会重排字段偏移布局并产生差异。

摘要

xla::DefaultDebugOptionsIgnoringFlags() @ 0x1e66a860 是构造每次 TPU 编译起始基线 xla.DebugOptions 消息的单一函数。它运行在任何 flag、环境覆盖或 PJRT CompileOptions.debug_options merge 之前。名称是字面含义:它构建二进制内默认值,并忽略 flag 层,因此其函数体就是 290 字段消息的权威种子。在 upstream OpenXLA 中,等价函数是 xla/debug_options_flags.cc 中的 DefaultDebugOptionsIgnoringFlags();这里是 TPU plugin 编译进去的副本。

Proto3 不携带 descriptor 级默认值,每个标量的 wire 默认值都是零值(false / 0 / "" / enum-0)。有效默认值是这个构造器在零状态之上存储的内容。因此函数体可以干净地分成两类:它显式写入的字段(本文档记录的非零种子),以及它从不触碰的约 218 个字段,这些字段保留入口处 DebugOptions::DebugOptions(this, 0) 基础构造调用(第 60 行)建立的 proto-zero 默认值。本页负责 explicit-write → value 映射。它负责字段 schema,字段编号、名称和类型位于 debugoptions-proto.md。它也不负责 flag 覆盖(xla-flag-atlas.md)或 AUTO/-1 sentinel 解析(autoproto-autoor-resolution.md)。

反编译函数体是一段扁平的原始结构体 store 序列:*((_DWORD *)this + N) = value*((_BYTE *)this + N) = 0/1… |= mask,以及把多个相邻字段打包进一次 8 字节 move 的 packed QWORD 写入。把 store 映射回 proto 字段,需要将其 (offset, has-bit) 对与各个 set_xla_* accessor 匹配;下面每个 CONFIRMED 行都是这样锚定的。|= mask 写入是 proto2 has-bit OR(消息 _has_bits_ word 中位于偏移 8–55 的 presence flag);它们记录某字段被设置过,但不改变其值,本身并不是默认值。

对重实现而言,契约是:

  • base-zero 不变量:任何缺席于此构造器的字段都保留其 proto3 零值;构造器从不清除非零基底,只负责播种。
  • 显式种子表:函数体写入的 field → value 对,通过 (offset, has-bit) → accessor 匹配恢复。
  • 打包约定:一个 QWORD store(0x1E0000000A)如何播种两个相邻 int 字段(低 DWORD = 字段 N,高 DWORD = 字段 N+1),以及 float 字段如何搭乘 packed QWORD 的上半部分。
  • repeated-enum 种子:两个 RepeatedField<int> 会逐元素 append(xla_gpu_enable_command_buffer 和第二个 command-buffer 列表),而不是作为标量存储。
函数xla::DefaultDebugOptionsIgnoringFlags()
地址0x1e66a860(约 610 行反编译代码)
Base ctor第 60 行的 DebugOptions::DebugOptions(this, 0) — 先将全部 290 个字段清零
消息xla.DebugOptions(290 个存活字段,descriptor index 403)
显式 scalar/string 种子约 70 个 store(本页表格)
Repeated-enum 种子2 个列表(offset 160 ×6 elems,offset 288 ×3 elems)
Map 种子xla_gpu_analytical_latency_estimator_options(6 个 string→string 对)
未触碰(proto-zero)字段约 218 个 — 保持 false/0/""/enum-0
记录中的 callerAllocateFlags() 0x1e6b8020,随后是 PJRT 编译路径

默认值如何恢复

store 到字段的映射问题

反编译器针对原始 DebugOptions 对象渲染函数体,它没有 proto 字段名,只有进入 C++ 消息布局的字节偏移。如下行:

c
*((_DWORD *)this + 142) = 4;          // 0x1e66a860 + body; offset 568
*((_BYTE *)this + 24) |= 0x20u;       // has-bit OR
```text

意思是“在字节偏移 568 处存储 32 位值 4,然后在偏移 24 的 `_has_bits_` word 中设置 has-bit `0x20`”。要命名该字段,需要将这个精确的 `(value-offset, has-bit-offset, has-bit-mask)` 三元组与生成的 `set_xla_*(…)` accessor 匹配,每个 accessor 都正好写入一个字段的偏移并 OR 一个 has-bit。`xla_gpu_autotune_level` 的 accessor(`0x1e66b860`)写入 `*((_DWORD *)this + 142)` 并 OR `*((_BYTE *)this + 24) |= 0x20`,完全匹配。因此偏移 568 是字段 123,其默认值是 **4**

下面每个 CONFIRMED 行都以这种方式锚定:offset/has-bit 对已与字段的 `set_*` accessor 交叉检查。只知道 offset 的行(在 grep 预算内未执行 accessor 交叉检查)标记为 HIGH;按语义推断的 packed-pair 成员按注明标记为 HIGH 或 MEDIUM。

### 打包约定

编译器会把相邻 store 合并成宽 move。反复出现三种模式:

```c
*(_QWORD *)((char *)this + 844) = 0x1E0000000ALL;   // two int32 fields
//   offset 844 (low DWORD)  = 0x0000000A = 10  -> field 327
//   offset 848 (high DWORD) = 0x0000001E = 30  -> field 328
*((_QWORD *)this + 84) = 0x3F8CCCCD00000000LL;       // a float in the upper half
//   offset 672 (low DWORD)  = 0          -> adjacent int/bool group
//   offset 676 (high DWORD) = 0x3F8CCCCD = 1.1f -> a float field
*(_QWORD *)((char *)this + 92) = 0x101010100000003LL; // int + four bools
//   offset 92  (low DWORD)  = 3          -> field 31 (backend_optimization_level)
//   offsets 96,97,98,99     = 01 01 01 01 -> four adjacent bool fields = true

坑点 — 单个 QWORD store 不是一个字段。偏移 92 处的 0x101010100000003 一次播种了五个 proto 字段:int32 xla_backend_optimization_level(低 DWORD = 3)加上 96–99 处四个 byte 宽 bool,每个都设为 1。把 QWORD 当作一个 64 位字段值的重实现会把五个字段全弄错。要按字节 lane 对照布局解码 packed store。

怪癖 — 反编译函数体中的 &stru_800000&unk_1E00007&stru_1000100 不是指针。IDA 把宽立即数 0x8000000x1E000070x1000100 错标成数据符号,是因为其数值与某个 section/struct 地址碰撞。它们是整数/packed-pair literal:0x800000 = 8 388 608(8 MiB),0x1000100 = packed {int=256, …} 对。


显式种子表

构造器在零基底之上写入的值。Offset 是进入 DebugOptions C++ 对象的字节偏移;field# 是 debugoptions-proto.md 上的 proto 字段。“Confidence” 反映 (offset, has-bit) 对是否已匹配到具名 accessor(CONFIRMED),或仅按 offset 定位 / 从打包推断(HIGH/MEDIUM)。

已确认 scalar 种子(accessor 锚定)

Field#NameTypeOffsetDefault
31xla_backend_optimization_levelint32923
123xla_gpu_autotune_levelint325684
142xla_multiheap_size_constraint_per_heapint32584-1
228xla_gpu_redzone_padding_bytesint646808388608 (8 MiB)
237xla_gpu_collective_permute_decomposer_thresholdint646880x7FFFFFFFFFFFFFFF (INT64_MAX)
311xla_cmd_buffer_trace_cache_sizeint6481616
327xla_gpu_executable_warn_stuck_timeout_secondsint3284410
328xla_gpu_executable_terminate_timeout_secondsint3284830

最后两个是规范 packed pair:第 548 行 *(_QWORD *)((char *)this + 844) = 0x1E0000000A 一次 move 写入二者(0x0A=10 位于 844 的低 lane,0x1E=30 位于 848 的高 lane)。两个 offset 都已由 accessor 确认。

高置信 scalar 种子(offset 定位)

这些 store 在函数体中没有歧义,但其 set_* accessor 未在 grep 预算内逐个交叉检查;offset 和 value 是字节精确的,字段分配基于与已确认邻居的布局相邻性。

OffsetStore (body line)Decoded valueType inferred
568 (field 123)*((_DWORD*)this+142)=44int32 (autotune_level)
576 (field 132)*((_DWORD*)this+144)=-1-1int32 (xla_dump_max_hlo_modules)
644*((_DWORD*)this+161)=55int32
712*((_QWORD*)this+89)=1515int64
724*((_DWORD*)this+181)=9595int32
728*((_QWORD*)this+91)=100000100000int64
768*((_QWORD*)this+96)=100100int64
792*((_QWORD*)this+99)=1616int64
812*((_DWORD*)this+203)=256256int32
836*((_DWORD*)this+209)=3232int32
956*((_DWORD*)this+239)=22enum/int32
976*((_DWORD*)this+244)=4040int32
984*((_DWORD*)this+246)=2020int32
1008*((_DWORD*)this+252)=18001800int32 (timeout s)
1024*((_QWORD*)this+128)=0x4000004194304 (4 MiB)int64
1040*((_QWORD*)this+130)=20482048int64

注意 — 其中若干是独立于符号匹配也可识别的 XLA 默认值:256(xla_gpu_memory_limit_slop_factor 类)、1800 s(30 分钟 terminate timeout)、4 MiB / 8 MiB(collective-combine/redzone 字节阈值)、INT64_MAX(“无阈值” sentinel)。值是字节精确的;只有精确字段名带有 HIGH(而非 CONFIRMED)提示。

Packed-pair int/enum 种子

Body lineStoreLow lane (offset → value)High lane (offset → value)
548+844 = 0x1E0000000A844 → 10 (field 327)848 → 30 (field 328)
553+936 = 0x2800000014936 → 20 (0x14)940 → 40 (0x28)
63+92 = 0x10101010000000392 → 3 (field 31)96–99 → bool 1,1,1,1

Float 种子(上半部分打包)

DebugOptions 中存在两个 float 字段(xla_gpu_auto_spmd_partitioning_memory_budget_ratio 字段 225,xla_gpu_autotune_gemm_rtol 字段 316)。二者都通过 packed QWORD 的上半部分播种:

Body lineStoreFloat laneValue
491*((_QWORD*)this+84)=0x3F8CCCCD00000000offset 676 = 0x3F8CCCCD1.1f
543*((_QWORD*)this+103)=0x53DCCCCCDoffset 824 = 0x3DCCCCCD0.1f

0x3F8CCCCD 解码为 IEEE-754 single 1.1000000238…0x3DCCCCCD 解码为 0.1000000015…,也就是规范的 1.10.1 float literal。+103 store 还在 float 上方的 lane 中携带 0x5,播种一个相邻小 int。

String 种子(ArenaStringPtr::Set

Body lineOffsetValueLikely field
6980"./cuda_sdk_lib" (len 14)xla_gpu_cuda_data_dir (61)
551520"inf" (len 3)一个 GPU string-typed timeout/mode 字段

所有其他 ArenaStringPtr::Set(... &nptr, 0) 调用(offset 408、432、464、472、480、488、496)都设置空字符串,它们是显式 no-op 种子,会重新建立 "",与 proto-zero 默认值一致。为完整性列出它们,但它们不增加非零默认值。

怪癖 — "./cuda_sdk_lib""inf" 是烘焙进共享 upstream 默认值中的 GPU/host-platform 字符串,即使没有 TPU 代码读取 xla_gpu_cuda_data_dir。这是 libtpu 未经 TPU 裁剪地编译了完整 OpenXLA DefaultDebugOptionsIgnoringFlags 函数体(包含 GPU 默认值)的直接证据,与 proto 携带全部 183 个 xla_gpu_* 字段一致。见 overview.md


Boolean 种子

函数体写入约 40 个独立的 *((_BYTE *)this + N) = 0/1 store,另有四个 bool 打包在 offset-92 QWORD 中。列出 40 个原始 (offset, 0|1) 行会成为反模式字节倾倒;结构才是重点:

text
bool stores in DefaultDebugOptionsIgnoringFlags (line / offset / value):
  set to 1 (true):
    91, 620, 631, 929, 1055, 910, 895, 989, 588, 622, 665, 621, 742,
    894, 671, 637, 930, 806, 743, 878, 933, 909, 1007, 1006, 932, 964,
    1053, 567, 567(+1)   and the 4 packed bools at 96-99
  set to 0 (false, explicit):
    555, 100, 629, 563, 596, 575, 589, 782, 546, 966, 907, 547, 697,
    859, 965, 103, 736, 809, 991, 554, 739, 961, 934, 811, 885, 630,
    636, 1013, 905, 783, 1035, 664, 877, 698, 857, 740, 1014, 833,
    856, 1034, 858, 879, 884, 931, 892, 904, 928, 1012, 911, 988, 967,
    950, 1015, 982, 980, 1004, 1032, 1033
```text

> **注意 —** 显式 `= 0` bool store 相对于零基底是冗余的(第 60 行已经清除了它们),编译器发出它们是因为源码无条件赋值 `set_field(false)`。它们不改变有效默认值;它们只强制 has-bit,使字段序列化为“显式设置为 false”,而不是“缺席”。该 presence 区分是前端的“默认值触碰了这里”信号(见 [`debugoptions-proto.md`](debugoptions-proto.md) 上的 proto3-optional presence 模型)。
>
> **坑点 —** 因为约 60 个 bool 被显式写入 `false` *且*设置 has-bit,从这些默认值发出的 wire-level `DebugOptions` **不是**稀疏的,它会为构造器命名的每个字段携带 presence bit,而不只是非零字段。只序列化非零字段的重实现会产生字节不同(但语义相等)的消息,并破坏任何 has-bit-sensitive consumer。

具名的 true-by-default bool 没有在 grep 预算内全部与 accessor 匹配;计数和 offset 列表是字节精确的,单个字段名按布局为 HIGH 置信度。可靠命名的 `true` 默认值(由与 accessor 锚定邻居相邻确认)包括与 `xla_backend_optimization_level` 同处 offsets 96–99 的四个 packed bool。

---

## Repeated-Enum 种子 — Command Buffer 列表

两个 `RepeatedField<int>` 成员通过 `GrowNoAnnotate` + index store 逐元素填充,而不是作为标量写入。二者都是 `CommandBufferCmdType` 枚举列表(枚举:`INVALID=0, FUSION=1, CUBLAS=2, CUDNN=3, COLLECTIVES=4, CONDITIONAL=5, WHILE=6, CUSTOM_CALL=7, CUBLASLT=8, DYNAMIC_SLICE_FUSION=9, …` — 见 [`debugoptions-proto.md`](debugoptions-proto.md))。

```c
// list at offset 160 (field 258 region) — 6 elements appended:
//   1, 2, 8, 7, 3, 9
//   = FUSION, CUBLAS, CUBLASLT, CUSTOM_CALL, CUDNN, DYNAMIC_SLICE_FUSION
// list at offset 288 — 3 elements appended:
//   4, 5, 3
//   = COLLECTIVES, CONDITIONAL, CUDNN

append 惯用法是同一个六行 RepeatedField<int>::GrowNoAnnotate / *((_DWORD *)this + 41) = newcount / array[idx] = enumval 块按元素重复(函数体第 98–285 行)。因此默认启用的 command-buffer 命令集是 {FUSION, CUBLAS, CUBLASLT, CUSTOM_CALL, CUDNN, DYNAMIC_SLICE_FUSION}

List offsetElement enum valuesDecoded
1601, 2, 8, 7, 3, 9FUSION, CUBLAS, CUBLASLT, CUSTOM_CALL, CUDNN, DYN_SLICE_FUSION
2884, 5, 3COLLECTIVES, CONDITIONAL, CUDNN

注意 — 两个列表是不同的 repeated 字段(不同 base offset:160 vs 288,并有各自独立的 count word:this+41this+73)。哪个是 xla_gpu_enable_command_buffer (258)、哪个是第二个 command-buffer enable 列表,未在预算内消歧;二者都是 TPU 上惰性的 GPU-namespace 字段,但由于共享 upstream 函数体播种它们,所以仍被播种。


Map 种子 — Analytical Latency Estimator Options

构造器通过 Map::TryEmplaceInternal(函数体第 335–468 行)填充一个 map<string,string>xla_gpu_analytical_latency_estimator_options,字段 357,约 offset 216),包含六个默认 key→value 对:

text
nccl_op_launch_us  -> "-1"
nic_speed_gbps     -> "-1"
chunk_prep_us      -> "-1"
rtt_us             -> "-1"
chunk_size_bytes   -> "-1"
gpus_per_node      -> "-1"
```text

每个值都是字符串 `"-1"`,即这些 GPU NCCL 建模旋钮的 AUTO/unknown sentinel。这里的 `-1` 约定是 [`autoproto-autoor-resolution.md`](autoproto-autoor-resolution.md) 中记录的同一个“稍后解析”sentinel 的字符串形式。在 TPU 上这些是惰性的(不会运行 GPU latency estimator),但共享函数体仍会播种它们。

| Key | Default value |
|---|---|
| `nccl_op_launch_us` | `"-1"` |
| `nic_speed_gbps` | `"-1"` |
| `chunk_prep_us` | `"-1"` |
| `rtt_us` | `"-1"` |
| `chunk_size_bytes` | `"-1"` |
| `gpus_per_node` | `"-1"` |

---

## 未触碰字段 — Proto-Zero 默认值

在 290 个字段中,构造器显式播种约 70 个 scalar/string + 2 个 repeated 列表 + 1 个 map。其余约 218 个字段在 `DebugOptions::DebugOptions(this, 0)` base 调用之后从未被写入,因此保持 proto3 零默认值:

| Field type | Untouched default |
|---|---|
| `bool` | `false` |
| `int32` / `int64` | `0` |
| `float` | `0.0` |
| `string` | `""` |
| `enum` | value-0 entry(例如 `StepMarkerLocation::STEP_MARK_AT_ENTRY=0`、`DetectionMode::DETECTION_MODE_NONE=0`、`ShapeChecks::IGNORE=0`) |
| `repeated` / `map` | empty |

> **注意 —** `xla_step_marker_location` (108) 由此构造器保留在 proto-zero `STEP_MARK_AT_ENTRY=0`:`DefaultDebugOptionsIgnoringFlags` @ `0x1e66a860` **没有**任何 store 写入 step-marker 字段的 offset。此构建中的任何非零 step-marker 默认值都来自 *flag* 层或 PJRT merge,而不是这个构造器。两个连接到 flag 的字段 `xla_tpu_detect_nan`(135,offset 580)和 `xla_tpu_detect_inf`(136)同样**没有**在这里写入,二者都因缺少 store 而保持 `false`。
>
> **坑点 —** “未触碰”只相对于*这个*函数。consumer 看到的有效运行时默认值是 `DefaultDebugOptionsIgnoringFlags()` 输出**随后**叠加 flag 层(`AllocateFlags`/`MakeDebugOptionsFlags`)和 PJRT `CompileOptions.debug_options` merge。此处为 proto-zero 的字段仍可能以非零值到达编译器。本页只是*第一*层;覆盖顺序见 [`xla-flag-atlas.md`](xla-flag-atlas.md)。

---