analyze_dumps/
analyze-dumps.rs

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
use std::{env, io, path::Path, process};

use memory_testing::*;

fn find_subarrays(needle: &[u8], haystack: &[u8]) -> Vec<usize> {
    let needle_len = needle.len();
    let haystack_len = haystack.len();
    let mut subarrays = vec![];

    if needle_len == 0 || haystack_len == 0 || needle_len > haystack_len {
        return vec![];
    }

    for i in 0..=(haystack_len - needle_len) {
        if &haystack[i..i + needle_len] == needle {
            subarrays.push(i);
        }
    }

    subarrays
}

const OK: &str = "✅";
const FAIL: &str = "❌";

fn comma_sep(nums: &[usize]) -> String {
    nums.iter()
        .map(ToString::to_string)
        .collect::<Vec<String>>()
        .join(", ")
}

fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        println!("Usage: ./analyze-dumps <base_dir>");
        process::exit(1);
    }
    let base_dir: &Path = args[1].as_ref();

    println!("Memory testing script started");

    let initial_core = std::fs::read(base_dir.join("output/initial_dump.bin"))?;
    let final_core = std::fs::read(base_dir.join("output/final_dump.bin"))?;

    let mut error = false;
    let mut table = comfy_table::Table::new();
    table.set_header(vec!["Name", "Initial", "Final", "OK"]);

    let cases = memory_testing::load_cases(base_dir);

    let test_string: Vec<u8> = TEST_STRING.as_bytes().to_vec();
    let test_initial_pos = find_subarrays(&test_string, &initial_core);

    if test_initial_pos.is_empty() {
        println!("ERROR: Test string not found in initial core dump, is the dump valid?");
        error = true;
    }

    for (idx, case) in cases.cases.iter().enumerate() {
        for lookup in &case.memory_lookups {
            let value = match &lookup.value {
                MemoryLookupValue::String { string } => string.as_bytes().to_vec(),
                MemoryLookupValue::Binary { hex } => hex::decode(hex).unwrap(),
            };

            let initial_pos = find_subarrays(&value, &initial_core);
            let final_pos = find_subarrays(&value, &final_core);

            let name = format!("{idx}: {} / {}", case.name, lookup.name);
            let ok_cond = final_pos.len() <= lookup.allowed_count.unwrap_or(0);

            table.add_row([
                name.as_str(),
                &comma_sep(&initial_pos),
                &comma_sep(&final_pos),
                if ok_cond { OK } else { FAIL },
            ]);

            if !ok_cond {
                error = true;
            }
        }
    }

    println!("{table}");

    process::exit(if error { 1 } else { 0 });
}