Skip to content

TpuProfiler ABI

本页中的所有地址和偏移都适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d)。其他版本会有所不同。

摘要

TpuProfiler_* 是 libtpu 的旧版 profiler C-ABI:五个导出的 C 符号 — TpuProfiler_CreateTpuProfiler_StartTpuProfiler_StopTpuProfiler_CollectDataTpuProfiler_Destroy — 编译自 learning/45eac/tfrc/executor/stream_executor/tpu_profiler_c_api.cc(该源路径被烘焙进每个 LogMessage 位置)。这是 TensorFlow / stream-executor 时代的 profiler 入口,是 tensorflow/core/tpu/c_api_decl.h 背后的表面,由 TF 和 TPUEstimator 通过 stream_executor::tpu::ProfilerApiFn() @ 0x10900EA0 访问。它早于现代 PJRT Profiler 扩展PLUGIN_Profiler_*,扩展类型 1),并与之并行运行;二者共享完全相同的后端 — 由全局工厂注册表构建的、包含 host 和 device 子 profiler 的 tsl::profiler::ProfilerCollection — 但它刻意采用更精简的 ABI。

这两个表面在三个结构性方面不同,读过 PJRT 页面的人在重新实现时必须内化全部三点。第一,错误处理:旧版 ABI 没有 Error_Destroy/Error_Message/Error_GetCode helper,也没有 PLUGIN_Profiler_Error 对象。失败会通过 TSL_SetStatus 写入调用方提供的 TF_Status*,这正是 TF 其余 C-API 所期望的方式。第二,句柄是 120 字节operator new(0x78)),不是 128 字节 — 它省略了 PJRT 句柄末尾的状态字节 padding,将唯一的状态字节放在 +112。第三,CollectData 直接序列化到调用方的缓冲区,然后在内联 XSpace 上调用 XSpace::Clear — 不存在自有的序列化 vector,也不像 PJRT 路径那样需要跟踪借用缓冲区生命周期。

本页负责说明旧版 TpuProfiler_* 名单(每个入口一行:签名、它转发到的 ProfilerCollection vtable slot,以及它执行的字节级句柄访问)、承载这些入口供 stream-executor 使用的 ProfilerApiFn 分发表,以及共享集合分发到的 xprof::tpu::TpuProfilerImpl device 子 profiler。PJRT 侧的结构体/vtable、CreateProfilers 工厂遍历,以及 XSpace 线格式由其他页面负责 — 这里只链接,不重复。

对于重新实现,契约是:

  • 五个导出的 TpuProfiler_* C 函数、它们的调用约定,以及 TF_Status* 输出的错误模型。
  • 120 字节旧版句柄布局,以及它在 +112 的单个状态字节。
  • 各入口调用的 ProfilerCollection vtable 偏移(+16 Start、+24 Stop、+32 CollectData、+8 destroying dtor)。
  • 破坏性的、query/fetch 双模式 CollectData 契约,以及成功后执行的 XSpace::Clear 重置。
  • 在该集合内生成 device XPlanes 的 TpuProfilerImpl device 子 profiler。
