github.com/cilium/cilium@v1.16.2/bpf/tests/common.h (about) 1 /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 /* Copyright Authors of Cilium */ 3 4 #pragma once 5 6 #include <linux/types.h> 7 #include <linux/bpf.h> 8 #include <bpf/compiler.h> 9 #include <bpf/loader.h> 10 #include <bpf/section.h> 11 12 /* We can use this macro inside the actual datapath code 13 * to compile-in the code for testing. The primary usecase 14 * is initializing map-in-map or prog-map. 15 */ 16 #define BPF_TEST 17 18 #ifndef ___bpf_concat 19 #define ___bpf_concat(a, b) a ## b 20 #endif 21 #ifndef ___bpf_apply 22 #define ___bpf_apply(fn, n) ___bpf_concat(fn, n) 23 #endif 24 #ifndef ___bpf_nth 25 #define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N 26 #endif 27 #ifndef ___bpf_narg 28 #define ___bpf_narg(...) \ 29 ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 30 #endif 31 32 #define __bpf_log_arg0(ptr, arg) do {} while (0) 33 #define __bpf_log_arg1(ptr, arg) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; ptr += sizeof(__u64) 34 #define __bpf_log_arg2(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 35 ptr += sizeof(__u64); __bpf_log_arg1(ptr, args) 36 #define __bpf_log_arg3(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 37 ptr += sizeof(__u64); __bpf_log_arg2(ptr, args) 38 #define __bpf_log_arg4(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 39 ptr += sizeof(__u64); __bpf_log_arg3(ptr, args) 40 #define __bpf_log_arg5(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 41 ptr += sizeof(__u64); __bpf_log_arg4(ptr, args) 42 #define __bpf_log_arg6(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 43 ptr += sizeof(__u64); __bpf_log_arg5(ptr, args) 44 #define __bpf_log_arg7(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 45 ptr += sizeof(__u64); __bpf_log_arg6(ptr, args) 46 #define __bpf_log_arg8(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 47 ptr += sizeof(__u64); __bpf_log_arg7(ptr, args) 48 #define __bpf_log_arg9(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 49 ptr += sizeof(__u64); __bpf_log_arg8(ptr, args) 50 #define __bpf_log_arg10(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 51 ptr += sizeof(__u64); __bpf_log_arg9(ptr, args) 52 #define __bpf_log_arg11(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 53 ptr += sizeof(__u64); __bpf_log_arg10(ptr, args) 54 #define __bpf_log_arg12(ptr, arg, args...) *(ptr++) = MKR_LOG_ARG; *(__u64 *)(ptr) = arg; \ 55 ptr += sizeof(__u64); __bpf_log_arg11(ptr, args) 56 #define __bpf_log_arg(ptr, args...) \ 57 ___bpf_apply(__bpf_log_arg, ___bpf_narg(args))(ptr, args) 58 59 /* These values have to stay in sync with the enum */ 60 /* values in bpf/tests/bpftest/trf.proto */ 61 #define TEST_ERROR 0 62 #define TEST_PASS 1 63 #define TEST_FAIL 2 64 #define TEST_SKIP 3 65 66 /* Max number of cpus to check when doing percpu hash assertions */ 67 #define NR_CPUS 128 68 69 /* Use an array map with 1 key and a large value size as buffer to write results */ 70 /* into. */ 71 struct { 72 __uint(type, BPF_MAP_TYPE_ARRAY); 73 __uint(key_size, sizeof(__u32)); 74 __uint(value_size, 8192); 75 __uint(pinning, LIBBPF_PIN_BY_NAME); 76 __uint(max_entries, 1); 77 } suite_result_map __section_maps_btf; 78 79 /* Values for the markers below are derived from this guide: */ 80 /* https://developers.google.com/protocol-buffers/docs/encoding#structure */ 81 82 #define PROTOBUF_WIRE_TYPE(field, type) ((field) << 3 | (type)) 83 84 #define PROTOBUF_VARINT 0 85 #define PROTOBUF_FIXED64 1 86 #define PROTOBUF_LENGTH_DELIMITED 2 87 88 /* message SuiteResult */ 89 #define MKR_TEST_RESULT PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED) 90 #define MKR_SUITE_LOG PROTOBUF_WIRE_TYPE(2, PROTOBUF_LENGTH_DELIMITED) 91 92 /* message TestResult */ 93 #define MKR_TEST_NAME PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED) 94 #define MKR_TEST_STATUS PROTOBUF_WIRE_TYPE(2, PROTOBUF_VARINT) 95 #define MKR_TEST_LOG PROTOBUF_WIRE_TYPE(3, PROTOBUF_LENGTH_DELIMITED) 96 97 /* message Log */ 98 #define MKR_LOG_FMT PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED) 99 #define MKR_LOG_ARG PROTOBUF_WIRE_TYPE(2, PROTOBUF_FIXED64) 100 101 /* Write a message to the unit log 102 * The conversion specifiers supported by *fmt* are the same as for 103 * bpf_trace_printk(). They are **%d**, **%i**, **%u**, **%x**, **%ld**, 104 * **%li**, **%lu**, **%lx**, **%lld**, **%lli**, **%llu**, **%llx**, 105 * **%p**. No modifier (size of field, padding with zeroes, etc.) 106 * is available 107 */ 108 #define test_log(fmt, args...) \ 109 ({ \ 110 static const char ____fmt[] = fmt; \ 111 if (test_result_cursor) { \ 112 *(suite_result_cursor++) = MKR_TEST_LOG; \ 113 } else { \ 114 *(suite_result_cursor++) = MKR_SUITE_LOG; \ 115 } \ 116 *(suite_result_cursor++) = 2 + sizeof(____fmt) + \ 117 ___bpf_narg(args) + \ 118 (___bpf_narg(args) * sizeof(unsigned long long)); \ 119 *(suite_result_cursor++) = MKR_LOG_FMT; \ 120 *(suite_result_cursor++) = sizeof(____fmt); \ 121 memcpy(suite_result_cursor, ____fmt, sizeof(____fmt)); \ 122 suite_result_cursor += sizeof(____fmt); \ 123 \ 124 \ 125 if (___bpf_narg(args) > 0) { \ 126 __bpf_log_arg(suite_result_cursor, args); \ 127 } \ 128 }) 129 130 /* This is a hack to allow us to convert the integer produced by __LINE__ */ 131 /* to a string so we can concat it at compile time. */ 132 #define STRINGIZE(x) STRINGIZE2(x) 133 #define STRINGIZE2(x) #x 134 #define LINE_STRING STRINGIZE(__LINE__) 135 136 /* Mark the current test as failed */ 137 #define test_fail() \ 138 if (test_result_cursor) { \ 139 *test_result_status = TEST_FAIL;\ 140 } else { \ 141 suite_result = TEST_FAIL; \ 142 } \ 143 144 /* Mark the current test as failed and exit the current TEST/CHECK */ 145 #define test_fail_now() \ 146 if (test_result_cursor) { \ 147 *test_result_status = TEST_FAIL;\ 148 break; \ 149 } else { \ 150 return TEST_FAIL; \ 151 } 152 153 /* Mark the current test as skipped */ 154 #define test_skip() \ 155 if (test_result_cursor) { \ 156 *test_result_status = TEST_SKIP;\ 157 } else { \ 158 suite_result = TEST_SKIP; \ 159 } 160 161 /* Mark the current test as skipped and exit the current TEST/CHECK */ 162 #define test_skip_now() \ 163 if (test_result_cursor) { \ 164 *test_result_status = TEST_SKIP; \ 165 break; \ 166 } else { \ 167 return TEST_SKIP; \ 168 } 169 170 /* Write message to the log and mark current test as failed. */ 171 #define test_error(fmt, ...) \ 172 { \ 173 test_log(fmt, ##__VA_ARGS__); \ 174 test_fail(); \ 175 } 176 177 /* Log a message bpf_then fail_now */ 178 #define test_fatal(fmt, ...) \ 179 { \ 180 test_log(fmt, ##__VA_ARGS__); \ 181 test_fail_now() \ 182 } 183 184 /* Assert that `cond` is true, fail the rest otherwise */ 185 #define assert(cond) \ 186 if (!(cond)) { \ 187 test_log("assert failed at " __FILE__ ":" LINE_STRING); \ 188 test_fail_now(); \ 189 } 190 191 /* Declare bpf_map_lookup_elem with the test_ prefix to avoid conflicts in the */ 192 /* future. */ 193 static void *(*test_bpf_map_lookup_elem)(void *map, const void *key) = (void *)1; 194 195 /* Init sets up a number of variables which will be used by other macros. */ 196 /* - suite_result will be returned from the eBPF program */ 197 /* - test_result_status is a pointer into the suite_result_map when in a test */ 198 /* - suite_result_cursor keeps track of where in the suite result we are. */ 199 /* - test_result_cursor is a pointer to the varint of a test result, used to */ 200 /* write the amount of bytes used after a test is done. */ 201 #define test_init() \ 202 char suite_result = TEST_PASS; \ 203 __maybe_unused char *test_result_status = 0; \ 204 char *suite_result_cursor; \ 205 { \ 206 __u32 __key = 0; \ 207 suite_result_cursor = \ 208 test_bpf_map_lookup_elem(&suite_result_map, &__key);\ 209 if (!suite_result_cursor) { \ 210 return TEST_ERROR; \ 211 } \ 212 } \ 213 __maybe_unused char *test_result_cursor = 0; \ 214 __maybe_unused __u16 test_result_size; \ 215 do { 216 /* */ 217 /* Each test is single iteration do-while loop so we can break, to exit the */ 218 /* test without unique label names and goto's */ 219 #define TEST(name, body) \ 220 do { \ 221 *(suite_result_cursor++) = MKR_TEST_RESULT; \ 222 /* test_result_cursor will stay at test result length varint */ \ 223 test_result_cursor = suite_result_cursor; \ 224 /* Reserve 2 bytes for the varint indicating test result length */ \ 225 suite_result_cursor += 2; \ 226 \ 227 static const char ____name[] = name; \ 228 *(suite_result_cursor++) = MKR_TEST_NAME; \ 229 *(suite_result_cursor++) = sizeof(____name); \ 230 memcpy(suite_result_cursor, ____name, sizeof(____name)); \ 231 suite_result_cursor += sizeof(____name); \ 232 \ 233 *(suite_result_cursor++) = MKR_TEST_STATUS; \ 234 test_result_status = suite_result_cursor; \ 235 \ 236 *test_result_status = TEST_PASS; \ 237 suite_result_cursor++; \ 238 \ 239 body \ 240 } while (0); \ 241 /* Write the total size of the test result in bytes as varint */ \ 242 test_result_size = (__u16)((long)suite_result_cursor - \ 243 (long)test_result_cursor) - 2; \ 244 if (test_result_size > 127) { \ 245 *(test_result_cursor) = (__u8)(test_result_size & 0b01111111) | \ 246 0b10000000; \ 247 test_result_size >>= 7; \ 248 *(test_result_cursor + 1) = (__u8)test_result_size; \ 249 } else { \ 250 *test_result_cursor = (__u8)(test_result_size) | 0b10000000; \ 251 } \ 252 test_result_cursor = 0; 253 254 #define test_finish() \ 255 } while (0); \ 256 return suite_result 257 258 #define PKTGEN(progtype, name) __section(progtype "/test/" name "/pktgen") 259 #define SETUP(progtype, name) __section(progtype "/test/" name "/setup") 260 #define CHECK(progtype, name) __section(progtype "/test/" name "/check") 261 262 /* Asserts that the sum of per-cpu metrics map slots for a key equals count */ 263 #define assert_metrics_count(key, count) \ 264 ({ \ 265 struct metrics_value *__entry = NULL; \ 266 __u64 sum = 0; \ 267 /* Iterate until lookup encounters null when hitting cpu number */ \ 268 /* Assumes at most 128 CPUS */ \ 269 for (int i = 0; i < NR_CPUS; i++) { \ 270 __entry = map_lookup_percpu_elem(&METRICS_MAP, &key, i); \ 271 if (!__entry) { \ 272 break; \ 273 } \ 274 sum += __entry->count; \ 275 } \ 276 assert(sum == count); \ 277 })