Skip to content

Add humility ereport subcommnad#620

Merged
mkeeter merged 7 commits intomasterfrom
mkeeter/humility-ereport
Apr 22, 2026
Merged

Add humility ereport subcommnad#620
mkeeter merged 7 commits intomasterfrom
mkeeter/humility-ereport

Conversation

@mkeeter
Copy link
Copy Markdown
Contributor

@mkeeter mkeeter commented Apr 17, 2026

This PR adds a humility ereport subcommand to dump ereports from packrat.

$ humility -d ~/Desktop/hubris.core.ereports ereport dump
humility: attached to dump
task: ereportulator (27)
timestamp: 9441
  k: hw.apollo.undervolt,
  v: 13,
  volts: 0,
  n: 5678,
  bus: "MainBusB"

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 --recover flag 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 a Result<Vec<Ereport>>.

There are also some incidental improvements to reflection, which I'd like to use more widely in a subsequent PR.

@mkeeter mkeeter requested review from hawkw and labbott April 17, 2026 20:35
Comment thread Cargo.toml
Comment thread humility-core/src/reflect.rs Outdated
Comment thread cmd/ereport/src/lib.rs Outdated
Comment thread cmd/ereport/src/lib.rs Outdated
Comment on lines +40 to +45
/// Print verbose info while processing ereports
#[clap(short, long, conflicts_with = "json")]
verbose: bool,
/// Print ereports in JSON format
#[clap(short, long)]
json: bool,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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...?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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).

Comment thread cmd/ereport/src/lib.rs
Comment on lines +105 to +122
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;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Whoops, this is now done as well.

Comment thread cmd/ereport/src/lib.rs Outdated
Comment thread cmd/ereport/src/lib.rs
Comment on lines +201 to +280
/// 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>"),
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread cmd/ereport/src/lib.rs Outdated
Comment on lines +255 to +260
// 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,
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if we're going to special-case "k", i think we should just be renaming it to "class", which is what it abbreviates:

Suggested change
// 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems reasonable, although this diff doesn't work 🙃

Argh, my bad --- thanks for doing it the correct way! :)

@mkeeter mkeeter force-pushed the mkeeter/humility-ereport branch 5 times, most recently from f3563b9 to 27469a4 Compare April 21, 2026 15:15
@mkeeter mkeeter force-pushed the mkeeter/humility-ereport branch from 27469a4 to 723b558 Compare April 21, 2026 20:36
Comment thread cmd/ereport/src/lib.rs Outdated
recover: bool,
}

/// Ereport header (required to agree with SP implementation)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

might be nice if there was a link here to where the SP implementation to which this must agree, or something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yup, added

Comment thread cmd/ereport/src/lib.rs
Comment on lines +43 to +45
/// Recover previously-sent ereports from the buffer
#[clap(short, long)]
recover: bool,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i feel like the doc for this could maybe be a little more clear about what this means?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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`).

Comment thread cmd/ereport/src/lib.rs

/// Recursively pretty-print a CBOR value
fn pretty_print_value(value: &ciborium::Value, indent: usize, is_key: bool) {
let pad = " ".repeat(indent);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can never remember the syntax here (and don't love the extra trailing "" argument), so I'm fine with a little extra allocation.

@mkeeter mkeeter force-pushed the mkeeter/humility-ereport branch from 723b558 to 99c2c80 Compare April 22, 2026 18:00
@mkeeter mkeeter merged commit aa255a6 into master Apr 22, 2026
12 checks passed
@mkeeter mkeeter deleted the mkeeter/humility-ereport branch April 22, 2026 20:05
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.

2 participants