源单元learning/45eac/tfrc/executor/stream_executor/tpu_profiler_c_api.cc
入口TpuProfiler_Create 0xEF33BC0 · _Start 0xEF33EA0 · _Stop 0xEF34080 · _CollectData 0xEF34240 · _Destroy 0xEF33DE0
分发表stream_executor::tpu::ProfilerApiFn()::profiler_api_fn(accessor @ 0x10900EA0
句柄120 字节(operator new(0x78));状态字节位于 +112
后端句柄 +104 处的 tsl::profiler::ProfilerCollection*;vtable @ 0x217738A0
Device 子 profilerxprof::tpu::TpuProfilerImplStart 0xEF347E0Stop 0xEF34820CollectData 0xEF34860
错误模型通过 TSL_SetStatus 输出到 TF_Status*;没有 Error_* helper
输出序列化的 tensorflow.profiler.XSpace,写入调用方缓冲区

旧版句柄

Create 之后的每个入口都在一个不透明的 120 字节句柄上操作。它是 PJRT PLUGIN_Profiler 句柄的旧版孪生体,但短 8 字节,并且直接序列化 XSpace 而不是先缓冲。下面的偏移直接读取自入口函数体:Create 执行 operator new(0x78),清零,然后写入 +112 = 1+104 = collectionCollectData 读取 +88(constructed 标志)并写入 +96(缓存大小);Destroy 读取 +104/+88

字段偏移类型含义
xspace+0x00tensorflow::profiler::XSpace (88 B)内联 proto2 消息;由第一次 CollectData 延迟构造
xspace_constructed+0x58 (88)uint8_t当且仅当 XSpace::XSpace() 已运行时为 1;在 Destroy 中控制 ~XSpace,在 CollectData 中控制延迟初始化
cached_xspace_size+0x60 (96)size_t集合 drain 后的 XSpace::ByteSizeLong();报告给调用方的大小
collection+0x68 (104)tsl::profiler::ProfilerCollection*自有后端;通过 vtable 分发并在 Destroy 中释放(vtable +8
state+0x70 (112)uint8_t1 = 已创建/已停止(未运行);0 = 正在运行。Create 设为 1,成功 Start 设为 0,成功 Stop 设为 1

怪癖 — 位于 +112 的旧版状态字节与 PJRT 句柄的 ready 字节具有相反极性,并且再往后一格。PJRT 在 Create 时写入 ready = 1,在 Start 时写入 ready = 0(并且 Stop 后仍保持 0);旧版在 Create 时写入 state = 1,Start 时写入 0,Stop 时恢复为 1。因此旧版字节是一个干净的双状态“是否正在运行?”标志,没有重载 — Start 以 state != 0 为保护条件,Stop 以 state != 1 为保护条件,并且 Stop 会把该字节恢复1。将 PJRT 状态机移植到旧版句柄的重新实现者,不能复制 PJRT 中 0 同时表示正在运行和已停止的重载编码。

陷阱 — 旧版句柄中没有自有的 std::vector<uint8_t>。PJRT 句柄在 +0x60 持有一个堆 vector 指针,调用方必须尊重其生命周期;旧版句柄在对应 slot 没有这种内容(+96 保存缓存的大小,不是指针)。TpuProfiler_CollectData 会把 XSpace 序列化到调用方的缓冲区,并立即对内联消息执行 XSpace::Clear。这里没有“借用缓冲区在下一次调用前有效”的规则 — CollectData 返回的瞬间,调用方就拥有这些字节。移植 PJRT vector 字段的重新实现会使每个偏移错位并导致泄漏。


TpuProfiler_Create0xEF33BC0

目的

分配 120 字节句柄,从默认构造的 ProfileOptions 构建共享 ProfilerCollection,并通过第一个输出参数返回句柄。这是唯一会触及工厂注册表的入口。

签名

c
/* learning/.../tpu_profiler_c_api.cc:27 */
void TpuProfiler_Create(TpuProfiler** out_handle, TF_Status* status);
```text

> **注意 —** 与 PJRT 的 `Create` 不同,旧版 `Create` ****接收 `serialized_options`。它在栈上默认构造 `tensorflow::ProfileOptions(0)`,并将其传给 `CreateProfilers`。TF profiler 服务通过注册自己的工厂(经由 `tsl::profiler::RegisterProfilerFactory` @ `0x1CF50780`)来配置实际捕获级别,而不是通过这个入口传递选项。当两个表面同时存在时,工厂 lambda 会读取 *PJRT* 路径解析出的任意选项;在纯 TF 路径上,已注册的工厂使用自己的默认值。

### 算法

```c
function TpuProfiler_Create(out_handle, status):                 // 0xEF33BC0
    VLOG(1) "TpuProfiler Create"                                 // tpu_profiler_c_api.cc:27
    ProfileOptions opts(0);                                      // stack, default
    h = operator new(0x78);                                      // 120-byte handle
    memzero(h, 0x78);                                            // vxorps + 4x vmovups
    h->state = 1;                                                // *(BYTE*)(h+112) = 1
    profs = tsl::profiler::CreateProfilers(opts);                // 0x1CF50860 — factory walk under mu
    coll  = operator new(0x20);                                  // 32-byte ProfilerCollection
    ProfilerCollection::ProfilerCollection(coll, &profs);        // 0xF6A15E0 — moves vector in
    drop_each(profs);  free(profs.data);                         // empty out the moved-from vector
    h->collection = coll;                                        // *(QWORD*)(h+104) = coll
    *out_handle = h;
    set_ok(status);                                              // status rep -> inline OK marker
    ~ProfileOptions(opts);

反编译函数体中的两个 drop_each 循环会释放 moved-from vector 中残留悬挂的任何 unique_ptr<ProfilerInterface>(调用每个元素的 vtable +8 dtor),然后 free vector 存储 — 这是标准 std::vector move 后清理,不是错误路径。set_ok 是规范的 absl::Status 序列:“如果 rep 不同于 inline-OK 标记 &dword_0 + 1,就把它交换进去并 Unref 旧的堆 rep”,所有旧版入口都共享这套序列。

函数映射

函数地址角色
TpuProfiler_Create0xEF33BC0入口;分配句柄 + 构建集合
tsl::profiler::CreateProfilers0x1CF50860在全局 mu 下遍历工厂
tsl::profiler::ProfilerCollection::ProfilerCollection(vector)0xF6A15E0将子 profiler vector move 到内联位置

TpuProfiler_Start0xEF33EA0

目的

重置先前的任何 XSpace,驱动 ProfilerCollection::Start(vtable +16),并将状态字节翻转为“正在运行”。对 double-start 幂等。

签名

c
/* tpu_profiler_c_api.cc:47 */
void TpuProfiler_Start(TpuProfiler* handle, TF_Status* status);
```text

### 算法

```c
function TpuProfiler_Start(h, status):                           // 0xEF33EA0
    VLOG(1) "TpuProfiler Start"                                  // :47
    if (h->state == 0):                                          // already running
        VLOG(2) "TpuProfiler has already been started."          // :49
        return                                                   // no error, no-op
    if (h->xspace_constructed == 1):                             // tear down stale XSpace
        XSpace::~XSpace(&h->xspace);
        h->xspace_constructed = 0;
    h->cached_xspace_size = 0;                                   // *(QWORD*)(h+96) = 0
    s = (*h->collection->vtable[+16])(h->collection);            // ProfilerCollection::Start 0xF6A1640
    write_status(status, s);                                     // ref/unref swap into TF_Status
    if (s == OK):
        h->state = 0;                                            // mark running
    else:
        LOG "<status>" at :57                                    // log the failure status

怪癖 — Start 会在启动之前主动销毁先前构造过的 XSpace+88 标志)并将缓存大小清零。这是旧版句柄的复用路径:因为 CollectData 在序列化后执行的是 XSpace::Clear(而不是 ~XSpace),内联消息会以已清空但仍已构造的状态存活;后续 Start 必须完全拆除它,使下一次 CollectData 重新构造一个新的。PJRT 句柄不会这样复用 — 它的 Start 中没有等价重置。

函数映射

函数地址角色
TpuProfiler_Start0xEF33EA0入口;重置 + Start + 状态翻转
ProfilerCollection::Start0xF6A1640vtable +16;将 Start 扇出到子 profiler

TpuProfiler_Stop0xEF34080

目的

驱动 ProfilerCollection::Stop(vtable +24),并将状态字节恢复为“已停止”。对 start 前 stop 幂等。

签名

c
/* tpu_profiler_c_api.cc:65 */
void TpuProfiler_Stop(TpuProfiler* handle, TF_Status* status);
```text

### 算法

```c
function TpuProfiler_Stop(h, status):                            // 0xEF34080
    VLOG(1) "TpuProfiler Stop"                                   // :65
    if (h->state == 1):                                          // never started / already stopped
        VLOG(2) "TpuProfiler has already been stopped."          // :67
        return                                                   // no error, no-op
    s = (*h->collection->vtable[+24])(h->collection);            // ProfilerCollection::Stop 0xF6A16C0
    write_status(status, s);
    if (s == OK):
        h->state = 1;                                            // mark stopped
    else:
        LOG "<status>" at :73

函数映射

函数地址角色
TpuProfiler_Stop0xEF34080入口;Stop + 状态恢复
ProfilerCollection::Stop0xF6A16C0vtable +24;将 Stop 扇出到子 profiler

TpuProfiler_CollectData0xEF34240

目的

drain + serialize 入口,也是五个入口中最复杂的一个。它从集合中把 device/host planes 拉取到内联 XSpace,报告序列化大小,并且在给定缓冲区时序列化进去并清空消息。它通过 size 指针暴露 query/fetch 双模式契约,以及 buffer-too-small 前置条件检查。

签名

c
/* tpu_profiler_c_api.cc:85 */
void TpuProfiler_CollectData(TpuProfiler* handle,
                             TF_Status*   status,
                             uint8_t*     buffer,            /* may be NULL = query */
                             size_t*      size_in_bytes);    /* in/out; must be non-NULL */
```text

### 算法

```c
function TpuProfiler_CollectData(h, status, buffer, size_io):    // 0xEF34240
    if (size_io == NULL):                                        // :85
        TSL_SetStatus(status, 3 /*INVALID_ARGUMENT*/,
                      "size_in_bytes cannot be null.");
        LOG <status>; return

    if (h->xspace_constructed == 0):                             // first call: actually drain
        XSpace::XSpace(&h->xspace, 0);                           // construct inline message
        h->xspace_constructed = 1;
        VLOG(1) "TpuProfiler CollectData"                        // :91
        s = (*h->collection->vtable[+32])(h->collection,
                                          &h->xspace);          // ProfilerCollection::CollectData 0xF6A1740
        write_status(status, s);
        if (s != OK):
            LOG <status> at :95; *size_io = 0; return
        h->cached_xspace_size = XSpace::ByteSizeLong(&h->xspace);// *(QWORD*)(h+96)
        VLOG(2) "Number of XPlanes: " << h->xspace.planes_count  // :101

    requested = *size_io;                                        // caller's buffer capacity
    *size_io  = h->cached_xspace_size;                           // report true size (in/out)
    if (buffer == NULL):                                         // QUERY MODE: size only
        return
    if (requested < cached_xspace_size):                         // FETCH MODE, buffer too small
        TSL_SetStatus(status, 9 /*FAILED_PRECONDITION*/,
            "Buffer provided was smaller than requested profile data. "
            "buffer size=<req> bytes, profile data size=<size> bytes.");  // :123
        LOG <status>; return
    VLOG(2) "Serializing XSpace to buffer."                       // :127
    CHECK(h->xspace_constructed);                                 // ud2 guard
    XSpace::SerializePartialToArray(&h->xspace, buffer, cached_xspace_size);
    CHECK(h->xspace_constructed);                                 // ud2 guard
    XSpace::Clear(&h->xspace);                                    // reset message (NOT ~XSpace)
    set_ok(status)

陷阱 — size 指针是输入/输出且必填的。NULLsize_in_bytes 会立即得到 INVALID_ARGUMENTTSL_SetStatus code 3)。在每条成功路径上,该入口都会在检查缓冲区之前,用真实序列化大小覆盖 *size_in_bytes。预期调用序列是两遍:先以 buffer = NULL 调用一次,将大小读入 *size_in_bytes,分配缓冲区,然后使用该缓冲区和已知容量再次调用。用过小缓冲区进行单次调用会得到 FAILED_PRECONDITION(code 9)以及上面的消息字符串 — 它不会截断。

