Profiler 工厂注册表和收集器管线
本页中的所有地址和偏移均适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d)。其他版本会有所不同。
摘要
PJRT Profiler 扩展只是很薄的一层 C-ABI 外壳。真正执行工作的机器位于下一层:一个进程全局的工厂注册表,一个把单次生命周期调用扇出到每个已组装收集器的 tsl::profiler::ProfilerCollection,一组把主机和设备 trace 状态排入共享 XSpace 的具体 ProfilerInterface 实现,以及一个按每次运行来控制哪些 TPU 芯片实际参与的 TpuProfilerControlListener 单例。本页说明的就是这台机器。扩展结构体、8 槽 PLUGIN_Profiler_Api vtable,以及 PLUGIN_Profiler handle 位于 ../pjrt/ext-profiler.md;这里从 PLUGIN_Profiler_Create 调用的 tsl::profiler::CreateProfilers(opts) 接起,并沿着数据流追踪到线路输出。
其形态是标准的 TSL/xprof profiler 架构,也就是 TensorFlow 发布的同一套架构。收集器通过 RegisterProfilerFactory @ 0x1CF50780 注册一个工厂 std::function<unique_ptr<ProfilerInterface>(const ProfileOptions&)>,进入由 absl::Mutex 保护的单个全局 vector。CreateProfilers @ 0x1CF50860 在同一个 mutex 下遍历该 vector,调用每个工厂,并把每个非空结果包装进 ProfilerController(顺序/崩溃隔离保护器),然后收集到返回的 vector<unique_ptr<ProfilerInterface>> 中。ProfilerCollection 接收这个 vector,并通过迭代其内部 vector、调用每个 controller 上匹配的 vtable 槽,暴露相同的三方法 ProfilerInterface 表面:Start/Stop/CollectData。collection 自身也是 一个 ProfilerInterface,因此该设计是一个组合体:profiler 组成的 profiler。
具体收集器是异构的。xprof::tpu::TpuProfilerImpl::CollectData @ 0xEF34860 是设备排出路径:它在 XSpace arena 上分配一个 XprofResponse proto,通过内部收集器拉取每芯片 trace 数据,并通过 ConvertResponseToTpuXSpace 把结果折叠进设备 XPlane。tsl::profiler::ThreadpoolProfilerInterface::CollectData @ 0xF3326C0 是一个近乎平凡的主机收集器,只在其 session 以非 OK 结束时追加一条诊断字符串。HostTracer 捕获主机 TraceMe 事件。它们都不是逐 trace event 触达的;它们只在 CollectData 时运行,排出由无锁 TraceMe 宏和硬件环形缓冲区累计的状态。与收集器路径正交的是,TpuProfilerControlListener(GetOrCreateTpuProfilerControlListener @ 0xF332800)让每个 TPU 芯片驱动在打开 trace buffer 之前或排出之后询问 CanStartProfiler/MustStopProfiler,因此 profiling 可以与执行并发运行,而 listener 逐芯片决定是否参与。
对重新实现而言,契约如下:
- 注册表:一个惰性构造的
vector<std::function<...>>(GetFactories()::factories),在mu下修改,永不清空。 - 工厂遍历:
CreateProfilers在整个遍历期间持有mu,调用每个工厂,丢弃 null 结果,把其余结果包装进ProfilerController。 ProfilerControllerFSM:一个 phase 计数器和一个粘性的 last-status,用来强制 Start→Stop→CollectData 顺序,并在任何错误之后短路。ProfilerCollection扇出:32 字节对象,迭代内部 vector,调用 vtable+0x10/+0x18/+0x20;第一个非平凡 status 胜出的合并;CollectData内部会破坏性地清空 vector。- 收集器语义:设备排出(
TpuProfilerImpl)与主机追加(ThreadpoolProfilerInterface、HostTracer)。 - 运行门控:
TpuProfilerControlListener委托给xprof::tpu::Profiler::Register/UnregisterChipProfiler。
| 注册表修改器 | tsl::profiler::RegisterProfilerFactory @ 0x1CF50780 |
| 注册表存储 | GetFactories()::factories @ 0x2257C830 — vector<std::function<...>>,惰性 new |
| 注册表 mutex | tsl::profiler::(anonymous)::mu @ 0x2257C828(absl::Mutex) |
| 工厂遍历 | tsl::profiler::CreateProfilers @ 0x1CF50860 |
| 崩溃/顺序保护器 | tsl::profiler::ProfilerController ctor @ 0x1CF50CE0,32 字节 |
| 组合体 | tsl::profiler::ProfilerCollection,32 字节,vtable @ 0x217738A0 |
| 扇出 | Start @ 0xF6A1640,Stop @ 0xF6A16C0,CollectData @ 0xF6A1740 |
| 设备收集器 | xprof::tpu::TpuProfilerImpl::CollectData @ 0xEF34860 |
| 主机收集器 | ThreadpoolProfilerInterface::CollectData @ 0xF3326C0;HostTracer(factory @ 0xF32F7C0) |
| 运行门控单例 | GetOrCreateTpuProfilerControlListener @ 0xF332800,16 字节,vtable symbol @ 0x2175C1A0(installed vptr 0x2175C1B0) |
| 输出 | 一个 tensorflow.profiler.XSpace proto,主机 + 每设备 planes |
工厂注册表
目的
profiler 是在链接时插入的,而不是在调用时插入的。每个后端,包括 host tracer、TPU device tracer、threadpool tracer、megascale RPC tracer,都会在 C++ 静态初始化期间贡献一个工厂函数。注册表就是这些工厂落入的单一全局列表,而 CreateProfilers 是唯一的读取者。这使得同一个后端既能从现代 PJRT 扩展触达,也能从旧版 TpuProfiler_* C-ABI 触达:两者都调用 CreateProfilers,而它遍历同一个共享列表。
入口点
<static init> _GLOBAL__sub_I_*profiler*.cc
└─ RegisterProfilerFactory(std::function<...>) ── 0x1CF50780 (under mu)
└─ GetFactories()::factories ── 0x2257C830 (lazy `new(0x18)`)
PLUGIN_Profiler_Create / TpuProfiler_Create
└─ CreateProfilers(opts) ── 0x1CF50860 (under mu)
```text
### 算法
`RegisterProfilerFactory` 是受保护的 append。`factories` vector 是一个 24 字节的 `{begin, end, cap_end}` header,在首次使用时于读取者也使用的同一个 mutex 下分配,然后对每个传入的 `std::function`(32 字节槽)执行 move-emplace。
```c
function RegisterProfilerFactory(fn /* std::function, 32 bytes */): // 0x1CF50780
mu.Lock(); // 0x2257C828
if (!guard(factories)): // one-time init
factories = operator new(0x18); // {0,0,0} vector header
zero(factories);
if (factories.size == factories.cap): // grow
emplace_back_slow_path(factories, fn);
else: // in-place move
slot = factories.begin + 32 * factories.size;
move_construct(slot, fn); // steals fn's callable; clears fn
factories.size += 1;
mu.Unlock();
// No return value. The vector is never shrunk or cleared for the process lifetime.注意 — 注册表没有注销路径,也没有 clear。在静态初始化期间注册的工厂会一直存在到进程退出。因此,
CreateProfilers对各个 handle 是幂等的:每个PLUGIN_Profiler_Create(或TpuProfiler_Create)看到的工厂集合完全相同。
函数映射
| 函数 | 地址 | 作用 |
|---|---|---|
RegisterProfilerFactory | 0x1CF50780 | 在 mu 下追加一个工厂 |
GetFactories()::factories | 0x2257C830 | 全局 vector<std::function<...>> |
(anonymous)::mu | 0x2257C828 | 保护该 vector 的 absl::Mutex |
工厂遍历 — CreateProfilers
目的
CreateProfilers 是每 session 收集器集合的构造器。它在每次 PLUGIN_Profiler_Create 时调用,接收解析后的 tensorflow::ProfileOptions,并返回一个 vector<unique_ptr<ProfilerInterface>>,其中每个选择加入的工厂对应一个被 ProfilerController 包装的收集器。ProfileOptions 在这里不会被检查;它会原样传给每个工厂,由工厂自行决定是否参与(工厂返回 NULL 表示退出,例如 CPU-only device_type 上的设备 tracer)。
入口点
PLUGIN_Profiler_Create (0xE6F0C60, see ../pjrt/ext-profiler.md)
└─ CreateProfilers(opts) ── 0x1CF50860
├─ mu.Lock() ── held across the whole walk
├─ for each factory slot:
│ ├─ result = factory(opts) ── indirect call, slot+16
│ └─ if result: ProfilerController(new(0x20), result) ── 0x1CF50CE0
└─ mu.Unlock()
└─ new ProfilerCollection(move(result)) ── 0xF6A15E0
```text
### 算法
```c
function CreateProfilers(out /* vector<unique_ptr<ProfilerInterface>> */, opts): // 0x1CF50860
out = {begin:0, size:0, cap:0};
mu.Lock(); // 0x2257C828
lazy_init(factories); // same one-time path as the mutator
for slot in factories: // 32 bytes per std::function
raw = slot.invoke(opts); // *(slot+16)(slot, &opts) — the factory call
if (raw != NULL): // factory opted in
ctrl = operator new(0x20); // ProfilerController, 32 bytes
ProfilerController(ctrl, raw); // 0x1CF50CE0 — takes ownership of raw
push_back(out, ctrl); // grows out (2x) as needed
mu.Unlock();
return out;注意 —
mu在0x1CF50860处会贯穿整个遍历过程被持有,包括间接的factory(opts)调用;mu.Unlock()是返回前的最后一条语句。因此工厂体在持有mu的状态下运行,并且不得重入调用RegisterProfilerFactory或CreateProfilers(mu是非递归的absl::Mutex);这样做会自死锁。不同 PJRT handle 除了这个被串行化的遍历之外不共享可变状态。怪癖 — 工厂返回
NULL会被静默丢弃,并不算错误。结合“此处不检查”的规则,如果某个ProfileOptions的device_type没有匹配任何工厂,就会得到一个空的ProfilerCollection。之后每个Start/Stop/CollectData都会对零个收集器成功,并生成一个空XSpace。重新实现必须把空 collection 成功视为有效结果,而不是要拒绝的错误配置。
函数映射
| 函数 | 地址 | 作用 |
|---|---|---|
CreateProfilers | 0x1CF50860 | 遍历注册表、包装、收集 |
ProfilerController::ProfilerController(unique_ptr) | 0x1CF50CE0 | 包装一个收集器以进行隔离 |
ProfilerCollection::ProfilerCollection(vector) | 0xF6A15E0 | 内联接收结果 vector |
ProfilerController 隔离保护器
目的
工厂返回的每个收集器在进入 collection 之前都会被包装进 ProfilerController。controller 是围绕内部 ProfilerInterface 的有限状态保护器:它强制合法调用顺序(Start → Stop → CollectData),缓存最后一个 status,使失败的阶段短路所有后续阶段,并在任何违规时记录一个 absl::Status。这是崩溃隔离层:出错或被乱序调用的收集器无法污染 collection 中其余收集器,也无法污染 PJRT handle 上的生命周期状态机。
布局
/* tsl::profiler::ProfilerController — 32 bytes (operator new(0x20)). */
struct ProfilerController {
/* +0x00 */ void** vtable; /* ProfilerInterface vtable */
/* +0x08 */ uint32_t phase; /* FSM: 0=created, 1=started, 2=stopped, 3=collected */
/* +0x0C */ uint32_t _pad;
/* +0x10 */ ProfilerInterface* inner; /* the wrapped collector; owned */
/* +0x18 */ absl::Status last_status; /* sticky; 1 == inline OkStatus */
};
```text
### 算法
`Start` 和 `CollectData` 主体(`0x1CF50DE0`、`0x1CF51060`)共享同一种形态:检查 phase,推进 phase,确认最后一个 status 为 OK,然后调用内部收集器匹配的 vtable 槽,并缓存新的 status。`Stop`(`0x1CF50F20`)在 phase 1→2 处遵循同样模式。
```c
function ProfilerController::Start(this): // 0x1CF50DE0
if (this.phase != 0): // wrong order
return Log(MakeErrorImpl<10 ABORTED>("Start called in the wrong order")); // profiler_controller.cc:51
this.phase = 1;
if (this.last_status != OK): // a prior phase already failed
return Log("Previous call returned an error.");
s = this.inner->Start(); // vtable +0x10
this.last_status = s; // cache (ref-counted)
if (s != OK): Log(s);
return s;
function ProfilerController::CollectData(this, xspace): // 0x1CF51060
if (this.phase != 2): // must follow a Stop
return Log("CollectData called in the wrong order.");
this.phase = 3;
if (this.last_status != OK):
return Log("Previous call returned an error.");
s = this.inner->CollectData(xspace); // vtable +0x20
this.last_status = s;
if (s != OK): Log(s);
return s;陷阱 — 让顺序违规可存活的是 controller,而不是
ProfilerCollection。PJRT handle 的ready字节(见../pjrt/ext-profiler.md)是一个粗粒度、复用过度的门控,它不会检测例如Stop前的CollectData。controller 在这里捕获这种情况,记录到profiler_controller.cc:84,并返回错误 status,且不会调用内部收集器。因此,乱序调用无法驱动一个半初始化的 device tracer。省略 controller、直接分发到收集器的重新实现,会把这些 bug 暴露为崩溃而不是记录的 status。
函数映射
| 函数 | 地址 | 作用 |
|---|---|---|
ProfilerController::Start | 0x1CF50DE0 | phase 0→1,受保护的内部 Start |
ProfilerController::Stop | 0x1CF50F20 | phase 1→2,受保护的内部 Stop |
ProfilerController::CollectData | 0x1CF51060 | phase 2→3,受保护的内部 CollectData |
ProfilerController::~ProfilerController (D2/D0) | 0x1CF50D20 / 0x1CF50DA0 | 丢弃内部对象,unref status |
ProfilerCollection 扇出
目的
ProfilerCollection 是组合体:它通过把每个调用转发给所有成员来实现 ProfilerInterface。PJRT handle 在 +0x70 处正好拥有一个这样的对象;五个 PJRT 生命周期方法会编组进入它的三个真实方法。因为它自身也是一个 ProfilerInterface,该设计可以干净地嵌套,尽管实践中 collection 持有的是 ProfilerController,而不是嵌套的 collection。
布局
/* tsl::profiler::ProfilerCollection — 32 bytes. vtable @ 0x217738A0. */
struct ProfilerCollection {
/* +0x00 */ void** vtable; /* {top, RTTI, D2, D0, Start, Stop, CollectData} */
/* +0x08 */ ProfilerController** begin; /* inner vector data */
/* +0x10 */ size_t size; /* element count */
/* +0x18 */ size_t capacity;
};
```text
`0x217738A0` 处的 vtable 是 [PJRT 层](../pjrt/ext-profiler.md)所调用偏移的来源:`Start` 位于 `+0x10`(`0xF6A1640`),`Stop` 位于 `+0x18`(`0xF6A16C0`),`CollectData` 位于 `+0x20`(`0xF6A1740`),destroying dtors 位于 `+0x08`(`0xF6A1840` D2 / `0xF6A18E0` D0)。
### 算法
三个扇出方法都共享一种 status 合并惯用法:按顺序遍历内部 vector,调用成员匹配的 vtable 槽,在 `Unref` 每个后续非内联 status 的同时保留**第一个**非平凡 status。`CollectData` 在遍历之后还会增加一个破坏性的清理 pass。
```c
function ProfilerCollection::Start(this): // 0xF6A1640
if (this.size == 0): return OkStatus; // empty collection — trivially OK
merged = OkStatus; // sentinel 1
for ctrl in this[0 .. size): // forward order
s = ctrl->Start(); // vtable +0x10 (the controller's Start)
if (merged == OkStatus): merged = s; // first status wins
else if (s is heap-rep): Unref(s); // discard later statuses
return merged;
function ProfilerCollection::CollectData(this, xspace): // 0xF6A1740
if (this.size != 0):
merged = OkStatus;
for ctrl in this[0 .. size):
s = ctrl->CollectData(xspace); // vtable +0x20 — SAME xspace, members APPEND
if (merged == OkStatus): merged = s; else if (s is heap-rep): Unref(s);
for ctrl in this[size-1 .. 0]: // REVERSE destroy pass
slot = take(ctrl); set slot = NULL;
if (slot): slot->~() // vtable +0x08 — destroying dtor
this.size = (begin_after - begin) >> 3; // collapses to 0 — vector cleared
else:
merged = OkStatus;
return merged;Stop @ 0xF6A16C0 在结构上与 vtable 槽 +0x18 处的 Start 相同,并且没有 destroy pass。
注意 — status 合并是第一个 status 胜出:在
Start/Stop/CollectData中,merged初始化为内联 Ok sentinel,并且只在它仍等于该 sentinel 时才被覆盖,因此它捕获第一个返回 status(OK 或非 OK)的成员,并对所有后续 status 执行Unref。由于空 collection 会提前返回 OK,第一个产生真实 status 的收集器决定合并结果。陷阱 — 单个共享的
XSpace*是 append 契约。每个成员都写入同一个 proto:TpuProfilerImpl添加设备XPlane,ThreadpoolProfilerInterface可能添加一个errors字符串,HostTracer添加 host plane。因此成员顺序决定输出中的 plane 顺序。破坏性的反向销毁 pass 随后把 collection 重置为空,这正是 PJRTCollectData依赖的一次性行为;第二次调用会在空 vector 上运行,并重新序列化缓存的字节。
函数映射
| 函数 | vtable 槽 | 地址 | 作用 |
|---|---|---|---|
ProfilerCollection::Start | +0x10 | 0xF6A1640 | 将 Start 扇出到所有成员 |
ProfilerCollection::Stop | +0x18 | 0xF6A16C0 | 将 Stop 扇出到所有成员 |
ProfilerCollection::CollectData | +0x20 | 0xF6A1740 | 扇出 CollectData,然后销毁成员 |
ProfilerCollection::~ (D2/D0) | +0x08 | 0xF6A1840 / 0xF6A18E0 | 丢弃剩余成员 + heap |
ProfilerCollection::ProfilerCollection(vector) | — | 0xF6A15E0 | 接收 CreateProfilers 结果 |
具体收集器
xprof::tpu::TpuProfilerImpl — 设备排出
这是唯一接触 TPU 硬件的收集器。它的 CollectData @ 0xEF34860 是从设备侧 trace transport(XprofResponse)到 XSpace 设备 planes 的桥梁。它在 XSpace 的 arena 上分配 response(因此 response 生命周期绑定到输出 proto),用 session 的 run_id 和第二个 metadata 字段为其打标,排出内部每芯片收集器,然后进行转换。
function TpuProfilerImpl::CollectData(this, xspace): // 0xEF34860
arena = arena_of(xspace); // xspace+8, untag low bit
resp = Arena::DefaultConstruct<XprofResponse>(arena);
if (this.field_0x10 && this.field_0x18): // session identifiers present
resp.set_at(296, this.field_0x10); resp.flags |= 0x100000; // e.g. run_id
resp.set_at(336, this.field_0x18); resp.flags |= 0x8000000;
inner = this.field_0x08; // per-chip collector
if (inner):
s = inner->vtable[+40](inner, resp); // drain into resp
this.field_0x08 = NULL; inner->~(); // release the inner collector
if (s != OK):
if (resp on heap): XprofResponse::SharedDtor(resp); free(resp);
return s;
wrapper = XprofResponseWrapper(resp, arena, {...});
ConvertResponseToTpuXSpace(wrapper, xspace, &nptr, 0); // resp -> device XPlanes appended to xspace
DropExcessBytes(xspace, xspace); // trim oversized event payloads
return OkStatus;
```text
> **注意 —** `ConvertResponseToTpuXSpace` 是每芯片 `TraceEntry` blob 变成 `/device:TPU:N` planes 上 `XEvent`/`XEventMetadata` 的地方;`DropExcessBytes` 对 event metadata 强制执行大小上限。两者属于 [trace-entries coder](trace-entries-coder.md) 和 [XPlane/XStat/TraceMe](xplane-xstat-traceme.md) 页面;在这里它们是设备排出路径的不透明尾部。
### `tsl::profiler::ThreadpoolProfilerInterface` — 主机追加
一个近乎平凡的主机收集器。它的 `CollectData` @ `0xF3326C0` 在 collect 时不做 tracing 工作;它只通过把字符串化的 status 追加到 XSpace 的 `errors` repeated field(`XSpace.errors = 2`,结构偏移 `+40`)来报告失败的 session。
```c
function ThreadpoolProfilerInterface::CollectData(this, xspace): // 0xF3326C0
if (this.status != OK): // this+0x08
msg = this.status.ToStringSlow();
slot = xspace.errors.Add(arena_of(xspace)); // repeated string errors, +40
xspace.has_bits |= 2;
assign(slot, msg);
return OkStatus; // always OK怪癖 —
ThreadpoolProfilerInterface不贡献任何XPlane。它唯一可见的输出是在其 threadpool-tracing session 失败时写入XSpace.errors的条目。扫描 planes 以寻找“threadpool tracer 捕获了什么”的消费者不会找到任何东西;信号在errors中,而不在planes中。
xprof::cpu::HostTracer — 主机 plane
HostTracer 是主机侧 TraceMe 收集器,由 CreateHostTracer @ 0xF32F7C0 注册(它包装了 CreateHostTracer(HostTracerOptions) @ 0xF32F820)。它的 Start/Stop/CollectData 位于 0xF32FA40 / 0xF32FAC0 / 0xF32FB40。在 CollectData 时,它把每线程 TraceMe recorders flush 到 /host:0 XPlane。TraceMe 捕获路径本身位于 XPlane/XStat/TraceMe。
收集器映射
| 收集器 | 工厂 | CollectData | 输出到 XSpace |
|---|---|---|---|
xprof::tpu::TpuProfilerImpl | (旧版 TpuProfiler_Create 路径) | 0xEF34860 | 通过 ConvertResponseToTpuXSpace 写入 /device:TPU:N planes |
ThreadpoolProfilerInterface | (static-init) | 0xF3326C0 | 仅失败时写入 errors 字符串 |
xprof::cpu::HostTracer | 0xF32F7C0 | 0xF32FB40 | 来自 TraceMe 的 /host:0 plane |
注意 — 完整的工厂清单尚未穷举。这三个由 symbol 和反编译主体确认;其他工厂(例如由
FLAGS_enable_megascale_profiler@0x2236E238门控的 megascale RPC tracer)分布在多个_GLOBAL__sub_I_*profiler*.ccstatic-init block 中,完整性为低置信度。
运行门控 — TpuProfilerControlListener
目的
收集(CollectData)是 session 结束时的一次性排出,但 tracing 会与 PJRT_LoadedExecutable_Execute 并发运行。两者之间的协调点是 TpuProfilerControlListener 单例,它在构造时安装到每个 TPU 芯片驱动中。芯片在打开其每核心 trace ring buffer 之前会询问 CanStartProfiler;为支持执行中途停止,它会轮询 MustStopProfiler。PJRT 收集器路径并不直接知道哪些芯片会参与;这完全由 listener 决定,并委托给全局 xprof::tpu::Profiler 单例。
布局和构造
/* xprof::tpu::TpuProfilerControlListener — 16 bytes, vtable symbol @ 0x2175C1A0 (installed vptr 0x2175C1B0). */
struct TpuProfilerControlListener {
/* +0x00 */ void** vtable;
/* +0x08 */ xprof::tpu::Profiler* profiler; /* the global profiler singleton */
};
function GetOrCreateTpuProfilerControlListener(): // 0xF332800
if (guard(singleton)): return singleton; // singleton @ 0x224C5D78, __cxa_guard @ 0x224C5D80
p = operator new(0x10);
p->profiler = Profiler::GetOrCreateProfilerSingleton(); // 0xF336640
p->vtable = off_2175C1B0; // installed vptr = vtable symbol 0x2175C1A0 + 0x10
singleton = p;
return singleton;
```text
### 算法
两个 gate 方法都是 `xprof::tpu::Profiler` 单例之上的薄适配器。`CanStartProfiler` 收集芯片身份和配置,并调用 `RegisterChipProfiler`;`MustStopProfiler` 调用 `UnregisterChipProfiler`。register/unregister 对才是真正的门控,返回给芯片驱动的 boolean 表示单例是否接受(start)或要求停止。
```c
function TpuProfilerControlListener::CanStartProfiler(this, chip_loc, chip_profiler, run_id): // 0xF3328C0
VLOG(1) "CanStartProfiler: chip_ordinal=" << chip_loc.index_on_host(); // deepsea_listeners.cc:33
cfg = chip_loc.chip->config;
flags = TpuChipConfig::GetSpecialPurposeSyncFlags(cfg);
// builds a request {chip_id, run_id, sync_flag_value, megacore} ...
return Profiler::RegisterChipProfiler(this.profiler); // delegate: accepts or vetoes the chip
function TpuProfilerControlListener::MustStopProfiler(this, chip_loc): // 0xF332A00
VLOG(1) "MustStopProfiler: chip_ordinal=" << chip_loc.index_on_host(); // deepsea_listeners.cc:56
return Profiler::UnregisterChipProfiler(this.profiler, chip_loc.index_on_host());注意 — listener 在 gate 附近还带有若干编译器侧注册方法:
RegisterLloModule@0xF332BC0、RegisterCompilerMetadata@0xF332AE0、RegisterDebugMetadata@0xF332D20、RegisterBarnaCoreSyncFlagMetadata@0xF333140,它们会在编译期间用 HLO source locations 丰富 device-planeXEventMetadata。它们是同一个单例的一部分,但属于设备排出路径的输入,而不是 start/stop gate;其细节属于 device-plane 页面。
函数映射
| 函数 | 地址 | 作用 |
|---|---|---|
GetOrCreateTpuProfilerControlListener | 0xF332800 | __cxa_guard 单例,包装 Profiler* |
CanStartProfiler(chip_loc, profiler, run_id) | 0xF3328C0 | 芯片进入门控:委托给 RegisterChipProfiler |
MustStopProfiler(chip_loc) | 0xF332A00 | 芯片退出轮询:委托给 UnregisterChipProfiler |
Profiler::GetOrCreateProfilerSingleton | 0xF336640 | 被包装的 xprof::tpu::Profiler |
注意 — listener 的 vtable 槽顺序相对于其抽象基类尚未提取;四个注册方法和两个 gate 方法已在引用地址处确认,但每个方法在 vtable(symbol
0x2175C1A0,installed vptr0x2175C1B0)中的位置为低置信度。
端到端数据流
JAX / PT-XLA ──Create──► PLUGIN_Profiler_Create (0xE6F0C60)
└─ CreateProfilers(opts) (0x1CF50860, under mu)
walk factories ─► [HostTracer][TpuProfilerImpl][Threadpool]...
each wrapped in ProfilerController (0x1CF50CE0)
└─ ProfilerCollection(move(vec)) (0xF6A15E0) ─► handle+0x70
──Start──► collection->Start (0xF6A1640) ─► each ctrl->Start ─► inner->Start
(meanwhile, per chip: TpuProfilerControlListener::CanStartProfiler)
──Stop───► collection->Stop (0xF6A16C0) ─► each ctrl->Stop ─► inner->Stop
(per chip: MustStopProfiler poll)
──CollectData──► collection->CollectData(&xspace) (0xF6A1740)
├─ each ctrl->CollectData(&xspace) (APPEND to one XSpace)
│ TpuProfilerImpl ─► /device:TPU:N planes (0xEF34860)
│ HostTracer ─► /host:0 plane
│ Threadpool ─► errors[] on failure (0xF3326C0)
└─ destroy all members; collection now empty (one-shot)
─► XSpace serialized to caller buffer (PJRT layer)
```text
---
## 与相邻层的关系
| 层 | 拥有内容 | 关系 |
|---|---|---|
| [PJRT Profiler 扩展](../pjrt/ext-profiler.md) | extension struct、`PLUGIN_Profiler_Api` vtable、handle、5 方法生命周期 | 调用 `CreateProfilers` 和本文档说明的 `ProfilerCollection` 扇出 |
| [旧版 `TpuProfiler_*` ABI](tpu-profiler-abi.md) | 5 个 C exports + 120 字节 handle | 通过 `CreateProfilers` 触达*同一个*注册表/collection |
| [XPlane / XStat / TraceMe](xplane-xstat-traceme.md) | XSpace event hierarchy + host capture | 这些收集器追加写入的 proto |
| [Trace-entries coder](trace-entries-coder.md) | 每芯片 `TraceEntry` decode | `ConvertResponseToTpuXSpace` 折叠进来的设备 payload |
---
## 交叉引用
- [PJRT Profiler 扩展](../pjrt/ext-profiler.md) — 位于这个 factory/collection 机器*之上*的 extension struct、`PLUGIN_Profiler_Api` vtable、handle layout 和 5 方法生命周期;本页记录支配它的遍历期间持锁 mutex 作用域和第一个 status 胜出的合并
- [Profiling 概览](overview.md) — 这个收集器管线在 libtpu 整体 telemetry 架构中的位置
- [旧版 TpuProfiler C-ABI](tpu-profiler-abi.md) — 同一个 `CreateProfilers` 注册表的第二个消费者,并通过 handle size 和 error channel 进行对比
- [XPlane / XStat / TraceMe](xplane-xstat-traceme.md) — 收集器把 planes 扇入的 `XSpace` proto
- [Trace-entries Coder](trace-entries-coder.md) — `TpuProfilerImpl` 的 `ConvertResponseToTpuXSpace` 背后的每芯片 `TraceEntry` decode