1 //! UProbestats executable.
2 use anyhow::{anyhow, bail, ensure, Result};
3 use binder::ProcessState;
4 use log::{debug, error, LevelFilter};
5 use rustutils::system_properties;
6 use std::process::exit;
7 use std::{
8 thread,
9 time::{Duration, Instant},
10 };
11 use uprobestats_bpf::bpf_perf_event_open;
12 use uprobestats_rs::{config_resolver, guardrail};
13
14 mod bpf_map;
15
main()16 fn main() {
17 logger::init(
18 logger::Config::default()
19 .with_tag_on_device("uprobestats")
20 .with_max_level(if is_user_build() { LevelFilter::Info } else { LevelFilter::Trace }),
21 );
22
23 if let Err(e) = main_impl() {
24 error!("{}", e);
25 exit(1);
26 };
27 }
28
main_impl() -> Result<()>29 fn main_impl() -> Result<()> {
30 debug!("started");
31
32 ensure!(is_uprobestats_enabled(), "Uprobestats disabled by flag");
33
34 let config = config_resolver::read_config("/data/misc/uprobestats-configs/config")?;
35 ensure!(
36 guardrail::is_allowed(&config, is_user_build(), true)?,
37 "uprobestats probing config disallowed on this device"
38 );
39 let task = config_resolver::resolve_single_task(config)?;
40
41 ProcessState::start_thread_pool();
42
43 let probes = config_resolver::resolve_probes(&task.task)?;
44 for probe in probes {
45 bpf_perf_event_open(
46 probe.filename.clone(),
47 probe.offset,
48 task.pid,
49 probe.bpf_program_path.clone(),
50 )?;
51 debug!(
52 "attached bpf {} to {} at {}",
53 probe.bpf_program_path, &probe.filename, &probe.offset
54 );
55 }
56
57 let duration_seconds: u64 = task.duration_seconds.try_into()?;
58 let now = Instant::now();
59 let duration = Duration::from_secs(duration_seconds);
60
61 let results = task.bpf_map_paths.into_iter().map(|map_path| {
62 debug!("Spawning thread for map_path: {}", map_path);
63 match thread::spawn({
64 let task_proto = task.task.clone();
65 move || bpf_map::poll_and_loop(&map_path, now, duration, task_proto)
66 })
67 .join()
68 {
69 Ok(result) => result.map_err(|e| anyhow!("Thread error: {}", e)),
70 Err(panic) => bail!("Thread panic: {:?}", panic),
71 }
72 });
73
74 let errors: Vec<_> = results
75 .filter_map(|r| match r {
76 Ok(()) => None,
77 Err(e) => Some(e),
78 })
79 .collect();
80
81 if !errors.is_empty() {
82 let msg = errors.into_iter().map(|e| e.to_string()).collect::<Vec<String>>().join(",");
83 let msg = format!("At least one thread returned error: {}", msg);
84 bail!("{}", msg);
85 }
86
87 debug!("done");
88
89 Ok(())
90 }
91
is_user_build() -> bool92 fn is_user_build() -> bool {
93 if let Ok(Some(val)) = system_properties::read("ro.build.type") {
94 return val == "user";
95 }
96 true
97 }
98
is_uprobestats_enabled() -> bool99 fn is_uprobestats_enabled() -> bool {
100 uprobestats_mainline_flags_rust::enable_uprobestats()
101 }
102