怪癖 — drain 是一次性的,但报告可重复。昂贵的 ProfilerCollection::CollectData(vtable +32)只在第一次调用时运行(由 xspace_constructed == 0 控制);后续调用会直接跳到报告 cached_xspace_size 并从内联 XSpace 重新序列化。但因为成功路径会以 XSpace::Clear 结束,第二次 fetch 会重新序列化一个现在为空的消息(大小仍反映缓存值,内容为空)。PJRT 路径不同:它把填充好的 XSpace 缓存在 vector 中,并在重复调用时返回相同的非空字节。重新实现者不能假设旧版第二次 fetch 会返回同一 payload — 它会返回一个已清空消息上的缓存大小

注意 — 与 PJRT 的 CollectData 不同,这个入口从句柄的内联 XSpace(位于 +0 的消息)进行序列化,而不是从栈上的局部对象序列化。ProfilerCollection::CollectData @ 0xF6A1740 会直接拿到 &h->xspace,因此每个子 profiler 都会把自己的 planes 追加到这个持久消息中;末尾的 Clear 让该消息可在一个 Start/Stop/CollectData 周期后复用。

函数映射

函数地址角色
TpuProfiler_CollectData0xEF34240入口;drain + 大小报告 + 序列化
ProfilerCollection::CollectData(XSpace*)0xF6A1740vtable +32;将 drain 扇出到共享 XSpace
TSL_SetStatus(TF C-API)absl::Status 写入 TF_Status*
XSpace::SerializePartialToArray(proto2)将消息序列化到调用方缓冲区
XSpace::Clear(proto2)fetch 后重置内联消息

