Skip to content

Add metadata UUID - #539

Open
TApplencourt wants to merge 17 commits into
develfrom
add_metadata_uuid
Open

Add metadata UUID#539
TApplencourt wants to merge 17 commits into
develfrom
add_metadata_uuid

Conversation

@TApplencourt

Copy link
Copy Markdown
Collaborator

No description provided.

TApplencourt and others added 5 commits September 10, 2026 22:44
print_handle_uuid_modules printed both to cuda, hip and mpi. Handle was
prepended by none of the three -- ze is its only user, through an inline copy
of its own -- and mpi prepends neither, so it carried both definitions for
nothing. What is left prints one module, and mpi prints none: -51 lines
across the three libraries, nothing added.

The byte-identical oracle compares generated TEXT, so it cannot tell a struct
that renders readably from one that hands its bytes over raw, and that is the
whole job of these modules. Measured out of tree instead, by loading each
library, filling every byte-array struct with control characters and reading
back what to_s produced.

That says this commit changes no rendering: cuda 5 blob structs / 4 unreadable
and hip 4 / 3 both before and after, mpi 0 / 0 -- the same fact as the dead
code, measured from the other side.

It also puts a number on the bug underneath. Which struct gets a module is
decided by a match on its class name, and nine structs across cuda, hip and
ze fall outside it: CUIPCEventHandleV1, CUIPCMemHandleV1, CUMemFabricHandleV1,
CUMemPoolPtrExportDataV1, HipIpcMemHandle_t, HipIpcEventHandle_t,
HipMemPoolPtrExportData, ZEDeviceLuidExt and ZESFabricLinkType reach a trace
as raw bytes. Fixing that means saying which rendering a type wants instead
of guessing from its name, and is left to a following commit.

ze 9/9 and xprof 2/2 pass on a Max 1550.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Which struct got a readable rendering was decided by a match on its class
name: /UUID/ in the shared printer, /UUID/ then /Handle\z/ in ze's. A name is
not the fact the renderer needs, and it was wrong in both directions.

It missed nine structs, which reached a trace as raw bytes -- their to_s
handed the array straight to string interpolation. Four in cuda
(CUIPCEventHandleV1, CUIPCMemHandleV1, CUMemFabricHandleV1,
CUMemPoolPtrExportDataV1), three in hip (HipIpcMemHandle_t,
HipIpcEventHandle_t, HipMemPoolPtrExportData), and ZEDeviceLuidExt, whose
header calls it "opaque data representing a device LUID".

It also matched two structs that hold no bytes at all.
ZEExternalMemoryImportWin32Handle and its Export twin end in Handle and are
{stype, pNext, flags, handle, name}, so the module's self[:data] found no
such field: printing either raises ArgumentError on devel today.

A `renderings:` map in the backend's YAML says which renderer a type wants,
beside the meta-parameters that are already declared there rather than
derived. Nothing about the shape can answer it -- an opaque handle, a UUID
and a fixed-width string are all uint8_t x[N], and zes_fabric_link_type_t is
a string that renders correctly with no module at all.

The renderers read members.first instead of a member name of their own, which
is what lets one Handle serve ze's `data` and cuda's `reserved`; the two
UUID orders stay separate because cuda and hip print first byte first and ze
last byte first. ze keeps KUUID: ze_kernel_uuid_t renders kid AND mid, which
a single-member renderer cannot express.

cuda and hip UUIDs now print under their real member name, `bytes`, where the
old renderer hardcoded `id`. Nothing parses that label.

Every backend is now 0 unreadable, where cuda was 4, hip 3 and ze 2, and ze
drops from 11 structs that raise on to_s to 9 -- the rest being the Base*
classes that PR #537 fixes. On a Max 1550 the pretty-printed trace is
identical, UUID rendering included; ze 9/9 and xprof 2/2 pass. cuda and hip
have no hardware here and are covered by the rendering test alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The renderers read members.first, which is why ze_kernel_uuid_t could not use
one: it is {kid, mid}, two uint8_t[16] UUIDs side by side, and a renderer that
reads the first member would have dropped mid. It kept a hand-written KUUID
module -- 44 generated lines spelling out both byte orders index by index --
and a `name == 'ze_kernel_uuid_t'` branch in print_struct to reach it.

