summaryrefslogtreecommitdiff
path: root/floss/hcidoc/src/main.rs
blob: 4589c3ba1882d46c117181250a4707f4d018737a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use clap::{Arg, ArgAction, Command};
use std::io::Write;

mod engine;
mod groups;
mod parser;

use crate::engine::RuleEngine;
use crate::groups::{collisions, connections, controllers, informational};
use crate::parser::{LogParser, Packet, SnoopOpcodes};

fn main() {
    let matches = Command::new("hcidoc")
        .version("0.1")
        .author("Abhishek Pandit-Subedi <abhishekpandit@google.com>")
        .about("Analyzes a linux HCI snoop log for specific behaviors and errors.")
        .arg(
            Arg::new("filename")
                .help("Path to the snoop log. If omitted, read from stdin instead."),
        )
        .arg(
            Arg::new("ignore-unknown")
                .long("ignore-unknown")
                .action(ArgAction::SetTrue)
                .help("Don't print warning for unknown opcodes"),
        )
        .arg(
            Arg::new("signals")
                .short('s')
                .long("signals")
                .action(ArgAction::SetTrue)
                .help("Report signals from active rules."),
        )
        .arg(
            Arg::new("signals-only")
                .long("signals-only")
                .action(ArgAction::SetTrue)
                .help("Only print signals from active rules, don't print other events."),
        )
        .get_matches();

    let filename = match matches.get_one::<String>("filename") {
        Some(f) => f,
        None => "",
    };

    let ignore_unknown_opcode = match matches.get_one::<bool>("ignore-unknown") {
        Some(v) => *v,
        None => false,
    };

    let mut report_signals = match matches.get_one::<bool>("signals") {
        Some(v) => *v,
        None => false,
    };

    let report_only_signals = match matches.get_one::<bool>("signals-only") {
        Some(v) => *v,
        None => false,
    };

    if report_only_signals {
        report_signals = true;
    }

    let parser = match LogParser::new(filename) {
        Ok(p) => p,
        Err(e) => {
            println!(
                "Failed to load parser on {}: {}",
                if filename.len() == 0 { "stdin" } else { filename },
                e
            );
            return;
        }
    };

    // Create engine with default rule groups.
    let mut engine = RuleEngine::new();
    engine.add_rule_group("Collisions".into(), collisions::get_collisions_group());
    engine.add_rule_group("Connections".into(), connections::get_connections_group());
    engine.add_rule_group("Controllers".into(), controllers::get_controllers_group());
    engine.add_rule_group("Informational".into(), informational::get_informational_group());

    // Decide where to write output.
    let mut writer: Box<dyn Write> = Box::new(std::io::stdout());

    for (pos, v) in parser.get_snoop_iterator().enumerate() {
        match Packet::try_from((pos, &*v)) {
            Ok(p) => engine.process(p),
            Err(e) => {
                if !ignore_unknown_opcode {
                    match v.opcode() {
                        SnoopOpcodes::Command | SnoopOpcodes::Event => {
                            eprintln!("#{}: {}", pos, e);
                        }
                        _ => (),
                    }
                }
            }
        }
    }

    if !report_only_signals {
        engine.report(&mut writer);
    }
    if report_signals {
        let _ = writeln!(&mut writer, "### Signals ###");
        engine.report_signals(&mut writer);
    }
}