TpuProfiler_Destroy0xEF33DE0

目的

拆除集合(销毁每个子 profiler),如果内联 XSpace 曾被构造过则拆除它,并释放 120 字节句柄。最简单的入口;不接收 status。

签名

c
/* tpu_profiler_c_api.cc:40 */
void TpuProfiler_Destroy(TpuProfiler* handle);
```text

### 算法

```c
function TpuProfiler_Destroy(h):                                 // 0xEF33DE0
    VLOG(1) "TpuProfiler Destroy"                                // :40
    if (h == NULL): return
    coll = h->collection;  h->collection = NULL;                 // h+104
    if (coll):
        (*coll->vtable[+8])(coll);                               // destroying dtor 0xF6A18E0 — frees coll
    if (h->xspace_constructed == 1):                             // h+88
        XSpace::~XSpace(&h->xspace);
    free(h);

注意 — Destroy 不接收 TF_Status* — 它不会失败并返回 void。它通过集合的 destroying dtor(vtable +8~ProfilerCollection D0 @ 0xF6A18E0)释放集合,这既会析构也会对 32 字节对象执行 operator delete。这里没有序列化 vector 需要释放(旧版句柄没有),因此唯一的条件清理是由 +88 控制的内联 XSpace

函数映射

函数地址角色
TpuProfiler_Destroy0xEF33DE0入口;拆除集合 + XSpace + 句柄
ProfilerCollection::~ProfilerCollection (D0)0xF6A18E0vtable +8;销毁 + 释放集合

