Skip to content

Commit 1585094

Browse files
committed
tests: add comprehensive test suite for userspace map generation and pinning logic
Expand `test_userspace_maps.ml` to verify the conditional generation of C code for BPF maps in userspace. These tests specifically target the branching logic governing map file descriptors (FD), setup routines, and pinning mechanisms. Key coverage areas include: - Map FD Declarations: Verifying that FDs are only generated when maps are used in userspace, unless `has_pinned_maps` is true, which forces declarations for all global maps. - Setup Code Generation: Ensuring `bpf_object__find_map_by_name` and related initialization logic are omitted when maps are only used on the eBPF side. - Pinning Logic: - Validating the generation of the `ensure_bpf_dir` helper function when pinned maps exist. - Confirming the use of `bpf_obj_get`, `bpf_map__pin`, and `_existing_fd` variables for pinned maps. - Confirming that non-pinned maps fall back to standard `bpf_map__fd` calls without pinning machinery. - Edge Case Validation: Testing scenarios where maps are defined but never accessed in the `main()` function to ensure code bloat is minimized.
1 parent 4e39a7d commit 1585094

1 file changed

Lines changed: 320 additions & 1 deletion

File tree

tests/test_userspace_maps.ml

Lines changed: 320 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ fn main(wrong_param: u32) -> i32 {
418418
check bool ("should have failed for: " ^ description) false true
419419
) invalid_programs
420420

