@@ -336,15 +336,80 @@ static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused)
336
336
return strcmp ((const char * ) key1 , (const char * ) key2 ) == 0 ;
337
337
}
338
338
339
+ static bool is_invalid_entry (char * buf , bool kernel )
340
+ {
341
+ if (kernel && strchr (buf , '[' ))
342
+ return true;
343
+ if (!kernel && !strchr (buf , '[' ))
344
+ return true;
345
+ return false;
346
+ }
347
+
348
+ static bool skip_entry (char * name )
349
+ {
350
+ /*
351
+ * We attach to almost all kernel functions and some of them
352
+ * will cause 'suspicious RCU usage' when fprobe is attached
353
+ * to them. Filter out the current culprits - arch_cpu_idle
354
+ * default_idle and rcu_* functions.
355
+ */
356
+ if (!strcmp (name , "arch_cpu_idle" ))
357
+ return true;
358
+ if (!strcmp (name , "default_idle" ))
359
+ return true;
360
+ if (!strncmp (name , "rcu_" , 4 ))
361
+ return true;
362
+ if (!strcmp (name , "bpf_dispatcher_xdp_func" ))
363
+ return true;
364
+ if (!strncmp (name , "__ftrace_invalid_address__" ,
365
+ sizeof ("__ftrace_invalid_address__" ) - 1 ))
366
+ return true;
367
+ return false;
368
+ }
369
+
370
+ /* Do comparision by ignoring '.llvm.<hash>' suffixes. */
371
+ static int compare_name (const char * name1 , const char * name2 )
372
+ {
373
+ const char * res1 , * res2 ;
374
+ int len1 , len2 ;
375
+
376
+ res1 = strstr (name1 , ".llvm." );
377
+ res2 = strstr (name2 , ".llvm." );
378
+ len1 = res1 ? res1 - name1 : strlen (name1 );
379
+ len2 = res2 ? res2 - name2 : strlen (name2 );
380
+
381
+ if (len1 == len2 )
382
+ return strncmp (name1 , name2 , len1 );
383
+ if (len1 < len2 )
384
+ return strncmp (name1 , name2 , len1 ) <= 0 ? -1 : 1 ;
385
+ return strncmp (name1 , name2 , len2 ) >= 0 ? 1 : -1 ;
386
+ }
387
+
388
+ static int load_kallsyms_compare (const void * p1 , const void * p2 )
389
+ {
390
+ return compare_name (((const struct ksym * )p1 )-> name , ((const struct ksym * )p2 )-> name );
391
+ }
392
+
393
+ static int search_kallsyms_compare (const void * p1 , const struct ksym * p2 )
394
+ {
395
+ return compare_name (p1 , p2 -> name );
396
+ }
397
+
339
398
static int get_syms (char * * * symsp , size_t * cntp , bool kernel )
340
399
{
341
- size_t cap = 0 , cnt = 0 , i ;
342
- char * name = NULL , * * syms = NULL ;
400
+ size_t cap = 0 , cnt = 0 ;
401
+ char * name = NULL , * ksym_name , * * syms = NULL ;
343
402
struct hashmap * map ;
403
+ struct ksyms * ksyms ;
404
+ struct ksym * ks ;
344
405
char buf [256 ];
345
406
FILE * f ;
346
407
int err = 0 ;
347
408
409
+ ksyms = load_kallsyms_custom_local (load_kallsyms_compare );
410
+ if (!ASSERT_OK_PTR (ksyms , "load_kallsyms_custom_local" ))
411
+ return - EINVAL ;
412
+
348
413
/*
349
414
* The available_filter_functions contains many duplicates,
350
415
* but other than that all symbols are usable in kprobe multi
@@ -368,33 +433,23 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
368
433
}
369
434
370
435
while (fgets (buf , sizeof (buf ), f )) {
371
- if (kernel && strchr (buf , '[' ))
372
- continue ;
373
- if (!kernel && !strchr (buf , '[' ))
436
+ if (is_invalid_entry (buf , kernel ))
374
437
continue ;
375
438
376
439
free (name );
377
440
if (sscanf (buf , "%ms$*[^\n]\n" , & name ) != 1 )
378
441
continue ;
379
- /*
380
- * We attach to almost all kernel functions and some of them
381
- * will cause 'suspicious RCU usage' when fprobe is attached
382
- * to them. Filter out the current culprits - arch_cpu_idle
383
- * default_idle and rcu_* functions.
384
- */
385
- if (!strcmp (name , "arch_cpu_idle" ))
386
- continue ;
387
- if (!strcmp (name , "default_idle" ))
388
- continue ;
389
- if (!strncmp (name , "rcu_" , 4 ))
390
- continue ;
391
- if (!strcmp (name , "bpf_dispatcher_xdp_func" ))
392
- continue ;
393
- if (!strncmp (name , "__ftrace_invalid_address__" ,
394
- sizeof ("__ftrace_invalid_address__" ) - 1 ))
442
+ if (skip_entry (name ))
395
443
continue ;
396
444
397
- err = hashmap__add (map , name , 0 );
445
+ ks = search_kallsyms_custom_local (ksyms , name , search_kallsyms_compare );
446
+ if (!ks ) {
447
+ err = - EINVAL ;
448
+ goto error ;
449
+ }
450
+
451
+ ksym_name = ks -> name ;
452
+ err = hashmap__add (map , ksym_name , 0 );
398
453
if (err == - EEXIST ) {
399
454
err = 0 ;
400
455
continue ;
@@ -407,8 +462,7 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
407
462
if (err )
408
463
goto error ;
409
464
410
- syms [cnt ++ ] = name ;
411
- name = NULL ;
465
+ syms [cnt ++ ] = ksym_name ;
412
466
}
413
467
414
468
* symsp = syms ;
@@ -418,42 +472,88 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
418
472
free (name );
419
473
fclose (f );
420
474
hashmap__free (map );
421
- if (err ) {
422
- for (i = 0 ; i < cnt ; i ++ )
423
- free (syms [i ]);
475
+ if (err )
424
476
free (syms );
477
+ return err ;
478
+ }
479
+
480
+ static int get_addrs (unsigned long * * addrsp , size_t * cntp , bool kernel )
481
+ {
482
+ unsigned long * addr , * addrs , * tmp_addrs ;
483
+ int err = 0 , max_cnt , inc_cnt ;
484
+ char * name = NULL ;
485
+ size_t cnt = 0 ;
486
+ char buf [256 ];
487
+ FILE * f ;
488
+
489
+ if (access ("/sys/kernel/tracing/trace" , F_OK ) == 0 )
490
+ f = fopen ("/sys/kernel/tracing/available_filter_functions_addrs" , "r" );
491
+ else
492
+ f = fopen ("/sys/kernel/debug/tracing/available_filter_functions_addrs" , "r" );
493
+
494
+ if (!f )
495
+ return - ENOENT ;
496
+
497
+ /* In my local setup, the number of entries is 50k+ so Let us initially
498
+ * allocate space to hold 64k entries. If 64k is not enough, incrementally
499
+ * increase 1k each time.
500
+ */
501
+ max_cnt = 65536 ;
502
+ inc_cnt = 1024 ;
503
+ addrs = malloc (max_cnt * sizeof (long ));
504
+ if (addrs == NULL ) {
505
+ err = - ENOMEM ;
506
+ goto error ;
425
507
}
508
+
509
+ while (fgets (buf , sizeof (buf ), f )) {
510
+ if (is_invalid_entry (buf , kernel ))
511
+ continue ;
512
+
513
+ free (name );
514
+ if (sscanf (buf , "%p %ms$*[^\n]\n" , & addr , & name ) != 2 )
515
+ continue ;
516
+ if (skip_entry (name ))
517
+ continue ;
518
+
519
+ if (cnt == max_cnt ) {
520
+ max_cnt += inc_cnt ;
521
+ tmp_addrs = realloc (addrs , max_cnt );
522
+ if (!tmp_addrs ) {
523
+ err = - ENOMEM ;
524
+ goto error ;
525
+ }
526
+ addrs = tmp_addrs ;
527
+ }
528
+
529
+ addrs [cnt ++ ] = (unsigned long )addr ;
530
+ }
531
+
532
+ * addrsp = addrs ;
533
+ * cntp = cnt ;
534
+
535
+ error :
536
+ free (name );
537
+ fclose (f );
538
+ if (err )
539
+ free (addrs );
426
540
return err ;
427
541
}
428
542
429
- static void test_kprobe_multi_bench_attach ( bool kernel )
543
+ static void do_bench_test ( struct kprobe_multi_empty * skel , struct bpf_kprobe_multi_opts * opts )
430
544
{
431
- LIBBPF_OPTS (bpf_kprobe_multi_opts , opts );
432
- struct kprobe_multi_empty * skel = NULL ;
433
545
long attach_start_ns , attach_end_ns ;
434
546
long detach_start_ns , detach_end_ns ;
435
547
double attach_delta , detach_delta ;
436
548
struct bpf_link * link = NULL ;
437
- char * * syms = NULL ;
438
- size_t cnt = 0 , i ;
439
-
440
- if (!ASSERT_OK (get_syms (& syms , & cnt , kernel ), "get_syms" ))
441
- return ;
442
-
443
- skel = kprobe_multi_empty__open_and_load ();
444
- if (!ASSERT_OK_PTR (skel , "kprobe_multi_empty__open_and_load" ))
445
- goto cleanup ;
446
-
447
- opts .syms = (const char * * ) syms ;
448
- opts .cnt = cnt ;
449
549
450
550
attach_start_ns = get_time_ns ();
451
551
link = bpf_program__attach_kprobe_multi_opts (skel -> progs .test_kprobe_empty ,
452
- NULL , & opts );
552
+ NULL , opts );
453
553
attach_end_ns = get_time_ns ();
454
554
455
555
if (!ASSERT_OK_PTR (link , "bpf_program__attach_kprobe_multi_opts" ))
456
- goto cleanup ;
556
+ return ;
457
557
458
558
detach_start_ns = get_time_ns ();
459
559
bpf_link__destroy (link );
@@ -462,17 +562,65 @@ static void test_kprobe_multi_bench_attach(bool kernel)
462
562
attach_delta = (attach_end_ns - attach_start_ns ) / 1000000000.0 ;
463
563
detach_delta = (detach_end_ns - detach_start_ns ) / 1000000000.0 ;
464
564
465
- printf ("%s: found %lu functions\n" , __func__ , cnt );
565
+ printf ("%s: found %lu functions\n" , __func__ , opts -> cnt );
466
566
printf ("%s: attached in %7.3lfs\n" , __func__ , attach_delta );
467
567
printf ("%s: detached in %7.3lfs\n" , __func__ , detach_delta );
568
+ }
569
+
570
+ static void test_kprobe_multi_bench_attach (bool kernel )
571
+ {
572
+ LIBBPF_OPTS (bpf_kprobe_multi_opts , opts );
573
+ struct kprobe_multi_empty * skel = NULL ;
574
+ char * * syms = NULL ;
575
+ size_t cnt = 0 ;
576
+
577
+ if (!ASSERT_OK (get_syms (& syms , & cnt , kernel ), "get_syms" ))
578
+ return ;
579
+
580
+ skel = kprobe_multi_empty__open_and_load ();
581
+ if (!ASSERT_OK_PTR (skel , "kprobe_multi_empty__open_and_load" ))
582
+ goto cleanup ;
583
+
584
+ opts .syms = (const char * * ) syms ;
585
+ opts .cnt = cnt ;
586
+
587
+ do_bench_test (skel , & opts );
468
588
469
589
cleanup :
470
590
kprobe_multi_empty__destroy (skel );
471
- if (syms ) {
472
- for (i = 0 ; i < cnt ; i ++ )
473
- free (syms [i ]);
591
+ if (syms )
474
592
free (syms );
593
+ }
594
+
595
+ static void test_kprobe_multi_bench_attach_addr (bool kernel )
596
+ {
597
+ LIBBPF_OPTS (bpf_kprobe_multi_opts , opts );
598
+ struct kprobe_multi_empty * skel = NULL ;
599
+ unsigned long * addrs = NULL ;
600
+ size_t cnt = 0 ;
601
+ int err ;
602
+
603
+ err = get_addrs (& addrs , & cnt , kernel );
604
+ if (err == - ENOENT ) {
605
+ test__skip ();
606
+ return ;
475
607
}
608
+
609
+ if (!ASSERT_OK (err , "get_addrs" ))
610
+ return ;
611
+
612
+ skel = kprobe_multi_empty__open_and_load ();
613
+ if (!ASSERT_OK_PTR (skel , "kprobe_multi_empty__open_and_load" ))
614
+ goto cleanup ;
615
+
616
+ opts .addrs = addrs ;
617
+ opts .cnt = cnt ;
618
+
619
+ do_bench_test (skel , & opts );
620
+
621
+ cleanup :
622
+ kprobe_multi_empty__destroy (skel );
623
+ free (addrs );
476
624
}
477
625
478
626
static void test_attach_override (void )
@@ -515,6 +663,10 @@ void serial_test_kprobe_multi_bench_attach(void)
515
663
test_kprobe_multi_bench_attach (true);
516
664
if (test__start_subtest ("modules" ))
517
665
test_kprobe_multi_bench_attach (false);
666
+ if (test__start_subtest ("kernel" ))
667
+ test_kprobe_multi_bench_attach_addr (true);
668
+ if (test__start_subtest ("modules" ))
669
+ test_kprobe_multi_bench_attach_addr (false);
518
670
}
519
671
520
672
void test_kprobe_multi_test (void )
0 commit comments