Mapping over every member instead makes that a `renderings:` row like any
other. For a struct holding one blob, mapping all members IS members.first, so
nothing single-member changes; the pair now renders
`{ kid: <uuid>, mid: <uuid> }`, byte for byte what KUUID produced.

Nothing about the shape can pick the renderer, which is why the row is still
needed: uint8_t[16] and char[16] are both UUIDs here (ze_uuid_t and CUuuid),
both are also opaque handles elsewhere, and cuda has no uint8_t byte array at
all. The C type does not carry the answer, so the header's answer stays
declared.

Counting only structs with exactly one byte-array member left ze_kernel_uuid_t
outside the one check that could have caught this. Counting any struct whose
members are all byte arrays takes ze from 10 to 11, still 0 unreadable, with
cuda 5 / hip 4 unchanged.

Generated ze_library.rb drops 67 lines against devel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`renderings:` keyed a renderer to a struct, so it could only speak for a struct
whose members all want the same one. Most byte arrays do not live alone:
zes_device_properties_t is six fixed-width strings among ten members, and
zet_metric_properties_t four among nine. Twenty-odd structs are that shape, and
a per-struct key cannot describe any of them.

Rows are per member now, and shaped like the meta-parameters beside them
because they are the same kind of fact -- something the header knows that the C
declaration does not carry:

  meta_parameters_struct:
    ze_kernel_uuid_t:
      - [ UuidReversed, kid ]
      - [ UuidReversed, mid ]

That retires the last of the shape-guessing. ze_kernel_uuid_t needed a renderer
that mapped over every member; now it names both. A member with no row renders
the way the base class renders it, which is what the strings want, so declaring
the 13 opaque members says everything and the 44 string members stay silent.

Handle becomes Blob, and stops formatting bytes by hand: read_bytes(size) keeps
all of them and `.b.inspect` escapes the ones that need it. A blob is not a C
string -- reading ze_ipc_mem_handle_t as text stops at the first NUL and
silently drops the other 63 bytes -- and inspect leaves printable bytes legible
where the old loop hexed every one.

The UUID renderer keeps whichever canonical dashes the array is long enough to
reach, so ze_device_luid_ext_t can ask for UUID at all: it is 8 bytes, and the
fixed 4-2-2-2-6 grouping raised NoMethodError on it (hex[10, 6] is nil). 16
bytes still render 8-4-4-4-12, and the LUID reads 17161514-1312-1110.

ze 11 byte-blob structs / 0 unreadable, cuda 5 / 0, hip 4 / 0. The kernel UUID
pair renders byte for byte what the hand-written KUUID did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things the previous commit left half-done.

load_meta_parameters_struct read the same files load_meta_parameters had just
read. There is one meta-parameter file per backend and it now yields both of
its sections, so each backend loads it once and takes what it needs; the
separate function is gone and the other five backends ask for
[:meta_parameters].

The renderers were emitted as one module per struct: ten near-identical
ZE*Rendering modules in ze alone, five of them the same body under different
names, plus a RENDERED map and a case to dispatch through. That is more
generated code than the hand-written KUUID it replaced. There is now one
Rendering module of plain functions per backend, holding only the renderers
that backend's rows ask for, and a rendered struct carries a to_s that names
them directly:

  def to_s
    "{ kid: #{Rendering.uuid_reversed(self[:kid].to_a)}, ... }"
  end

A row names that function, so `uuid` in the YAML is `Rendering.uuid` in the
bindings and there is nothing in between. An earlier attempt kept a shared
helper under the renderers, and in cuda the helper and the `Uuid` renderer both
emitted `def self.uuid` -- the second silently redefining the first into
infinite recursion. The name collision was the layer announcing itself.

