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
mod codecov;

use codecov::Tracker;
use instruction_hooking::{InstructionHook, INSTRUCTION_HOOKS};

use std::any::{Any, TypeId};

use auxtools::*;

fn with_tracker_option<F>(f: F, create: bool)
where
	F: FnOnce(&mut Tracker) {
	INSTRUCTION_HOOKS.with(|hooks|{
		let mut hooks_ref: std::cell::RefMut<Vec<Box<dyn InstructionHook>>> = hooks.borrow_mut();
		let tracker_tid = TypeId::of::<Tracker>();
		let tracker_option = hooks_ref
			.iter_mut()
			.find(|hook|(*hook).as_ref().type_id() == tracker_tid);

		match tracker_option {
			Some(existing_hook) => {
				let mut_hook = existing_hook.as_mut();
				let any_hook = mut_hook.as_any();
				let existing_tracker = any_hook.downcast_mut::<Tracker>().unwrap();
				f(existing_tracker);
			},
			None => {
				if create {
					let mut created_tracker = Tracker::new();
					f(&mut created_tracker);
					hooks_ref.push(Box::new(created_tracker));
				}
			}
		}
	});
}

// INSTRUCTION_HOOKS are cleared on shutdown so we don't need to worry about that.
#[hook("/proc/start_code_coverage")]
fn start_code_coverage(coverage_file: Value) {
	let coverage_file_string_result = coverage_file.as_string();
	if let Err(runtime) = coverage_file_string_result {
		return Err(runtime);
	}

	let coverage_file_string = coverage_file_string_result.unwrap();

	let mut init_result = false;
	with_tracker_option(|tracker|{
		init_result = tracker.init_context(coverage_file_string.clone());
	}, true);

	if !init_result {
		return Err(runtime!(
			"A code coverage context for {} already exists!",
			coverage_file_string
		));
	}

	Ok(Value::null())
}

#[hook("/proc/stop_code_coverage")]
fn stop_code_coverage(coverage_file: Value) {
	let coverage_file_string_result = coverage_file.as_string();
	if let Err(runtime) = coverage_file_string_result {
		return Err(runtime);
	}

	let coverage_file_string = coverage_file_string_result.unwrap();

	let mut result = Ok(Value::null());
	with_tracker_option(|tracker|{
		let inner_result = tracker.finalize_context(&coverage_file_string);
		result = match inner_result {
			Ok(had_entry) => {
				if !had_entry {
					Err(runtime!(
						"A code coverage context for {} does not exist!",
						coverage_file_string
					))
				} else {
					Ok(Value::null())
				}
			},
			Err(error) => Err(runtime!(
				"A error occurred while trying to save the coverage file: {}",
				error
			))
		}
	}, false);

	result
}