On Tue, 13 Oct 2026 10:03:21 +0800
Li Pengfei <[email protected]> wrote:
> Hi Steven, Masami,
>
> Open question 2 in the v7 cover letter was whether the map generation
> should be part of the ABI. I think it has to be, and I would rather
> settle the record format with you before sending a v8.
>
> The problem is that reset clears the map and resets the element cursor to
> zero, so a new stack takes slot 42 immediately afterwards. A stack_id 42
> recorded before the reset is still sitting in the ring buffer, and
> resolving it now yields the new occupant of that slot. Userspace cannot
> tell that apart from a correct lookup - it gets a plausible stack, not an
> error.
>
> I first tried to fix it in the table, storing the generation per slot and
> comparing it with the current one on lookup. That does not work: the
> comparison tells you which generation the slot now holds, not which
> generation the caller's id came from, and the case that matters is
> exactly when those differ and the slot has already been refilled. The
> identity has to travel with the record.
>
> So for v8:
>
> struct stack_id_entry {
> struct trace_entry ent;
> u64 stack_cookie; /* gen << 32 | id */
> };
>
> A single opaque 64-bit cookie, high half generation, low half slot. An id
> is still only valid within its own generation; the cookie makes crossing
> that boundary detectable.
>
> stack_map_bin reports the generation of the dump in the header word that
> is currently reserved (ABI version 1 -> 2), so a dump and a record can be
> paired. Text output splits the cookie rather than printing it raw:
>
> <stack_id 42 gen 3>
>
> Cost, against the workload measured in the v7 cover letter:
>
> full stack, ~20 frames trace_entry + size + 8*20 = 172 B
> v7, 4-byte id trace_entry + 4 = 12 B
> v8, 8-byte cookie trace_entry + 8 = 16 B
This is reasonable.
>
> Retention on that workload drops from about 2.17x to about 2.0x. I will
> re-measure rather than quote that from arithmetic.
>
> Two alternatives I rejected, in case you would have picked one:
>
> Packing the generation into the existing 32-bit id keeps the record at 4
> bytes, but with FTRACE_STACKMAP_BITS_MAX at 18 the slot index needs 19
> bits, leaving 12 bits of generation in a signed field. 4096 resets wrap,
> and a wrapped generation passes the check - the same failure, only
> rarer. It also ties the id layout to the capacity limit.
>
> Two separate u32 fields come to the same 16 bytes and would make the
> format file self-describing, so an event filter could match the slot
> directly. I went with the cookie because an opaque value says the whole
> thing is the identifier - two fields invite userspace to read the slot
> number on its own, which is exactly what is not meaningful without its
> generation. If the filtering matters more, that is an easy change.
>
> One consequence I had not expected. If a trace spans several resets,
> dumping stack_map_bin before each one keeps the whole timeline resolvable
> afterwards: every record says which generation it belongs to and every
> dump header says which generation it is. Without the tag, saving several
> dumps is pointless, since nothing says which dump a given record should
> be read against.
>
> Does that format look right?
Yeah, the cookie + generation looks like the best way forward.
Thanks,
-- Steve
>
> v7 split the series into 10 focused patches following Masami's review of
> v6, and the design is otherwise unchanged.