check_renderings raises unless every row names a member of that struct, and one
whose element type is a byte. It caught CUmemFabricHandle_v1.data on the first
run: cuda spells that array `unsigned char`, a third spelling neither the code
nor these commit messages had accounted for.

Verified against the devel build, every struct holding a byte array, same
inputs: the set of structs rendering a UUID is unchanged except ZEDeviceLuidExt
joining it, every UUID value is identical, and nothing raises on either side.
Generated ze_library.rb is 45 lines smaller than devel. make check 48/48.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TApplencourt

Copy link
Copy Markdown
Collaborator Author

@thapi-local-reviewer review

@thapi-local-reviewer thapi-local-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll examine the key files to understand the context before reporting.

Reviewed 46a1e955 — local model qwen3.8:27b. Verify before acting.

TApplencourt and others added 8 commits September 11, 2026 15:30
The rendering rows had four names that did not say what they held.
`print_renderer_modules` emits one module, not several, and took a list of
struct names its `naming` argument already carried. `RENDERERS` held renderer
bodies, not renderers or structs. `spec` and `STRUCT_SPEC` named the
`meta_parameters_struct` YAML section without saying so.

The checks also ran per struct as it was printed. They now run once, before
anything is emitted, so a bad row stops the build at the row rather than part
way through a library.

`presence` and the `rendered` temporary go with them, and the comment repeated
across five YAML files moves to backends/README.md, which the backends had no
shared page for until now.

Generated ze, cuda and hip libraries are byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A member that is not there and a member that is not bytes fail for the same
reason -- the renderer has nothing to read -- so they no longer get a branch
and a message each. `byte_array_members` returns the readable members and the
caller subtracts, which also names every bad member in one raise rather than
stopping at the first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`Rendering` named nothing: a library generator renders everything. The module
holds the functions that turn a byte array into text, so it is `Bytes`, and a
call site reads `Bytes.uuid_reversed(...)`.

The UUID bodies were one template with an `ORDER` sentinel substituted by gsub,
which put a word in the source that is not Ruby and is not in the output. Each
body now opens by putting the bytes in its own order and they share the rest.

Two duplicate checks kept a hash and raised on the key already in it. Counting
with tally and subtracting says the same thing without the accumulator, and
names every duplicate rather than stopping at the first.

Generated output changes only in the module name and the added `ordered =`
line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`puts initializer if initializer` prints a blank line for an empty string,
which is why the caller was handing it nil instead. The printer now asks
whether there is text, so the caller joins what it has and stops caring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`meta_parameters_struct` reaches a UUID only when a struct wraps it. A bare
byte array has no struct to hang a renderer on, so `cuDeviceGetLuid`'s 8-byte
`luid` printed as `"n\x16?\xBE\xB9XDM"` where ze, which wraps the same bytes in
`ze_device_luid_ext_t`, prints `6e163fbe-b958-444d`.

`meta_parameters_function` says the same thing about a function's parameters
that `meta_parameters_struct` says about a struct's members, in the same
`[ renderer, thing ]` rows and reaching the same `Bytes` functions.

A parameter is traced under a name the tracepoint decorates -- `luid` arrives
as `luid_vals` -- so the rows are re-keyed from the command index rather than
by guessing the suffix. A parameter the function does not trace as bytes
raises, which a wrong name would otherwise only show at trace time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`meta_parameters_function` names a parameter, `luid`; the payload carries the
field the tracepoint decorates, `luid_vals`. The first version re-keyed the
rows from the command index, so every babeltrace-library generator took
`COMMANDS` to learn a name its own model already spells out. Measured across
the 7 backends: 973 string fields, none of which collide once `_val`/`_vals`
is stripped. The model is enough, and `print_babeltrace_lib` goes back to
taking a naming and its rows.

