fix: expose driver-manager boot observability
This commit is contained in:
@@ -9,31 +9,31 @@ const USAGE: &str = "Usage: redbear-boot-check [--json]\n\n\
|
||||
Boot process runtime check. Validates critical boot services are\n\
|
||||
properly ordered, DRM device is ready, and greeter is healthy.";
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum CheckResult {
|
||||
Pass,
|
||||
Fail,
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
Skip,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
impl CheckResult {
|
||||
fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::Pass => "PASS",
|
||||
Self::Fail => "FAIL",
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
Self::Skip => "SKIP",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
struct Check {
|
||||
name: String,
|
||||
result: CheckResult,
|
||||
detail: String,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
impl Check {
|
||||
fn pass(name: &str, detail: &str) -> Self {
|
||||
Check {
|
||||
@@ -49,21 +49,15 @@ impl Check {
|
||||
detail: detail.to_string(),
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn skip(name: &str, detail: &str) -> Self {
|
||||
Check {
|
||||
name: name.to_string(),
|
||||
result: CheckResult::Skip,
|
||||
detail: detail.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
struct Report {
|
||||
checks: Vec<Check>,
|
||||
json_mode: bool,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
impl Report {
|
||||
fn new(json_mode: bool) -> Self {
|
||||
Report {
|
||||
@@ -89,8 +83,6 @@ impl Report {
|
||||
let icon = match check.result {
|
||||
CheckResult::Pass => "[PASS]",
|
||||
CheckResult::Fail => "[FAIL]",
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
CheckResult::Skip => "[SKIP]",
|
||||
};
|
||||
println!("{icon} {}: {}", check.name, check.detail);
|
||||
}
|
||||
@@ -104,16 +96,17 @@ impl Report {
|
||||
}
|
||||
#[derive(serde::Serialize)]
|
||||
struct JsonReport {
|
||||
driver_manager: bool,
|
||||
pcid_spawner: bool,
|
||||
drm_device: bool,
|
||||
compositor_socket: bool,
|
||||
greeter_service: bool,
|
||||
checks: Vec<JsonCheck>,
|
||||
}
|
||||
let pcid = self
|
||||
let driver_manager = self
|
||||
.checks
|
||||
.iter()
|
||||
.find(|c| c.name == "PCID_SPAWNER")
|
||||
.find(|c| c.name == "DRIVER_MANAGER")
|
||||
.map_or(false, |c| c.result == CheckResult::Pass);
|
||||
let drm = self
|
||||
.checks
|
||||
@@ -142,7 +135,8 @@ impl Report {
|
||||
if let Err(err) = serde_json::to_writer(
|
||||
std::io::stdout(),
|
||||
&JsonReport {
|
||||
pcid_spawner: pcid,
|
||||
driver_manager,
|
||||
pcid_spawner: driver_manager,
|
||||
drm_device: drm,
|
||||
compositor_socket: socket,
|
||||
greeter_service: greeter,
|
||||
@@ -171,22 +165,32 @@ fn parse_args() -> Result<bool, String> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn check_pcid_spawner() -> Check {
|
||||
let service = "/usr/lib/init.d/00_pcid-spawner.service";
|
||||
if std::path::Path::new(service).exists() {
|
||||
if std::path::Path::new("/scheme/pci").exists() {
|
||||
fn check_driver_manager() -> Check {
|
||||
let config_service = "/etc/init.d/00_driver-manager.service";
|
||||
let package_service = "/usr/lib/init.d/00_driver-manager.service";
|
||||
if std::path::Path::new(config_service).exists()
|
||||
|| std::path::Path::new(package_service).exists()
|
||||
{
|
||||
if std::path::Path::new("/scheme/driver-manager").exists()
|
||||
&& std::path::Path::new("/scheme/pci").exists()
|
||||
{
|
||||
Check::pass(
|
||||
"PCID_SPAWNER",
|
||||
"pcid-spawner service present, /scheme/pci registered",
|
||||
"DRIVER_MANAGER",
|
||||
"driver-manager service present, /scheme/driver-manager and /scheme/pci registered",
|
||||
)
|
||||
} else if std::path::Path::new("/scheme/pci").exists() {
|
||||
Check::fail(
|
||||
"DRIVER_MANAGER",
|
||||
"driver-manager installed but /scheme/driver-manager not registered",
|
||||
)
|
||||
} else {
|
||||
Check::fail(
|
||||
"PCID_SPAWNER",
|
||||
"pcid-spawner installed but /scheme/pci not registered",
|
||||
"DRIVER_MANAGER",
|
||||
"driver-manager installed but /scheme/pci not registered",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Check::fail("PCID_SPAWNER", "pcid-spawner service not found")
|
||||
Check::fail("DRIVER_MANAGER", "driver-manager service not found")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,8 +221,11 @@ fn check_compositor_socket() -> Check {
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn check_greeter_service() -> Check {
|
||||
let service = "/usr/lib/init.d/20_greeter.service";
|
||||
if !std::path::Path::new(service).exists() {
|
||||
let config_service = "/etc/init.d/20_greeter.service";
|
||||
let package_service = "/usr/lib/init.d/20_greeter.service";
|
||||
if !std::path::Path::new(config_service).exists()
|
||||
&& !std::path::Path::new(package_service).exists()
|
||||
{
|
||||
return Check::fail("GREETER_SERVICE", "greeter service definition not found");
|
||||
}
|
||||
let greeterd = "/usr/bin/redbear-greeterd";
|
||||
@@ -236,23 +243,6 @@ fn check_greeter_service() -> Check {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn check_pcid_spawner() -> Check {
|
||||
Check::skip("PCID_SPAWNER", "requires Redox runtime")
|
||||
}
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn check_drm_device() -> Check {
|
||||
Check::skip("DRM_DEVICE", "requires Redox runtime")
|
||||
}
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn check_compositor_socket() -> Check {
|
||||
Check::skip("COMPOSITOR_SOCKET", "requires Redox runtime")
|
||||
}
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn check_greeter_service() -> Check {
|
||||
Check::skip("GREETER_SERVICE", "requires Redox runtime")
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
{
|
||||
@@ -261,13 +251,13 @@ fn run() -> Result<(), String> {
|
||||
return Err(String::new());
|
||||
}
|
||||
println!("{PROGRAM}: boot check requires Redox runtime");
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
let json_mode = parse_args()?;
|
||||
let mut report = Report::new(json_mode);
|
||||
report.add(check_pcid_spawner());
|
||||
report.add(check_driver_manager());
|
||||
report.add(check_drm_device());
|
||||
report.add(check_compositor_socket());
|
||||
report.add(check_greeter_service());
|
||||
|
||||
@@ -195,6 +195,10 @@ enum BootTimelineEvent {
|
||||
bus: String,
|
||||
count: usize,
|
||||
},
|
||||
BusEnumerationFailed {
|
||||
bus: String,
|
||||
error: String,
|
||||
},
|
||||
Probe {
|
||||
device: String,
|
||||
driver: String,
|
||||
@@ -357,13 +361,13 @@ const INTEGRATIONS: &[IntegrationCheck] = &[
|
||||
functional_probe: Some(probe_smolnetd_surface),
|
||||
},
|
||||
IntegrationCheck {
|
||||
name: "pcid-spawner",
|
||||
name: "driver-manager",
|
||||
category: "Core",
|
||||
description: "PCI driver autoload daemon",
|
||||
artifact_path: Some("/usr/bin/pcid-spawner"),
|
||||
control_path: Some("/scheme/pci"),
|
||||
test_hint: "lspci",
|
||||
note: "The PCI scheme proves discovery is live, but not which driver handled each device.",
|
||||
description: "Device driver lifecycle and PCI handoff daemon",
|
||||
artifact_path: Some("/usr/bin/driver-manager"),
|
||||
control_path: Some("/scheme/driver-manager"),
|
||||
test_hint: "redbear-info --boot",
|
||||
note: "The driver-manager scheme proves the lifecycle daemon is live; PCI enumeration is reported separately through /scheme/pci and the boot timeline.",
|
||||
functional_probe: Some(probe_directory_readable),
|
||||
},
|
||||
IntegrationCheck {
|
||||
@@ -599,6 +603,7 @@ fn run() -> Result<(), String> {
|
||||
OutputMode::Table => print_table(&report, options.verbose),
|
||||
OutputMode::Json => print_json(&report),
|
||||
OutputMode::Test => print_tests(&report, options.verbose),
|
||||
OutputMode::Tui => {}
|
||||
OutputMode::Quirks => {}
|
||||
OutputMode::Probe => {}
|
||||
OutputMode::Boot => {}
|
||||
@@ -1516,6 +1521,7 @@ fn output_mode_flag(mode: OutputMode) -> &'static str {
|
||||
OutputMode::Table => "table output",
|
||||
OutputMode::Json => "--json",
|
||||
OutputMode::Test => "--test",
|
||||
OutputMode::Tui => "--tui",
|
||||
OutputMode::Quirks => "--quirks",
|
||||
OutputMode::Probe => "--probe",
|
||||
OutputMode::Boot => "--boot",
|
||||
@@ -2551,6 +2557,18 @@ fn collect_boot_timeline(runtime: &Runtime) -> Result<Vec<BootTimelineEntry>, St
|
||||
.map(|value| value as usize)
|
||||
.ok_or_else(|| format!("boot timeline entry {} is missing count", index + 1))?,
|
||||
},
|
||||
"bus_enumeration_failed" => BootTimelineEvent::BusEnumerationFailed {
|
||||
bus: value
|
||||
.get("bus")
|
||||
.and_then(JsonValue::as_str)
|
||||
.ok_or_else(|| format!("boot timeline entry {} is missing bus", index + 1))?
|
||||
.to_string(),
|
||||
error: value
|
||||
.get("error")
|
||||
.and_then(JsonValue::as_str)
|
||||
.ok_or_else(|| format!("boot timeline entry {} is missing error", index + 1))?
|
||||
.to_string(),
|
||||
},
|
||||
"probe" => BootTimelineEvent::Probe {
|
||||
device: value
|
||||
.get("device")
|
||||
@@ -2592,12 +2610,24 @@ fn print_boot_timeline(entries: &[BootTimelineEntry]) {
|
||||
let mut bound = 0usize;
|
||||
let mut deferred = 0usize;
|
||||
let mut failed = 0usize;
|
||||
let mut bus_failures = 0usize;
|
||||
|
||||
for entry in entries {
|
||||
let delta = entry.ts.saturating_sub(first_ts);
|
||||
match &entry.event {
|
||||
BootTimelineEvent::BusEnumerated { bus, count } => {
|
||||
println!("[{delta:>4}ms] bus {bus} enumerated {count} device(s)");
|
||||
println!(
|
||||
"[{delta:>4}ms] bus {} enumerated {count} device(s)",
|
||||
format_timeline_text(bus)
|
||||
);
|
||||
}
|
||||
BootTimelineEvent::BusEnumerationFailed { bus, error } => {
|
||||
bus_failures += 1;
|
||||
println!(
|
||||
"[{delta:>4}ms] bus {} enumeration failed: {}",
|
||||
format_timeline_text(bus),
|
||||
format_timeline_text(error)
|
||||
);
|
||||
}
|
||||
BootTimelineEvent::Probe {
|
||||
device,
|
||||
@@ -2612,15 +2642,17 @@ fn print_boot_timeline(entries: &[BootTimelineEntry]) {
|
||||
}
|
||||
println!(
|
||||
"[{delta:>4}ms] probed {} -> {} ({})",
|
||||
format_timeline_device(device),
|
||||
driver,
|
||||
format_timeline_text(&format_timeline_device(device)),
|
||||
format_timeline_text(driver),
|
||||
status.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Total: {bound} bound, {deferred} deferred, {failed} failed");
|
||||
println!(
|
||||
"Total: {bound} bound, {deferred} deferred, {failed} failed, {bus_failures} bus failure(s)"
|
||||
);
|
||||
}
|
||||
|
||||
fn collect_device_status(runtime: &Runtime, requested: &str) -> Result<DeviceStatusReport, String> {
|
||||
@@ -2866,6 +2898,10 @@ fn format_timeline_device(device: &str) -> String {
|
||||
})
|
||||
}
|
||||
|
||||
fn format_timeline_text(value: &str) -> String {
|
||||
value.escape_default().to_string()
|
||||
}
|
||||
|
||||
fn latest_boot_status_for_device(
|
||||
runtime: &Runtime,
|
||||
location: &redox_driver_sys::pci::PciLocation,
|
||||
@@ -4218,19 +4254,25 @@ mod tests {
|
||||
BOOT_TIMELINE_PATH,
|
||||
concat!(
|
||||
"{\"ts\":1000,\"event\":\"bus_enumerated\",\"bus\":\"pci\",\"count\":5}\n",
|
||||
"{\"ts\":1040,\"event\":\"bus_enumeration_failed\",\"bus\":\"usb\",\"error\":\"SchemeUnavailable\"}\n",
|
||||
"{\"ts\":1120,\"event\":\"probe\",\"device\":\"pci/00:02:0\",\"driver\":\"redox-drm\",\"status\":\"bound\"}\n",
|
||||
"{\"ts\":1750,\"event\":\"probe\",\"device\":\"pci/00:19:0\",\"driver\":\"e1000d\",\"status\":\"deferred\"}\n"
|
||||
),
|
||||
);
|
||||
|
||||
let timeline = collect_boot_timeline(&Runtime::from_root(root.clone())).unwrap();
|
||||
assert_eq!(timeline.len(), 3);
|
||||
assert_eq!(timeline.len(), 4);
|
||||
assert!(matches!(
|
||||
timeline[0].event,
|
||||
BootTimelineEvent::BusEnumerated { ref bus, count } if bus == "pci" && count == 5
|
||||
));
|
||||
assert!(matches!(
|
||||
timeline[1].event,
|
||||
BootTimelineEvent::BusEnumerationFailed { ref bus, ref error }
|
||||
if bus == "usb" && error == "SchemeUnavailable"
|
||||
));
|
||||
assert!(matches!(
|
||||
timeline[2].event,
|
||||
BootTimelineEvent::Probe {
|
||||
ref driver,
|
||||
status: BootProbeStatus::Bound,
|
||||
@@ -4238,6 +4280,7 @@ mod tests {
|
||||
} if driver == "redox-drm"
|
||||
));
|
||||
assert_eq!(format_timeline_device("pci/00:02:0"), "00.02.0");
|
||||
assert_eq!(format_timeline_text("pci\n\u{1b}[31m"), "pci\\n\\u{1b}[31m");
|
||||
|
||||
fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user