421-
(** Test 6: Map file descriptor generation for userspace *)
421+
(** Test 7: Map file descriptor generation for userspace *)
422422
let test_map_fd_generation () =
423423
let code = {|
424424
pin var shared_counter : hash<u32, u32>(1024)
@@ -475,6 +475,315 @@ fn main() -> i32 {
475475
with
476476
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
477477

478+
(** Test 8: No map FD declarations when outer condition is false (no map ops, no exec, no pinned maps) *)
479+
let test_map_fd_not_generated_without_usage () =
480+
(* Map used only in eBPF program (not in main), no pinned maps, no exec *)
481+
let code = {|
482+
var ebpf_side_only : hash<u32, u64>(1024)
483+
484+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
485+
ebpf_side_only[1] = 100
486+
return XDP_PASS
487+
}
488+
489+
fn main() -> i32 {
490+
return 0
491+
}
492+
|} in
493+
try
494+
let ast = parse_string_with_builtins code in
495+
match get_generated_userspace_code ast "test_no_fd.ks" with
496+
| Some generated_content ->
497+
(* uses_map_operations=false, uses_exec=false, has_pinned_maps=false
498+
→ map_fd_declarations = "" → no "int ebpf_side_only_fd = -1" *)
499+
let has_fd_decl = contains_pattern generated_content "int ebpf_side_only_fd" in
500+
check bool "no fd declaration when no userspace map usage" false has_fd_decl
501+
| None ->
502+
fail "Failed to generate userspace code"
503+
with
504+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
505+
506+
(** Test 9: Only userspace-used maps get FD declarations when no pinned maps *)
507+
let test_map_fd_only_for_userspace_used_maps () =
508+
(* used_map is referenced in main; ebpf_only_map is referenced only in @xdp fn *)
509+
let code = {|
510+
var used_map : hash<u32, u64>(1024)
511+
var ebpf_only_map : hash<u32, u64>(1024)
512+
513+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
514+
ebpf_only_map[1] = 100
515+
return XDP_PASS
516+
}
517+
518+
fn main() -> i32 {
519+
used_map[1] = 42
520+
return 0
521+
}
522+
|} in
523+
try
524+
let ast = parse_string_with_builtins code in
525+
match get_generated_userspace_code ast "test_used_maps_fd.ks" with
526+
| Some generated_content ->
527+
(* uses_map_operations=true, has_pinned_maps=false
528+
→ maps_for_fd = used_global_maps_with_exec = [used_map] (not ebpf_only_map) *)
529+
let has_used_map_fd = contains_pattern generated_content "int used_map_fd" in
530+
let has_ebpf_only_fd = contains_pattern generated_content "int ebpf_only_map_fd" in
531+
check bool "used map gets fd declaration" true has_used_map_fd;
532+
check bool "ebpf-only map does NOT get fd declaration" false has_ebpf_only_fd
533+
| None ->
534+
fail "Failed to generate userspace code"
535+
with
536+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
537+
538+
(** Test 10: Pinned maps cause all global maps (including eBPF-only ones) to get FD declarations *)
539+
let test_map_fd_pinned_includes_all_global_maps () =
540+
(* pinned_map is pinned and used in main; other_map is non-pinned and used only in @xdp fn *)
541+
let code = {|
542+
pin var pinned_map : hash<u32, u64>(1024)
543+
var other_map : hash<u32, u64>(512)
544+
545+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
546+
other_map[1] = 100
547+
return XDP_PASS
548+
}
549+
550+
fn main() -> i32 {
551+
pinned_map[1] = 10
552+
return 0
553+
}
554+
|} in
555+
try
556+
let ast = parse_string_with_builtins code in
557+
match get_generated_userspace_code ast "test_pinned_fd.ks" with
558+
| Some generated_content ->
559+
(* has_pinned_maps=true → outer condition true, maps_for_fd = global_maps
560+
→ BOTH pinned_map and other_map get int ..._fd = -1 declarations *)
561+
let has_pinned_map_fd = contains_pattern generated_content "int pinned_map_fd" in
562+
let has_other_map_fd = contains_pattern generated_content "int other_map_fd" in
563+
check bool "pinned map gets fd declaration" true has_pinned_map_fd;
564+
check bool "non-pinned ebpf-only map ALSO gets fd declaration (global_maps used)" true has_other_map_fd
565+
| None ->
566+
fail "Failed to generate userspace code"
567+
with
568+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
569+
570+
(** Test 11: Map setup code not generated when no userspace map usage, no exec, no pinned maps *)
571+
let test_map_setup_not_generated_without_usage () =
572+
(* Map used only in eBPF program, no map access in main, no pinned maps, no exec *)
573+
let code = {|
574+
var ebpf_only : hash<u32, u64>(1024)
575+
576+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
577+
ebpf_only[1] = 100
578+
return XDP_PASS
579+
}
580+
581+
fn main() -> i32 {
582+
return 0
583+
}
584+
|} in
585+
try
586+
let ast = parse_string_with_builtins code in
587+
match get_generated_userspace_code ast "test_no_setup.ks" with
588+
| Some generated_content ->
589+
(* map_setup_code="" → all_setup_code="" → no bpf_object__find_map_by_name anywhere *)
590+
let has_find_map = contains_pattern generated_content "bpf_object__find_map_by_name" in
591+
check bool "no bpf_object__find_map_by_name when no userspace map usage" false has_find_map
592+
| None ->
593+
fail "Failed to generate userspace code"
594+
with
595+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
596+
597+
(** Test 12: Map setup code uses only userspace-used maps when no pinned maps *)
598+
let test_map_setup_only_for_used_maps () =
599+
(* var count triggers skeleton loading (has_global_vars=true → needs_object_loading=true).
600+
used_map is referenced in main; ebpf_only_map is referenced only in @xdp fn. *)
601+
let code = {|
602+
var count : u64 = 0
603+
var used_map : hash<u32, u64>(1024)
604+
var ebpf_only_map : hash<u32, u64>(1024)
605+
606+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
607+
ebpf_only_map[1] = 100
608+
return XDP_PASS
609+
}
610+
611+
fn main() -> i32 {
612+
used_map[1] = 42
613+
return 0
614+
}
615+
|} in
616+
try
617+
let ast = parse_string_with_builtins code in
618+
match get_generated_userspace_code ast "test_setup_used_maps.ks" with
619+
| Some generated_content ->
620+
(* has_pinned_maps=false, uses_map_operations=true
621+
→ maps_for_setup = used_global_maps_with_exec = [used_map]
622+
setup_call injects all_setup_code; only used_map gets find_map_by_name *)
623+
let has_used_map_setup = contains_pattern generated_content "find_map_by_name.*used_map" in
624+
let has_ebpf_only_setup = contains_pattern generated_content "find_map_by_name.*ebpf_only_map" in
625+
check bool "used_map has setup code (find_map_by_name)" true has_used_map_setup;
626+
check bool "ebpf_only_map does NOT get setup code" false has_ebpf_only_setup
627+
| None ->
628+
fail "Failed to generate userspace code"
629+
with
630+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
631+
632+
(** Test 13: Map setup code includes all global maps when pinned maps exist *)
633+
let test_map_setup_pinned_includes_all_global_maps () =
634+
(* var count triggers skeleton loading.
635+
pinned_map is pinned. other_map is non-pinned and eBPF-only (not accessed in main).
636+
Because has_pinned_maps=true, maps_for_setup = global_maps = [pinned_map, other_map]. *)
637+
let code = {|
638+
var count : u64 = 0
639+
pin var pinned_map : hash<u32, u64>(1024)
640+
var other_map : hash<u32, u64>(512)
641+
642+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
643+
other_map[1] = 100
644+
return XDP_PASS
645+
}
646+
647+
fn main() -> i32 {
648+
return 0
649+
}
650+
|} in
651+
try
652+
let ast = parse_string_with_builtins code in
653+
match get_generated_userspace_code ast "test_setup_pinned.ks" with
654+
| Some generated_content ->
655+
(* has_pinned_maps=true → maps_for_setup = global_maps = [pinned_map, other_map]
656+
setup_call is triggered by has_pinned_maps; all_setup_code includes setup for BOTH maps *)
657+
let has_pinned_map_setup = contains_pattern generated_content "find_map_by_name.*pinned_map" in
658+
let has_other_map_setup = contains_pattern generated_content "find_map_by_name.*other_map" in
659+
check bool "pinned_map gets setup code (find_map_by_name)" true has_pinned_map_setup;
660+
check bool "eBPF-only other_map ALSO gets setup code (global_maps used)" true has_other_map_setup
661+
| None ->
662+
fail "Failed to generate userspace code"
663+
with
664+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
665+
666+
(** Test 14: Directory creation helper (ensure_bpf_dir) generated when pinned maps exist *)
667+
let test_mkdir_helper_generated_with_pinned_maps () =
668+
let code = {|
669+
pin var my_map : hash<u32, u64>(1024)
670+
671+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
672+
return XDP_PASS
673+
}
674+
675+
fn main() -> i32 {
676+
my_map[1] = 99
677+
return 0
678+
}
679+
|} in
680+
try
681+
let ast = parse_string_with_builtins code in
682+
match get_generated_userspace_code ast "test_mkdir_pinned.ks" with
683+
| Some generated_content ->
684+
(* has_pinned_maps=true → mkdir_helper_function is the ensure_bpf_dir function *)
685+
let has_ensure_bpf_dir = contains_pattern generated_content "ensure_bpf_dir" in
686+
check bool "ensure_bpf_dir present when pinned maps exist" true has_ensure_bpf_dir
687+
| None ->
688+
fail "Failed to generate userspace code"
689+
with
690+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
691+
692+
(** Test 15: Directory creation helper (ensure_bpf_dir) not generated without pinned maps *)
693+
let test_mkdir_helper_not_generated_without_pinned_maps () =
694+
let code = {|
695+
var regular_map : hash<u32, u64>(1024)
696+
697+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
698+
return XDP_PASS
699+
}
700+
701+
fn main() -> i32 {
702+
regular_map[1] = 42
703+
return 0
704+
}
705+
|} in
706+
try
707+
let ast = parse_string_with_builtins code in
708+
match get_generated_userspace_code ast "test_mkdir_no_pinned.ks" with
709+
| Some generated_content ->
710+
(* has_pinned_maps=false → mkdir_helper_function = "" *)
711+
let has_ensure_bpf_dir = contains_pattern generated_content "ensure_bpf_dir" in
712+
check bool "ensure_bpf_dir absent when no pinned maps" false has_ensure_bpf_dir
713+
| None ->
714+
fail "Failed to generate userspace code"
715+
with
716+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
717+
718+
(** Test 16: Pinned map setup emits bpf_obj_get / ensure_bpf_dir / bpf_map__pin logic *)
719+
let test_pin_logic_pinned_map_setup () =
720+
let code = {|
721+
var count : u64 = 0
722+
pin var pinned_counter : hash<u32, u64>(1024)
723+
724+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
725+
return XDP_PASS
726+
}
727+
728+
fn main() -> i32 {
729+
return 0
730+
}
731+
|} in
732+
try
733+
let ast = parse_string_with_builtins code in
734+
match get_generated_userspace_code ast "test_pin_logic.ks" with
735+
| Some generated_content ->
736+
(* Some pin_path branch: generates bpf_obj_get to check existing pin *)
737+
let has_bpf_obj_get = contains_pattern generated_content "bpf_obj_get" in
738+
(* ... _existing_fd variable for the pinned map *)
739+
let has_existing_fd = contains_pattern generated_content "pinned_counter_existing_fd" in
740+
(* ... ensure_bpf_dir to create directory before pinning *)
741+
let has_ensure_bpf_dir = contains_pattern generated_content "ensure_bpf_dir" in
742+
(* ... bpf_map__pin to pin the map object *)
743+
let has_bpf_map_pin = contains_pattern generated_content "bpf_map__pin.*pinned_counter" in
744+
check bool "bpf_obj_get present for pinned map" true has_bpf_obj_get;
745+
check bool "_existing_fd variable present for pinned map" true has_existing_fd;
746+
check bool "ensure_bpf_dir called before pinning" true has_ensure_bpf_dir;
747+
check bool "bpf_map__pin called to pin the map" true has_bpf_map_pin
748+
| None ->
749+
fail "Failed to generate userspace code"
750+
with
751+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
752+
753+
(** Test 17: Non-pinned map setup uses plain bpf_map__fd only *)
754+
let test_pin_logic_non_pinned_map_setup () =
755+
let code = {|
756+
var count : u64 = 0
757+
var regular_map : hash<u32, u64>(1024)
758+
759+
@xdp fn test(ctx: *xdp_md) -> xdp_action {
760+
return XDP_PASS
761+
}
762+
763+
fn main() -> i32 {
764+
regular_map[1] = 42
765+
return 0
766+
}
767+
|} in
768+
try
769+
let ast = parse_string_with_builtins code in
770+
match get_generated_userspace_code ast "test_no_pin_logic.ks" with
771+
| Some generated_content ->
772+
(* None branch: plain fd fetch *)
773+
let has_bpf_map_fd = contains_pattern generated_content "bpf_map__fd.*regular_map" in
774+
(* None branch: no pinning machinery *)
775+
let has_bpf_obj_get = contains_pattern generated_content "bpf_obj_get" in
776+
let has_existing_fd = contains_pattern generated_content "regular_map_existing_fd" in
777+
let has_bpf_map_pin = contains_pattern generated_content "bpf_map__pin.*regular_map" in
778+
check bool "bpf_map__fd used for non-pinned map" true has_bpf_map_fd;
779+
check bool "bpf_obj_get NOT present for non-pinned map" false has_bpf_obj_get;
780+
check bool "_existing_fd NOT present for non-pinned map" false has_existing_fd;
781+
check bool "bpf_map__pin NOT called for non-pinned map" false has_bpf_map_pin
782+
| None ->
783+
fail "Failed to generate userspace code"
784+
with
785+
| exn -> fail ("Error occurred: " ^ Printexc.to_string exn)
786+
478787
let global_function_maps_tests = [
479788
"global_map_accessibility", `Quick, test_global_map_accessibility;
480789
"global_only_map_access", `Quick, test_global_only_map_access;
@@ -483,6 +792,16 @@ let global_function_maps_tests = [
483792
"global_function_code_structure", `Quick, test_global_function_code_structure;
484793
"global_function_error_handling", `Quick, test_global_function_error_handling;
485794
"map_fd_generation", `Quick, test_map_fd_generation;
795+
"map_fd_not_generated_without_usage", `Quick, test_map_fd_not_generated_without_usage;
796+
"map_fd_only_for_userspace_used_maps", `Quick, test_map_fd_only_for_userspace_used_maps;
797+
"map_fd_pinned_includes_all_global_maps", `Quick, test_map_fd_pinned_includes_all_global_maps;
798+
"mkdir_helper_generated_with_pinned_maps", `Quick, test_mkdir_helper_generated_with_pinned_maps;
799+
"mkdir_helper_not_generated_without_pinned_maps", `Quick, test_mkdir_helper_not_generated_without_pinned_maps;
800+
"map_setup_not_generated_without_usage", `Quick, test_map_setup_not_generated_without_usage;
801+
"map_setup_only_for_used_maps", `Quick, test_map_setup_only_for_used_maps;
802+
"map_setup_pinned_includes_all_global_maps", `Quick, test_map_setup_pinned_includes_all_global_maps;
803+
"pin_logic_pinned_map_setup", `Quick, test_pin_logic_pinned_map_setup;
804+
"pin_logic_non_pinned_map_setup", `Quick, test_pin_logic_non_pinned_map_setup;
486805
]
487806

488807
let () =

0 commit comments

Comments
 (0)