ProfilerApiFn 分发表

stream_executor::tpu::ProfilerApiFn() @ 0x10900EA0 是一个很简单的 accessor — 它的整个函数体就是 return &ProfilerApiFn()::profiler_api_fn;。返回的对象是函数局部静态表,首次使用时用指向五个 TpuProfiler_* 导出函数的指针填充。这是 stream-executor 的 TPU 后端用来通过稳定结构体访问旧版 ABI 的 shim,与 PJRT 侧通过 扩展节点 访问 PLUGIN_Profiler_Api 的方式相同。

text
stream_executor::tpu::ProfilerApiFn()         @ 0x10900EA0  (accessor)
  └─ returns &profiler_api_fn  (function-local static)
        profiler_api_fn[0] ── &TpuProfiler_Create       0xEF33BC0
        profiler_api_fn[1] ── &TpuProfiler_Start        0xEF33EA0
        profiler_api_fn[2] ── &TpuProfiler_Stop         0xEF34080
        profiler_api_fn[3] ── &TpuProfiler_CollectData  0xEF34240
        profiler_api_fn[4] ── &TpuProfiler_Destroy      0xEF33DE0
```text

| 属性 | 值 |
|---|---|
| Accessor 符号 | `stream_executor::tpu::ProfilerApiFn()` |
| Accessor 地址 | `0x10900EA0` |
| 后备存储 | `ProfilerApiFn()::profiler_api_fn`(函数局部静态) |
| Slot 内容 | 五个 `TpuProfiler_*` 函数指针 |
| Slot 顺序 / 额外元数据 | Create/Start/Stop/CollectData/Destroy 顺序;任何尾随元数据字段未逐项追踪 |

> **注意 —** accessor 返回一个*指向*静态表内部的指针;表本身由函数局部静态对象的 guarded initializer 在第一次调用时填充。确切字节布局(表是否在五个指针之外携带尾随 version/metadata 字段)未逐项解码 — slot 顺序与导出顺序以及 stream-executor TPU 后端中的调用点一致,因此对五个入口的置信度为 HIGH,对任何额外字段的置信度为 LOW。

---

## `TpuProfilerImpl` Device 子 Profiler

`xprof::tpu::TpuProfilerImpl` **不是**每句柄 collector — 该角色属于 `ProfilerCollection`,也就是两个 ABI 都持有的同一类对象。`TpuProfilerImpl` 是该集合中的一个 `tsl::profiler::ProfilerInterface` *成员*:device 侧 profiler,负责与 xdb(TPU debugger)通信,并把每芯片 trace stream 转换为 device XPlanes。`TpuProfiler_*` 和 `PLUGIN_Profiler_*` 以完全相同的方式访问它 — 集合迭代其内部 vector,并在每个成员上调用对应的 vtable slot,`TpuProfilerImpl` 只是其中一个成员。它的对象布局(从三个方法体读取):`+8` = inner profiler/RPC handle,`+16` = Start timestamp,`+24` = Stop timestamp。

### 算法

```c
function TpuProfilerImpl::Start(this):                           // 0xEF347E0
    this[+16] = absl::GetCurrentTimeNanos();                     // record start time
    inner = this[+8];
    if (!inner): return OK
    s = (*inner->vtable[+24])(inner);                            // inner profiler Start
    xla::profiler::AddPluginMetadata(inner);                     // stamp build CL/timestamp
    return s

