TfTpu_Initialize Bootstrap
本页上的所有地址都适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(buildlibtpu_lts_20260413_b_RC00,build-id md589edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,ELF x86-64 DYN,未 strip;反混淆后的 C++ 符号按原文引用)。其他版本会有所不同。
摘要
PJRT_Plugin_Initialize(PJRT slot 8)是框架侧需要按下的唯一按钮,但它触发的工作是 TPU driver bootstrap:解析 flag 字符串、获取跨进程锁,并运行 Google module-init DAG,最终执行那些 dlopen 时构造函数风暴只记录下来的 HAL/platform 注册。这个 bootstrap 是一条短而明确的调用链:TryAcquireTpuLock → GetLibTpuInitArguments → InitializeDriver → InitGoogleExceptChangeRootAndUser → RealInitGoogle → GoogleInitializer::RunInitializers;它完全幂等:InitializeDriver 内部单个函数静态 has_initialized 字节会让第一次之后的每次调用都成为 no-op。本页负责说明这条链。PJRT 侧 gate(struct_size 兼容性检查、kPjRtCApiTpuInitType selector、error-wrapper boxing)见 module-init-plugin-discovery.md §3,这里只做概要;本页新增的是 InitializeDriver 的内部、option ingest 的内部,以及 TfTpu_*ApiFn() C-API 函数指针表的角色。
对重实现者来说,决定性的结构事实是:不存在一个单一的 TfTpu_Initialize orchestrator 在一个函数体里构建 runtime。TfTpu_Initialize 这个名字确实是一个真实导出的 C 符号,位于 0xe6f54a0,导出为 @@VERS_1.0,10 字节,但它只是一个两条指令的 tail-shim,直接转发到 tpu::driver::InitializeDriver @ 0x204cecc0;二进制实际通过 PJRT 路径(PJRT_Plugin_Initialize)到达 InitializeDriver,而不是通过 TfTpu_Initialize。InitializeDriver 本身只做三件事:把默认 Cloud-TPU flags 合入 argv,把该 argv 交给 Google 的 InitGoogle flag-parse-and-run-modules 机制,并注册少量 telemetry gauges。所有对顺序敏感的事情都发生在 RunInitializers 内部,按拓扑依赖顺序执行,并与 C++ static-init 顺序解耦。这是经典的 Google base/init_google 模式:加载时注册,首次 init 时运行。
第三个主题,即 TfTpu_*ApiFn() 表,是 bootstrap 对 runtime 其余部分重要的原因。除 PJRT 外,libtpu 还携带一个第二套更老的 C-ABI surface:StreamExecutor TPU shim,它通过原始 C 函数指针结构体分发(stream_executor::tpu::ExecutorApiFn()、OpsApiFn()、ProfilerApiFn())。每个 accessor 都是一个 leak-on-exit Meyers singleton,持有 TfTpu_*Fn 名册。bootstrap 的 RunInitializers 会运行 RegisterTpuPlatform;该函数会读取 ExecutorApiFn()(经由 IsStreamExecutorEnabled),并且如果表的第一个 slot 非空,就在 PJRT 下安装 StreamExecutor TpuPlatform。因此 bootstrap 正是两套 C-ABI surface 被接线到一起的时刻。
对重实现来说,契约是:
InitializeDriver函数体:has_initializedonce-guard、"./tpu_driver"合成argv[0]、AppendNewCloudTPUArgs、vector<string>→char**argv 物化、InitGoogleExceptChangeRootAndUser(name="N/A", &argc, &argv, change_root=0)调用,以及四个 telemetry 注册。- option ingest:
GetLibTpuInitArguments读取 envLIBTPU_INIT_ARGS,按空格拆分,构建vector<string>再构建vector<char const*>;RealInitGoogle通过ParseCommandLineNonHelpFlags消费它(--xla_*/TPU_*absl-flag 解析),这些 flag 由dlopen构造函数风暴预注册。 - ApiFn 表:三个
stream_executor::tpu::*ApiFn()Meyers singletons,以及在 DAG 运行期间 gate StreamExecutorTpuPlatform安装的IsStreamExecutorEnabled/IsInitializedslot-0 探测。
| Bootstrap gate | pjrt::tpu_plugin::PJRT_Plugin_Initialize @ 0xe6a9d00(303 B),PJRT slot 8 |
| Driver bring-up | tpu::driver::InitializeDriver @ 0x204cecc0(1764 B) |
TfTpu_Initialize 符号 | @ 0xe6f54a0(10 B,@@VERS_1.0)— 2 指令 tail-shim → InitializeDriver(备用入口,不是 PJRT 路径) |
| Option ingest | tensorflow::tpu::GetLibTpuInitArguments @ 0x20ccca20(851 B),env LIBTPU_INIT_ARGS |
| Lock gate | tensorflow::tpu::TryAcquireTpuLock @ 0x20ccbc40(3531 B),env TPU_LOAD_LIBRARY |
| Flag parse + DAG run | RealInitGoogle @ 0x210ae860 → ParseCommandLineNonHelpFlags + GoogleInitializer::RunInitializers @ 0x210b2d20 |
| 幂等 guard | InitializeDriver::has_initialized(函数静态字节 @ 0x225899e0,.bss) |
| Init-type selector | kPjRtCApiTpuInitType(静态 = 2)@ 0x22255b40(.data) |
| ApiFn 表 | ExecutorApiFn @ 0x20819360,OpsApiFn @ 0x10900e80,ProfilerApiFn @ 0x10900ea0(Meyers singletons) |
| 置信度 | CONFIRMED(byte-anchored vs decompile),除非某行或 callout 另有说明 |
1. 进入 InitializeDriver 的两个入口
目的
tpu::driver::InitializeDriver @ 0x204cecc0 是实际的 driver bootstrap 函数体。有两个不同的调用方会到达它;重实现者必须理解它们是进入同一个幂等函数的并行入口,而不是分层调用栈。
入口点
PATH A — the PJRT path (the one the framework drives)
PJRT_Plugin_Initialize 0xe6a9d00 (slot 8)
├─ ActualStructSizeIsGreaterOrEqual("PJRT_Plugin_Initialize_Args", 27, 16, args->struct_size)
├─ if kPjRtCApiTpuInitType != 0: (statically 2)
│ ├─ TryAcquireTpuLock("PJRT_Plugin_Initialize_Args") 0x20ccbc40 ── env TPU_LOAD_LIBRARY
│ ├─ GetLibTpuInitArguments(&argv_vec) 0x20ccca20 ── env LIBTPU_INIT_ARGS
│ └─ InitializeDriver(flag=1, argc, argv,
│ init_type_is_2 = (kPjRtCApiTpuInitType == 2)) 0x204cecc0
└─ else: return NULL (init-type 0 → no-op)
PATH B — the TfTpu_Initialize alternate entry
TfTpu_Initialize 0xe6f54a0 (10 B, 2 instructions)
mov $0x1, %ecx (hardcodes 4th arg init_type_is_2 = true)
jmp tpu::driver::InitializeDriver 0x204cecc0 (tail-jump; rdi/esi/rdx from caller)
```text
> **NOTE —** 页面标题命名了 `TfTpu_Initialize`,但实际活跃路径是 Path A。`TfTpu_Initialize @ 0xe6f54a0` 确实是一个 10 字节的 forwarding shim:它的整个函数体只有两条指令,`mov $0x1, %ecx` 然后 `jmp InitializeDriver`。它唯一做的事情就是把第 4 个参数(`InitializeDriver(bool, int, char const**, bool)` 的 `bool init_type_is_2`,通过 `%ecx` 传递)硬编码为 `1`;另外三个参数(`rdi`/`esi`/`rdx` = driver_flag, argc, argv)从调用方原样透传,且 `jmp`(不是 `call`)使其成为没有自身栈帧的 tail-shim。(IDA C 重建会把硬编码的 `%ecx` 渲染成一个假的 `&dword_0+1` argv 指针;反汇编才是权威:`%ecx` 是最后的 `bool`,不是 argv。)它存在的目的,是让 legacy `tensorflow/core/tpu/` 调用方(它们把 driver bring-up 拼写为 `TfTpu_Initialize`,就像 legacy StreamExecutor C-ABI 把所有东西都拼成 `TfTpu_*`)到达 PJRT gate 到达的同一个函数体。重实现可以导出其中一种或两种拼写;两者都必须汇入由同一个 `has_initialized` 字节保护的单个 `InitializeDriver`,否则同时触碰*两个* surface 的进程会双重初始化。
### 算法
```c
function InitializeDriver(bool driver_flag, int argc, char const** argv_in, bool init_type_is_2): // 0x204cecc0
// (a) hard idempotence gate — first instruction
if driver_flag == 0 || (has_initialized & 1) != 0: // function-static byte
return // already up, or disabled
// (b) synthesize argv[0] and fold in Cloud-TPU defaults
args = new vector<string>()
args[0] = "./tpu_driver" // 24-B std::string, SSO inline
AppendNewCloudTPUArgs(&args, argc, argv_in) // 0x204c7340 — append Cloud-TPU flags + caller argv
// (c) flatten vector<string> -> char** (SSO-aware: byte-23 sign bit = heap/inline)
n = args.size()
cargv = malloc(8 * (n + 1)) // +1 for the NULL terminator
for i in 0..n:
cargv[i] = args[i].is_long() ? args[i].heap_ptr : &args[i].inline_buf // byte 23 < 0 ⇒ heap
cargv[n] = NULL
// (d) parse flags + run the module DAG (the real work)
InitGoogleExceptChangeRootAndUser("N/A", 3, &n, &cargv, change_root=0) // 0x210b0180
// → RealInitGoogle: ParseCommandLineNonHelpFlags(&n, &cargv) // the --xla_*/TPU_* parse
// → RealInitGoogle: GoogleInitializer::RunInitializers("module") // *** runs HAL + platform ***
free(cargv)
// (e) telemetry gauges
RegisterLibtpuGaugeTelemetry("megascale.error.detected.gauge", 30, 1)
RegisterMegascaleErrorHandler("megascale.error.detected.gauge")
RegisterLibtpuGaugeTelemetry("slice.error.detected.gauge", 26, 1)
if EnableRuntimeUptimeTelemetry(): // anon-ns predicate
InitializeUptimeMetricViaEnvironmentVariables(0, 26)
// (f) latch the guard, free the temporary vector<string>
has_initialized = 1
destroy(args) // frees each long string then the buffer
returnGOTCHA — 合成的
argv[0]是字符串字面量"./tpu_driver",写作一个 24 字节 inlinestd::string(decompile 显示strcpy(v7, "./tpu_driver")写入一个operator new(0x18)slot,并带有0x0C的 size byte)。随后RealInitGoogle会把它当作SetProgramUsageMessage中的程序名,以及日志里的argv[0]。如果重实现者传入宿主进程真实的argv[0],就会改变 absl 看到的程序名,并可能改变 flag-file / usage 行为;libtpu 有意伪造一个稳定名称,让 TPU driver 对命令行的视图独立于嵌入进程。
函数映射
| 函数 | 地址 | 大小 | 角色 |
|---|---|---|---|
tpu::driver::InitializeDriver | 0x204cecc0 | 1764 B | bootstrap 函数体(guard、argv、InitGoogle、telemetry) |
TfTpu_Initialize | 0xe6f54a0 | 10 B | 2 指令 tail-shim(mov $1,%ecx; jmp)→ InitializeDriver(legacy/备用入口) |
tpu::driver::AppendNewCloudTPUArgs | 0x204c7340 | — | 将 Cloud-TPU 默认 flags 合入 argv vector<string> |
InitGoogleExceptChangeRootAndUser | 0x210b0180 | 8 B | 薄 wrapper → RealInitGoogle(…, change_root=0) |
RealInitGoogle | 0x210ae860 | large | flag parse + RunInitializers + 进程级 init |
GoogleInitializer::RunInitializers | 0x210b2d20 | — | 拓扑 module-DAG 运行(PHASE B) |
RegisterLibtpuGaugeTelemetry | — | — | 注册一个命名 telemetry gauge |
RegisterMegascaleErrorHandler | — | — | 安装 Megascale error-detection handler |
EnableRuntimeUptimeTelemetry | — | — | 控制 uptime metrics 的 anon-ns predicate |
注意事项
has_initialized 字节是 InitializeDriver 中唯一的幂等保证;PJRT gate 的 TryAcquireTpuLock once-lock 和 DAG 自己的逐模块运行状态是独立的、分层的 guard。因此 bootstrap 在三个独立层面上都是幂等的:重新调用 PJRT_Plugin_Initialize 是一个快速 no-op,因为锁已经持有,并且 has_initialized 已设置,并且每个 module 的运行状态都是 DONE。重实现若丢掉三者之一,在常规单线程 init 下仍然安全,但会在其他 guard 所覆盖的某个并发或重复 init 场景下失去幂等性。
2. Option Ingest:LIBTPU_INIT_ARGS 与 Flag Parse
目的
TPU runtime 几乎完全由 absl command-line flags 配置,但 plugin .so 没有自己的命令行。libtpu 会从环境变量伪造一个命令行。GetLibTpuInitArguments @ 0x20ccca20 将 LIBTPU_INIT_ARGS 转换为 argv 风格的向量;InitializeDriver 前置 argv[0] 和 Cloud-TPU defaults;RealInitGoogle 将结果交给 ParseCommandLineNonHelpFlags,由它绑定到 dlopen 构造函数风暴预注册的 absl flag tables。
算法
function GetLibTpuInitArguments() -> {vector<string> store, vector<char const*> argv}: // 0x20ccca20
s = getenv("LIBTPU_INIT_ARGS") // str @ file 0x918c880
if s == NULL:
return { {}, {} } // empty argv
// (a) split the env string on ' ' (space char, 0x20) into string_views
views = absl::StrSplit(string_view(s, strlen(s)), ByChar(' '), AllowEmpty)
// (b) deep-copy each view into an owned std::string (24-B SSO records, NUL-terminated)
store = vector<string>()
for v in views:
store.push_back(string(v.data, v.len)) // long strings heap-alloc; short ones inline
// (c) materialize a parallel vector<char const*> pointing at each owned string's data
argv = vector<char const*>()
for str in store:
argv.push_back(str.is_long() ? str.heap_ptr : &str.inline_buf)
return { store, argv } // store owns the bytes; argv is the C view
```text
> **NOTE —** 拆分使用单个空格和 `AllowEmpty`,不是 shell 风格 tokenizer:没有引号处理,没有 escape 处理,也不按 tab/newline 拆分。`LIBTPU_INIT_ARGS="--xla_tpu_foo=1 --bar"` 中的双空格会在两个 flag 之间产生一个空字符串 token。Cloud-TPU production 会把该变量设为以空格拼接的 `--xla_*` / `--tpu_*` flag 列表;重实现者必须复现这种*纯空格拆分*,因为任何本身包含空格的 flag 值都会被拆成多个 token,并且很可能被拒绝。
### parse 如何消费它
`InitializeDriver` 构造最终的 `char**`:`["./tpu_driver", <Cloud-TPU defaults…>, <LIBTPU_INIT_ARGS tokens…>, NULL]`,并把 `&argc`/`&argv` 传给 `InitGoogleExceptChangeRootAndUser`,后者转发到 `RealInitGoogle @ 0x210ae860`。在那里,flag parse 是 `ParseCommandLineNonHelpFlags(&argc, &argv, remove_flags)`(non-help variant,因此这里不会特殊处理 `--help`),并被 `GoogleInitializer::Require("command_line_flags_parsing")` 和 `Require("command_line_flags_parsed")` module gate 包围。无法识别的 flags 由 absl flag 机制处理,而不是由 libtpu 直接处理。
> **QUIRK —** flag *registry* 在 `dlopen` 时构建,而不是在这里构建。`--xla_*` / `TPU_*` flags 之所以存在,是因为数百个 `_GLOBAL__sub_I_*_flags.cc` 静态构造函数在 `INIT_ARRAY` 风暴期间运行并填充了 absl flag tables(`debug_options_flags.cc`、`deepsea_platform_flags.cc`,以及 `_GLOBAL__sub_I_tpu_platform_registration.cc` 注册的逐模块 `FLAGS_tf_jf_*` 定义)。`ParseCommandLineNonHelpFlags` 只是把值*绑定到*这个已存在的 registry。重实现者不能把 flag *注册*推迟到 init time,同时还期望 init-time parsing 成功;当 `RealInitGoogle` 运行时,registry 必须已经填充。因此可识别 flag name 的集合由构造函数风暴定义([module-init-plugin-discovery.md](module-init-plugin-discovery.md) §2.2),而不是由本函数定义。
### `RealInitGoogle` 除 parse 外还做什么
`RealInitGoogle` 是完整的 `InitGoogle` 函数体,而不只是 flag parser。在 parse 前后,它会运行标准 Google 进程 bring-up:`InitializeSymbolizer`、`StartUpWallTimer`、kernel-version logging、一个 `mlock_style` flag switch(对 code pages 执行 `mlockall`)、`nice` priority adjustment、terminate-handler / signal-handler installation、对 `.data` 和 `.text` 进行 hugepage remapping、RCU domain init,以及 CPU/wall profiler registration;随后调用 `GoogleInitializer::RunInitializers("module")`(DAG run),最后把 `init_google_state = 2` 并通知 `InitGoogleDoneNotification`。就 bootstrap 目的而言,这些事情中只有两个重要:flag parse 和 DAG run。其余内容作为周边 `InitGoogle` 行为记录在这里,以便重实现者知道它们发生在 TPU bootstrap *内部*,并不能单独调用。
---
## 3. DAG Run:注册变成执行的地方
### 目的
`GoogleInitializer::RunInitializers("module") @ 0x210b2d20` 从 bootstrap 尾部的 `RealInitGoogle` 内被调用;这里才会按拓扑依赖顺序真正*运行* HAL factories、XLA target functors,以及构造函数风暴只是*注册*下来的 StreamExecutor `TpuPlatform`。本页不重新说明 DAG 机制本身,那在 [module-init-plugin-discovery.md](module-init-plugin-discovery.md) §3;本页负责其边界:bootstrap 是触发器,而它运行的其中一个 module 会接入 §4 的 ApiFn 表。
### 运行内容
```text
RealInitGoogle 0x210ae860
└─ GoogleInitializer::RunInitializers("module") 0x210b2d20 *** PHASE B ***
└─ drive registered modules in topological dep order:
google_init_module_tpu_hal_{jxc,pxc,vxc,glc,gfc}_*
→ TpuHalFactory::Register(PlatformType, TpuVersion, factory) 0x1fbb16a0
(register per-TpuVersion; NO silicon scan yet)
google_init_module_xla_target_{jellyfish,…,ghostlite}
→ RegisterTargetCreationFunctor(N, …)
google_init_module_tpu_platform 0x213eabc0 (jmp)
→ RegisterTpuPlatform 0xe99a3a0 ── reads ExecutorApiFn() (§4),
installs StreamExecutor TpuPlatform
… all other registered modules in dep order …QUIRK — 这里仍然不会触碰 silicon。HAL factories 按
(PlatformType, TpuVersion)注册;选择实际TpuVersion的 PCI-device-ID scan 要再过两个阶段才运行,位于PJRT_Client_Create(slot 15)内部。因此 bootstrap 结束后,runtime 拥有完整填充的factory registry 和完整填充的 flag state,但还没有选定 device。TpuVersion detection 被推迟:首次 init 时注册(这里),首次 client 时检测。
4. TfTpu_*ApiFn() 函数指针表
目的
除 PJRT 外,libtpu 还携带 legacy StreamExecutor TPU C-ABI:194 个导出的 Tpu<Class>_<Method> 符号(@@VERS_1.0;例如 TpuExecutor_* ×25、TpuTransferManager_* ×19、TpuStream_* ×8,等等),通过原始 C 函数指针结构体分发。dispatch indirection 是一小族 accessor 函数,每个都返回一个进程全局表。bootstrap 是这些表变得相关的地方:RunInitializers 步骤会运行 RegisterTpuPlatform,该函数读取 executor 表,以决定是否安装 StreamExecutor platform。表的内容(逐名册的 TfTpu_*Fn slot-by-slot map)由 shim overview 负责:../shim/overview.md;本节只负责它们的形状、存储,以及 bootstrap 如何探测它们。
Accessor 是 Meyers singletons
// Each is a one-line return-address-of-a-function-local-static — leak-on-exit, no destructor.
TfTpu_ExecutorApiFn* stream_executor::tpu::ExecutorApiFn(): // 0x20819360
return &ExecutorApiFn::executor_api_fn // process-global table
TfTpu_OpsApiFn* stream_executor::tpu::OpsApiFn(): // 0x10900e80
return &OpsApiFn::ops_api_fn
TfTpu_ProfilerApiFn* stream_executor::tpu::ProfilerApiFn(): // 0x10900ea0
return &ProfilerApiFn::profiler_api_fn
```text
这三个都与 PJRT `pjrt_api` singleton 使用同一习惯用法:函数局部 static,其 storage 位于 `.bss`/`.lbss`,并通过地址返回。一旦填充,它们在进程生命周期内不可变,并且无锁读取。该表是一个平坦的 C 函数指针数组:每个 roster entry 一个 slot;因此通过它进行 dispatch 就是一次间接调用,成本模型与 legacy `c_api_decl.h` surface 完全相同。
### Bootstrap 如何探测 executor 表
`RegisterTpuPlatform @ 0xe99a3a0`(在 DAG 内运行,§3)根据 executor 表是否 live 来 gate platform installation:
```c
function RegisterTpuPlatform(): // 0xe99a3a0
fn = stream_executor::tpu::ExecutorApiFn() // 0x20819360 — the table
if IsStreamExecutorEnabled(fn) // 0x20819380 — probe + handshake
and !tpu_platform_registered: // byte guard @ 0x224c5388
p = new tensorflow::tpu::TpuPlatform() // 0xe999960, sizeof 0x98
PlatformManager::RegisterPlatform(p) // 0x1d0fe120
tpu_platform_registered = 1
return 1
function IsStreamExecutorEnabled(table): // 0x20819380
if table[0] == NULL: return 0 // slot 0 = init fn-ptr unset ⇒ disabled
handle = table[0](table) // call init fn; returns an opaque handle
if handle == NULL: return 0
table[1](handle) // call finalize fn (slot+8) on the handle
return 1
function IsInitialized(table): // 0x208193c0
return table[0] != NULL // cheap liveness probe — slot 0 non-nullGOTCHA — executor 表通过 slot 0 非空来 dispatch,而不是通过单独的 "enabled" flag。
IsInitialized是廉价探测(table[0] != 0);IsStreamExecutorEnabled是较重的探测:它实际调用 slot 0(一个返回 opaque handle 的 init thunk),然后对该 handle 调用 slot 1(finalize thunk),把一次干净的往返视为 shim 已接线的证据。重实现者填充这些表时,必须让 slot 0 保持 NULL 以保持 StreamExecutor off(此时RegisterTpuPlatform不注册任何东西,PJRT 独立存在),或者用可工作的 init/finalize pair 填充 slot 0 以将其切换为 on。不存在第三种状态。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
stream_executor::tpu::ExecutorApiFn | 0x20819360 | 返回 &executor_api_fn(Meyers singleton,executor roster table) |
stream_executor::tpu::OpsApiFn | 0x10900e80 | 返回 &ops_api_fn(Meyers singleton,ops roster table) |
stream_executor::tpu::ProfilerApiFn | 0x10900ea0 | 返回 &profiler_api_fn(Meyers singleton,profiler roster table) |
stream_executor::tpu::IsStreamExecutorEnabled | 0x20819380 | slot-0 探测 + init/finalize handshake(gate TpuPlatform) |
stream_executor::tpu::IsInitialized | 0x208193c0 | 廉价的 table[0] != NULL liveness probe |
tensorflow::tpu::RegisterTpuPlatform | 0xe99a3a0 | 读取 executor 表,安装 StreamExecutor TpuPlatform |
注意事项
重实现者必须把握的一点是:InitializeDriver 不会填充这些表。它运行 DAG,而 DAG 运行 RegisterTpuPlatform,后者读取一个已经填充的 ExecutorApiFn()。表的填充路径(哪个 TfTpu_*Fn setter 写入这些 slot,以及何时写入)本页没有追踪;这是 ../shim/overview.md 负责的独立问题。这里由字节确认的是消费侧:accessor 形状、slot-0 dispatch 规则,以及 RegisterTpuPlatform 在 bootstrap 期间执行的 handshake。最终位于 PJRT 下方的 SE platform,以及它如何服务 Tpu*_* exports,见 ../pjrt/stream-executor-host-interpreter.md。
NOTE — 在刚加载的 libtpu 中,观察到 executor 表的 slot 0 为空(
.bsssingleton 在加载时被零填充,且没有 static ctor 写入它)。是否有任何路径会在RegisterTpuPlatform运行前填充它,从而决定 StreamExecutorTpuPlatform是否会在 PJRT-only configuration 中安装,本页没有追踪(对 populated-vs-empty 结果为 LOW confidence;probe logic 为 CONFIRMED)。PJRT stack 并不需要它:PJRT 直接到达 driver core,而Tpu*_*exports 会独立包装同一个 backing implementation。
5. Bootstrap Once-Guards
目的
bootstrap 通过叠加三个独立的 once 机制来保证可重入安全。只复现 PJRT gate 或只复现 driver guard 的重实现,会得到不同的幂等性轮廓。
| Guard | 位置 | 地址 / token | 重新调用时跳过什么 |
|---|---|---|---|
absl::Mutex once-lock | TryAcquireTpuLock::mu | guard 0x225925d0 / obj 0x225925c8 | 跨进程 acquisition;第二次调用会看到锁已持有 |
| 函数静态字节 | InitializeDriver::has_initialized | 0x225899e0(.bss) | 整个 InitializeDriver 函数体(argv 构建、InitGoogle、telemetry) |
absl::Mutex + 逐模块 run-state | GoogleInitializer::RunInitializers | inside 0x210b2d20 | 重新运行任何状态已是 DONE 的 module |
| 函数静态字节 | RegisterTpuPlatform::tpu_platform_registered | 0x224c5388 | 重新安装 StreamExecutor TpuPlatform |
__cxa_guard for InitGoogleDoneNotification | inside RealInitGoogle | guard in .bss | 重新启用 init-done notification |
| init-type selector | kPjRtCApiTpuInitType(= 2) | 0x22255b40(.data) | 如果它为 0,则跳过整个 bring-up |
| env gate | TPU_LOAD_LIBRARY(位于 TryAcquireTpuLock) | str @ file 0x887356a | — 控制是否尝试获取锁 |
| env args | LIBTPU_INIT_ARGS(位于 GetLibTpuInitArguments) | str @ file 0x918c880 | — 提供 flag 字符串 |
NOTE —
kPjRtCApiTpuInitType在.data中静态为2;init-type 2 走完整 bring-up(InitializeDriver(…, init_type_is_2 = true)),init-type 0 让PJRT_Plugin_Initialize成为 no-op。init-type2与假设中的1是否会改变InitializeDriver的行为,本页没有追踪;init_type_is_2参数会被计算并传入,但反编译的InitializeDriver函数体在追踪区域内没有明显基于它分支(对 init-type 改变 driver path 为 LOW confidence;静态 selector 值2和透传为 CONFIRMED)。
相关组件
| 组件 | 关系 |
|---|---|
PJRT_Plugin_Initialize @ 0xe6a9d00 | PJRT 侧 gate;调用进入此 bootstrap(完整 gate 见 module-init-plugin-discovery.md §3) |
tpu::driver::InitializeDriver @ 0x204cecc0 | 本页负责的 bootstrap 函数体 |
TfTpu_Initialize @ 0xe6f54a0 | 进入同一个 InitializeDriver 的 legacy/备用 10 字节 tail-shim 入口 |
GetLibTpuInitArguments @ 0x20ccca20 | LIBTPU_INIT_ARGS option ingest |
RealInitGoogle @ 0x210ae860 | flag parse + module-DAG run + 进程 bring-up |
GoogleInitializer::RunInitializers @ 0x210b2d20 | 执行已注册 modules 的 DAG run(PHASE B) |
ExecutorApiFn / OpsApiFn / ProfilerApiFn | StreamExecutor C-ABI dispatch tables(Meyers singletons) |
RegisterTpuPlatform @ 0xe99a3a0 | 读取 executor 表,在 PJRT 下安装 SE TpuPlatform |
交叉引用
- overview.md — lifecycle section map:从
dlopen到可用 client - module-init-plugin-discovery.md — 本页上方的 wrapper:discovery handshake、注册 flags 和 modules 的
dlopen构造函数风暴,以及调用此 bootstrap 的PJRT_Plugin_Initializegate - get-pjrt-api-thunk.md —
GetPjrtApithunk,以及在此 bootstrap 之前发生的 lazy 140-slotPJRT_Apibuild - ../shim/overview.md — 逐 roster 的
TfTpu_*FnC-API 表:slot-by-slot 内容,以及本页只探测的填充路径 - ../pjrt/stream-executor-host-interpreter.md — 消费
ExecutorApiFn并位于 PJRT client 下方的 StreamExecutor platform