use bcc::perf_event::{Event, SoftwareEvent}; use bcc::table::Entry; use bcc::{BPFBuilder, BpfProgType, PerfEvent, XDPMode, BPF, XDP}; fn main() { println!("smoketest: empty table"); let bpf = BPF::new("BPF_HASH(start, u32);").unwrap(); let table = bpf.table("start").unwrap(); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 0); println!("smoketest: sized histogram"); let bpf = BPF::new("BPF_HISTOGRAM(dist, int, 256);").unwrap(); let table = bpf.table("dist").unwrap(); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 256); println!("smoketest: hash insert and delete"); let bpf = BPF::new("BPF_HASH(dist);").unwrap(); let mut table = bpf.table("dist").unwrap(); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 0); assert!(table.delete_all().is_ok()); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 0); assert!(table .set( &mut [0, 0, 0, 0, 0, 0, 0, 1], &mut [0, 0, 0, 0, 0, 0, 0, 42] ) .is_ok()); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 1); assert_eq!(entries.get(0).unwrap().value, [0, 0, 0, 0, 0, 0, 0, 42]); assert!(table.delete_all().is_ok()); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 0); println!("smoketest: invalid hash usage"); assert!(bpf.table("invalid\0_table_name").is_err()); let mut table = bpf.table("does_not_exist").unwrap(); assert!(table.get(&mut [0, 0, 0, 0, 0, 0, 0, 1]).is_err()); assert!(table.delete(&mut [0, 0, 0, 0, 0, 0, 0, 1]).is_err()); assert!(table .set( &mut [0, 0, 0, 0, 0, 0, 0, 1], &mut [0, 0, 0, 0, 0, 0, 0, 42] ) .is_err()); println!("smoketest: array"); let bpf = BPF::new("BPF_ARRAY(dist, u64, 64);").unwrap(); let mut table = bpf.table("dist").unwrap(); let entries: Vec = table.iter().collect(); assert_eq!(entries.len(), 64); assert!(table .set(&mut [0, 0, 0, 0], &mut [0, 0, 0, 0, 0, 0, 0, 42]) .is_ok()); assert!(table .set(&mut [1, 0, 0, 0], &mut [0, 0, 0, 0, 0, 0, 13, 37]) .is_ok()); let entries: Vec = table.iter().collect(); assert_eq!(entries.get(1).unwrap().value, [0, 0, 0, 0, 0, 0, 13, 37]); assert_eq!(entries.get(0).unwrap().value, [0, 0, 0, 0, 0, 0, 0, 42]); println!("smoketest: cflags"); assert!(BPFBuilder::new("int main() { return RETURN_CODE; }") .unwrap() .cflags(&["-DRETURN_CODE=0", "-DUNUSED_DEFINE=1"]) .unwrap() .build() .is_ok()); println!("smoketest: debug level"); assert!(BPFBuilder::new("") .unwrap() .debug(Default::default()) .build() .is_ok()); println!("smoketest: tail calls"); run_tail_calls(); println!("smoketest: xdp forwarding"); run_xdp_forwarding(); println!("smoketest passed"); } #[cfg(any( feature = "v0_6_0", feature = "v0_6_1", feature = "v0_7_0", feature = "v0_8_0", ))] fn run_tail_calls() { let mut bpf = BPFBuilder::new( "#include BPF_PROG_ARRAY(programs, 1); int my_func(struct bpf_perf_event_data *ctx) { return 0; } int on_event(struct bpf_perf_event_data *ctx){ programs.call(ctx, 0); return 0; }", ) .unwrap() .build() .unwrap(); PerfEvent::new() .handler("on_event") .event(Event::Software(SoftwareEvent::CpuClock)) .sample_frequency(Some(99)) .attach(&mut bpf) .unwrap(); let mut table = bpf.table("programs").unwrap(); let mut index = 0_u32.to_ne_bytes(); assert!(bpf.load_func("my_func", BpfProgType::PerfEvent).is_err()) } #[cfg(any( feature = "v0_9_0", feature = "v0_10_0", feature = "v0_11_0", feature = "v0_12_0", feature = "v0_13_0", feature = "v0_14_0", feature = "v0_15_0", feature = "v0_16_0", feature = "v0_17_0", feature = "v0_18_0", feature = "v0_19_0", feature = "v0_20_0", feature = "v0_21_0", feature = "v0_22_0", feature = "v0_23_0", not(feature = "specific"), ))] fn run_tail_calls() { let mut bpf = BPFBuilder::new( "#include BPF_PROG_ARRAY(programs, 1); int my_func(struct bpf_perf_event_data *ctx) { return 0; } int on_event(struct bpf_perf_event_data *ctx){ programs.call(ctx, 0); return 0; }", ) .unwrap() .build() .unwrap(); PerfEvent::new() .handler("on_event") .event(Event::Software(SoftwareEvent::CpuClock)) .sample_frequency(Some(99)) .attach(&mut bpf) .unwrap(); let mut table = bpf.table("programs").unwrap(); let mut index = 0_u32.to_ne_bytes(); let fd = bpf.load_func("my_func", BpfProgType::PerfEvent).unwrap(); let fd2 = bpf.load_func("my_func", BpfProgType::PerfEvent).unwrap(); assert_eq!( fd, fd2, "loading the same function more than once should return the same fd" ); table.set(&mut index, &mut fd.to_ne_bytes()).unwrap(); assert!(bpf .load_func("non_existent_func", BpfProgType::PerfEvent) .is_err()); } fn run_xdp_forwarding() { let mut bpf = BPFBuilder::new( "#include int my_func(struct xdp_md *ctx) { return XDP_PASS; }", ) .unwrap() .build() .unwrap(); XDP::new() .device("lo") .handler("my_func") .mode(XDPMode::XDP_FLAGS_SKB_MODE) .attach(&mut bpf) .expect("failed to attach XDP program to device"); }