use grok::Grok;
fn parse_stall_duration_seconds(kmsg: &str) -> Option<f64> {
let grok = Grok::with_default_patterns();
let pattern = grok
.compile(r"failed to run for %{INT:seconds}\.%{INT:millis}s", false)
.expect("grok pattern compiles with fancy-regex backend");
let matches = pattern.match_against(kmsg)?;
let seconds: u64 = matches.get("seconds")?.parse().ok()?;
let millis: u64 = matches.get("millis")?.parse().ok()?;
Some(seconds as f64 + (millis as f64) / 1000.0)
}
#[test]
fn parses_kernel_stall_message() {
let kmsg = "\
[ 42.001] sched_ext: enabled scx-ktstr
[ 44.105] kworker/0:1[42] failed to run for 2.004s
[ 44.120] sched_ext: scx-ktstr: BPF scheduler \"scx-ktstr\" errored, disabling
";
assert_eq!(parse_stall_duration_seconds(kmsg), Some(2.004));
}
#[test]
fn returns_none_when_no_stall_message() {
let kmsg = "[ 42.001] sched_ext: enabled scx-ktstr\n";
assert_eq!(parse_stall_duration_seconds(kmsg), None);
}
#[test]
fn parses_the_first_stall_line_when_multiple() {
let kmsg = "\
a[1] failed to run for 1.500s
b[2] failed to run for 3.700s
";
assert_eq!(parse_stall_duration_seconds(kmsg), Some(1.500));
}
#[test]
fn parses_exact_second_boundary() {
let kmsg = "x[1] failed to run for 5.000s\n";
assert_eq!(parse_stall_duration_seconds(kmsg), Some(5.0));
}
#[test]
fn computes_seconds_plus_millis_correctly() {
assert_eq!(
parse_stall_duration_seconds("a[1] failed to run for 0.123s\n"),
Some(0.123),
);
assert_eq!(
parse_stall_duration_seconds("b[2] failed to run for 7.999s\n"),
Some(7.999),
);
}