perf evsel: Assemble off-cpu samples
authorHoward Chu <howardchu95@gmail.com>
Thu, 1 May 2025 02:28:03 +0000 (19:28 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 6 May 2025 00:49:08 +0000 (21:49 -0300)
Use the data in bpf-output samples, to assemble off-cpu samples.

In evsel__is_offcpu_event(), check if sample_type is PERF_SAMPLE_RAW to
support off-cpu sample data created by an older version of perf.

Testing compatibility on off-cpu samples collected by perf before this patch series:

See below, the sample_type still uses PERF_SAMPLE_CALLCHAIN

$ perf script --header -i ./perf.data.ptn | grep "event : name = offcpu-time"
 # event : name = offcpu-time, , id = { 237917, 237918, 237919, 237920 }, type = 1 (software), size = 136, config = 0xa (PERF_COUNT_SW_BPF_OUTPUT), { sample_period, sample_freq } = 1, sample_type = IP|TID|TIME|CALLCHAIN|CPU|PERIOD|IDENTIFIER, read_format = ID|LOST, disabled = 1, freq = 1, sample_id_all = 1

The output is correct.

  $ perf script -i ./perf.data.ptn | grep offcpu-time
  gmain    2173 [000] 18446744069.414584:  100102015 offcpu-time:
  NetworkManager     901 [000] 18446744069.414584:    5603579 offcpu-time:
  Web Content 1183550 [000] 18446744069.414584:      46278 offcpu-time:
  gnome-control-c 2200559 [000] 18446744069.414584: 11998247014 offcpu-time:
  <SNIP>
  $

And after this patch series:

  $ perf script --header -i ./perf.data.off-cpu-v9 | grep "event : name = offcpu-time"
   # event : name = offcpu-time, , id = { 237959, 237960, 237961, 237962 }, type = 1 (software), size = 136, config = 0xa (PERF_COUNT_SW_BPF_OUTPUT), { sample_period, sample_freq } = 1, sample_type = IP|TID|TIME|CPU|PERIOD|RAW|IDENTIFIER, read_format = ID|LOST, disabled = 1, freq = 1, sample_id_all = 1

  $ ./perf script -i ./perf.data.off-cpu-v9 | grep offcpu-time
  gnome-shell    1875 [001] 4789616.361225:  100097057 offcpu-time:
  gnome-shell    1875 [001] 4789616.461419:  100107463 offcpu-time:
      firefox 2206821 [002] 4789616.475690:  255257245 offcpu-time:
  $

Committer testing:

The command to record those samples:

  root@number:~# perf record --off-cpu -a sleep 1
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 2.092 MB perf.data (1552 samples) ]
  root@number:~#

Then, before this patch series, the sample_type for the "offcpu-time" event is:

  root@number:~# perf evlist -v | grep offcpu-time
  offcpu-time: type: 1 (PERF_TYPE_SOFTWARE), size: 136, config: 0xa (PERF_COUNT_SW_BPF_OUTPUT), { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CALLCHAIN|CPU|PERIOD|IDENTIFIER, read_format: ID|LOST, disabled: 1, freq: 1, sample_id_all: 1
  root@number:~#

And after it, after recording it again:

  root@number:~# perf record --off-cpu -a sleep 1 ; perf evlist -v | grep offcpu-time
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 2.151 MB perf.data (2843 samples) ]
  offcpu-time: type: 1 (PERF_TYPE_SOFTWARE), size: 136, config: 0xa (PERF_COUNT_SW_BPF_OUTPUT), { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CPU|PERIOD|IDENTIFIER, read_format: ID|LOST, disabled: 1, sample_id_all: 1
  root@number:~#

Suggested-by: Namhyung Kim <namhyung@kernel.org>
Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Howard Chu <howardchu95@gmail.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Gautam Menghani <gautam@linux.ibm.com>
Tested-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20241108204137.2444151-7-howardchu95@gmail.com
Link: https://lore.kernel.org/r/20250501022809.449767-6-howardchu95@gmail.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/evsel.c

index 2415e067b8926b4077f0b1a8b5ccd2776e81c24f..b60461e16804bb1f0e8c0473b8e24cc0df95e2a9 100644 (file)
@@ -1277,7 +1277,8 @@ static void evsel__set_default_freq_period(struct record_opts *opts,
 
 bool evsel__is_offcpu_event(struct evsel *evsel)
 {
-       return evsel__is_bpf_output(evsel) && evsel__name_is(evsel, OFFCPU_EVENT);
+       return evsel__is_bpf_output(evsel) && evsel__name_is(evsel, OFFCPU_EVENT) &&
+              evsel->core.attr.sample_type & PERF_SAMPLE_RAW;
 }
 
 /*
@@ -2923,6 +2924,35 @@ static inline bool evsel__has_branch_counters(const struct evsel *evsel)
        return false;
 }
 
+static int __set_offcpu_sample(struct perf_sample *data)
+{
+       u64 *array = data->raw_data;
+       u32 max_size = data->raw_size, *p32;
+       const void *endp = (void *)array + max_size;
+
+       if (array == NULL)
+               return -EFAULT;
+
+       OVERFLOW_CHECK_u64(array);
+       p32 = (void *)array++;
+       data->pid = p32[0];
+       data->tid = p32[1];
+
+       OVERFLOW_CHECK_u64(array);
+       data->period = *array++;
+
+       OVERFLOW_CHECK_u64(array);
+       data->callchain = (struct ip_callchain *)array++;
+       OVERFLOW_CHECK(array, data->callchain->nr * sizeof(u64), max_size);
+       data->ip = data->callchain->ips[1];
+       array += data->callchain->nr;
+
+       OVERFLOW_CHECK_u64(array);
+       data->cgroup = *array;
+
+       return 0;
+}
+
 int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
                        struct perf_sample *data)
 {
@@ -3277,6 +3307,9 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
                array = (void *)array + sz;
        }
 
+       if (evsel__is_offcpu_event(evsel))
+               return __set_offcpu_sample(data);
+
        return 0;
 }