function TpuProfilerImpl::Stop(this):                            // 0xEF34820
    inner = this[+8];
    s = inner ? (*inner->vtable[+32])(inner) : OK;               // inner profiler Stop
    this[+24] = absl::GetCurrentTimeNanos();                     // record stop time
    return s

function TpuProfilerImpl::CollectData(this, XSpace* xs):         // 0xEF34860
    arena = xs->arena;                                           // proto2 arena (tagged ptr at xs+8)
    resp  = Arena::DefaultConstruct<xprof::XprofResponse>(arena);
    if (this[+16] && this[+24]):                                 // both timestamps present
        resp.start_time_ns = this[+16]; set_bit 0x100000;        // resp+296, presence @ resp+16
        resp.stop_time_ns  = this[+24]; set_bit 0x8100000;       // resp+336
    inner = this[+8];
    if (inner):
        rc = (*inner->vtable[+40])(inner, resp);                 // fill response from chip drain
        this[+8] = NULL;                                         // drop inner — one-shot
        (*inner->vtable[+8])(inner);                             // inner destroying dtor
        if (rc != OK) { ... return rc; }                         // free arena-less resp, propagate
    wrap = XprofResponseWrapper(resp, arena);
    xprof::ConvertResponseToTpuXSpace(wrap, xs, ...);            // append device XPlanes into xs
    xprof::DropExcessBytes(xs);                                  // trim oversized payloads
    ~XprofResponseWrapper(wrap);
    return OK

TpuProfilerImpl::CollectData 是 device XPlanes 诞生的位置:它把会话 start/stop 时间戳标记进 xprof::XprofResponse,要求其 inner profiler(vtable +40)从每芯片 trace buffers 填充该 response,然后 ConvertResponseToTpuXSpace 将 response 转换为追加到共享 XSpace 上的 XEvents/XStats,DropExcessBytes 会修剪结果。一次性语义在这里也存在 — 单次 drain 后,inner profiler 会被置空并销毁(镜像 ProfilerCollection 的 vector-clear)。提供 XprofResponse 输入的每芯片族解码,以及 XEvent 形状处理,由 trace-entry codecXPlane emission 页面负责。

函数映射

