Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib/test_fprobe: add testcase for mixed fprobe

Add the testcase for the fprobe, which will hook the same target with two
fprobe: entry, entry+exit. And the two fprobes will be registered with
different order.

fgraph and ftrace are both used for the fprobe, and this testcase is for
the mixed situation.

Link: https://lore.kernel.org/all/20251015083238.2374294-3-dongml2@chinatelecom.cn/

Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>

authored by

Menglong Dong and committed by
Masami Hiramatsu (Google)
08ed5c81 2c67dc45

+98 -1
+98 -1
lib/tests/test_fprobe.c
··· 12 12 13 13 static struct kunit *current_test; 14 14 15 - static u32 rand1, entry_val, exit_val; 15 + static u32 rand1, entry_only_val, entry_val, exit_val; 16 + static u32 entry_only_count, entry_count, exit_count; 16 17 17 18 /* Use indirect calls to avoid inlining the target functions */ 18 19 static u32 (*target)(u32 value); ··· 191 190 KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); 192 191 } 193 192 193 + /* Handler for fprobe entry only case */ 194 + static notrace int entry_only_handler(struct fprobe *fp, unsigned long ip, 195 + unsigned long ret_ip, 196 + struct ftrace_regs *fregs, void *data) 197 + { 198 + KUNIT_EXPECT_FALSE(current_test, preemptible()); 199 + KUNIT_EXPECT_EQ(current_test, ip, target_ip); 200 + 201 + entry_only_count++; 202 + entry_only_val = (rand1 / div_factor); 203 + 204 + return 0; 205 + } 206 + 207 + static notrace int fprobe_entry_multi_handler(struct fprobe *fp, unsigned long ip, 208 + unsigned long ret_ip, 209 + struct ftrace_regs *fregs, 210 + void *data) 211 + { 212 + KUNIT_EXPECT_FALSE(current_test, preemptible()); 213 + KUNIT_EXPECT_EQ(current_test, ip, target_ip); 214 + 215 + entry_count++; 216 + entry_val = (rand1 / div_factor); 217 + 218 + return 0; 219 + } 220 + 221 + static notrace void fprobe_exit_multi_handler(struct fprobe *fp, unsigned long ip, 222 + unsigned long ret_ip, 223 + struct ftrace_regs *fregs, 224 + void *data) 225 + { 226 + unsigned long ret = ftrace_regs_get_return_value(fregs); 227 + 228 + KUNIT_EXPECT_FALSE(current_test, preemptible()); 229 + KUNIT_EXPECT_EQ(current_test, ip, target_ip); 230 + KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor)); 231 + 232 + exit_count++; 233 + exit_val = ret; 234 + } 235 + 236 + static void check_fprobe_multi(struct kunit *test) 237 + { 238 + entry_only_count = entry_count = exit_count = 0; 239 + entry_only_val = entry_val = exit_val = 0; 240 + 241 + target(rand1); 242 + 243 + /* Verify all handlers were called */ 244 + KUNIT_EXPECT_EQ(test, 1, entry_only_count); 245 + KUNIT_EXPECT_EQ(test, 1, entry_count); 246 + KUNIT_EXPECT_EQ(test, 1, exit_count); 247 + 248 + /* Verify values are correct */ 249 + KUNIT_EXPECT_EQ(test, (rand1 / div_factor), entry_only_val); 250 + KUNIT_EXPECT_EQ(test, (rand1 / div_factor), entry_val); 251 + KUNIT_EXPECT_EQ(test, (rand1 / div_factor), exit_val); 252 + } 253 + 254 + /* Test multiple fprobes hooking the same target function */ 255 + static void test_fprobe_multi(struct kunit *test) 256 + { 257 + struct fprobe fp1 = { 258 + .entry_handler = fprobe_entry_multi_handler, 259 + .exit_handler = fprobe_exit_multi_handler, 260 + }; 261 + struct fprobe fp2 = { 262 + .entry_handler = entry_only_handler, 263 + }; 264 + 265 + current_test = test; 266 + 267 + /* Test Case 1: Register in order 1 -> 2 */ 268 + KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp1, "fprobe_selftest_target", NULL)); 269 + KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp2, "fprobe_selftest_target", NULL)); 270 + 271 + check_fprobe_multi(test); 272 + 273 + /* Unregister all */ 274 + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp1)); 275 + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp2)); 276 + 277 + /* Test Case 2: Register in order 2 -> 1 */ 278 + KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp2, "fprobe_selftest_target", NULL)); 279 + KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp1, "fprobe_selftest_target", NULL)); 280 + 281 + check_fprobe_multi(test); 282 + 283 + /* Unregister all */ 284 + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp1)); 285 + KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp2)); 286 + } 287 + 194 288 static unsigned long get_ftrace_location(void *func) 195 289 { 196 290 unsigned long size, addr = (unsigned long)func; ··· 313 217 KUNIT_CASE(test_fprobe_syms), 314 218 KUNIT_CASE(test_fprobe_data), 315 219 KUNIT_CASE(test_fprobe_skip), 220 + KUNIT_CASE(test_fprobe_multi), 316 221 {} 317 222 }; 318 223