@@ -336,15 +336,80 @@ static bool symbol_equal(long key1, long key2, void *ctx __maybe_unused)
336336 return strcmp ((const char * ) key1 , (const char * ) key2 ) == 0 ;
337337}
338338
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+
339398static int get_syms (char * * * symsp , size_t * cntp , bool kernel )
340399{
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 ;
343402 struct hashmap * map ;
403+ struct ksyms * ksyms ;
404+ struct ksym * ks ;
344405 char buf [256 ];
345406 FILE * f ;
346407 int err = 0 ;
347408
409+ ksyms = load_kallsyms_custom_local (load_kallsyms_compare );
410+ if (!ASSERT_OK_PTR (ksyms , "load_kallsyms_custom_local" ))
411+ return - EINVAL ;
412+
348413 /*
349414 * The available_filter_functions contains many duplicates,
350415 * 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)
368433 }
369434
370435 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 ))
374437 continue ;
375438
376439 free (name );
377440 if (sscanf (buf , "%ms$*[^\n]\n" , & name ) != 1 )
378441 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 ))
395443 continue ;
396444
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 );
398453 if (err == - EEXIST ) {
399454 err = 0 ;
400455 continue ;
@@ -407,8 +462,7 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
407462 if (err )
408463 goto error ;
409464
410- syms [cnt ++ ] = name ;
411- name = NULL ;
465+ syms [cnt ++ ] = ksym_name ;
412466 }
413467
414468 * symsp = syms ;
@@ -418,42 +472,88 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)
418472 free (name );
419473 fclose (f );
420474 hashmap__free (map );
421- if (err ) {
422- for (i = 0 ; i < cnt ; i ++ )
423- free (syms [i ]);
475+ if (err )
424476 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 ;
425507 }
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 );
426540 return err ;
427541}
428542
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 )
430544{
431- LIBBPF_OPTS (bpf_kprobe_multi_opts , opts );
432- struct kprobe_multi_empty * skel = NULL ;
433545 long attach_start_ns , attach_end_ns ;
434546 long detach_start_ns , detach_end_ns ;
435547 double attach_delta , detach_delta ;
436548 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 ;
449549
450550 attach_start_ns = get_time_ns ();
451551 link = bpf_program__attach_kprobe_multi_opts (skel -> progs .test_kprobe_empty ,
452- NULL , & opts );
552+ NULL , opts );
453553 attach_end_ns = get_time_ns ();
454554
455555 if (!ASSERT_OK_PTR (link , "bpf_program__attach_kprobe_multi_opts" ))
456- goto cleanup ;
556+ return ;
457557
458558 detach_start_ns = get_time_ns ();
459559 bpf_link__destroy (link );
@@ -462,17 +562,65 @@ static void test_kprobe_multi_bench_attach(bool kernel)
462562 attach_delta = (attach_end_ns - attach_start_ns ) / 1000000000.0 ;
463563 detach_delta = (detach_end_ns - detach_start_ns ) / 1000000000.0 ;
464564
465- printf ("%s: found %lu functions\n" , __func__ , cnt );
565+ printf ("%s: found %lu functions\n" , __func__ , opts -> cnt );
466566 printf ("%s: attached in %7.3lfs\n" , __func__ , attach_delta );
467567 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 );
468588
469589cleanup :
470590 kprobe_multi_empty__destroy (skel );
471- if (syms ) {
472- for (i = 0 ; i < cnt ; i ++ )
473- free (syms [i ]);
591+ if (syms )
474592 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 ;
475607 }
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 );
476624}
477625
478626static void test_attach_override (void )
@@ -515,6 +663,10 @@ void serial_test_kprobe_multi_bench_attach(void)
515663 test_kprobe_multi_bench_attach (true);
516664 if (test__start_subtest ("modules" ))
517665 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);
518670}
519671
520672void test_kprobe_multi_test (void )
0 commit comments