1use anyhow::Result;
7use clap::Parser;
8use log::*;
9
10mod monitors;
11use monitors::HintsTlsMonitor;
12use monitors::{CacheMonitor, CacheMonitorValue, PerfSampleMonitor, SoftDirtyCacheMonitor};
13use std::mem::MaybeUninit;
14pub mod bpf_intf;
15pub mod bpf_skel; pub use bpf_skel as bpf;
17
18#[derive(Debug, Parser)]
20#[command(verbatim_doc_comment)]
21struct Opts {
22 #[clap(short = 'p', long)]
24 pid: Option<u32>,
25
26 #[clap(short = 'v', long, action = clap::ArgAction::Count)]
29 verbose: u8,
30
31 #[clap(long, action = clap::ArgAction::SetTrue)]
34 soft_dirty: bool,
35
36 #[clap(long, action = clap::ArgAction::SetTrue)]
38 perf_sample: bool,
39
40 #[clap(long = "perf-freq", default_value_t = 1000)]
42 perf_freq: u64,
43
44 #[clap(long, action = clap::ArgAction::SetTrue)]
47 json: bool,
48
49 #[clap(short = 'i', long, default_value_t = 1)]
51 interval: u64,
52
53 #[clap(long, default_value_t = 4)]
55 ring_mb: u32,
56
57 #[clap(long, action = clap::ArgAction::SetTrue)]
59 hints: bool,
60
61 #[clap(long = "hints-map")]
63 hints_map: Option<String>,
64}
65
66fn main() -> Result<()> {
67 let opts = Opts::parse();
68
69 let llv = match opts.verbose {
70 0 => simplelog::LevelFilter::Info,
71 1 => simplelog::LevelFilter::Debug,
72 _ => simplelog::LevelFilter::Trace,
73 };
74
75 let mut lcfg = simplelog::ConfigBuilder::new();
76 lcfg.set_time_offset_to_local()
77 .expect("Failed to set local time offset")
78 .set_time_level(simplelog::LevelFilter::Error)
79 .set_location_level(simplelog::LevelFilter::Off)
80 .set_target_level(simplelog::LevelFilter::Off)
81 .set_thread_level(simplelog::LevelFilter::Off);
82
83 simplelog::TermLogger::init(
84 llv,
85 lcfg.build(),
86 simplelog::TerminalMode::Stderr,
87 simplelog::ColorChoice::Auto,
88 )?;
89
90 let pid_opt = opts.pid;
93
94 let any_monitor_flag = opts.soft_dirty || opts.perf_sample || opts.hints;
95 let mut soft_dirty_open: MaybeUninit<libbpf_rs::OpenObject> = MaybeUninit::uninit();
97 let mut perf_open: MaybeUninit<libbpf_rs::OpenObject> = MaybeUninit::uninit();
98 let mut hints_open: MaybeUninit<libbpf_rs::OpenObject> = MaybeUninit::uninit();
99
100 let mut monitors: Vec<Box<dyn CacheMonitor<'_>>> = Vec::new();
101
102 let ring_size_bytes = (opts.ring_mb as u64)
103 .saturating_mul(1024 * 1024)
104 .max(4 * 1024);
105
106 if !any_monitor_flag || opts.soft_dirty {
107 let monitor = SoftDirtyCacheMonitor::new(&mut soft_dirty_open, pid_opt, ring_size_bytes)?;
108 monitors.push(Box::new(monitor));
109 }
110 if !any_monitor_flag || opts.perf_sample {
111 let monitor = PerfSampleMonitor::new(&mut perf_open, pid_opt, opts.perf_freq)?;
112 monitors.push(Box::new(monitor));
113 }
114
115 if !any_monitor_flag || opts.hints {
116 let pin_path = opts
117 .hints_map
118 .as_deref()
119 .ok_or_else(|| anyhow::anyhow!("--hints-map must be provided with --hints"))?;
120 let monitor = HintsTlsMonitor::new(&mut hints_open, pin_path, ring_size_bytes)?;
121 monitors.push(Box::new(monitor));
122 }
123
124 if monitors.is_empty() {
125 return Err(anyhow::anyhow!("No cache monitors selected"));
126 }
127
128 let sleep_dur = std::time::Duration::from_secs(opts.interval.max(1));
129 let shutdown_flag = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
130 let sf_clone = shutdown_flag.clone();
131 ctrlc::set_handler(move || {
132 sf_clone.store(true, std::sync::atomic::Ordering::Relaxed);
133 })?;
134
135 while !shutdown_flag.load(std::sync::atomic::Ordering::Relaxed) {
136 for m in monitors.iter_mut() {
137 m.poll()?;
138 }
139
140 std::thread::sleep(sleep_dur);
141
142 for m in monitors.iter_mut() {
143 if opts.json {
144 m.consume(
145 &mut |v: CacheMonitorValue| match serde_json::to_string(&v) {
146 Ok(s) => println!("{}", s),
147 Err(e) => error!("Failed to serialize value: {e}"),
148 },
149 )?;
150 } else {
151 m.consume(&mut |v: CacheMonitorValue| println!("{:?}", v))?;
152 }
153 }
154 }
155
156 Ok(())
157}