1use bstr::BString;
6use fuchsia_inspect::Inspector;
7use futures::future::BoxFuture;
8use regex::bytes::Regex;
9use starnix_sync::Mutex;
10use std::collections::hash_map::Entry;
11use std::collections::{BTreeMap, HashMap};
12use std::sync::LazyLock;
13
14const DESIRED_PATH_PREFIXES: &[&str] = &["/dev/", "/proc/", "/sys/"];
16
17const IGNORED_PATH_PREFIXES: &[&str] = &[
19 #[cfg(not(target_arch = "aarch64"))]
21 "/proc/sys/abi/swp",
22 "/sys/class/android_usb",
24 "/sys/dev/block",
27 "/sys/dev/char",
28 "/sys/fs/f2fs",
30 "/sys/fs/incremental-fs",
31 "/sys/fs/pstore",
32 "/sys/kernel/tracing",
36 "/sys/kernel/debug/tracing",
37];
38
39const NUMBER_DEDUPER: &str = r#"(block/[A-Za-z]+|cpu|proc/|pid_|uid_|task|task/)\d+"#;
42
43static NOT_FOUND_COUNTS: LazyLock<Mutex<HashMap<BString, u64>>> =
44 LazyLock::new(|| Mutex::new(HashMap::new()));
45
46pub fn track_file_not_found(path: BString) {
47 if DESIRED_PATH_PREFIXES.iter().any(|&prefix| path.starts_with(prefix.as_bytes())) {
48 match NOT_FOUND_COUNTS.lock().entry(path) {
49 Entry::Occupied(mut o) => *o.get_mut() += 1,
50 Entry::Vacant(v) => {
51 crate::log_debug!(
52 tag = "not_found",
53 path:% = v.key();
54 "couldn't resolve",
55 );
56 v.insert(1);
57 }
58 }
59 }
60}
61
62pub fn not_found_lazy_node_callback() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> {
63 Box::pin(async {
64 let inspector = Inspector::default();
65
66 let original_counts = NOT_FOUND_COUNTS.lock();
73 let original_counts = original_counts.iter().map(|(p, n)| (p.as_slice(), *n));
74 for (path, count) in dedupe_uninteresting_numbers_in_paths(original_counts) {
75 if path.ends_with("uevent") {
78 continue;
79 }
80 if IGNORED_PATH_PREFIXES.iter().any(|prefix| path.starts_with(prefix)) {
81 continue;
82 }
83 inspector.root().record_uint(path, count);
84 }
85 Ok(inspector)
86 })
87}
88
89fn dedupe_uninteresting_numbers_in_paths<'a>(
90 original_counts: impl Iterator<Item = (&'a [u8], u64)>,
91) -> BTreeMap<String, u64> {
92 let number_deduper = Regex::new(NUMBER_DEDUPER).unwrap();
93 let mut numbers_collapsed = BTreeMap::new();
94 for (orig_path, count) in original_counts {
95 let collapsed = number_deduper.replace_all(&*orig_path, "${1}N".as_bytes());
96 *numbers_collapsed.entry(String::from_utf8_lossy(&*collapsed).to_string()).or_default() +=
97 count;
98 }
99 numbers_collapsed
100}
101
102#[cfg(test)]
103mod tests {
104 use super::dedupe_uninteresting_numbers_in_paths;
105
106 #[test]
107 fn dedupe_expected_paths() {
108 let original_paths = &[
109 "/dev/pmsg0",
110 "/proc/1006/cgroup",
111 "/proc/1006/schedstat",
112 "/proc/268/cgroup",
113 "/proc/470/schedstat",
114 "/proc/47/schedstat",
115 "/proc/32/cgroup",
116 "/proc/2/schedstat",
117 "/proc/2/cgroup",
118 "/proc/sys/kernel/domainname",
119 "/proc/sys/net/ipv4/conf",
120 "/proc/sys/net/ipv6/conf/default/accept_ra_rt_info_min_plen",
121 "/proc/uid_concurrent_policy_time",
122 "/sys/block/loop0/queue/nr_requests",
123 "/sys/block/loop10/queue/nr_requests",
124 "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state",
125 "/sys/devices/system/cpu/cpu1/cpufreq/stats/time_in_state",
126 "/sys/devices/system/cpu/cpu0uevent",
127 "/sys/devices/system/cpu/cpu1uevent",
128 "/sys/devices/virtual/block/loop0/queueuevent",
129 "/sys/devices/virtual/block/loop1/queueuevent",
130 "/sys/fs/f2fs/features",
131 "/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable",
132 "/sys/kernel/debug/tracing/events/f2fs/f2fs_get_data_block/enable",
133 "/sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total/enable",
134 "/sys/kernel/debug/tracing/events/i2c/enable",
135 "/sys/kernel/debug/tracing/events/i2c/i2c_read/enable",
136 "/sys/kernel/debug/tracing/per_cpu/cpu20/trace",
137 "/sys/kernel/debug/tracing/per_cpu/cpu7/trace",
138 "/sys/kernel/tracing/options/record-tgid",
139 "/sys/kernel/tracing/per_cpu/cpu20/trace",
140 "/sys/kernel/tracing/per_cpu/cpu3/trace",
141 "/proc/1/task/1004/wchan",
142 "/proc/1/task/1009/wchan",
143 "/proc/2/task1004/wchan",
144 "/proc/2/task1009/wchan",
145 ];
146 let observed =
147 dedupe_uninteresting_numbers_in_paths(original_paths.iter().map(|p| (p.as_bytes(), 1)))
148 .into_iter()
149 .map(|(p, n)| (p.to_string(), n))
150 .collect::<Vec<(String, u64)>>();
151 let expected = [
152 ("/dev/pmsg0", 1),
153 ("/proc/N/cgroup", 4),
154 ("/proc/N/schedstat", 4),
155 ("/proc/N/task/N/wchan", 2),
156 ("/proc/N/taskN/wchan", 2),
157 ("/proc/sys/kernel/domainname", 1),
158 ("/proc/sys/net/ipv4/conf", 1),
159 ("/proc/sys/net/ipv6/conf/default/accept_ra_rt_info_min_plen", 1),
160 ("/proc/uid_concurrent_policy_time", 1),
161 ("/sys/block/loopN/queue/nr_requests", 2),
162 ("/sys/devices/system/cpu/cpuN/cpufreq/stats/time_in_state", 2),
163 ("/sys/devices/system/cpu/cpuNuevent", 2),
164 ("/sys/devices/virtual/block/loopN/queueuevent", 2),
165 ("/sys/fs/f2fs/features", 1),
166 ("/sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable", 1),
167 ("/sys/kernel/debug/tracing/events/f2fs/f2fs_get_data_block/enable", 1),
168 ("/sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total/enable", 1),
169 ("/sys/kernel/debug/tracing/events/i2c/enable", 1),
170 ("/sys/kernel/debug/tracing/events/i2c/i2c_read/enable", 1),
171 ("/sys/kernel/debug/tracing/per_cpu/cpuN/trace", 2),
172 ("/sys/kernel/tracing/options/record-tgid", 1),
173 ("/sys/kernel/tracing/per_cpu/cpuN/trace", 2),
174 ]
175 .iter()
176 .map(|(p, n)| (p.to_string(), *n))
177 .collect::<Vec<(String, u64)>>();
178 pretty_assertions::assert_eq!(observed, expected);
179 }
180}