rust-bcc/examples/softirqs.rs

182 lines
4.7 KiB
Rust

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<u32> 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<AtomicBool>) -> 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<usize> = 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(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);
}
_ => {}
}
}