Skip to content

Commit ed2da4c

Browse files
committed
[difftest] refactor difftest error output
Signed-off-by: Avimitin <[email protected]>
1 parent 8e3d048 commit ed2da4c

File tree

6 files changed

+103
-34
lines changed

6 files changed

+103
-34
lines changed

sail-impl/boat/Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sail-impl/boat/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] }
1414
clap = { version = "4.5.37", features = ["derive"] }
1515
serde = { version = "1.0", features = ["derive"] }
1616
serde_json = "1.0"
17+
anyhow = "1"

sail-impl/boat/difftest/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ version.workspace = true
55

66
[dependencies]
77
clap = { workspace = true }
8+
thiserror = { workspace = true }
89
tracing-subscriber = { workspace = true }
910
tracing = { workspace = true }
1011
serde = { workspace = true }
1112
serde_json = { workspace = true }
13+
anyhow = { workspace = true }
1214
glob = "0.3.2"
1315
# Rust standard process library have unexpected behavior of finding exeuctable in PATH.
1416
which = "7.0.3"

sail-impl/boat/difftest/src/boat.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
use anyhow::Context;
12
use serde::Deserialize;
3+
use std::fmt::Display;
24

35
pub type BoatLog = Vec<BoatEvent>;
46

57
pub fn run_process(
68
args: &[String],
79
elf_path: impl AsRef<std::ffi::OsStr>,
8-
) -> Result<BoatLog, String> {
9-
let boat_exec = which::which("boat").unwrap_or_else(|err| panic!("boat exec not found: {err}"));
10+
) -> anyhow::Result<BoatLog> {
11+
let boat_exec = which::which("boat").with_context(|| "boat exec not found")?;
1012

1113
const EVENT_PATH: &str = "./boat_trace_event.jsonl";
1214
let result = std::process::Command::new(&boat_exec)
@@ -17,17 +19,17 @@ pub fn run_process(
1719
.arg(EVENT_PATH)
1820
.args(args)
1921
.output()
20-
.unwrap_or_else(|err| panic!("fail exeuting boat: {err}"));
22+
.with_context(|| "fail exeuting boat")?;
2123

2224
if !result.status.success() {
23-
return Err(format!(
24-
"fail to execute '{boat_exec:?}' with args {args:?} for elf {}",
25+
anyhow::bail!(
26+
"fail to execute boat with args {args:?} for elf {}",
2527
elf_path.as_ref().to_str().unwrap()
26-
));
28+
);
2729
}
2830

2931
let trace_event =
30-
std::fs::read(EVENT_PATH).unwrap_or_else(|err| panic!("fail reading {EVENT_PATH}: {err}"));
32+
std::fs::read(EVENT_PATH).with_context(|| format!("fail reading {EVENT_PATH}"))?;
3133

3234
let boat_log = get_boat_events(&trace_event);
3335

@@ -52,6 +54,7 @@ pub struct BoatLogLine {
5254
pub fields: BoatEvent,
5355
}
5456

57+
#[allow(dead_code)]
5558
#[derive(Debug, Deserialize)]
5659
#[serde(tag = "event_type")]
5760
pub enum BoatEvent {
@@ -74,6 +77,23 @@ pub enum BoatEvent {
7477
ResetVector { new_addr: u64 },
7578
}
7679

80+
impl Display for BoatEvent {
81+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82+
match self {
83+
Self::ArchState {
84+
action,
85+
pc,
86+
reg_idx,
87+
data,
88+
} => indoc::writedoc!(
89+
f,
90+
"PC={pc:#018x} {action} to register [x{reg_idx}] with [{data:#018x}]"
91+
),
92+
_ => write!(f, "{self:#?}"),
93+
}
94+
}
95+
}
96+
7797
impl BoatEvent {
7898
pub fn get_reset_vector(&self) -> Option<u64> {
7999
match self {

sail-impl/boat/difftest/src/main.rs

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
use anyhow::Context;
12
use boat::{BoatEvent, BoatLog};
3+
use clap::Parser;
24
use serde::Deserialize;
35
use serde::Deserializer;
46
use spike::SpikeLog;
7+
use tracing::{error, info};
8+
use tracing_subscriber;
9+
510
mod boat;
611
mod spike;
712

@@ -37,25 +42,48 @@ struct CaseConfig {
3742
end_pattern: EndPattern,
3843
}
3944

40-
fn main() {
41-
let cfg_raw = std::fs::read("./sail_difftest_config.json")
42-
.unwrap_or_else(|err| panic!("fail to read sail difftest config: {err}"));
43-
let cfg: CaseConfig = serde_json::from_slice(&cfg_raw)
44-
.unwrap_or_else(|err| panic!("fail to parse config: {err}"));
45+
#[derive(clap::Parser, Debug)]
46+
#[command(version, about, long_about = None)]
47+
struct DiffTestArgs {
48+
#[arg(short = 'c', long, default_value_t = String::from("./sail_difftest_config.json"))]
49+
config_path: String,
50+
}
51+
52+
fn main() -> anyhow::Result<()> {
53+
let args = DiffTestArgs::parse();
54+
55+
tracing_subscriber::fmt()
56+
.pretty()
57+
.with_file(false)
58+
.with_line_number(false)
59+
.without_time()
60+
.init();
61+
62+
let cfg_raw =
63+
std::fs::read(args.config_path).with_context(|| "fail to read sail difftest config")?;
64+
let cfg: CaseConfig =
65+
serde_json::from_slice(&cfg_raw).with_context(|| "fail to parse sail difftest config")?;
66+
4567
let all_elf_files = glob::glob(&cfg.elf_path_glob)
46-
.unwrap_or_else(|err| panic!("invalid path glob {}: {}", cfg.elf_path_glob, err));
47-
all_elf_files.for_each(|path| {
48-
let path = path.unwrap_or_else(|err| panic!("glob leads to unreadable path: {err}"));
49-
println!("Running elf {path:?}");
50-
let spike_log = spike::run_process(&cfg.spike_args, &path).unwrap();
51-
let boat_log = boat::run_process(&cfg.boat_args, &path).unwrap();
68+
.with_context(|| format!("invalid path glob {}", cfg.elf_path_glob))?;
69+
70+
for path in all_elf_files {
71+
let path = path.with_context(|| "internal error: glob leads to unreadable path")?;
72+
73+
info!("running difftest for {path:?}");
74+
75+
let spike_log = spike::run_process(&cfg.spike_args, &path)?;
76+
let boat_log = boat::run_process(&cfg.boat_args, &path)?;
5277
let diff_result = diff(&spike_log, &boat_log, &cfg.end_pattern);
78+
5379
if !diff_result.is_same {
54-
panic!("\n{}\n", diff_result.context);
80+
error!("\n{}", diff_result.context);
5581
} else {
56-
println!("difftest pass")
82+
info!("difftest pass")
5783
}
58-
});
84+
}
85+
86+
Ok(())
5987
}
6088

6189
struct DiffMeta {
@@ -170,13 +198,18 @@ fn diff(spike_log: &SpikeLog, boat_log: &BoatLog, end_pattern: &EndPattern) -> D
170198

171199
if match_event.is_none() {
172200
return DiffMeta::failed(indoc::formatdoc! {"
173-
At PC={pc:#018x} boat write {data:#018x} to register x{reg_idx}, but this action was not found at spike side.
201+
At PC={pc:#018x} boat write {data:#018x} to register x{reg_idx},
202+
but this action was not found at spike side.
203+
204+
------------
205+
|Event Dump|
206+
------------
174207
175-
Displaying Spike event dump:
176-
{spike_event:#?}
208+
We get boat:
209+
{search_result}
177210
178-
Displaying Boat event dump:
179-
{search_result:#?}
211+
But have spike:
212+
{spike_event}
180213
"});
181214
}
182215
}

sail-impl/boat/difftest/src/spike.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
use std::fmt::Display;
22
use std::ops::{Deref, DerefMut};
33

4+
use anyhow::Context;
5+
46
pub fn run_process(
57
args: &[String],
68
elf_path: impl AsRef<std::ffi::OsStr>,
7-
) -> Result<SpikeLog, String> {
8-
let spike_exec =
9-
which::which("spike").unwrap_or_else(|err| panic!("spike exec not found: {err}"));
9+
) -> anyhow::Result<SpikeLog> {
10+
let spike_exec = which::which("spike").with_context(|| "spike exec not found")?;
1011

1112
let result = std::process::Command::new(&spike_exec)
1213
.args(args)
1314
.arg(&elf_path)
1415
.output()
15-
.unwrap_or_else(|err| panic!("fail exeuting spike: {err}"));
16+
.with_context(|| "fail exeuting spike")?;
1617

1718
if !result.status.success() {
1819
println!("{}", String::from_utf8_lossy(&result.stderr));
19-
return Err(format!(
20-
"fail to execute '{spike_exec:?}' with args {} for elf {}",
20+
anyhow::bail!(
21+
"fail to execute spike with args {} for elf {}",
2122
args.join(" "),
2223
elf_path.as_ref().to_str().unwrap()
23-
));
24+
);
2425
}
2526

26-
std::fs::write("./spike_commit.log", &result.stderr).unwrap();
27+
std::fs::write("./spike_commit.log", &result.stderr)
28+
.with_context(|| "fail storing spike commit log")?;
2729
let stdout = String::from_utf8_lossy(&result.stderr);
2830
let spike_log_ast = parse_spike_log(stdout);
2931

@@ -70,6 +72,8 @@ impl SpikeLog {
7072
self.0.is_empty()
7173
}
7274

75+
// TODO: future usage
76+
#[allow(dead_code)]
7377
pub fn has_register_commits(&self) -> Vec<&SpikeLogSyntax> {
7478
self.0
7579
.iter()
@@ -364,6 +368,7 @@ impl SpikeLogSyntax {
364368
// behavior to spike, so trim memory here
365369
ParseCursor::RegParseBegin if elem != "mem" && !elem.starts_with("0x") => {
366370
const VALID: [char; 4] = ['x', 'f', 'v', 'c'];
371+
// TODO: handle CSR
367372
if VALID.contains(&elem.chars().next().unwrap()) {
368373
ctx.cursor = ParseCursor::RegParseName(elem);
369374
} else {

0 commit comments

Comments
 (0)