diff --git a/Cargo.toml b/Cargo.toml index 5493c7a8..086dbcdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,9 @@ edition = '2018' [dependencies] bcc-sys = "0.13.0" byteorder = "1.3.4" -failure = "0.1.7" libc = "0.2.68" regex = "1.3.5" +thiserror = "1.0.19" [dev-dependencies] clap = "2.33.0" diff --git a/examples/biosnoop.rs b/examples/biosnoop.rs index 26a4e9b9..60fb56ec 100644 --- a/examples/biosnoop.rs +++ b/examples/biosnoop.rs @@ -1,7 +1,7 @@ use bcc::core::BPF; use bcc::perf::init_perf_map; +use bcc::BccError; use clap::{App, Arg}; -use failure::Error; use core::sync::atomic::{AtomicBool, Ordering}; use std::ptr; @@ -24,7 +24,7 @@ struct data_t { name: [u8; 16], } -fn do_main(runnable: Arc) -> Result<(), Error> { +fn do_main(runnable: Arc) -> Result<(), BccError> { let matches = App::new("biosnoop") .about("Trace block I/O") .arg( @@ -134,7 +134,6 @@ fn main() { if let Err(x) = do_main(runnable) { eprintln!("Error: {}", x); - eprintln!("{}", x.backtrace()); std::process::exit(1); } } diff --git a/examples/opensnoop.rs b/examples/opensnoop.rs index c9b9b66e..c0084ff8 100644 --- a/examples/opensnoop.rs +++ b/examples/opensnoop.rs @@ -1,12 +1,7 @@ -extern crate bcc; -extern crate byteorder; -extern crate failure; -extern crate libc; - use bcc::core::BPF; use bcc::perf::init_perf_map; +use bcc::BccError; use clap::{App, Arg}; -use failure::Error; use core::sync::atomic::{AtomicBool, Ordering}; use std::ptr; @@ -34,7 +29,7 @@ struct data_t { fname: [u8; 255], // NAME_MAX } -fn do_main(runnable: Arc) -> Result<(), Error> { +fn do_main(runnable: Arc) -> Result<(), BccError> { let matches = App::new("opensnoop") .about("Prints out filename + PID every time a file is opened") .arg( @@ -112,7 +107,6 @@ fn main() { match do_main(runnable) { Err(x) => { eprintln!("Error: {}", x); - eprintln!("{}", x.backtrace()); std::process::exit(1); } _ => {} diff --git a/examples/runqlat.rs b/examples/runqlat.rs index 89abbeed..8ef5e2e2 100644 --- a/examples/runqlat.rs +++ b/examples/runqlat.rs @@ -1,6 +1,6 @@ use bcc::core::BPF; +use bcc::BccError; use clap::{App, Arg}; -use failure::Error; use core::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -23,7 +23,7 @@ fn attach_events(bpf: &mut BPF) { .unwrap(); } -#[cfg(any(not(feature = "v0_4_0"), not(feature = "v0_5_0"),))] +#[cfg(not(any(feature = "v0_4_0", feature = "v0_5_0")))] fn attach_events(bpf: &mut BPF) { if bpf.support_raw_tracepoint() { let raw_tp_sched_wakeup = bpf.load_raw_tracepoint("raw_tp__sched_wakeup").unwrap(); @@ -50,7 +50,7 @@ fn attach_events(bpf: &mut BPF) { } } -fn do_main(runnable: Arc) -> Result<(), Error> { +fn do_main(runnable: Arc) -> Result<(), BccError> { let matches = App::new("runqlat") .about("Reports distribution of scheduler latency") .arg( @@ -131,7 +131,6 @@ fn main() { match do_main(runnable) { Err(x) => { eprintln!("Error: {}", x); - eprintln!("{}", x.backtrace()); std::process::exit(1); } _ => {} diff --git a/examples/softirqs.rs b/examples/softirqs.rs index a00a1147..1f045ca8 100644 --- a/examples/softirqs.rs +++ b/examples/softirqs.rs @@ -1,6 +1,7 @@ use bcc::core::BPF; +use bcc::BccError; use clap::{App, Arg}; -use failure::Error; +use std::error::Error; use core::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -73,7 +74,7 @@ impl fmt::Display for SoftIRQ { } } -fn do_main(runnable: Arc) -> Result<(), Error> { +fn do_main(runnable: Arc) -> Result<(), BccError> { let matches = App::new("softirqs") .about("Reports time spent in IRQ Handlers") .arg( @@ -169,7 +170,6 @@ fn main() { match do_main(runnable) { Err(x) => { eprintln!("Error: {}", x); - eprintln!("{}", x.backtrace()); std::process::exit(1); } _ => {} diff --git a/examples/strlen.rs b/examples/strlen.rs index b44be1dd..6930d873 100644 --- a/examples/strlen.rs +++ b/examples/strlen.rs @@ -1,17 +1,16 @@ extern crate bcc; extern crate byteorder; -extern crate failure; extern crate libc; use bcc::core::BPF; +use bcc::BccError; use byteorder::{NativeEndian, ReadBytesExt}; -use failure::Error; use core::sync::atomic::{AtomicBool, Ordering}; use std::io::Cursor; use std::sync::Arc; -fn do_main(runnable: Arc) -> Result<(), Error> { +fn do_main(runnable: Arc) -> Result<(), BccError> { let code = " #include @@ -75,7 +74,6 @@ fn main() { match do_main(runnable) { Err(x) => { eprintln!("Error: {}", x); - eprintln!("{}", x.backtrace()); std::process::exit(1); } _ => {} diff --git a/examples/tcpretrans.rs b/examples/tcpretrans.rs index 3dedc5b0..9055e611 100644 --- a/examples/tcpretrans.rs +++ b/examples/tcpretrans.rs @@ -1,8 +1,7 @@ use bcc::core::BPF; -extern crate chrono; +use bcc::BccError; use chrono::Utc; use clap::{App, Arg}; -use failure::Error; use core::sync::atomic::{AtomicBool, Ordering}; use std::net::Ipv4Addr; @@ -38,7 +37,7 @@ struct ipv6_data_t { type_: u64, } -fn do_main(runnable: Arc) -> Result<(), Error> { +fn do_main(runnable: Arc) -> Result<(), BccError> { let matches = App::new("biosnoop") .arg( Arg::with_name("duration") @@ -81,7 +80,7 @@ fn do_main(runnable: Arc) -> Result<(), Error> { Ok(()) } -fn print_ipv4_event() -> Box { +fn print_ipv4_event() -> Box { Box::new(|x| { let event = parse_ipv4_struct(x); println!( @@ -97,7 +96,7 @@ fn print_ipv4_event() -> Box { }) } -fn print_ipv6_event() -> Box { +fn print_ipv6_event() -> Box { Box::new(|x| { let event = parse_ipv6_struct(x); println!( @@ -131,7 +130,6 @@ fn main() { if let Err(x) = do_main(runnable) { eprintln!("Error: {}", x); - eprintln!("{}", x.backtrace()); std::process::exit(1); } } diff --git a/src/core/kprobe/v0_4_0.rs b/src/core/kprobe/v0_4_0.rs index 6002c326..8d0050e6 100644 --- a/src/core/kprobe/v0_4_0.rs +++ b/src/core/kprobe/v0_4_0.rs @@ -1,10 +1,10 @@ use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_ENTRY as BPF_PROBE_ENTRY; use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_RETURN as BPF_PROBE_RETURN; use bcc_sys::bccapi::*; -use failure::*; use crate::core::make_alphanumeric; use crate::types::MutPointer; +use crate::BccError; use regex::Regex; @@ -23,11 +23,9 @@ pub struct Kprobe { } impl Kprobe { - fn new(name: &str, attach_type: u32, function: &str, code: File) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Kprobe name: {}", name))?; - let cfunction = CString::new(function) - .map_err(|_| format_err!("Nul byte in Kprobe function: {}", function))?; + fn new(name: &str, attach_type: u32, function: &str, code: File) -> Result { + let cname = CString::new(name)?; + let cfunction = CString::new(function)?; let (pid, cpu, group_fd) = (-1, 0, -1); let ptr = unsafe { bpf_attach_kprobe( @@ -43,7 +41,15 @@ impl Kprobe { ) }; if ptr.is_null() { - Err(format_err!("Failed to attach Kprobe: {}", name)) + match attach_type { + BPF_PROBE_ENTRY => Err(BccError::AttachKprobe { + name: name.to_string(), + }), + BPF_PROBE_RETURN => Err(BccError::AttachKretprobe { + name: name.to_string(), + }), + _ => unreachable!(), + } } else { Ok(Self { p: ptr, @@ -53,19 +59,17 @@ impl Kprobe { } } - pub fn attach_kprobe(function: &str, code: File) -> Result { + pub fn attach_kprobe(function: &str, code: File) -> Result { let name = format!("p_{}", &make_alphanumeric(function)); Kprobe::new(&name, BPF_PROBE_ENTRY, function, code) - .map_err(|_| format_err!("Failed to attach Kprobe: {}", name)) } - pub fn attach_kretprobe(function: &str, code: File) -> Result { + pub fn attach_kretprobe(function: &str, code: File) -> Result { let name = format!("r_{}", &make_alphanumeric(function)); Kprobe::new(&name, BPF_PROBE_RETURN, function, code) - .map_err(|_| format_err!("Failed to attach Kretprobe: {}", name)) } - pub fn get_kprobe_functions(event_re: &str) -> Result, Error> { + pub fn get_kprobe_functions(event_re: &str) -> Result, BccError> { let mut fns: Vec = vec![]; enum Section { @@ -117,19 +121,18 @@ impl Kprobe { Section::End => (), } // All functions defined as NOKPROBE_SYMBOL() start with the - // prefix _kbl_addr_*, blacklisting them by looking at the name + // prefix _kbl_addr_*, excluding them by looking at the name // allows to catch also those symbols that are defined in kernel // modules. if fname.starts_with("_kbl_addr_") { continue; } - // Explicitly blacklist perf-related functions, they are all - // non-attachable. - else if fname.starts_with("__perf") || fname.starts_with("perf_") { + // Exclude perf-related functions, they are all non-attachable. + if fname.starts_with("__perf") || fname.starts_with("perf_") { continue; } // Exclude all gcc 8's extra .cold functions - else if re.is_match(fname) { + if re.is_match(fname) { continue; } if (t == "t" || t == "w") && fname.contains(event_re) { diff --git a/src/core/kprobe/v0_6_0.rs b/src/core/kprobe/v0_6_0.rs index 59d10e45..d48bf00e 100644 --- a/src/core/kprobe/v0_6_0.rs +++ b/src/core/kprobe/v0_6_0.rs @@ -1,9 +1,9 @@ use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_ENTRY as BPF_PROBE_ENTRY; use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_RETURN as BPF_PROBE_RETURN; use bcc_sys::bccapi::*; -use failure::*; use crate::core::make_alphanumeric; +use crate::BccError; use regex::Regex; @@ -21,11 +21,9 @@ pub struct Kprobe { } impl Kprobe { - fn new(name: &str, attach_type: u32, function: &str, code: File) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Kprobe name: {}", name))?; - let cfunction = CString::new(function) - .map_err(|_| format_err!("Nul byte in Kprobe function: {}", function))?; + fn new(name: &str, attach_type: u32, function: &str, code: File) -> Result { + let cname = CString::new(name)?; + let cfunction = CString::new(function)?; let ptr = unsafe { bpf_attach_kprobe( code.as_raw_fd(), @@ -36,7 +34,15 @@ impl Kprobe { ) }; if ptr < 0 { - Err(format_err!("Failed to attach Kprobe: {}", name)) + match attach_type { + BPF_PROBE_ENTRY => Err(BccError::AttachKprobe { + name: name.to_string(), + }), + BPF_PROBE_RETURN => Err(BccError::AttachKretprobe { + name: name.to_string(), + }), + _ => unreachable!(), + } } else { Ok(Self { p: ptr, @@ -46,19 +52,17 @@ impl Kprobe { } } - pub fn attach_kprobe(function: &str, code: File) -> Result { + pub fn attach_kprobe(function: &str, code: File) -> Result { let name = format!("p_{}", &make_alphanumeric(function)); Kprobe::new(&name, BPF_PROBE_ENTRY, function, code) - .map_err(|_| format_err!("Failed to attach Kprobe: {}", name)) } - pub fn attach_kretprobe(function: &str, code: File) -> Result { + pub fn attach_kretprobe(function: &str, code: File) -> Result { let name = format!("r_{}", &make_alphanumeric(function)); Kprobe::new(&name, BPF_PROBE_RETURN, function, code) - .map_err(|_| format_err!("Failed to attach Kretprobe: {}", name)) } - pub fn get_kprobe_functions(event_re: &str) -> Result, Error> { + pub fn get_kprobe_functions(event_re: &str) -> Result, BccError> { let mut fns: Vec = vec![]; enum Section { @@ -110,19 +114,18 @@ impl Kprobe { Section::End => (), } // All functions defined as NOKPROBE_SYMBOL() start with the - // prefix _kbl_addr_*, blacklisting them by looking at the name + // prefix _kbl_addr_*, excluding them by looking at the name // allows to catch also those symbols that are defined in kernel // modules. if fname.starts_with("_kbl_addr_") { continue; } - // Explicitly blacklist perf-related functions, they are all - // non-attachable. - else if fname.starts_with("__perf") || fname.starts_with("perf_") { + // Exclude perf-related functions, they are all non-attachable. + if fname.starts_with("__perf") || fname.starts_with("perf_") { continue; } // Exclude all gcc 8's extra .cold functions - else if re.is_match(fname) { + if re.is_match(fname) { continue; } if (t == "t" || t == "w") && fname.contains(event_re) { diff --git a/src/core/kprobe/v0_9_0.rs b/src/core/kprobe/v0_9_0.rs index c4c3c33b..59b1220b 100644 --- a/src/core/kprobe/v0_9_0.rs +++ b/src/core/kprobe/v0_9_0.rs @@ -1,9 +1,9 @@ use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_ENTRY as BPF_PROBE_ENTRY; use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_RETURN as BPF_PROBE_RETURN; use bcc_sys::bccapi::*; -use failure::*; use crate::core::make_alphanumeric; +use crate::BccError; use regex::Regex; @@ -21,11 +21,9 @@ pub struct Kprobe { } impl Kprobe { - fn new(name: &str, attach_type: u32, function: &str, code: File) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Kprobe name: {}", name))?; - let cfunction = CString::new(function) - .map_err(|_| format_err!("Nul byte in Kprobe function: {}", function))?; + fn new(name: &str, attach_type: u32, function: &str, code: File) -> Result { + let cname = CString::new(name)?; + let cfunction = CString::new(function)?; let ptr = unsafe { bpf_attach_kprobe( code.as_raw_fd(), @@ -37,7 +35,15 @@ impl Kprobe { ) }; if ptr < 0 { - Err(format_err!("Failed to attach Kprobe: {}", name)) + match attach_type { + BPF_PROBE_ENTRY => Err(BccError::AttachKprobe { + name: name.to_string(), + }), + BPF_PROBE_RETURN => Err(BccError::AttachKretprobe { + name: name.to_string(), + }), + _ => unreachable!(), + } } else { Ok(Self { p: ptr, @@ -47,19 +53,17 @@ impl Kprobe { } } - pub fn attach_kprobe(function: &str, code: File) -> Result { + pub fn attach_kprobe(function: &str, code: File) -> Result { let name = format!("p_{}", &make_alphanumeric(function)); Kprobe::new(&name, BPF_PROBE_ENTRY, function, code) - .map_err(|_| format_err!("Failed to attach Kprobe: {}", name)) } - pub fn attach_kretprobe(function: &str, code: File) -> Result { + pub fn attach_kretprobe(function: &str, code: File) -> Result { let name = format!("r_{}", &make_alphanumeric(function)); Kprobe::new(&name, BPF_PROBE_RETURN, function, code) - .map_err(|_| format_err!("Failed to attach Kretprobe: {}", name)) } - pub fn get_kprobe_functions(event_re: &str) -> Result, Error> { + pub fn get_kprobe_functions(event_re: &str) -> Result, BccError> { let mut fns: Vec = vec![]; enum Section { @@ -111,19 +115,18 @@ impl Kprobe { Section::End => (), } // All functions defined as NOKPROBE_SYMBOL() start with the - // prefix _kbl_addr_*, blacklisting them by looking at the name + // prefix _kbl_addr_*, excluding them by looking at the name // allows to catch also those symbols that are defined in kernel // modules. if fname.starts_with("_kbl_addr_") { continue; } - // Explicitly blacklist perf-related functions, they are all - // non-attachable. - else if fname.starts_with("__perf") || fname.starts_with("perf_") { + // Exclude perf-related functions, they are all non-attachable. + if fname.starts_with("__perf") || fname.starts_with("perf_") { continue; } // Exclude all gcc 8's extra .cold functions - else if re.is_match(fname) { + if re.is_match(fname) { continue; } if (t == "t" || t == "w") && fname.contains(event_re) { diff --git a/src/core/mod.rs b/src/core/mod.rs index b2c2024f..56869cf8 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -4,7 +4,6 @@ mod tracepoint; mod uprobe; use bcc_sys::bccapi::*; -use failure::*; use self::kprobe::Kprobe; use self::raw_tracepoint::RawTracepoint; @@ -13,6 +12,7 @@ use self::uprobe::Uprobe; use crate::perf::{self, PerfReader}; use crate::symbol::SymbolCache; use crate::table::Table; +use crate::BccError; use core::ffi::c_void; use core::sync::atomic::{AtomicPtr, Ordering}; @@ -59,11 +59,11 @@ impl BPF { feature = "v0_7_0", feature = "v0_8_0", ))] - pub fn new(code: &str) -> Result { + pub fn new(code: &str) -> Result { let cs = CString::new(code)?; let ptr = unsafe { bpf_module_create_c_from_string(cs.as_ptr(), 2, ptr::null_mut(), 0) }; if ptr.is_null() { - return Err(format_err!("couldn't create BPF program")); + return Err(BccError::Compilation); } Ok(BPF { @@ -79,12 +79,12 @@ impl BPF { // 0.9.0 changes the API for bpf_module_create_c_from_string() #[cfg(any(feature = "v0_9_0", feature = "v0_10_0",))] - pub fn new(code: &str) -> Result { + pub fn new(code: &str) -> Result { let cs = CString::new(code)?; let ptr = unsafe { bpf_module_create_c_from_string(cs.as_ptr(), 2, ptr::null_mut(), 0, true) }; if ptr.is_null() { - return Err(format_err!("couldn't create BPF program")); + return Err(BccError::Compilation); } Ok(BPF { @@ -105,7 +105,7 @@ impl BPF { feature = "v0_13_0", not(feature = "specific"), ))] - pub fn new(code: &str) -> Result { + pub fn new(code: &str) -> Result { let cs = CString::new(code)?; let ptr = unsafe { bpf_module_create_c_from_string( @@ -118,7 +118,7 @@ impl BPF { ) }; if ptr.is_null() { - return Err(format_err!("couldn't create BPF program")); + return Err(BccError::Compilation); } Ok(BPF { @@ -143,20 +143,20 @@ impl BPF { Table::new(id, self.ptr()) } - pub fn load_net(&mut self, name: &str) -> Result { + pub fn load_net(&mut self, name: &str) -> Result { self.load(name, bpf_prog_type_BPF_PROG_TYPE_SCHED_ACT, 0, 0) } - pub fn load_kprobe(&mut self, name: &str) -> Result { + pub fn load_kprobe(&mut self, name: &str) -> Result { self.load(name, bpf_prog_type_BPF_PROG_TYPE_KPROBE, 0, 0) } - pub fn load_uprobe(&mut self, name: &str) -> Result { + pub fn load_uprobe(&mut self, name: &str) -> Result { // it's BPF_PROG_TYPE_KPROBE even though it's a uprobe, it's weird self.load(name, bpf_prog_type_BPF_PROG_TYPE_KPROBE, 0, 0) } - pub fn load_tracepoint(&mut self, name: &str) -> Result { + pub fn load_tracepoint(&mut self, name: &str) -> Result { self.load(name, bpf_prog_type_BPF_PROG_TYPE_TRACEPOINT, 0, 0) } @@ -172,7 +172,7 @@ impl BPF { feature = "v0_13_0", not(feature = "specific"), ))] - pub fn load_raw_tracepoint(&mut self, name: &str) -> Result { + pub fn load_raw_tracepoint(&mut self, name: &str) -> Result { self.load(name, bpf_prog_type_BPF_PROG_TYPE_RAW_TRACEPOINT, 0, 0) } @@ -183,7 +183,7 @@ impl BPF { prog_type: u32, _log_level: i32, log_size: u32, - ) -> Result { + ) -> Result { let cname = CString::new(name).unwrap(); unsafe { let start: *mut bpf_insn = @@ -192,7 +192,9 @@ impl BPF { let license = bpf_module_license(self.ptr()); let version = bpf_module_kern_version(self.ptr()); if start.is_null() { - return Err(format_err!("Error in bpf_function_start for {}", name)); + return Err(BccError::Loading { + name: name.to_string(), + }); } let mut log_buf: Vec = Vec::with_capacity(log_size as usize); // TODO: we're ignoring any changes bpf_prog_load made to log_buf right now @@ -208,7 +210,9 @@ impl BPF { log_buf.capacity() as u32, ); if fd < 0 { - return Err(format_err!("error loading BPF program: {}", name)); + return Err(BccError::Loading { + name: name.to_string(), + }); } Ok(File::from_raw_fd(fd)) } @@ -227,7 +231,7 @@ impl BPF { prog_type: u32, log_level: i32, log_size: u32, - ) -> Result { + ) -> Result { let cname = CString::new(name).unwrap(); unsafe { let start: *mut bpf_insn = @@ -236,7 +240,9 @@ impl BPF { let license = bpf_module_license(self.ptr()); let version = bpf_module_kern_version(self.ptr()); if start.is_null() { - return Err(format_err!("Error in bpf_function_start for {}", name)); + return Err(BccError::Loading { + name: name.to_string(), + }); } let mut log_buf: Vec = Vec::with_capacity(log_size as usize); // TODO: we're ignoring any changes bpf_prog_load made to log_buf right now @@ -254,7 +260,9 @@ impl BPF { log_buf.capacity() as u32, ); if fd < 0 { - return Err(format_err!("error loading BPF program: {}", name)); + return Err(BccError::Loading { + name: name.to_string(), + }); } Ok(File::from_raw_fd(fd)) } @@ -274,7 +282,7 @@ impl BPF { prog_type: u32, log_level: i32, log_size: u32, - ) -> Result { + ) -> Result { let cname = CString::new(name).unwrap(); unsafe { let start: *mut bpf_insn = @@ -283,7 +291,9 @@ impl BPF { let license = bpf_module_license(self.ptr()); let version = bpf_module_kern_version(self.ptr()); if start.is_null() { - return Err(format_err!("Error in bpf_function_start for {}", name)); + return Err(BccError::Loading { + name: name.to_string(), + }); } let mut log_buf: Vec = Vec::with_capacity(log_size as usize); // TODO: we're ignoring any changes bpf_prog_load made to log_buf right now @@ -301,7 +311,9 @@ impl BPF { log_buf.capacity() as u32, ); if fd < 0 { - return Err(format_err!("error loading BPF program: {}", name)); + return Err(BccError::Loading { + name: name.to_string(), + }); } Ok(File::from_raw_fd(fd)) } @@ -313,25 +325,25 @@ impl BPF { symbol: &str, file: File, pid: pid_t, - ) -> Result<(), Error> { + ) -> Result<(), BccError> { let uprobe = Uprobe::attach_uretprobe(binary_path, symbol, file, pid)?; self.uprobes.insert(uprobe); Ok(()) } - pub fn attach_kprobe(&mut self, function: &str, file: File) -> Result<(), Error> { + pub fn attach_kprobe(&mut self, function: &str, file: File) -> Result<(), BccError> { let kprobe = Kprobe::attach_kprobe(function, file)?; self.kprobes.insert(kprobe); Ok(()) } - pub fn attach_kretprobe(&mut self, function: &str, file: File) -> Result<(), Error> { + pub fn attach_kretprobe(&mut self, function: &str, file: File) -> Result<(), BccError> { let kretprobe = Kprobe::attach_kretprobe(function, file)?; self.kprobes.insert(kretprobe); Ok(()) } - pub fn get_kprobe_functions(&mut self, event_re: &str) -> Result, Error> { + pub fn get_kprobe_functions(&mut self, event_re: &str) -> Result, BccError> { Kprobe::get_kprobe_functions(event_re) } @@ -341,13 +353,18 @@ impl BPF { symbol: &str, file: File, pid: pid_t, - ) -> Result<(), Error> { + ) -> Result<(), BccError> { let uprobe = Uprobe::attach_uprobe(binary_path, symbol, file, pid)?; self.uprobes.insert(uprobe); Ok(()) } - pub fn attach_tracepoint(&mut self, subsys: &str, name: &str, file: File) -> Result<(), Error> { + pub fn attach_tracepoint( + &mut self, + subsys: &str, + name: &str, + file: File, + ) -> Result<(), BccError> { let tracepoint = Tracepoint::attach_tracepoint(subsys, name, file)?; self.tracepoints.insert(tracepoint); Ok(()) @@ -365,13 +382,13 @@ impl BPF { feature = "v0_13_0", not(feature = "specific"), ))] - pub fn attach_raw_tracepoint(&mut self, name: &str, file: File) -> Result<(), Error> { + pub fn attach_raw_tracepoint(&mut self, name: &str, file: File) -> Result<(), BccError> { let raw_tracepoint = RawTracepoint::attach_raw_tracepoint(name, file)?; self.raw_tracepoints.insert(raw_tracepoint); Ok(()) } - pub fn ksymname(&mut self, name: &str) -> Result { + pub fn ksymname(&mut self, name: &str) -> Result { self.sym_caches .entry(-1) .or_insert_with(|| SymbolCache::new(-1)); @@ -396,7 +413,7 @@ impl BPF { || self.ksymname("bpf_get_raw_tracepoint").is_ok() } - pub fn init_perf_map(&mut self, table: Table, cb: F) -> Result<(), Error> + pub fn init_perf_map(&mut self, table: Table, cb: F) -> Result<(), BccError> where F: Fn() -> Box, { diff --git a/src/core/raw_tracepoint/v0_6_0.rs b/src/core/raw_tracepoint/v0_6_0.rs index 06f65661..49dbcd96 100644 --- a/src/core/raw_tracepoint/v0_6_0.rs +++ b/src/core/raw_tracepoint/v0_6_0.rs @@ -1,5 +1,5 @@ +use crate::BccError; use bcc_sys::bccapi::*; -use failure::*; use std::ffi::CString; use std::fs::File; @@ -14,12 +14,13 @@ pub struct RawTracepoint { } impl RawTracepoint { - pub fn attach_raw_tracepoint(name: &str, file: File) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Tracepoint name: {}", name))?; + pub fn attach_raw_tracepoint(name: &str, file: File) -> Result { + let cname = CString::new(name)?; let ptr = unsafe { bpf_attach_raw_tracepoint(file.as_raw_fd(), cname.as_ptr() as *mut _) }; if ptr < 0 { - Err(format_err!("Failed to attach raw tracepoint: {}", name)) + Err(BccError::AttachRawTracepoint { + name: name.to_string(), + }) } else { Ok(Self { name: cname, diff --git a/src/core/tracepoint/v0_4_0.rs b/src/core/tracepoint/v0_4_0.rs index 6f3a05bc..d47a8b44 100644 --- a/src/core/tracepoint/v0_4_0.rs +++ b/src/core/tracepoint/v0_4_0.rs @@ -1,7 +1,7 @@ use bcc_sys::bccapi::*; -use failure::*; use crate::types::MutPointer; +use crate::BccError; use std::ffi::CString; use std::fs::File; @@ -18,11 +18,9 @@ pub struct Tracepoint { } impl Tracepoint { - pub fn attach_tracepoint(subsys: &str, name: &str, file: File) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Tracepoint name: {}", name))?; - let csubsys = CString::new(subsys) - .map_err(|_| format_err!("Nul byte in Tracepoint subsys: {}", subsys))?; + pub fn attach_tracepoint(subsys: &str, name: &str, file: File) -> Result { + let cname = CString::new(name)?; + let csubsys = CString::new(subsys)?; // NOTE: BPF events are system-wide and do not support CPU filter let (pid, cpu, group_fd) = (-1, 0, -1); let ptr = unsafe { @@ -38,11 +36,10 @@ impl Tracepoint { ) }; if ptr.is_null() { - Err(format_err!( - "Failed to attach tracepoint: {}:{}", - subsys, - name - )) + Err(BccError::AttachTracepoint { + subsys: subsys.to_string(), + name: name.to_string(), + }) } else { Ok(Self { subsys: csubsys, diff --git a/src/core/tracepoint/v0_6_0.rs b/src/core/tracepoint/v0_6_0.rs index cda77d41..591c4a06 100644 --- a/src/core/tracepoint/v0_6_0.rs +++ b/src/core/tracepoint/v0_6_0.rs @@ -1,5 +1,6 @@ use bcc_sys::bccapi::*; -use failure::*; + +use crate::BccError; use std::ffi::CString; use std::fs::File; @@ -15,19 +16,16 @@ pub struct Tracepoint { } impl Tracepoint { - pub fn attach_tracepoint(subsys: &str, name: &str, file: File) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Tracepoint name: {}", name))?; - let csubsys = CString::new(subsys) - .map_err(|_| format_err!("Nul byte in Tracepoint subsys: {}", subsys))?; + pub fn attach_tracepoint(subsys: &str, name: &str, file: File) -> Result { + let cname = CString::new(name)?; + let csubsys = CString::new(subsys)?; let ptr = unsafe { bpf_attach_tracepoint(file.as_raw_fd(), csubsys.as_ptr(), cname.as_ptr()) }; if ptr < 0 { - Err(format_err!( - "Failed to attach tracepoint: {}:{}", - subsys, - name - )) + Err(BccError::AttachTracepoint { + subsys: subsys.to_string(), + name: name.to_string(), + }) } else { Ok(Self { subsys: csubsys, diff --git a/src/core/uprobe/v0_4_0.rs b/src/core/uprobe/v0_4_0.rs index fa326024..1f9931ca 100644 --- a/src/core/uprobe/v0_4_0.rs +++ b/src/core/uprobe/v0_4_0.rs @@ -1,11 +1,11 @@ use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_ENTRY as BPF_PROBE_ENTRY; use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_RETURN as BPF_PROBE_RETURN; use bcc_sys::bccapi::*; -use failure::*; use crate::core::make_alphanumeric; use crate::symbol; use crate::types::MutPointer; +use crate::BccError; use std::ffi::CString; use std::fs::File; @@ -28,11 +28,9 @@ impl Uprobe { addr: u64, file: File, pid: pid_t, - ) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Uprobe name: {}", name))?; - let cpath = - CString::new(path).map_err(|_| format_err!("Nul byte in Uprobe path: {}", name))?; + ) -> Result { + let cname = CString::new(name)?; + let cpath = CString::new(path)?; // TODO: maybe pass in the CPU & PID instead of let (cpu, group_fd) = (0, -1); let uprobe_ptr = unsafe { @@ -50,7 +48,15 @@ impl Uprobe { ) }; if uprobe_ptr.is_null() { - Err(format_err!("Failed to attach Uprobe: {}", name)) + match attach_type { + BPF_PROBE_ENTRY => Err(BccError::AttachUprobe { + name: name.to_string(), + }), + BPF_PROBE_RETURN => Err(BccError::AttachUretprobe { + name: name.to_string(), + }), + _ => unreachable!(), + } } else { Ok(Self { code_fd: file, @@ -65,12 +71,11 @@ impl Uprobe { symbol: &str, code: File, pid: pid_t, - ) -> Result { + ) -> Result { let (path, addr) = symbol::resolve_symbol_path(binary_path, symbol, 0x0, pid)?; let alpha_path = make_alphanumeric(&path); let ev_name = format!("r_{}_0x{:x}", &alpha_path, addr); Uprobe::new(&ev_name, BPF_PROBE_ENTRY, &path, addr, code, pid) - .map_err(|_| format_err!("Failed to attach Uprobe to binary: {}", binary_path)) } pub fn attach_uretprobe( @@ -78,12 +83,11 @@ impl Uprobe { symbol: &str, code: File, pid: pid_t, - ) -> Result { + ) -> Result { let (path, addr) = symbol::resolve_symbol_path(binary_path, symbol, 0x0, pid)?; let alpha_path = make_alphanumeric(&path); let ev_name = format!("r_{}_0x{:x}", &alpha_path, addr); Uprobe::new(&ev_name, BPF_PROBE_RETURN, &path, addr, code, pid) - .map_err(|_| format_err!("Failed to attach Uretprobe to binary: {}", binary_path)) } } diff --git a/src/core/uprobe/v0_6_0.rs b/src/core/uprobe/v0_6_0.rs index 7e403be1..4c722c82 100644 --- a/src/core/uprobe/v0_6_0.rs +++ b/src/core/uprobe/v0_6_0.rs @@ -1,10 +1,10 @@ use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_ENTRY as BPF_PROBE_ENTRY; use bcc_sys::bccapi::bpf_probe_attach_type_BPF_PROBE_RETURN as BPF_PROBE_RETURN; use bcc_sys::bccapi::*; -use failure::*; use crate::core::make_alphanumeric; use crate::symbol; +use crate::BccError; use std::ffi::CString; use std::fs::File; @@ -26,11 +26,9 @@ impl Uprobe { addr: u64, file: File, pid: pid_t, - ) -> Result { - let cname = - CString::new(name).map_err(|_| format_err!("Nul byte in Uprobe name: {}", name))?; - let cpath = - CString::new(path).map_err(|_| format_err!("Nul byte in Uprobe path: {}", name))?; + ) -> Result { + let cname = CString::new(name)?; + let cpath = CString::new(path)?; let uprobe_ptr = unsafe { bpf_attach_uprobe( file.as_raw_fd(), @@ -42,7 +40,15 @@ impl Uprobe { ) }; if uprobe_ptr < 0 { - Err(format_err!("Failed to attach Uprobe: {}", name)) + match attach_type { + BPF_PROBE_ENTRY => Err(BccError::AttachUprobe { + name: name.to_string(), + }), + BPF_PROBE_RETURN => Err(BccError::AttachUretprobe { + name: name.to_string(), + }), + _ => unreachable!(), + } } else { Ok(Self { code_fd: file, @@ -57,12 +63,11 @@ impl Uprobe { symbol: &str, code: File, pid: pid_t, - ) -> Result { + ) -> Result { let (path, addr) = symbol::resolve_symbol_path(binary_path, symbol, 0x0, pid)?; let alpha_path = make_alphanumeric(&path); let ev_name = format!("r_{}_0x{:x}", &alpha_path, addr); Uprobe::new(&ev_name, BPF_PROBE_ENTRY, &path, addr, code, pid) - .map_err(|_| format_err!("Failed to attach Uprobe to binary: {}", binary_path)) } pub fn attach_uretprobe( @@ -70,12 +75,11 @@ impl Uprobe { symbol: &str, code: File, pid: pid_t, - ) -> Result { + ) -> Result { let (path, addr) = symbol::resolve_symbol_path(binary_path, symbol, 0x0, pid)?; let alpha_path = make_alphanumeric(&path); let ev_name = format!("r_{}_0x{:x}", &alpha_path, addr); Uprobe::new(&ev_name, BPF_PROBE_RETURN, &path, addr, code, pid) - .map_err(|_| format_err!("Failed to attach Uretprobe to binary: {}", binary_path)) } } diff --git a/src/cpuonline.rs b/src/cpuonline.rs index 48711951..4adbc8c1 100644 --- a/src/cpuonline.rs +++ b/src/cpuonline.rs @@ -1,4 +1,4 @@ -use failure::*; +use crate::BccError; use std::fs::File; use std::io::Read; @@ -7,14 +7,18 @@ use std::str::FromStr; const CPUONLINE: &str = "/sys/devices/system/cpu/online"; // loosely based on https://github.com/iovisor/bcc/blob/v0.3.0/src/python/bcc/utils.py#L15 -fn read_cpu_range(cpu_range_str: &str) -> Result, Error> { +fn read_cpu_range(cpu_range_str: &str) -> Result, BccError> { let mut cpus = Vec::new(); let cpu_range_str_trim = cpu_range_str.trim(); for cpu_range in cpu_range_str_trim.split(',') { let rangeop: Vec<&str> = cpu_range.splitn(2, '-').collect(); let first = match usize::from_str(rangeop[0]) { Ok(res) => res, - Err(e) => return Err(format_err!("Fail to recognize first cpu number: {}", e)), + Err(_) => { + return Err(BccError::InvalidCpuRange { + range: cpu_range_str.to_string(), + }) + } }; if rangeop.len() == 1 { cpus.push(first); @@ -22,7 +26,11 @@ fn read_cpu_range(cpu_range_str: &str) -> Result, Error> { } let last = match usize::from_str(rangeop[1]) { Ok(res) => res, - Err(e) => return Err(format_err!("Fail to recognize second cpu number: {}", e)), + Err(_) => { + return Err(BccError::InvalidCpuRange { + range: cpu_range_str.to_string(), + }) + } }; for n in first..=last { cpus.push(n); @@ -31,7 +39,7 @@ fn read_cpu_range(cpu_range_str: &str) -> Result, Error> { Ok(cpus) } -pub fn get() -> Result, Error> { +pub fn get() -> Result, BccError> { let mut buffer = String::new(); File::open(CPUONLINE)?.read_to_string(&mut buffer)?; read_cpu_range(&buffer) diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..d5631931 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,43 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum BccError { + #[error("failed to attach kprobe: ({name})")] + AttachKprobe { name: String }, + #[error("failed to attach kretprobe ({name})")] + AttachKretprobe { name: String }, + #[error("failed to attach raw tracepoint ({name})")] + AttachRawTracepoint { name: String }, + #[error("failed to attach tracepoint ({subsys}:{name})")] + AttachTracepoint { subsys: String, name: String }, + #[error("failed to attach uprobe ({name})")] + AttachUprobe { name: String }, + #[error("failed to attach uretprobe ({name})")] + AttachUretprobe { name: String }, + #[error("error compiling bpf")] + Compilation, + #[error("io error")] + IoError(#[from] std::io::Error), + #[error("error initializing perf map")] + InitializePerfMap, + #[error("invalid cpu range ({range})")] + InvalidCpuRange { range: String }, + #[error("error loading bpf probe ({name})")] + Loading { name: String }, + #[error("null string")] + NullString(#[from] std::ffi::NulError), + #[error("error opening perf buffer")] + OpenPerfBuffer, + #[error("failed to delete key from table")] + DeleteTableValue, + #[error("failed to get value from table")] + GetTableValue, + #[error("failed to set value in table")] + SetTableValue, + #[error("table has wrong size for key or leaf")] + TableInvalidSize, + #[error("unknown symbol ({name}) in module ({module})")] + UnknownSymbol { name: String, module: String }, + #[error("invalid utf8")] + Utf8Error(#[from] std::str::Utf8Error), +} diff --git a/src/lib.rs b/src/lib.rs index 747607e1..066cf1f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,10 @@ pub mod core; mod cpuonline; +mod error; pub mod perf; pub mod symbol; pub mod table; mod types; + +pub use error::BccError; diff --git a/src/perf.rs b/src/perf.rs index 405a592d..de9396b1 100644 --- a/src/perf.rs +++ b/src/perf.rs @@ -1,15 +1,14 @@ use bcc_sys::bccapi::*; use byteorder::{NativeEndian, WriteBytesExt}; -use failure::*; use core::ffi::c_void; use core::sync::atomic::{AtomicPtr, Ordering}; -use std; use std::io::Cursor; use crate::cpuonline; use crate::table::Table; use crate::types::*; +use crate::BccError; struct PerfCallback { raw_cb: Box, @@ -53,7 +52,7 @@ pub struct PerfMap { pub readers: Vec, } -pub fn init_perf_map(mut table: Table, cb: F) -> Result +pub fn init_perf_map(mut table: Table, cb: F) -> Result where F: Fn() -> Box, { @@ -62,7 +61,7 @@ where let leaf = vec![0; leaf_size]; if key_size != 4 || leaf_size != 4 { - return Err(format_err!("passed table has wrong size")); + return Err(BccError::TableInvalidSize); } let mut readers: Vec = vec![]; @@ -77,10 +76,11 @@ where let mut key = vec![]; key.write_u32::(*cpu as u32)?; cur.write_u32::(perf_fd)?; - table - .set(&mut key, &mut cur.get_mut()) - .context("Unable to initialize perf map")?; - cur.set_position(0); + if table.set(&mut key, &mut cur.get_mut()).is_ok() { + cur.set_position(0); + } else { + return Err(BccError::InitializePerfMap); + } } Ok(PerfMap { readers }) } @@ -97,7 +97,10 @@ impl PerfMap { } } -fn open_perf_buffer(cpu: usize, raw_cb: Box) -> Result { +fn open_perf_buffer( + cpu: usize, + raw_cb: Box, +) -> Result { let callback = Box::new(PerfCallback { raw_cb }); let reader = unsafe { bpf_open_perf_buffer( @@ -110,7 +113,7 @@ fn open_perf_buffer(cpu: usize, raw_cb: Box) -> Result< ) }; if reader.is_null() { - return Err(format_err!("failed to open perf buffer")); + return Err(BccError::OpenPerfBuffer); } Ok(PerfReader { ptr: AtomicPtr::new(reader as *mut perf_reader), diff --git a/src/symbol.rs b/src/symbol.rs index 4b2062e3..1ca75289 100644 --- a/src/symbol.rs +++ b/src/symbol.rs @@ -1,5 +1,6 @@ +use crate::BccError; + use bcc_sys::bccapi::*; -use failure::*; use libc::free; use core::ffi::c_void; @@ -14,7 +15,7 @@ pub fn resolve_symbol_path( symname: &str, addr: u64, pid: pid_t, -) -> Result<(String, u64), Error> { +) -> Result<(String, u64), BccError> { let pid: pid_t = match pid { -1 => 0, x => x, @@ -28,7 +29,7 @@ pub fn resolve_symname( symname: &str, addr: u64, pid: pid_t, -) -> Result<(String, u64), Error> { +) -> Result<(String, u64), BccError> { let mut symbol = unsafe { mem::zeroed::() }; let cmodule = CString::new(module)?; let csymname = CString::new(symname)?; @@ -44,12 +45,10 @@ pub fn resolve_symname( ) }; if res < 0 { - Err(format_err!( - "unable to locate symbol {} in module {}: {}", - &symname, - module, - res - )) + Err(BccError::UnknownSymbol { + name: symname.to_string(), + module: module.to_string(), + }) } else { let module = unsafe { CStr::from_ptr(symbol.module as *mut i8) @@ -75,7 +74,7 @@ impl SymbolCache { } } - pub fn resolve_name(&self, module: &str, name: &str) -> Result { + pub fn resolve_name(&self, module: &str, name: &str) -> Result { let cmodule = CString::new(module)?; let cname = CString::new(name)?; let mut addr: u64 = 0; @@ -89,12 +88,10 @@ impl SymbolCache { ) }; if res < 0 { - Err(format_err!( - "unable to locate symbol {} in module {}: {}", - &name, - module, - res - )) + Err(BccError::UnknownSymbol { + name: name.to_string(), + module: module.to_string(), + }) } else { Ok(addr) } diff --git a/src/table.rs b/src/table.rs index 50f20bbc..7b62dbb2 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,10 +1,9 @@ use bcc_sys::bccapi::*; -use failure::*; use libc::{c_int, size_t}; use crate::types::MutPointer; +use crate::BccError; -use std; use std::ffi::CStr; #[derive(Clone, Debug)] @@ -37,23 +36,23 @@ impl Table { } } - pub fn delete(&mut self, key: &mut [u8]) -> Result<(), Error> { + pub fn delete(&mut self, key: &mut [u8]) -> Result<(), BccError> { let fd = self.fd(); let res = unsafe { bpf_delete_elem(fd, key.as_mut_ptr() as MutPointer) }; match res { 0 => Ok(()), - _ => Err(format_err!("unable to delete element ({:?})", key)), + _ => Err(BccError::DeleteTableValue), } } - pub fn delete_all(&mut self) -> Result<(), Error> { + pub fn delete_all(&mut self) -> Result<(), BccError> { for mut e in self.iter() { self.delete(&mut e.key)?; } Ok(()) } - pub fn get(&mut self, key: &mut [u8]) -> Result, Error> { + pub fn get(&mut self, key: &mut [u8]) -> Result, BccError> { let mut leaf = vec![0; self.leaf_size()]; let res = unsafe { bpf_lookup_elem( @@ -64,11 +63,11 @@ impl Table { }; match res { 0 => Ok(leaf), - _ => Err(format_err!("unable to get element ({:?})", leaf)), + _ => Err(BccError::GetTableValue), } } - pub fn set(&mut self, key: &mut [u8], leaf: &mut [u8]) -> Result<(), Error> { + pub fn set(&mut self, key: &mut [u8], leaf: &mut [u8]) -> Result<(), BccError> { let res = unsafe { bpf_update_elem( self.fd(), @@ -80,11 +79,7 @@ impl Table { // TODO: maybe we can get an errno here to enhance the error message with? match res { 0 => Ok(()), - _ => Err(format_err!( - "unable to update element ({:?}={:?})", - key, - leaf - )), + _ => Err(BccError::SetTableValue), } }