Re: [PATCH v3 3/4] perf-stat: introduce config stat.bpf-counter-events

2021-04-20 Thread Jiri Olsa
On Mon, Apr 19, 2021 at 08:26:02PM +, Song Liu wrote:
> 
> 
> > On Apr 17, 2021, at 9:45 AM, Namhyung Kim  wrote:
> > 
> > Hi Song,
> > 
> > On Sat, Apr 17, 2021 at 7:13 AM Song Liu  wrote:
> >> 
> >> Currently, to use BPF to aggregate perf event counters, the user uses
> >> --bpf-counters option. Enable "use bpf by default" events with a config
> >> option, stat.bpf-counter-events. Events with name in the option will use
> >> BPF.
> >> 
> >> This also enables mixed BPF event and regular event in the same sesssion.
> >> For example:
> >> 
> >>   perf config stat.bpf-counter-events=instructions
> >>   perf stat -e instructions,cs
> >> 
> >> The second command will use BPF for "instructions" but not "cs".
> >> 
> >> Signed-off-by: Song Liu 
> >> ---
> >> @@ -535,12 +549,13 @@ static int enable_counters(void)
> >>struct evsel *evsel;
> >>int err;
> >> 
> >> -   if (target__has_bpf()) {
> >> -   evlist__for_each_entry(evsel_list, evsel) {
> >> -   err = bpf_counter__enable(evsel);
> >> -   if (err)
> >> -   return err;
> >> -   }
> >> +   evlist__for_each_entry(evsel_list, evsel) {
> >> +   if (!evsel__is_bpf(evsel))
> >> +   continue;
> >> +
> >> +   err = bpf_counter__enable(evsel);
> >> +   if (err)
> >> +   return err;
> > 
> > I just realized it doesn't have a disable counterpart.
> 
> I guess it is not really necessary for perf-stat? It is probably good
> to have it though. How about we do it in a follow up patch?

good catch, should it at least do:
  evsel->follower_skel->bss->enabled = 0;

because then the follower goes down only with perf process, right?
still doing the counts till the end..?

also while checking on that I realized we open the counters
in separate path for this in bperf_reload_leader_program by
calling evsel__open_per_cpu.. and we're missing all the
fallback code and setup from create_perf_stat_counter

I think we should at least call create_perf_stat_counter,
and also propagate error value if it fails

jirka

> 
> [...]
> 
> >> +   if (!evsel__bpf_counter_events)
> >> +   return false;
> >> +
> >> +   ptr = strstr(evsel__bpf_counter_events, name);
> >> +   name_len = strlen(name);
> >> +
> >> +   /* check name matches a full token in evsel__bpf_counter_events */
> >> +   match = (ptr != NULL) &&
> >> +   ((ptr == evsel__bpf_counter_events) || (*(ptr - 1) == 
> >> ',')) &&
> >> +   ((*(ptr + name_len) == ',') || (*(ptr + name_len) == 
> >> '\0'));
> > 
> > I'm not sure we have an event name which is a substring of another.
> > Maybe it can retry if it fails to match.
> 
> We have ref-cycles and cycles. And some raw events may be substring of others?
> 
> Thanks,
> Song
> 
> [...]
> 



Re: [PATCH v3 3/4] perf-stat: introduce config stat.bpf-counter-events

2021-04-19 Thread Song Liu



> On Apr 19, 2021, at 7:26 AM, Jiri Olsa  wrote:
> 
> On Fri, Apr 16, 2021 at 03:13:24PM -0700, Song Liu wrote:
> 
> SNIP
> 
>> +/*
>> + * Returns:
>> + * 0   if all events use BPF;
>> + * 1   if some events do NOT use BPF;
>> + * < 0 on errors;
>> + */
>> static int read_bpf_map_counters(void)
>> {
>> +bool has_none_bpf_events = false;
>>  struct evsel *counter;
>>  int err;
>> 
>>  evlist__for_each_entry(evsel_list, counter) {
>> +if (!evsel__is_bpf(counter)) {
>> +has_none_bpf_events = true;
>> +continue;
>> +}
>>  err = bpf_counter__read(counter);
>>  if (err)
>>  return err;
>>  }
>> -return 0;
>> +return has_none_bpf_events ? 1 : 0;
>> }
>> 
>> static void read_counters(struct timespec *rs)
>> @@ -442,9 +455,10 @@ static void read_counters(struct timespec *rs)
>>  int err;
>> 
>>  if (!stat_config.stop_read_counter) {
>> -if (target__has_bpf())
>> -err = read_bpf_map_counters();
>> -else
>> +err = read_bpf_map_counters();
>> +if (err < 0)
>> +return;
>> +if (err)
>>  err = read_affinity_counters(rs);
> 
> this part is confusing for me.. I understand we don't want to enter
> read_affinity_counters when there's no bpf counter, so we don't set
> affinities in vain.. but there must be better way ;-)
> 
>> diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c
>> index 5de991ab46af9..3189b63714371 100644
>> --- a/tools/perf/util/bpf_counter.c
>> +++ b/tools/perf/util/bpf_counter.c
>> @@ -792,6 +792,8 @@ int bpf_counter__load(struct evsel *evsel, struct target 
>> *target)
>>  evsel->bpf_counter_ops = _program_profiler_ops;
>>  else if (target->use_bpf)
>>  evsel->bpf_counter_ops = _ops;
>> +else if (evsel__match_bpf_counter_events(evsel->name))
>> +evsel->bpf_counter_ops = _ops;
> 
> please put this with the target->use_bpf check,
> it seems like it's another thing

Thanks for the review. Fixing these in v4. 

Song



Re: [PATCH v3 3/4] perf-stat: introduce config stat.bpf-counter-events

2021-04-19 Thread Song Liu



> On Apr 17, 2021, at 9:45 AM, Namhyung Kim  wrote:
> 
> Hi Song,
> 
> On Sat, Apr 17, 2021 at 7:13 AM Song Liu  wrote:
>> 
>> Currently, to use BPF to aggregate perf event counters, the user uses
>> --bpf-counters option. Enable "use bpf by default" events with a config
>> option, stat.bpf-counter-events. Events with name in the option will use
>> BPF.
>> 
>> This also enables mixed BPF event and regular event in the same sesssion.
>> For example:
>> 
>>   perf config stat.bpf-counter-events=instructions
>>   perf stat -e instructions,cs
>> 
>> The second command will use BPF for "instructions" but not "cs".
>> 
>> Signed-off-by: Song Liu 
>> ---
>> @@ -535,12 +549,13 @@ static int enable_counters(void)
>>struct evsel *evsel;
>>int err;
>> 
>> -   if (target__has_bpf()) {
>> -   evlist__for_each_entry(evsel_list, evsel) {
>> -   err = bpf_counter__enable(evsel);
>> -   if (err)
>> -   return err;
>> -   }
>> +   evlist__for_each_entry(evsel_list, evsel) {
>> +   if (!evsel__is_bpf(evsel))
>> +   continue;
>> +
>> +   err = bpf_counter__enable(evsel);
>> +   if (err)
>> +   return err;
> 
> I just realized it doesn't have a disable counterpart.

I guess it is not really necessary for perf-stat? It is probably good
to have it though. How about we do it in a follow up patch?

[...]

>> +   if (!evsel__bpf_counter_events)
>> +   return false;
>> +
>> +   ptr = strstr(evsel__bpf_counter_events, name);
>> +   name_len = strlen(name);
>> +
>> +   /* check name matches a full token in evsel__bpf_counter_events */
>> +   match = (ptr != NULL) &&
>> +   ((ptr == evsel__bpf_counter_events) || (*(ptr - 1) == ',')) 
>> &&
>> +   ((*(ptr + name_len) == ',') || (*(ptr + name_len) == '\0'));
> 
> I'm not sure we have an event name which is a substring of another.
> Maybe it can retry if it fails to match.

We have ref-cycles and cycles. And some raw events may be substring of others?

Thanks,
Song

[...]



Re: [PATCH v3 3/4] perf-stat: introduce config stat.bpf-counter-events

2021-04-19 Thread Jiri Olsa
On Fri, Apr 16, 2021 at 03:13:24PM -0700, Song Liu wrote:

SNIP

> +/*
> + * Returns:
> + * 0   if all events use BPF;
> + * 1   if some events do NOT use BPF;
> + * < 0 on errors;
> + */
>  static int read_bpf_map_counters(void)
>  {
> + bool has_none_bpf_events = false;
>   struct evsel *counter;
>   int err;
>  
>   evlist__for_each_entry(evsel_list, counter) {
> + if (!evsel__is_bpf(counter)) {
> + has_none_bpf_events = true;
> + continue;
> + }
>   err = bpf_counter__read(counter);
>   if (err)
>   return err;
>   }
> - return 0;
> + return has_none_bpf_events ? 1 : 0;
>  }
>  
>  static void read_counters(struct timespec *rs)
> @@ -442,9 +455,10 @@ static void read_counters(struct timespec *rs)
>   int err;
>  
>   if (!stat_config.stop_read_counter) {
> - if (target__has_bpf())
> - err = read_bpf_map_counters();
> - else
> + err = read_bpf_map_counters();
> + if (err < 0)
> + return;
> + if (err)
>   err = read_affinity_counters(rs);

this part is confusing for me.. I understand we don't want to enter
read_affinity_counters when there's no bpf counter, so we don't set
affinities in vain.. but there must be better way ;-)

> diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c
> index 5de991ab46af9..3189b63714371 100644
> --- a/tools/perf/util/bpf_counter.c
> +++ b/tools/perf/util/bpf_counter.c
> @@ -792,6 +792,8 @@ int bpf_counter__load(struct evsel *evsel, struct target 
> *target)
>   evsel->bpf_counter_ops = _program_profiler_ops;
>   else if (target->use_bpf)
>   evsel->bpf_counter_ops = _ops;
> + else if (evsel__match_bpf_counter_events(evsel->name))
> + evsel->bpf_counter_ops = _ops;

please put this with the target->use_bpf check,
it seems like it's another thing

thanks,
jirka



Re: [PATCH v3 3/4] perf-stat: introduce config stat.bpf-counter-events

2021-04-17 Thread Namhyung Kim
Hi Song,

On Sat, Apr 17, 2021 at 7:13 AM Song Liu  wrote:
>
> Currently, to use BPF to aggregate perf event counters, the user uses
> --bpf-counters option. Enable "use bpf by default" events with a config
> option, stat.bpf-counter-events. Events with name in the option will use
> BPF.
>
> This also enables mixed BPF event and regular event in the same sesssion.
> For example:
>
>perf config stat.bpf-counter-events=instructions
>perf stat -e instructions,cs
>
> The second command will use BPF for "instructions" but not "cs".
>
> Signed-off-by: Song Liu 
> ---
> @@ -535,12 +549,13 @@ static int enable_counters(void)
> struct evsel *evsel;
> int err;
>
> -   if (target__has_bpf()) {
> -   evlist__for_each_entry(evsel_list, evsel) {
> -   err = bpf_counter__enable(evsel);
> -   if (err)
> -   return err;
> -   }
> +   evlist__for_each_entry(evsel_list, evsel) {
> +   if (!evsel__is_bpf(evsel))
> +   continue;
> +
> +   err = bpf_counter__enable(evsel);
> +   if (err)
> +   return err;

I just realized it doesn't have a disable counterpart.

> }
>
> if (stat_config.initial_delay < 0) {
> @@ -784,11 +799,9 @@ static int __run_perf_stat(int argc, const char **argv, 
> int run_idx)
> if (affinity__setup() < 0)
> return -1;
>
> -   if (target__has_bpf()) {
> -   evlist__for_each_entry(evsel_list, counter) {
> -   if (bpf_counter__load(counter, ))
> -   return -1;
> -   }
> +   evlist__for_each_entry(evsel_list, counter) {
> +   if (bpf_counter__load(counter, ))
> +   return -1;
> }
>
> evlist__for_each_cpu (evsel_list, i, cpu) {

[SNIP]
> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index 2d2614eeaa20e..080ddcfefbcd2 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -492,6 +492,28 @@ const char *evsel__hw_names[PERF_COUNT_HW_MAX] = {
> "ref-cycles",
>  };
>
> +char *evsel__bpf_counter_events;
> +
> +bool evsel__match_bpf_counter_events(const char *name)
> +{
> +   int name_len;
> +   bool match;
> +   char *ptr;
> +
> +   if (!evsel__bpf_counter_events)
> +   return false;
> +
> +   ptr = strstr(evsel__bpf_counter_events, name);
> +   name_len = strlen(name);
> +
> +   /* check name matches a full token in evsel__bpf_counter_events */
> +   match = (ptr != NULL) &&
> +   ((ptr == evsel__bpf_counter_events) || (*(ptr - 1) == ',')) &&
> +   ((*(ptr + name_len) == ',') || (*(ptr + name_len) == '\0'));

I'm not sure we have an event name which is a substring of another.
Maybe it can retry if it fails to match.

Thanks,
Namhyung

> +
> +   return match;
> +}
> +
>  static const char *__evsel__hw_name(u64 config)
>  {
> if (config < PERF_COUNT_HW_MAX && evsel__hw_names[config])


[PATCH v3 3/4] perf-stat: introduce config stat.bpf-counter-events

2021-04-16 Thread Song Liu
Currently, to use BPF to aggregate perf event counters, the user uses
--bpf-counters option. Enable "use bpf by default" events with a config
option, stat.bpf-counter-events. Events with name in the option will use
BPF.

This also enables mixed BPF event and regular event in the same sesssion.
For example:

   perf config stat.bpf-counter-events=instructions
   perf stat -e instructions,cs

The second command will use BPF for "instructions" but not "cs".

Signed-off-by: Song Liu 
---
 tools/perf/Documentation/perf-stat.txt |  2 ++
 tools/perf/builtin-stat.c  | 43 +-
 tools/perf/util/bpf_counter.c  |  2 ++
 tools/perf/util/config.c   |  4 +++
 tools/perf/util/evsel.c| 22 +
 tools/perf/util/evsel.h|  8 +
 tools/perf/util/target.h   |  5 ---
 7 files changed, 66 insertions(+), 20 deletions(-)

diff --git a/tools/perf/Documentation/perf-stat.txt 
b/tools/perf/Documentation/perf-stat.txt
index 6ec5960b08c3d..78afe13cd7d47 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -97,6 +97,8 @@ report::
Use BPF programs to aggregate readings from perf_events.  This
allows multiple perf-stat sessions that are counting the same metric 
(cycles,
instructions, etc.) to share hardware counters.
+   To use BPF programs on common hardware events by default, use
+   "perf config stat.bpf-counter-events=".
 
 --bpf-attr-map::
With option "--bpf-counters", different perf-stat sessions share
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 2a2c15cac80a3..0c76271f3ef53 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -413,6 +413,8 @@ static int read_affinity_counters(struct timespec *rs)
evlist__for_each_entry(evsel_list, counter) {
if (evsel__cpu_iter_skip(counter, cpu))
continue;
+   if (evsel__is_bpf(counter))
+   continue;
if (!counter->err) {
counter->err = read_counter_cpu(counter, rs,

counter->cpu_iter - 1);
@@ -423,17 +425,28 @@ static int read_affinity_counters(struct timespec *rs)
return 0;
 }
 
+/*
+ * Returns:
+ * 0   if all events use BPF;
+ * 1   if some events do NOT use BPF;
+ * < 0 on errors;
+ */
 static int read_bpf_map_counters(void)
 {
+   bool has_none_bpf_events = false;
struct evsel *counter;
int err;
 
evlist__for_each_entry(evsel_list, counter) {
+   if (!evsel__is_bpf(counter)) {
+   has_none_bpf_events = true;
+   continue;
+   }
err = bpf_counter__read(counter);
if (err)
return err;
}
-   return 0;
+   return has_none_bpf_events ? 1 : 0;
 }
 
 static void read_counters(struct timespec *rs)
@@ -442,9 +455,10 @@ static void read_counters(struct timespec *rs)
int err;
 
if (!stat_config.stop_read_counter) {
-   if (target__has_bpf())
-   err = read_bpf_map_counters();
-   else
+   err = read_bpf_map_counters();
+   if (err < 0)
+   return;
+   if (err)
err = read_affinity_counters(rs);
if (err < 0)
return;
@@ -535,12 +549,13 @@ static int enable_counters(void)
struct evsel *evsel;
int err;
 
-   if (target__has_bpf()) {
-   evlist__for_each_entry(evsel_list, evsel) {
-   err = bpf_counter__enable(evsel);
-   if (err)
-   return err;
-   }
+   evlist__for_each_entry(evsel_list, evsel) {
+   if (!evsel__is_bpf(evsel))
+   continue;
+
+   err = bpf_counter__enable(evsel);
+   if (err)
+   return err;
}
 
if (stat_config.initial_delay < 0) {
@@ -784,11 +799,9 @@ static int __run_perf_stat(int argc, const char **argv, 
int run_idx)
if (affinity__setup() < 0)
return -1;
 
-   if (target__has_bpf()) {
-   evlist__for_each_entry(evsel_list, counter) {
-   if (bpf_counter__load(counter, ))
-   return -1;
-   }
+   evlist__for_each_entry(evsel_list, counter) {
+   if (bpf_counter__load(counter, ))
+   return -1;
}
 
evlist__for_each_cpu (evsel_list, i, cpu) {
diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c
index 5de991ab46af9..3189b63714371 100644
--- a/tools/perf/util/bpf_counter.c
+++