use bcc::BccError; use bcc::{Tracepoint, BPF}; use clap::{App, Arg}; use core::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::{fmt, mem, ptr, thread, time}; // A simple tool for reporting on time spent in softirq handlers // // Based on: https://github.com/iovisor/bcc/blob/master/tools/softirqs.py #[repr(C)] struct irq_key_t { vec: u32, slot: u64, } #[allow(non_camel_case_types)] enum SoftIRQ { HI, TIMER, NET_TX, NET_RX, BLOCK, IRQ_POLL, TASKLET, SCHED, HRTIMER, RCU, UNKNOWN, } impl From for SoftIRQ { fn from(val: u32) -> Self { match val { 0 => SoftIRQ::HI, 1 => SoftIRQ::TIMER, 2 => SoftIRQ::NET_TX, 3 => SoftIRQ::NET_RX, 4 => SoftIRQ::BLOCK, 5 => SoftIRQ::IRQ_POLL, 6 => SoftIRQ::TASKLET, 7 => SoftIRQ::SCHED, 8 => SoftIRQ::HRTIMER, 9 => SoftIRQ::RCU, _ => SoftIRQ::UNKNOWN, } } } impl SoftIRQ { fn name(&self) -> String { match *self { SoftIRQ::HI => "HI".to_owned(), SoftIRQ::TIMER => "TIMER".to_owned(), SoftIRQ::NET_TX => "NET_TX".to_owned(), SoftIRQ::NET_RX => "NET_RX".to_owned(), SoftIRQ::BLOCK => "BLOCK".to_owned(), SoftIRQ::IRQ_POLL => "IRQ_POLL".to_owned(), SoftIRQ::TASKLET => "TASKLET".to_owned(), SoftIRQ::SCHED => "SCHED".to_owned(), SoftIRQ::HRTIMER => "HRTIMER".to_owned(), SoftIRQ::RCU => "RCU".to_owned(), SoftIRQ::UNKNOWN => "UNKNOWN".to_owned(), } } } impl fmt::Display for SoftIRQ { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name()) } } fn do_main(runnable: Arc) -> Result<(), BccError> { let matches = App::new("softirqs") .about("Reports time spent in IRQ Handlers") .arg( Arg::with_name("interval") .long("interval") .value_name("Seconds") .help("Integration window duration and period for stats output") .takes_value(true), ) .arg( Arg::with_name("windows") .long("windows") .value_name("Count") .help("The number of intervals before exit") .takes_value(true), ) .get_matches(); let interval: usize = matches .value_of("interval") .unwrap_or("1") .parse() .expect("Invalid argument for interval"); let windows: Option = matches .value_of("windows") .map(|v| v.parse().expect("Invalid argument for windows")); let code = if cfg!(any( feature = "v0_4_0", feature = "v0_5_0", feature = "v0_6_0", feature = "v0_6_1", )) { include_str!("softirqs_0_4_0.c") } else { include_str!("softirqs_0_7_0.c") }; // compile the above BPF code! let mut module = BPF::new(code)?; // tracepoints! Tracepoint::new() .handler("softirq_entry") .subsystem("irq") .tracepoint("softirq_entry") .attach(&mut module)?; Tracepoint::new() .handler("softirq_exit") .subsystem("irq") .tracepoint("softirq_exit") .attach(&mut module)?; let table = module.table("dist")?; let mut window = 0; while runnable.load(Ordering::SeqCst) { thread::sleep(time::Duration::new(interval as u64, 0)); println!("======"); for entry in table.iter() { let data = parse_struct(&entry.key); let value = entry.value; let id = data.vec; let mut v = [0_u8; 8]; for i in 0..8 { v[i] = *value.get(i).unwrap_or(&0); } let time: u64 = unsafe { mem::transmute(v) }; if time > 0 { let softirq = SoftIRQ::from(id); println!("softirq: {} time (ns): {}", softirq, time); } } if let Some(windows) = windows { window += 1; if window >= windows { return Ok(()); } } } Ok(()) } fn parse_struct(x: &[u8]) -> irq_key_t { unsafe { ptr::read_unaligned(x.as_ptr() as *const irq_key_t) } } fn main() { let runnable = Arc::new(AtomicBool::new(true)); let r = runnable.clone(); ctrlc::set_handler(move || { r.store(false, Ordering::SeqCst); }) .expect("Failed to set handler for SIGINT / SIGTERM"); match do_main(runnable) { Err(x) => { eprintln!("Error: {}", x); std::process::exit(1); } _ => {} } }