函数地址角色
xprof::tpu::TpuProfilerImpl::Start0xEF347E0记录 start ns;inner Start + AddPluginMetadata
xprof::tpu::TpuProfilerImpl::Stop0xEF34820inner Stop;记录 stop ns
xprof::tpu::TpuProfilerImpl::CollectData(XSpace*)0xEF34860drain → XprofResponse → device XPlanes
xprof::tpu::TpuProfilerImpl::~TpuProfilerImpl (D2/D0)0xEF34DC0 / 0xEF34E00base / destroying dtor
xla::profiler::AddPluginMetadata0xF3165C0将 libtpu build CL/timestamp 标记为 XStat
xprof::ConvertResponseToTpuXSpace(in CollectData)response → device-plane XEvents

陷阱 — 不要把 TpuProfilerImpl 与句柄的 collector 混为一谈。120 字节旧版句柄的 +104 字段是一个 ProfilerCollection*,其 vtable slots +16/+24/+32它自己的 Start/Stop/CollectData;该集合的内部 vector 持有 TpuProfilerImpl(device)、HostTracer(host)、ThreadpoolProfilerInterface(host threadpool),以及工厂注册表构建出的其他任何对象。TpuProfilerImpl 自己的 +8 字段又低一层 — 它的 inner 每芯片 RPC profiler。三个不同对象,三个不同 vtable;本页中的入口只会触及最上层对象(ProfilerCollection)。


与 PJRT Profiler 扩展的关系

两个 ABI 都是同一个 ProfilerCollection 之上的薄 marshaller。下面的并列表是重新实现者所需的信息;PJRT 列由 PJRT Profiler 扩展 负责。

方面旧版 TpuProfiler_*(本页)PJRT PLUGIN_Profiler_*
发现方式stream_executor::tpu::ProfilerApiFn()PJRT_Profiler_Extension(type 1)→ PLUGIN_Profiler_Api vtable
消费方TF、TPUEstimator、stream-executorJAX、PyTorch-XLA,经由 PjRtCApiClient::Profile()
错误模型通过 TSL_SetStatus 输出到 TF_Status*;没有 error helper返回 PLUGIN_Profiler_Error* + 3 个 Error_* helper
Create 选项无(默认 ProfileOptions(0)serialized_optionstensorflow.ProfileOptions proto)
句柄大小120 字节(new 0x78128 字节(new 0x80
状态字节+112,双状态(1=已停止,0=正在运行)+0x78,重载(0=正在运行已停止)
CollectData 输出序列化到调用方缓冲区;XSpace::Clear序列化到自有 std::vector<uint8_t>;借用缓冲区
后端共享 ProfilerCollection(vtable @ 0x217738A0相同的 ProfilerCollection
输出格式tensorflow.profiler.XSpace相同

怪癖 — 两个表面会产生相同的一组子 profiler,因为它们共享全局工厂注册表。TF 通过 tsl::profiler::RegisterProfilerFactory @ 0x1CF50780 注册其 profiler;CreateProfilers @ 0x1CF50860(由 TpuProfiler_CreatePLUGIN_Profiler_Create 共同调用)会遍历该注册表。一个同时驱动两个 ABI 的进程会得到两个独立句柄和两个独立集合,每个都由同一份工厂列表构建 — 它们不共享捕获状态,只共享工厂配方。


此处未覆盖的内容

  • 完整的 profiler_api_fn 静态表字节布局 — 五个 TpuProfiler_* 指针已通过导出顺序和调用点确认(HIGH);任何尾随 version/metadata 字段未逐项解码(LOW)。
  • TpuProfilerImpl+8 背后的 inner 每芯片 RPC profiler — 它的 vtable(+24 Start、+32 Stop、+40 CollectData、+8 dtor)会被执行,但实现类及其 xdb 线协议超出范围。
  • ConvertResponseToTpuXSpace / DropExcessBytes 内部机制 — response-to-XPlane 转换由 XPlane emissiontrace-entry codec 页面负责。
  • AddPluginMetadata 的确切 StatType — 在 PJRT 页面确认了数值 0xA6(166);interned string 名称未解析(LOW)。

交叉引用