linux-kernel-panic-parser 0.1.1

Lossless, architecture-neutral parsing of Linux kernel panic logs
Documentation
//! Run with `cargo run --example parse -- path/to/panic.log`.
//! Add `--debug` to output the parsed Rust structure instead of the original log.
use linux_kernel_panic_parser::{LineKind, parse};
use std::{
    env,
    error::Error,
    fs,
    io::{self, Read},
};

fn main() -> Result<(), Box<dyn Error>> {
    let mut debug = false;
    let mut path = None;
    let mut positional_only = false;
    for argument in env::args_os().skip(1) {
        if !positional_only && argument == "--debug" {
            debug = true;
        } else if !positional_only && argument == "--" {
            positional_only = true;
        } else if !positional_only && (argument == "--help" || argument == "-h") {
            println!(
                "Usage: parse [--debug] [--] [path/to/panic.log]\nReads stdin when no path is supplied.\n--debug prints the parsed Rust structure to stdout."
            );
            return Ok(());
        } else if (!positional_only && argument.to_string_lossy().starts_with('-'))
            || path.is_some()
        {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Usage: parse [--debug] [--] [path/to/panic.log]",
            )
            .into());
        } else {
            path = Some(argument);
        }
    }
    let text = if let Some(path) = path {
        fs::read_to_string(path)?
    } else {
        let mut text = String::new();
        io::stdin().read_to_string(&mut text)?;
        text
    };
    let log = parse(text);
    if debug {
        println!("{log:#?}");
        return Ok(());
    }
    for line in log.lines() {
        match line.kind() {
            LineKind::Panic { message } => eprintln!("panic: {message}"),
            LineKind::Cpu {
                cpu,
                uid,
                pid,
                details,
            } => {
                eprintln!("cpu: {cpu}, uid: {uid:?}, pid: {pid}, {details}");
                if let Some(time) = line.timestamp() {
                    eprintln!("wall time: {time}");
                }
                if let Some(uptime) = line.uptime() {
                    eprintln!("uptime: {uptime}");
                }
            }
            LineKind::Symbol {
                label,
                segment,
                frame,
            } => {
                eprintln!(
                    "{label}: segment {segment:?}, {}+{}/{}",
                    frame.symbol, frame.offset, frame.size
                );
            }
            LineKind::Diagnostic { label, details }
                if matches!(label.as_str(), "Oops" | "BUG" | "#PF") =>
            {
                eprintln!("{label}: {details}");
            }
            LineKind::Frame(frame) => {
                eprintln!("frame: {}+{}/{}", frame.symbol, frame.offset, frame.size)
            }
            _ => {}
        }
    }
    // Original log goes to stdout; summaries go to stderr.
    print!("{log}");
    Ok(())
}