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.


Reply via email to