top | item 40218452

(no title)

talldrinkofwhat | 1 year ago

This sounds extremely appealing as I either have to take the performance hit and nose through the entire debug log to get to where the error occurred, or have errors occur and not be able to reconstruct the events that drove them.

Any tips on rolling your own? My first whack would probably be treating it like a packed message, e.g.

<start_char> <n_packed_bytes> <time> <file> <line> <n_args> <arg1_type><arg1_raw_dump> ... <format_str>

with everything aside from the format string having agreed upon byte-counts, and probably have the file names getting dumped to a lookup chart. There's probably something more elegant, but I've never been good at straying too far eclectic.

discuss

order

SAI_Peregrinus|1 year ago

Ferrous Systems made defmt[1] for Rust. The linked design page details the inner workings. One trick is that even the format string is packed into a lookup table. They're all stored in a lookup table in a special .elf section, and only the index into that table is logged. So it's more like <format string index> <[length], n packed bytes for first variable> <[length], n packed bytes for second variable>… etc. Some types don't need a length, if not needed it is omitted. There's no need to transmit the file, line, or number of args because that's all in the lookup table, indexed just like the format string!

E.g. a slice needs a length.

    defmt::error!("Data: {=[u8]}!", [0, 1, 2]);
    // on the wire: [1, 3, 0, 1, 2]
    //  string index ^  ^  ^^^^^^^ the slice data
    //   LEB128(length) ^ 
LEB128[2] is the same compressed integer encoding scheme used by DWARF & WASM.

The entire "Data: {=[u8]}!" format string is just one byte on the wire (assuming it's in the first 255 or fewer log statements, the index is also LEB128 compressed.)

[1] https://defmt.ferrous-systems.com/design

[2] https://en.wikipedia.org/wiki/LEB128