2023-11-16 19:43 UTC+0000 ~ Manu Bretelle <chantr4@xxxxxxxxx> > This test adds a hardcoded key/value pair to a test map and verify > that bpftool is able to dump its content and read/format it. > > $ BPFTOOL_PATH=../bpftool > CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="sudo -E" cargo test -- > --nocapture > Finished test [unoptimized + debuginfo] target(s) in 0.05s > Running unittests src/main.rs > (target/debug/deps/bpftool_tests-afa5a7eef3cdeafb) > > running 7 tests > Running command "../bpftool" "version" > test bpftool_tests::run_bpftool ... ok > Running command "../bpftool" "map" "dump" "id" "1848713" "--json" > Running command "../bpftool" "prog" "list" "--json" > Running command "../bpftool" "map" "list" "--json" > Running command "../bpftool" "prog" "list" "--json" > Running command "../bpftool" "prog" "show" "id" "3160759" "--json" > Running command "../bpftool" "map" "list" "--json" > test bpftool_tests::run_bpftool_map_dump_id ... ok > test bpftool_tests::run_bpftool_prog_show_id ... ok > test bpftool_tests::run_bpftool_map_pids ... ok > test bpftool_tests::run_bpftool_prog_pids ... ok > test bpftool_tests::run_bpftool_prog_list ... ok > test bpftool_tests::run_bpftool_map_list ... ok > > test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered > out; finished in 0.22s > > Signed-off-by: Manu Bretelle <chantr4@xxxxxxxxx> > --- > .../bpftool_tests/src/bpf/bpftool_tests.bpf.c | 7 ++ > .../bpf/bpftool_tests/src/bpftool_tests.rs | 86 +++++++++++++++++++ > 2 files changed, 93 insertions(+) > > diff --git a/tools/testing/selftests/bpf/bpftool_tests/src/bpf/bpftool_tests.bpf.c b/tools/testing/selftests/bpf/bpftool_tests/src/bpf/bpftool_tests.bpf.c > index e2b18dd36207..a90c8921b4ee 100644 > --- a/tools/testing/selftests/bpf/bpftool_tests/src/bpf/bpftool_tests.bpf.c > +++ b/tools/testing/selftests/bpf/bpftool_tests/src/bpf/bpftool_tests.bpf.c > @@ -11,6 +11,13 @@ struct { > __type(value, u64); > } pid_write_calls SEC(".maps"); > > +struct { > + __uint(type, BPF_MAP_TYPE_HASH); > + __uint(max_entries, 10); > + __type(key, char[6]); > + __type(value, char[6]); > +} bpftool_test_map SEC(".maps"); > + > int my_pid = 0; > > SEC("tp/syscalls/sys_enter_write") > diff --git a/tools/testing/selftests/bpf/bpftool_tests/src/bpftool_tests.rs b/tools/testing/selftests/bpf/bpftool_tests/src/bpftool_tests.rs > index 695a1cbc5be8..90187152c1d1 100644 > --- a/tools/testing/selftests/bpf/bpftool_tests/src/bpftool_tests.rs > +++ b/tools/testing/selftests/bpf/bpftool_tests/src/bpftool_tests.rs > @@ -45,6 +45,37 @@ struct Map { > pids: Vec<Pid>, > } > > +/// A struct representing a formatted map entry from `bpftool map dump -j` > +#[derive(Serialize, Deserialize, Debug)] > +struct FormattedMapItem { > + key: String, > + value: String, > +} > + > +type MapVecString = Vec<String>; > + > +/// A struct representing a map entry from `bpftool map dump -j` > +#[derive(Serialize, Deserialize, Debug)] > +struct MapItem { > + key: MapVecString, > + value: MapVecString, > + formatted: Option<FormattedMapItem>, > +} > + > +/// A helper function to convert a vector of strings as returned by bpftool > +/// into a vector of bytes. > +/// bpftool returns key/value in the form of a sequence of strings > +/// hexadecimal numbers. We need to convert them back to bytes. So sorry about that. > +/// for instance, the value of the key "key" is represented as ["0x6b","0x65","0x79"] s/for/For/ > +fn to_vec_u8(m: &MapVecString) -> Vec<u8> { > + m.iter() > + .map(|s| { > + u8::from_str_radix(s.trim_start_matches("0x"), 16) > + .unwrap_or_else(|_| panic!("Failed to parse {:?}", s)) > + }) > + .collect() > +} > + > /// Setup our bpftool_tests.bpf.c program. > /// Open and load and return an opened object. > fn setup() -> Result<BpftoolTestsSkel<'static>> { > @@ -114,6 +145,61 @@ fn run_bpftool_map_pids() { > ); > } > > +/// A test to validate that we can run `bpftool map dump <id>` > +/// and extract the expected information. > +/// The test adds a key, value pair to the map and then dumps it. > +/// The test validates that the dumped data matches what was added. > +/// It also validate that bpftool was able to format the key/value pairs. s/validate/validates/ s/pairs/pair/ > +#[test] > +fn run_bpftool_map_dump_id() { > + // By having key/value null terminated, we can check that bpftool also returns the > + // formatted content. > + let key = b"key\0\0\0"; > + let value = b"value\0"; > + let skel = setup().expect("Failed to set up BPF program"); > + let binding = skel.maps(); > + let bpftool_test_map_map = binding.bpftool_test_map(); > + bpftool_test_map_map > + .update(key, value, libbpf_rs::MapFlags::NO_EXIST) > + .expect("Failed to update map"); > + let map_id = bpftool_test_map_map > + .info() > + .expect("Failed to get map info") > + .info > + .id; > + > + let output = run_bpftool_command(&["map", "dump", "id", &map_id.to_string(), "--json"]); > + assert!(output.status.success(), "bpftool returned an error."); > + > + let items = > + serde_json::from_slice::<Vec<MapItem>>(&output.stdout).expect("Failed to parse JSON"); > + > + assert_eq!(items.len(), 1); > + > + let item = items.first().expect("Expected a map item"); > + assert_eq!(to_vec_u8(&item.key), key); > + assert_eq!(to_vec_u8(&item.value), value); > + > + // Validate "formatted" values. > + // The keys and values are null terminated so we need to trim them before comparing. key and value, singular > + let formatted = item > + .formatted > + .as_ref() > + .expect("Formatted values are missing"); > + assert_eq!( > + formatted.key, > + std::str::from_utf8(key) > + .expect("Invalid UTF-8") Do we need to check the UTF-8 encoding for the strings we provide in the test? > + .trim_end_matches('\0'), > + ); > + assert_eq!( > + formatted.value, > + std::str::from_utf8(value) > + .expect("Invalid UTF-8") > + .trim_end_matches('\0'), > + ); > +} > + > /// A test to validate that we can list programs using bpftool > #[test] > fn run_bpftool_prog_list() {