]> git.baikalelectronics.ru Git - kernel.git/commitdiff
libperf: Handle read format in perf_evsel__read()
authorNamhyung Kim <namhyung@kernel.org>
Fri, 19 Aug 2022 00:36:42 +0000 (17:36 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 19 Aug 2022 18:56:27 +0000 (15:56 -0300)
The perf_counts_values should be increased to read the new lost data.
Also adjust values after read according the read format.

This supports PERF_FORMAT_GROUP which has a different data format but
it's only available for leader events.  Currently it doesn't have an API
to read sibling (member) events in the group.  But users may read the
sibling event directly.

Also reading from mmap would be disabled when the read format has ID or
LOST bit as it's not exposed via mmap.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220819003644.508916-3-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/lib/perf/evsel.c
tools/lib/perf/include/perf/event.h
tools/lib/perf/include/perf/evsel.h

index 952f3520d5c261bf827fcf551168c91c4b1e259f..8ce5bbd096666cb9e8ba23d075be99c6719a30f6 100644 (file)
@@ -305,6 +305,9 @@ int perf_evsel__read_size(struct perf_evsel *evsel)
        if (read_format & PERF_FORMAT_ID)
                entry += sizeof(u64);
 
+       if (read_format & PERF_FORMAT_LOST)
+               entry += sizeof(u64);
+
        if (read_format & PERF_FORMAT_GROUP) {
                nr = evsel->nr_members;
                size += sizeof(u64);
@@ -314,24 +317,98 @@ int perf_evsel__read_size(struct perf_evsel *evsel)
        return size;
 }
 
+/* This only reads values for the leader */
+static int perf_evsel__read_group(struct perf_evsel *evsel, int cpu_map_idx,
+                                 int thread, struct perf_counts_values *count)
+{
+       size_t size = perf_evsel__read_size(evsel);
+       int *fd = FD(evsel, cpu_map_idx, thread);
+       u64 read_format = evsel->attr.read_format;
+       u64 *data;
+       int idx = 1;
+
+       if (fd == NULL || *fd < 0)
+               return -EINVAL;
+
+       data = calloc(1, size);
+       if (data == NULL)
+               return -ENOMEM;
+
+       if (readn(*fd, data, size) <= 0) {
+               free(data);
+               return -errno;
+       }
+
+       /*
+        * This reads only the leader event intentionally since we don't have
+        * perf counts values for sibling events.
+        */
+       if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+               count->ena = data[idx++];
+       if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+               count->run = data[idx++];
+
+       /* value is always available */
+       count->val = data[idx++];
+       if (read_format & PERF_FORMAT_ID)
+               count->id = data[idx++];
+       if (read_format & PERF_FORMAT_LOST)
+               count->lost = data[idx++];
+
+       free(data);
+       return 0;
+}
+
+/*
+ * The perf read format is very flexible.  It needs to set the proper
+ * values according to the read format.
+ */
+static void perf_evsel__adjust_values(struct perf_evsel *evsel, u64 *buf,
+                                     struct perf_counts_values *count)
+{
+       u64 read_format = evsel->attr.read_format;
+       int n = 0;
+
+       count->val = buf[n++];
+
+       if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+               count->ena = buf[n++];
+
+       if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+               count->run = buf[n++];
+
+       if (read_format & PERF_FORMAT_ID)
+               count->id = buf[n++];
+
+       if (read_format & PERF_FORMAT_LOST)
+               count->lost = buf[n++];
+}
+
 int perf_evsel__read(struct perf_evsel *evsel, int cpu_map_idx, int thread,
                     struct perf_counts_values *count)
 {
        size_t size = perf_evsel__read_size(evsel);
        int *fd = FD(evsel, cpu_map_idx, thread);
+       u64 read_format = evsel->attr.read_format;
+       struct perf_counts_values buf;
 
        memset(count, 0, sizeof(*count));
 
        if (fd == NULL || *fd < 0)
                return -EINVAL;
 
+       if (read_format & PERF_FORMAT_GROUP)
+               return perf_evsel__read_group(evsel, cpu_map_idx, thread, count);
+
        if (MMAP(evsel, cpu_map_idx, thread) &&
+           !(read_format & (PERF_FORMAT_ID | PERF_FORMAT_LOST)) &&
            !perf_mmap__read_self(MMAP(evsel, cpu_map_idx, thread), count))
                return 0;
 
-       if (readn(*fd, count->values, size) <= 0)
+       if (readn(*fd, buf.values, size) <= 0)
                return -errno;
 
+       perf_evsel__adjust_values(evsel, buf.values, count);
        return 0;
 }
 
index 57f54781f5edea69906ded25a52335803319d7c7..93bf93a59c99b600cb30d758b7f3acbd4d9955f6 100644 (file)
@@ -77,7 +77,7 @@ struct perf_record_lost_samples {
 };
 
 /*
- * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID | PERF_FORMAT_LOST
  */
 struct perf_record_read {
        struct perf_event_header header;
@@ -86,6 +86,7 @@ struct perf_record_read {
        __u64                    time_enabled;
        __u64                    time_running;
        __u64                    id;
+       __u64                    lost;
 };
 
 struct perf_record_throttle {
index 699c0ed97d34ed051524f38f97a174124bdfb56b..6f92204075c244bc623b26dc2c97fa4c835a4228 100644 (file)
@@ -18,8 +18,10 @@ struct perf_counts_values {
                        uint64_t val;
                        uint64_t ena;
                        uint64_t run;
+                       uint64_t id;
+                       uint64_t lost;
                };
-               uint64_t values[3];
+               uint64_t values[5];
        };
 };