use bcc::perf_event::PerfMapBuilder;
use bcc::BccError;
use bcc::{Uprobe, Uretprobe, BPF};
use clap::{App, Arg};
use core::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{fmt, ptr};
#[repr(C)]
struct latency_event_t {
pid: u32,
delta: u64,
comm: [u8; 16], host: [u8; 80],
}
impl fmt::Display for latency_event_t {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let now = time::OffsetDateTime::now_utc();
let date = now.date();
let time = now.time();
let dt_string = format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}Z",
date.year(),
date.month() as u8,
date.day(),
time.hour(),
time.minute(),
time.second(),
time.nanosecond(),
);
write!(
f,
"{:<9} {:<6} {:<16} {:>10.2} {}",
dt_string,
self.pid,
get_string(&self.comm),
(self.delta as f32) / 1e6,
get_string(&self.host)
)
}
}
fn do_main(runnable: Arc<AtomicBool>) -> Result<(), BccError> {
let matches = App::new("gethostlatency")
.about("Prints the latency for getaddrinfo/gethostbyname(2) calls")
.arg(
Arg::with_name("pid")
.long("pid")
.short("p")
.help("Only trace calls from this PID")
.takes_value(true),
)
.get_matches();
let pid: Option<i32> = matches
.value_of("pid")
.map(|v| v.parse::<i32>().expect("Invalid argument for PID"));
let libc_so_path = "/lib/x86_64-linux-gnu/libc.so.6";
let code = include_str!("gethostlatency.c");
let mut module = BPF::new(code)?;
for symbol in &["getaddrinfo", "gethostbyname", "gethostbyname2"] {
Uprobe::new()
.handler("do_entry")
.binary(libc_so_path)
.symbol(symbol)
.pid(pid)
.attach(&mut module)?;
Uretprobe::new()
.handler("do_return")
.binary(libc_so_path)
.symbol(symbol)
.pid(pid)
.attach(&mut module)?;
}
let table = module.table("events")?;
let mut perf_map = PerfMapBuilder::new(table, perf_data_callback).build()?;
println!(
"{:<9} {:<6} {:<16} {:>10} {}",
"TIME", "PID", "COMM", "LATms", "HOST"
);
while runnable.load(Ordering::SeqCst) {
perf_map.poll(200);
}
Ok(())
}
fn perf_data_callback() -> Box<dyn FnMut(&[u8]) + Send> {
Box::new(|x| {
println!("{}", parse_struct(x));
})
}
fn parse_struct(x: &[u8]) -> latency_event_t {
unsafe { ptr::read_unaligned(x.as_ptr() as *const latency_event_t) }
}
fn get_string(x: &[u8]) -> String {
match x.iter().position(|&r| r == 0) {
Some(zero_pos) => String::from_utf8_lossy(&x[0..zero_pos]).to_string(),
None => String::from_utf8_lossy(x).to_string(),
}
}
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);
}
_ => {}
}
}