Conversation
| /// Print verbose info while processing ereports | ||
| #[clap(short, long, conflicts_with = "json")] | ||
| verbose: bool, | ||
| /// Print ereports in JSON format | ||
| #[clap(short, long)] | ||
| json: bool, |
There was a problem hiding this comment.
nit, take it or leave it: i don't actually think that it is not actually necessary for these to conflict, if you decided to write the verbose info to stderr and the JSON to stdout? that way, i could do $ humility ereport dump --verbose --json > ereports.json and get the verbose info to the console...?
There was a problem hiding this comment.
I've decided I don't actually like verbose, since it just prints an extra line showing buffer extents (which you can get from readvar without too much pain).
I do want to keep printing either the decoded info or JSON to stdout, though, for consistency with other Humility commands (with log messages going to stderr).
| while !buf_data.is_empty() { | ||
| let header = | ||
| EreportHeader::read_from_prefix(buf_data).ok_or_else(|| { | ||
| anyhow!("could not get ereport header from buffer") | ||
| })?; | ||
| buf_data = &buf_data[HEADER_SIZE..]; | ||
| let (ereport_data, next) = | ||
| buf_data.split_at(usize::from(header.data_len)); | ||
| let task = hubris.lookup_module(HubrisTask::Task(header.task.into())); | ||
| let contents = ciborium::from_reader(ereport_data)?; | ||
| ereports.push(Ereport { | ||
| task_name: task.map(|t| t.name.clone()).ok(), | ||
| task_index: header.task.into(), | ||
| timestamp: header.timestamp.into(), | ||
| recovered: false, | ||
| contents, | ||
| }); | ||
| buf_data = next; |
There was a problem hiding this comment.
I would quite like it if the ereport dump also included the ereport's ENA. I think we ought to be able to do that by additionally looking up the earliest_ena field, assigning that ENA to the first ereport, and incrementing it by 1 for each subsequent ereport we encounter.
We could also probably do this when recovering, by counting backwards.
There was a problem hiding this comment.
Whoops, this is now done as well.
| /// Recursively pretty-print a CBOR value | ||
| fn pretty_print_value(value: &ciborium::Value, indent: usize, is_key: bool) { | ||
| let pad = " ".repeat(indent); | ||
|
|
||
| match value { | ||
| ciborium::Value::Null => print!("null"), | ||
| ciborium::Value::Bool(b) => print!("{b}"), | ||
| ciborium::Value::Integer(n) => { | ||
| // ciborium::value::Integer can be converted to i128 | ||
| let n: i128 = (*n).into(); | ||
| print!("{n}"); | ||
| } | ||
| ciborium::Value::Float(f) => print!("{f}"), | ||
| ciborium::Value::Text(s) => { | ||
| if is_key { | ||
| print!("{s}") | ||
| } else { | ||
| print!("\"{s}\"") | ||
| } | ||
| } | ||
| ciborium::Value::Bytes(b) => { | ||
| print!("h'{}'", hex::encode(b)) | ||
| } | ||
|
|
||
| ciborium::Value::Array(items) => { | ||
| if items.is_empty() { | ||
| print!("[]"); | ||
| return; | ||
| } | ||
| println!("["); | ||
| for (i, item) in items.iter().enumerate() { | ||
| print!("{pad} "); | ||
| pretty_print_value(item, indent + 1, false); | ||
| if i + 1 < items.len() { | ||
| println!(","); | ||
| } else { | ||
| println!(); | ||
| } | ||
| } | ||
| print!("{pad}]"); | ||
| } | ||
|
|
||
| ciborium::Value::Map(entries) => { | ||
| if entries.is_empty() { | ||
| print!("{pad}{{}}"); | ||
| return; | ||
| } | ||
| if indent > 0 { | ||
| println!("{pad}{{"); | ||
| } | ||
| for (i, (k, v)) in entries.iter().enumerate() { | ||
| print!("{pad} "); | ||
| pretty_print_value(k, indent + 1, true); | ||
| print!(": "); | ||
| // Special-case handling for the `k`, which is an ereport name | ||
| // and is printed without quotes | ||
| let is_ereport_key = match k { | ||
| ciborium::Value::Text(t) => t == "k", | ||
| _ => false, | ||
| }; | ||
| pretty_print_value(v, indent + 1, is_ereport_key); | ||
| if i + 1 < entries.len() { | ||
| println!(","); | ||
| } else { | ||
| println!(); | ||
| } | ||
| } | ||
| if indent > 0 { | ||
| print!("{pad}}}"); | ||
| } | ||
| } | ||
|
|
||
| ciborium::Value::Tag(tag, inner) => { | ||
| print!("tag({tag}) "); | ||
| pretty_print_value(inner, indent, false); | ||
| } | ||
|
|
||
| _ => print!("<unknown>"), | ||
| } | ||
| } |
There was a problem hiding this comment.
one thing that would be nice about potentially using the same CBOR deserialization library as MGS is that if we did that, we could perhaps share the pretty-printer with faux-mgs. not a blocker for this PR, but just a thought.
| // Special-case handling for the `k`, which is an ereport name | ||
| // and is printed without quotes | ||
| let is_ereport_key = match k { | ||
| ciborium::Value::Text(t) => t == "k", | ||
| _ => false, | ||
| }; |
There was a problem hiding this comment.
if we're going to special-case "k", i think we should just be renaming it to "class", which is what it abbreviates:
| // Special-case handling for the `k`, which is an ereport name | |
| // and is printed without quotes | |
| let is_ereport_key = match k { | |
| ciborium::Value::Text(t) => t == "k", | |
| _ => false, | |
| }; | |
| // Special-case handling for the `k`, which is an ereport name | |
| // and is printed without quotes | |
| let is_ereport_key = match k { | |
| ciborium::Value::Text(t) => t == "class", | |
| _ => false, | |
| }; |
we should probably special-case the "v" field as "version", as well.
There was a problem hiding this comment.
Seems reasonable, although this diff doesn't work 🙃
I'm adding a remapping pass of k -> class, v -> version at the end of fn ereport_dump(..), so that both the human-friendly and JSON outputs use those names.
There was a problem hiding this comment.
Seems reasonable, although this diff doesn't work 🙃
Argh, my bad --- thanks for doing it the correct way! :)
f3563b9 to
27469a4
Compare
27469a4 to
723b558
Compare
| recover: bool, | ||
| } | ||
|
|
||
| /// Ereport header (required to agree with SP implementation) |
There was a problem hiding this comment.
might be nice if there was a link here to where the SP implementation to which this must agree, or something?
| /// Recover previously-sent ereports from the buffer | ||
| #[clap(short, long)] | ||
| recover: bool, |
There was a problem hiding this comment.
i feel like the doc for this could maybe be a little more clear about what this means?
There was a problem hiding this comment.
Sure, added a longer docstring (which clap shows when you pass in --help)
humility-ereport-dump
Print the contents of the ereport buffer
USAGE:
humility ereport dump [OPTIONS]
OPTIONS:
-h, --help
Print help information
-j, --json
Print ereports in JSON format
-r, --recover
Recover previously-sent ereports from the buffer
The circular buffer may contain previously-sent ereports outside of
its valid range, i.e. before the `front` index. When this flag is
set, we examine those bytes to find valid-looking ereports and print
them (with `recovered: true`).
|
|
||
| /// Recursively pretty-print a CBOR value | ||
| fn pretty_print_value(value: &ciborium::Value, indent: usize, is_key: bool) { | ||
| let pad = " ".repeat(indent); |
There was a problem hiding this comment.
if you wanted to do this without allocating, you could instead write something like this in your format strings:
println!("{:indent$}stuff you want indented", "");not actually important, i just think it's nice.
There was a problem hiding this comment.
I can never remember the syntax here (and don't love the extra trailing "" argument), so I'm fine with a little extra allocation.
723b558 to
99c2c80
Compare
This PR adds a
humility ereportsubcommand to dump ereports from packrat.It includes both normal and JSON formatting, e.g.
[ { "task_name": "ereportulator", "task_index": 27, "timestamp": 9441, "recovered": false, "contents": { "k": "hw.apollo.undervolt", "v": 13, "volts": 0.0, "n": 5678, "bus": "MainBusB" } } ]By default, it prints just ereports which are in the valid portion of the circular buffer; the
--recoverflag also searches for ereports which have been sent but are still available in RAM.This is written to be library-friendly:
pub fn ereport_dump(..)does no printing of its own, just returns aResult<Vec<Ereport>>.There are also some incidental improvements to reflection, which I'd like to use more widely in a subsequent PR.