That restatement hid a bug. `print_bytes_module` emitted the `Bytes` functions
the struct rows named, so a renderer only a function row asks for was called
and never defined: `[ uuid_reversed, luid ]` generated a call to
`CUDA::Bytes.uuid_reversed`, which is not there. It passed only because
`CUuuid` happens to name `uuid` too. Both sections now say what to emit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TApplencourt and others added 3 commits September 11, 2026 20:00
Four notes had drifted or restated what is already written down elsewhere.

print_typedefs still said cuda "prepends a UUID module to matching structs":
that mechanism is gone, replaced by the declared rows. BYTE_TYPES claimed cuda
uses all three byte spellings; measured, cuda and hip write char and unsigned
char, ze writes char and uint8_t, and no backend emits int8. The
byte_array_members example was written as a doctest but names NAMING, which
only a backend defines, so it could never run -- it is prose now.
print_bytes_module's closing paragraph is its own commit message.

backends/README.md named the three renderers without saying what any of them
prints, so it now carries a table. Also fixes "menbers" and "stoping".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
print_struct_with_namespace took a `prepends:` list so a backend could mix a
hand-written module into a struct class. That was how Handle and UUID reached
the structs that needed them; declaring the rendering per member retired the
last caller. The `prepend Version` lines in ze_library.rb come from a heredoc
in gen_ze_library.rb, not from here.

With the loop gone the two heredocs around it join back into one, and
print_struct_rendered is a two-line wrapper over a single call, so its two
callers make that call themselves.

Generated output is unchanged: all 62 files byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ared struct

Two ways a wrong row stayed quiet.

A backend that never called print_bytes_module could carry both rendering
sections and have them dropped on the floor: itt, omp and mpi read only
[:meta_parameters]. Adding meta_parameters_struct to mpi's yaml built clean and
generated nothing. Every backend calls print_bytes_module now -- it emits
nothing when there are no rows, but the check comes with it, so a section that
does nothing says so instead of passing.

Declaring one member of a struct replaces the whole to_s, so a byte array left
undeclared is interpolated raw. Removing ze_kernel_uuid_t's `mid` row built
clean and rendered `{ kid: 100f0e0d-..., mid: <16 control characters> }`. A
declared struct must now name a renderer for each of its byte arrays; every
struct declared today already does.

The three constants each model exported collapse to one META_PARAMETERS, since
both readers now take the whole spec and pick their own section.

Generated output is unchanged: all 62 files byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
event_function_name and parameter_name were written as new helpers, but
parameter_name already existed inline in gen_babeltrace_model_helper.rb as the
same gsub. Both undo a decoration LTTng.rb's own constants create -- an event
is <provider>:<function>_<suffix>, a parameter is traced under _val or _vals --
so they belong there, next to START and STOP, and the second copy is gone.

Three names claimed more than the code does. rendering_rows does nothing
rendering-specific: it indexes a section whose rows read [value, key], so it is
indexed_rows, and its raise says "declared twice" rather than "rendered twice".
rendered_to_s returns the TEXT of a to_s for the generated file, so it is
struct_to_s_definition. print_struct_with_namespace's `initializer:` predates
carrying a to_s as well, so it is `body:` -- which is what ze passes it, two
definitions joined.

The prose framed meta_parameters_struct and meta_parameters_function as "the
two rendering sections", a separate species that happens to share a file. They
are not: a meta-parameter is a fact the header knows that the C declaration
cannot carry, and [InArray, globalDim, tensorRank] and [uuid_reversed, id] are
the same kind of fact. They differ in what a row keys on, nothing more.

Also: payload_fields names the &.[](:members) dance that appeared twice, the
reject+map over it is one filter_map, load_meta_parameters' three copies of
merge-raise-on-duplicate are one loop, and the comments that had grown into
essays are cut to what the code cannot say.

Generated output is unchanged: all 62 files byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant