2019-07-21 13:24:21 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <perf/evlist.h>
|
2019-07-21 13:24:55 +02:00
|
|
|
#include <perf/evsel.h>
|
2019-09-03 11:01:04 +02:00
|
|
|
#include <linux/bitops.h>
|
2019-07-21 13:24:21 +02:00
|
|
|
#include <linux/list.h>
|
2019-09-03 11:01:04 +02:00
|
|
|
#include <linux/hash.h>
|
2019-09-03 11:19:56 +02:00
|
|
|
#include <sys/ioctl.h>
|
2019-07-21 13:24:21 +02:00
|
|
|
#include <internal/evlist.h>
|
2019-07-21 13:24:26 +02:00
|
|
|
#include <internal/evsel.h>
|
2019-09-03 11:01:04 +02:00
|
|
|
#include <internal/xyarray.h>
|
2019-10-07 14:53:21 +02:00
|
|
|
#include <internal/mmap.h>
|
|
|
|
#include <internal/cpumap.h>
|
|
|
|
#include <internal/threadmap.h>
|
|
|
|
#include <internal/lib.h>
|
2019-07-21 13:24:32 +02:00
|
|
|
#include <linux/zalloc.h>
|
2019-07-21 13:24:35 +02:00
|
|
|
#include <stdlib.h>
|
2019-09-03 11:19:56 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
2019-08-16 16:00:45 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <poll.h>
|
2019-10-07 14:53:21 +02:00
|
|
|
#include <sys/mman.h>
|
2019-07-21 13:24:43 +02:00
|
|
|
#include <perf/cpumap.h>
|
|
|
|
#include <perf/threadmap.h>
|
2019-08-06 13:21:53 +02:00
|
|
|
#include <api/fd/array.h>
|
2022-05-24 10:54:32 +03:00
|
|
|
#include "internal.h"
|
libperf: Add perf_evlist__init() function
Add the perf_evlist__init() function to initialize a perf_evlist struct.
Committer testing:
Fix a change in init ordering that was causing this backtrace:
(gdb) run stat sleep 1
Starting program: /root/bin/perf stat sleep 1
Program received signal SIGSEGV, Segmentation fault.
0x00000000004f6b55 in __perf_evlist__propagate_maps (evlist=0xbb34c0, evsel=0x0) at util/evlist.c:161
161 if (!evsel->own_cpus || evlist->has_user_cpus) {
Missing separate debuginfos, use: dnf debuginfo-install bzip2-libs-1.0.6-29.fc30.x86_64 elfutils-libelf-0.176-3.fc30.x86_64 elfutils-libs-0.176-3.fc30.x86_64 glib2-2.60.4-1.fc30.x86_64 libbabeltrace-1.5.6-2.fc30.x86_64 libgcc-9.1.1-1.fc30.x86_64 libunwind-1.3.1-2.fc30.x86_64 libuuid-2.33.2-1.fc30.x86_64 libxcrypt-4.4.6-2.fc30.x86_64 libzstd-1.4.0-1.fc30.x86_64 numactl-libs-2.0.12-2.fc30.x86_64 pcre-8.43-2.fc30.x86_64 perl-libs-5.28.2-436.fc30.x86_64 popt-1.16-17.fc30.x86_64 python2-libs-2.7.16-2.fc30.x86_64 slang-2.3.2-5.fc30.x86_64 xz-libs-5.2.4-5.fc30.x86_64 zlib-1.2.11-15.fc30.x86_64
(gdb) bt
#0 0x00000000004f6b55 in __perf_evlist__propagate_maps (evlist=0xbb34c0, evsel=0x0) at util/evlist.c:161
#1 0x00000000004f6c7a in perf_evlist__propagate_maps (evlist=0xbb34c0) at util/evlist.c:178
#2 0x00000000004f955e in perf_evlist__set_maps (evlist=0xbb34c0, cpus=0x0, threads=0x0) at util/evlist.c:1128
#3 0x00000000004f66f8 in evlist__init (evlist=0xbb34c0, cpus=0x0, threads=0x0) at util/evlist.c:52
#4 0x00000000004f6790 in evlist__new () at util/evlist.c:64
#5 0x0000000000456071 in cmd_stat (argc=3, argv=0x7fffffffd670) at builtin-stat.c:1705
#6 0x00000000004dd0fa in run_builtin (p=0xa21e00 <commands+288>, argc=3, argv=0x7fffffffd670) at perf.c:304
#7 0x00000000004dd367 in handle_internal_command (argc=3, argv=0x7fffffffd670) at perf.c:356
#8 0x00000000004dd4ae in run_argv (argcp=0x7fffffffd4cc, argv=0x7fffffffd4c0) at perf.c:400
#9 0x00000000004dd81a in main (argc=3, argv=0x7fffffffd670) at perf.c:522
(gdb) bt
So move the initialization of the core evlist (calling
perf_evlist__init()) to before perf_evlist__set_maps() in
evlist__init().
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexey Budankov <alexey.budankov@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20190721112506.12306-39-jolsa@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-21 13:24:25 +02:00
|
|
|
|
|
|
|
void perf_evlist__init(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(&evlist->entries);
|
2019-07-21 13:24:28 +02:00
|
|
|
evlist->nr_entries = 0;
|
2019-10-07 14:53:31 +02:00
|
|
|
fdarray__init(&evlist->pollfd, 64);
|
2021-02-25 12:51:47 +09:00
|
|
|
perf_evlist__reset_id_hash(evlist);
|
libperf: Add perf_evlist__init() function
Add the perf_evlist__init() function to initialize a perf_evlist struct.
Committer testing:
Fix a change in init ordering that was causing this backtrace:
(gdb) run stat sleep 1
Starting program: /root/bin/perf stat sleep 1
Program received signal SIGSEGV, Segmentation fault.
0x00000000004f6b55 in __perf_evlist__propagate_maps (evlist=0xbb34c0, evsel=0x0) at util/evlist.c:161
161 if (!evsel->own_cpus || evlist->has_user_cpus) {
Missing separate debuginfos, use: dnf debuginfo-install bzip2-libs-1.0.6-29.fc30.x86_64 elfutils-libelf-0.176-3.fc30.x86_64 elfutils-libs-0.176-3.fc30.x86_64 glib2-2.60.4-1.fc30.x86_64 libbabeltrace-1.5.6-2.fc30.x86_64 libgcc-9.1.1-1.fc30.x86_64 libunwind-1.3.1-2.fc30.x86_64 libuuid-2.33.2-1.fc30.x86_64 libxcrypt-4.4.6-2.fc30.x86_64 libzstd-1.4.0-1.fc30.x86_64 numactl-libs-2.0.12-2.fc30.x86_64 pcre-8.43-2.fc30.x86_64 perl-libs-5.28.2-436.fc30.x86_64 popt-1.16-17.fc30.x86_64 python2-libs-2.7.16-2.fc30.x86_64 slang-2.3.2-5.fc30.x86_64 xz-libs-5.2.4-5.fc30.x86_64 zlib-1.2.11-15.fc30.x86_64
(gdb) bt
#0 0x00000000004f6b55 in __perf_evlist__propagate_maps (evlist=0xbb34c0, evsel=0x0) at util/evlist.c:161
#1 0x00000000004f6c7a in perf_evlist__propagate_maps (evlist=0xbb34c0) at util/evlist.c:178
#2 0x00000000004f955e in perf_evlist__set_maps (evlist=0xbb34c0, cpus=0x0, threads=0x0) at util/evlist.c:1128
#3 0x00000000004f66f8 in evlist__init (evlist=0xbb34c0, cpus=0x0, threads=0x0) at util/evlist.c:52
#4 0x00000000004f6790 in evlist__new () at util/evlist.c:64
#5 0x0000000000456071 in cmd_stat (argc=3, argv=0x7fffffffd670) at builtin-stat.c:1705
#6 0x00000000004dd0fa in run_builtin (p=0xa21e00 <commands+288>, argc=3, argv=0x7fffffffd670) at perf.c:304
#7 0x00000000004dd367 in handle_internal_command (argc=3, argv=0x7fffffffd670) at perf.c:356
#8 0x00000000004dd4ae in run_argv (argcp=0x7fffffffd4cc, argv=0x7fffffffd4c0) at perf.c:400
#9 0x00000000004dd81a in main (argc=3, argv=0x7fffffffd670) at perf.c:522
(gdb) bt
So move the initialization of the core evlist (calling
perf_evlist__init()) to before perf_evlist__set_maps() in
evlist__init().
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexey Budankov <alexey.budankov@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20190721112506.12306-39-jolsa@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-21 13:24:25 +02:00
|
|
|
}
|
2019-07-21 13:24:26 +02:00
|
|
|
|
2019-07-21 13:24:43 +02:00
|
|
|
static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
|
|
|
|
struct perf_evsel *evsel)
|
|
|
|
{
|
perf parse-events: Support user CPUs mixed with threads/processes
Counting events system-wide with a specified CPU prior to this change
worked:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' -a sleep 1
Performance counter stats for 'system wide':
59,393,419,099 msr/tsc/
33,927,965,927 msr/tsc,cpu=cpu_core/
25,465,608,044 msr/tsc,cpu=cpu_atom/
```
However, when counting with process the counts became system wide:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
59,233,549 msr/tsc/
59,227,556 msr/tsc,cpu=cpu_core/
59,224,053 msr/tsc,cpu=cpu_atom/
```
Make the handling of CPU maps with event parsing clearer. When an
event is parsed creating an evsel the cpus should be either the PMU's
cpumask or user specified CPUs.
Update perf_evlist__propagate_maps so that it doesn't clobber the user
specified CPUs. Try to make the behavior clearer, firstly fix up
missing cpumasks. Next, perform sanity checks and adjustments from the
global evlist CPU requests and for the PMU including simplifying to
the "any CPU"(-1) value. Finally remove the event if the cpumask is
empty.
So that events are opened with a CPU and a thread change stat's
create_perf_stat_counter to give both.
With the change things are fixed:
```
$ perf stat --no-scale -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
63,704,975 msr/tsc/
47,060,704 msr/tsc,cpu=cpu_core/ (4.62%)
16,640,591 msr/tsc,cpu=cpu_atom/ (2.18%)
```
However, note the "--no-scale" option is used. This is necessary as
the running time for the event on the counter isn't the same as the
enabled time because the thread doesn't necessarily run on the CPUs
specified for the counter. All counter values are scaled with:
scaled_value = value * time_enabled / time_running
and so without --no-scale the scaled_value becomes very large. This
problem already exists on hybrid systems for the same reason. Here are
2 runs of the same code with an instructions event that counts the
same on both types of core, there is no real multiplexing happening on
the event:
```
$ perf stat -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
87,896,447 cpu_atom/instructions/ (14.37%)
98,171,964 cpu_core/instructions/ (85.63%)
...
$ perf stat --no-scale -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
13,069,890 cpu_atom/instructions/ (19.32%)
83,460,274 cpu_core/instructions/ (80.68%)
...
```
The scaling has inflated per-PMU instruction counts and the overall
count by 2x.
To fix this the kernel needs changing when a task+CPU event (or just
task event on hybrid) is scheduled out. A fix could be that the state
isn't inactive but off for such events, so that time_enabled counts
don't accumulate on them.
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-13-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-07-18 20:05:14 -07:00
|
|
|
if (perf_cpu_map__is_empty(evsel->cpus)) {
|
|
|
|
if (perf_cpu_map__is_empty(evsel->pmu_cpus)) {
|
|
|
|
/*
|
|
|
|
* Assume the unset PMU cpus were for a system-wide
|
|
|
|
* event, like a software or tracepoint.
|
|
|
|
*/
|
|
|
|
evsel->pmu_cpus = perf_cpu_map__new_online_cpus();
|
|
|
|
}
|
|
|
|
if (evlist->has_user_cpus && !evsel->system_wide) {
|
|
|
|
/*
|
|
|
|
* Use the user CPUs unless the evsel is set to be
|
|
|
|
* system wide, such as the dummy event.
|
|
|
|
*/
|
|
|
|
evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* System wide and other modes, assume the cpu map
|
|
|
|
* should be set to all PMU CPUs.
|
|
|
|
*/
|
|
|
|
evsel->cpus = perf_cpu_map__get(evsel->pmu_cpus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Avoid "any CPU"(-1) for uncore and PMUs that require a CPU, even if
|
|
|
|
* requested.
|
|
|
|
*/
|
|
|
|
if (evsel->requires_cpu && perf_cpu_map__has_any_cpu(evsel->cpus)) {
|
2023-05-27 00:21:44 -07:00
|
|
|
perf_cpu_map__put(evsel->cpus);
|
perf parse-events: Support user CPUs mixed with threads/processes
Counting events system-wide with a specified CPU prior to this change
worked:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' -a sleep 1
Performance counter stats for 'system wide':
59,393,419,099 msr/tsc/
33,927,965,927 msr/tsc,cpu=cpu_core/
25,465,608,044 msr/tsc,cpu=cpu_atom/
```
However, when counting with process the counts became system wide:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
59,233,549 msr/tsc/
59,227,556 msr/tsc,cpu=cpu_core/
59,224,053 msr/tsc,cpu=cpu_atom/
```
Make the handling of CPU maps with event parsing clearer. When an
event is parsed creating an evsel the cpus should be either the PMU's
cpumask or user specified CPUs.
Update perf_evlist__propagate_maps so that it doesn't clobber the user
specified CPUs. Try to make the behavior clearer, firstly fix up
missing cpumasks. Next, perform sanity checks and adjustments from the
global evlist CPU requests and for the PMU including simplifying to
the "any CPU"(-1) value. Finally remove the event if the cpumask is
empty.
So that events are opened with a CPU and a thread change stat's
create_perf_stat_counter to give both.
With the change things are fixed:
```
$ perf stat --no-scale -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
63,704,975 msr/tsc/
47,060,704 msr/tsc,cpu=cpu_core/ (4.62%)
16,640,591 msr/tsc,cpu=cpu_atom/ (2.18%)
```
However, note the "--no-scale" option is used. This is necessary as
the running time for the event on the counter isn't the same as the
enabled time because the thread doesn't necessarily run on the CPUs
specified for the counter. All counter values are scaled with:
scaled_value = value * time_enabled / time_running
and so without --no-scale the scaled_value becomes very large. This
problem already exists on hybrid systems for the same reason. Here are
2 runs of the same code with an instructions event that counts the
same on both types of core, there is no real multiplexing happening on
the event:
```
$ perf stat -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
87,896,447 cpu_atom/instructions/ (14.37%)
98,171,964 cpu_core/instructions/ (85.63%)
...
$ perf stat --no-scale -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
13,069,890 cpu_atom/instructions/ (19.32%)
83,460,274 cpu_core/instructions/ (80.68%)
...
```
The scaling has inflated per-PMU instruction counts and the overall
count by 2x.
To fix this the kernel needs changing when a task+CPU event (or just
task event on hybrid) is scheduled out. A fix could be that the state
isn't inactive but off for such events, so that time_enabled counts
don't accumulate on them.
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-13-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-07-18 20:05:14 -07:00
|
|
|
evsel->cpus = perf_cpu_map__get(evsel->pmu_cpus);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Globally requested CPUs replace user requested unless the evsel is
|
|
|
|
* set to be system wide.
|
|
|
|
*/
|
|
|
|
if (evlist->has_user_cpus && !evsel->system_wide) {
|
|
|
|
assert(!perf_cpu_map__has_any_cpu(evlist->user_requested_cpus));
|
|
|
|
if (!perf_cpu_map__equal(evsel->cpus, evlist->user_requested_cpus)) {
|
|
|
|
perf_cpu_map__put(evsel->cpus);
|
|
|
|
evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
|
2024-11-14 16:04:48 +00:00
|
|
|
}
|
perf parse-events: Support user CPUs mixed with threads/processes
Counting events system-wide with a specified CPU prior to this change
worked:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' -a sleep 1
Performance counter stats for 'system wide':
59,393,419,099 msr/tsc/
33,927,965,927 msr/tsc,cpu=cpu_core/
25,465,608,044 msr/tsc,cpu=cpu_atom/
```
However, when counting with process the counts became system wide:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
59,233,549 msr/tsc/
59,227,556 msr/tsc,cpu=cpu_core/
59,224,053 msr/tsc,cpu=cpu_atom/
```
Make the handling of CPU maps with event parsing clearer. When an
event is parsed creating an evsel the cpus should be either the PMU's
cpumask or user specified CPUs.
Update perf_evlist__propagate_maps so that it doesn't clobber the user
specified CPUs. Try to make the behavior clearer, firstly fix up
missing cpumasks. Next, perform sanity checks and adjustments from the
global evlist CPU requests and for the PMU including simplifying to
the "any CPU"(-1) value. Finally remove the event if the cpumask is
empty.
So that events are opened with a CPU and a thread change stat's
create_perf_stat_counter to give both.
With the change things are fixed:
```
$ perf stat --no-scale -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
63,704,975 msr/tsc/
47,060,704 msr/tsc,cpu=cpu_core/ (4.62%)
16,640,591 msr/tsc,cpu=cpu_atom/ (2.18%)
```
However, note the "--no-scale" option is used. This is necessary as
the running time for the event on the counter isn't the same as the
enabled time because the thread doesn't necessarily run on the CPUs
specified for the counter. All counter values are scaled with:
scaled_value = value * time_enabled / time_running
and so without --no-scale the scaled_value becomes very large. This
problem already exists on hybrid systems for the same reason. Here are
2 runs of the same code with an instructions event that counts the
same on both types of core, there is no real multiplexing happening on
the event:
```
$ perf stat -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
87,896,447 cpu_atom/instructions/ (14.37%)
98,171,964 cpu_core/instructions/ (85.63%)
...
$ perf stat --no-scale -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
13,069,890 cpu_atom/instructions/ (19.32%)
83,460,274 cpu_core/instructions/ (80.68%)
...
```
The scaling has inflated per-PMU instruction counts and the overall
count by 2x.
To fix this the kernel needs changing when a task+CPU event (or just
task event on hybrid) is scheduled out. A fix could be that the state
isn't inactive but off for such events, so that time_enabled counts
don't accumulate on them.
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-13-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-07-18 20:05:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure cpus only references valid PMU CPUs. */
|
|
|
|
if (!perf_cpu_map__has_any_cpu(evsel->cpus) &&
|
|
|
|
!perf_cpu_map__is_subset(evsel->pmu_cpus, evsel->cpus)) {
|
|
|
|
struct perf_cpu_map *tmp = perf_cpu_map__intersect(evsel->pmu_cpus, evsel->cpus);
|
|
|
|
|
perf stat: Fix out of bounds CPU map access when handling armv8_pmu events
It was reported that 'perf stat' crashed when using with armv8_pmu (CPU)
events with the task mode. As 'perf stat' uses an empty cpu map for
task mode but armv8_pmu has its own cpu mask, it has confused which map
it should use when accessing file descriptors and this causes segfaults:
(gdb) bt
#0 0x0000000000603fc8 in perf_evsel__close_fd_cpu (evsel=<optimized out>,
cpu=<optimized out>) at evsel.c:122
#1 perf_evsel__close_cpu (evsel=evsel@entry=0x716e950, cpu=7) at evsel.c:156
#2 0x00000000004d4718 in evlist__close (evlist=0x70a7cb0) at util/evlist.c:1242
#3 0x0000000000453404 in __run_perf_stat (argc=3, argc@entry=1, argv=0x30,
argv@entry=0xfffffaea2f90, run_idx=119, run_idx@entry=1701998435)
at builtin-stat.c:929
#4 0x0000000000455058 in run_perf_stat (run_idx=1701998435, argv=0xfffffaea2f90,
argc=1) at builtin-stat.c:947
#5 cmd_stat (argc=1, argv=0xfffffaea2f90) at builtin-stat.c:2357
#6 0x00000000004bb888 in run_builtin (p=p@entry=0x9764b8 <commands+288>,
argc=argc@entry=4, argv=argv@entry=0xfffffaea2f90) at perf.c:312
#7 0x00000000004bbb54 in handle_internal_command (argc=argc@entry=4,
argv=argv@entry=0xfffffaea2f90) at perf.c:364
#8 0x0000000000435378 in run_argv (argcp=<synthetic pointer>,
argv=<synthetic pointer>) at perf.c:408
#9 main (argc=4, argv=0xfffffaea2f90) at perf.c:538
To fix this, I simply used the given cpu map unless the evsel actually
is not a system-wide event (like uncore events).
Fixes: 7736627b865d ("perf stat: Use affinity for closing file descriptors")
Reported-by: Wei Li <liwei391@huawei.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Barry Song <song.bao.hua@hisilicon.com>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lore.kernel.org/lkml/20201007081311.1831003-1-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-07 17:13:11 +09:00
|
|
|
perf_cpu_map__put(evsel->cpus);
|
perf parse-events: Support user CPUs mixed with threads/processes
Counting events system-wide with a specified CPU prior to this change
worked:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' -a sleep 1
Performance counter stats for 'system wide':
59,393,419,099 msr/tsc/
33,927,965,927 msr/tsc,cpu=cpu_core/
25,465,608,044 msr/tsc,cpu=cpu_atom/
```
However, when counting with process the counts became system wide:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
59,233,549 msr/tsc/
59,227,556 msr/tsc,cpu=cpu_core/
59,224,053 msr/tsc,cpu=cpu_atom/
```
Make the handling of CPU maps with event parsing clearer. When an
event is parsed creating an evsel the cpus should be either the PMU's
cpumask or user specified CPUs.
Update perf_evlist__propagate_maps so that it doesn't clobber the user
specified CPUs. Try to make the behavior clearer, firstly fix up
missing cpumasks. Next, perform sanity checks and adjustments from the
global evlist CPU requests and for the PMU including simplifying to
the "any CPU"(-1) value. Finally remove the event if the cpumask is
empty.
So that events are opened with a CPU and a thread change stat's
create_perf_stat_counter to give both.
With the change things are fixed:
```
$ perf stat --no-scale -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
63,704,975 msr/tsc/
47,060,704 msr/tsc,cpu=cpu_core/ (4.62%)
16,640,591 msr/tsc,cpu=cpu_atom/ (2.18%)
```
However, note the "--no-scale" option is used. This is necessary as
the running time for the event on the counter isn't the same as the
enabled time because the thread doesn't necessarily run on the CPUs
specified for the counter. All counter values are scaled with:
scaled_value = value * time_enabled / time_running
and so without --no-scale the scaled_value becomes very large. This
problem already exists on hybrid systems for the same reason. Here are
2 runs of the same code with an instructions event that counts the
same on both types of core, there is no real multiplexing happening on
the event:
```
$ perf stat -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
87,896,447 cpu_atom/instructions/ (14.37%)
98,171,964 cpu_core/instructions/ (85.63%)
...
$ perf stat --no-scale -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
13,069,890 cpu_atom/instructions/ (19.32%)
83,460,274 cpu_core/instructions/ (80.68%)
...
```
The scaling has inflated per-PMU instruction counts and the overall
count by 2x.
To fix this the kernel needs changing when a task+CPU event (or just
task event on hybrid) is scheduled out. A fix could be that the state
isn't inactive but off for such events, so that time_enabled counts
don't accumulate on them.
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-13-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-07-18 20:05:14 -07:00
|
|
|
evsel->cpus = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Was event requested on all the PMU's CPUs but the user requested is
|
|
|
|
* any CPU (-1)? If so switch to using any CPU (-1) to reduce the number
|
|
|
|
* of events.
|
|
|
|
*/
|
|
|
|
if (!evsel->system_wide &&
|
|
|
|
!evsel->requires_cpu &&
|
|
|
|
perf_cpu_map__equal(evsel->cpus, evsel->pmu_cpus) &&
|
|
|
|
perf_cpu_map__has_any_cpu(evlist->user_requested_cpus)) {
|
2019-07-21 13:24:43 +02:00
|
|
|
perf_cpu_map__put(evsel->cpus);
|
perf parse-events: Support user CPUs mixed with threads/processes
Counting events system-wide with a specified CPU prior to this change
worked:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' -a sleep 1
Performance counter stats for 'system wide':
59,393,419,099 msr/tsc/
33,927,965,927 msr/tsc,cpu=cpu_core/
25,465,608,044 msr/tsc,cpu=cpu_atom/
```
However, when counting with process the counts became system wide:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
59,233,549 msr/tsc/
59,227,556 msr/tsc,cpu=cpu_core/
59,224,053 msr/tsc,cpu=cpu_atom/
```
Make the handling of CPU maps with event parsing clearer. When an
event is parsed creating an evsel the cpus should be either the PMU's
cpumask or user specified CPUs.
Update perf_evlist__propagate_maps so that it doesn't clobber the user
specified CPUs. Try to make the behavior clearer, firstly fix up
missing cpumasks. Next, perform sanity checks and adjustments from the
global evlist CPU requests and for the PMU including simplifying to
the "any CPU"(-1) value. Finally remove the event if the cpumask is
empty.
So that events are opened with a CPU and a thread change stat's
create_perf_stat_counter to give both.
With the change things are fixed:
```
$ perf stat --no-scale -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
63,704,975 msr/tsc/
47,060,704 msr/tsc,cpu=cpu_core/ (4.62%)
16,640,591 msr/tsc,cpu=cpu_atom/ (2.18%)
```
However, note the "--no-scale" option is used. This is necessary as
the running time for the event on the counter isn't the same as the
enabled time because the thread doesn't necessarily run on the CPUs
specified for the counter. All counter values are scaled with:
scaled_value = value * time_enabled / time_running
and so without --no-scale the scaled_value becomes very large. This
problem already exists on hybrid systems for the same reason. Here are
2 runs of the same code with an instructions event that counts the
same on both types of core, there is no real multiplexing happening on
the event:
```
$ perf stat -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
87,896,447 cpu_atom/instructions/ (14.37%)
98,171,964 cpu_core/instructions/ (85.63%)
...
$ perf stat --no-scale -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
13,069,890 cpu_atom/instructions/ (19.32%)
83,460,274 cpu_core/instructions/ (80.68%)
...
```
The scaling has inflated per-PMU instruction counts and the overall
count by 2x.
To fix this the kernel needs changing when a task+CPU event (or just
task event on hybrid) is scheduled out. A fix could be that the state
isn't inactive but off for such events, so that time_enabled counts
don't accumulate on them.
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-13-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-07-18 20:05:14 -07:00
|
|
|
evsel->cpus = perf_cpu_map__get(evlist->user_requested_cpus);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity check assert before the evsel is potentially removed. */
|
|
|
|
assert(!evsel->requires_cpu || !perf_cpu_map__has_any_cpu(evsel->cpus));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Empty cpu lists would eventually get opened as "any" so remove
|
|
|
|
* genuinely empty ones before they're opened in the wrong place.
|
|
|
|
*/
|
|
|
|
if (perf_cpu_map__is_empty(evsel->cpus)) {
|
|
|
|
struct perf_evsel *next = perf_evlist__next(evlist, evsel);
|
|
|
|
|
|
|
|
perf_evlist__remove(evlist, evsel);
|
|
|
|
/* Keep idx contiguous */
|
|
|
|
if (next)
|
|
|
|
list_for_each_entry_from(next, &evlist->entries, node)
|
|
|
|
next->idx--;
|
|
|
|
|
|
|
|
return;
|
2019-07-21 13:24:43 +02:00
|
|
|
}
|
|
|
|
|
2022-10-03 13:46:43 -07:00
|
|
|
if (evsel->system_wide) {
|
|
|
|
perf_thread_map__put(evsel->threads);
|
|
|
|
evsel->threads = perf_thread_map__new_dummy();
|
|
|
|
} else {
|
2022-05-24 10:54:36 +03:00
|
|
|
perf_thread_map__put(evsel->threads);
|
|
|
|
evsel->threads = perf_thread_map__get(evlist->threads);
|
|
|
|
}
|
|
|
|
|
2024-11-07 12:53:06 +00:00
|
|
|
perf_cpu_map__merge(&evlist->all_cpus, evsel->cpus);
|
2019-07-21 13:24:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
|
|
|
|
{
|
2024-11-14 16:04:48 +00:00
|
|
|
struct perf_evsel *evsel, *n;
|
2019-07-21 13:24:43 +02:00
|
|
|
|
2022-10-03 13:46:44 -07:00
|
|
|
evlist->needs_map_propagation = true;
|
2022-05-02 21:17:53 -07:00
|
|
|
|
perf parse-events: Support user CPUs mixed with threads/processes
Counting events system-wide with a specified CPU prior to this change
worked:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' -a sleep 1
Performance counter stats for 'system wide':
59,393,419,099 msr/tsc/
33,927,965,927 msr/tsc,cpu=cpu_core/
25,465,608,044 msr/tsc,cpu=cpu_atom/
```
However, when counting with process the counts became system wide:
```
$ perf stat -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
59,233,549 msr/tsc/
59,227,556 msr/tsc,cpu=cpu_core/
59,224,053 msr/tsc,cpu=cpu_atom/
```
Make the handling of CPU maps with event parsing clearer. When an
event is parsed creating an evsel the cpus should be either the PMU's
cpumask or user specified CPUs.
Update perf_evlist__propagate_maps so that it doesn't clobber the user
specified CPUs. Try to make the behavior clearer, firstly fix up
missing cpumasks. Next, perform sanity checks and adjustments from the
global evlist CPU requests and for the PMU including simplifying to
the "any CPU"(-1) value. Finally remove the event if the cpumask is
empty.
So that events are opened with a CPU and a thread change stat's
create_perf_stat_counter to give both.
With the change things are fixed:
```
$ perf stat --no-scale -e 'msr/tsc/,msr/tsc,cpu=cpu_core/,msr/tsc,cpu=cpu_atom/' perf test -F 10
10.1: Basic parsing test : Ok
10.2: Parsing without PMU name : Ok
10.3: Parsing with PMU name : Ok
Performance counter stats for 'perf test -F 10':
63,704,975 msr/tsc/
47,060,704 msr/tsc,cpu=cpu_core/ (4.62%)
16,640,591 msr/tsc,cpu=cpu_atom/ (2.18%)
```
However, note the "--no-scale" option is used. This is necessary as
the running time for the event on the counter isn't the same as the
enabled time because the thread doesn't necessarily run on the CPUs
specified for the counter. All counter values are scaled with:
scaled_value = value * time_enabled / time_running
and so without --no-scale the scaled_value becomes very large. This
problem already exists on hybrid systems for the same reason. Here are
2 runs of the same code with an instructions event that counts the
same on both types of core, there is no real multiplexing happening on
the event:
```
$ perf stat -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
87,896,447 cpu_atom/instructions/ (14.37%)
98,171,964 cpu_core/instructions/ (85.63%)
...
$ perf stat --no-scale -e instructions perf test -F 10
...
Performance counter stats for 'perf test -F 10':
13,069,890 cpu_atom/instructions/ (19.32%)
83,460,274 cpu_core/instructions/ (80.68%)
...
```
The scaling has inflated per-PMU instruction counts and the overall
count by 2x.
To fix this the kernel needs changing when a task+CPU event (or just
task event on hybrid) is scheduled out. A fix could be that the state
isn't inactive but off for such events, so that time_enabled counts
don't accumulate on them.
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250719030517.1990983-13-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
2025-07-18 20:05:14 -07:00
|
|
|
/* Clear the all_cpus set which will be merged into during propagation. */
|
|
|
|
perf_cpu_map__put(evlist->all_cpus);
|
|
|
|
evlist->all_cpus = NULL;
|
|
|
|
|
2024-11-14 16:04:48 +00:00
|
|
|
list_for_each_entry_safe(evsel, n, &evlist->entries, node)
|
2019-07-21 13:24:43 +02:00
|
|
|
__perf_evlist__propagate_maps(evlist, evsel);
|
|
|
|
}
|
|
|
|
|
2019-07-21 13:24:26 +02:00
|
|
|
void perf_evlist__add(struct perf_evlist *evlist,
|
|
|
|
struct perf_evsel *evsel)
|
|
|
|
{
|
2021-07-06 17:16:59 +02:00
|
|
|
evsel->idx = evlist->nr_entries;
|
2019-07-21 13:24:26 +02:00
|
|
|
list_add_tail(&evsel->node, &evlist->entries);
|
2019-07-21 13:24:28 +02:00
|
|
|
evlist->nr_entries += 1;
|
2022-10-03 13:46:44 -07:00
|
|
|
|
|
|
|
if (evlist->needs_map_propagation)
|
|
|
|
__perf_evlist__propagate_maps(evlist, evsel);
|
2019-07-21 13:24:26 +02:00
|
|
|
}
|
2019-07-21 13:24:27 +02:00
|
|
|
|
|
|
|
void perf_evlist__remove(struct perf_evlist *evlist,
|
|
|
|
struct perf_evsel *evsel)
|
|
|
|
{
|
|
|
|
list_del_init(&evsel->node);
|
2019-07-21 13:24:28 +02:00
|
|
|
evlist->nr_entries -= 1;
|
2019-07-21 13:24:27 +02:00
|
|
|
}
|
2019-07-21 13:24:32 +02:00
|
|
|
|
|
|
|
struct perf_evlist *perf_evlist__new(void)
|
|
|
|
{
|
|
|
|
struct perf_evlist *evlist = zalloc(sizeof(*evlist));
|
|
|
|
|
|
|
|
if (evlist != NULL)
|
|
|
|
perf_evlist__init(evlist);
|
|
|
|
|
|
|
|
return evlist;
|
|
|
|
}
|
2019-07-21 13:24:34 +02:00
|
|
|
|
|
|
|
struct perf_evsel *
|
|
|
|
perf_evlist__next(struct perf_evlist *evlist, struct perf_evsel *prev)
|
|
|
|
{
|
|
|
|
struct perf_evsel *next;
|
|
|
|
|
|
|
|
if (!prev) {
|
|
|
|
next = list_first_entry(&evlist->entries,
|
|
|
|
struct perf_evsel,
|
|
|
|
node);
|
|
|
|
} else {
|
|
|
|
next = list_next_entry(prev, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Empty list is noticed here so don't need checking on entry. */
|
|
|
|
if (&next->node == &evlist->entries)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
2019-07-21 13:24:35 +02:00
|
|
|
|
2019-10-07 14:53:33 +02:00
|
|
|
static void perf_evlist__purge(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *pos, *n;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry_safe(evlist, n, pos) {
|
|
|
|
list_del_init(&pos->node);
|
|
|
|
perf_evsel__delete(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
evlist->nr_entries = 0;
|
|
|
|
}
|
|
|
|
|
2019-10-07 14:53:32 +02:00
|
|
|
void perf_evlist__exit(struct perf_evlist *evlist)
|
|
|
|
{
|
2022-03-28 16:26:44 -07:00
|
|
|
perf_cpu_map__put(evlist->user_requested_cpus);
|
2020-03-18 19:31:01 -07:00
|
|
|
perf_cpu_map__put(evlist->all_cpus);
|
2019-10-07 14:53:32 +02:00
|
|
|
perf_thread_map__put(evlist->threads);
|
2022-03-28 16:26:44 -07:00
|
|
|
evlist->user_requested_cpus = NULL;
|
2020-03-08 18:59:17 +08:00
|
|
|
evlist->all_cpus = NULL;
|
2019-10-07 14:53:32 +02:00
|
|
|
evlist->threads = NULL;
|
|
|
|
fdarray__exit(&evlist->pollfd);
|
|
|
|
}
|
|
|
|
|
2019-07-21 13:24:35 +02:00
|
|
|
void perf_evlist__delete(struct perf_evlist *evlist)
|
|
|
|
{
|
2019-10-07 14:53:21 +02:00
|
|
|
if (evlist == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
perf_evlist__munmap(evlist);
|
2019-10-07 14:53:32 +02:00
|
|
|
perf_evlist__close(evlist);
|
2019-10-07 14:53:33 +02:00
|
|
|
perf_evlist__purge(evlist);
|
2019-10-07 14:53:32 +02:00
|
|
|
perf_evlist__exit(evlist);
|
2019-07-21 13:24:35 +02:00
|
|
|
free(evlist);
|
|
|
|
}
|
2019-07-21 13:24:43 +02:00
|
|
|
|
|
|
|
void perf_evlist__set_maps(struct perf_evlist *evlist,
|
|
|
|
struct perf_cpu_map *cpus,
|
|
|
|
struct perf_thread_map *threads)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Allow for the possibility that one or another of the maps isn't being
|
|
|
|
* changed i.e. don't put it. Note we are assuming the maps that are
|
|
|
|
* being applied are brand new and evlist is taking ownership of the
|
|
|
|
* original reference count of 1. If that is not the case it is up to
|
|
|
|
* the caller to increase the reference count.
|
|
|
|
*/
|
2022-03-28 16:26:44 -07:00
|
|
|
if (cpus != evlist->user_requested_cpus) {
|
|
|
|
perf_cpu_map__put(evlist->user_requested_cpus);
|
|
|
|
evlist->user_requested_cpus = perf_cpu_map__get(cpus);
|
2019-07-21 13:24:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (threads != evlist->threads) {
|
|
|
|
perf_thread_map__put(evlist->threads);
|
|
|
|
evlist->threads = perf_thread_map__get(threads);
|
|
|
|
}
|
|
|
|
|
|
|
|
perf_evlist__propagate_maps(evlist);
|
|
|
|
}
|
2019-07-21 13:24:55 +02:00
|
|
|
|
|
|
|
int perf_evlist__open(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry(evlist, evsel) {
|
|
|
|
err = perf_evsel__open(evsel, evsel->cpus, evsel->threads);
|
|
|
|
if (err < 0)
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
perf_evlist__close(evlist);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void perf_evlist__close(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry_reverse(evlist, evsel)
|
|
|
|
perf_evsel__close(evsel);
|
|
|
|
}
|
2019-07-21 13:24:56 +02:00
|
|
|
|
|
|
|
void perf_evlist__enable(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry(evlist, evsel)
|
|
|
|
perf_evsel__enable(evsel);
|
|
|
|
}
|
|
|
|
|
|
|
|
void perf_evlist__disable(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry(evlist, evsel)
|
|
|
|
perf_evsel__disable(evsel);
|
|
|
|
}
|
2019-09-03 10:54:48 +02:00
|
|
|
|
|
|
|
u64 perf_evlist__read_format(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *first = perf_evlist__first(evlist);
|
|
|
|
|
|
|
|
return first->attr.read_format;
|
|
|
|
}
|
2019-09-03 11:01:04 +02:00
|
|
|
|
|
|
|
#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
|
|
|
|
|
|
|
|
static void perf_evlist__id_hash(struct perf_evlist *evlist,
|
|
|
|
struct perf_evsel *evsel,
|
2024-02-28 23:07:57 -08:00
|
|
|
int cpu_map_idx, int thread, u64 id)
|
2019-09-03 11:01:04 +02:00
|
|
|
{
|
|
|
|
int hash;
|
2024-02-28 23:07:57 -08:00
|
|
|
struct perf_sample_id *sid = SID(evsel, cpu_map_idx, thread);
|
2019-09-03 11:01:04 +02:00
|
|
|
|
|
|
|
sid->id = id;
|
|
|
|
sid->evsel = evsel;
|
|
|
|
hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS);
|
|
|
|
hlist_add_head(&sid->node, &evlist->heads[hash]);
|
|
|
|
}
|
|
|
|
|
2021-02-25 12:51:47 +09:00
|
|
|
void perf_evlist__reset_id_hash(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i)
|
|
|
|
INIT_HLIST_HEAD(&evlist->heads[i]);
|
|
|
|
}
|
|
|
|
|
2019-09-03 11:01:04 +02:00
|
|
|
void perf_evlist__id_add(struct perf_evlist *evlist,
|
|
|
|
struct perf_evsel *evsel,
|
2024-02-28 23:07:57 -08:00
|
|
|
int cpu_map_idx, int thread, u64 id)
|
2019-09-03 11:01:04 +02:00
|
|
|
{
|
2024-02-28 23:07:57 -08:00
|
|
|
if (!SID(evsel, cpu_map_idx, thread))
|
|
|
|
return;
|
|
|
|
|
|
|
|
perf_evlist__id_hash(evlist, evsel, cpu_map_idx, thread, id);
|
2019-09-03 11:01:04 +02:00
|
|
|
evsel->id[evsel->ids++] = id;
|
|
|
|
}
|
2019-09-03 11:19:56 +02:00
|
|
|
|
|
|
|
int perf_evlist__id_add_fd(struct perf_evlist *evlist,
|
|
|
|
struct perf_evsel *evsel,
|
2024-02-28 23:07:57 -08:00
|
|
|
int cpu_map_idx, int thread, int fd)
|
2019-09-03 11:19:56 +02:00
|
|
|
{
|
|
|
|
u64 read_data[4] = { 0, };
|
|
|
|
int id_idx = 1; /* The first entry is the counter value */
|
|
|
|
u64 id;
|
|
|
|
int ret;
|
|
|
|
|
2024-02-28 23:07:57 -08:00
|
|
|
if (!SID(evsel, cpu_map_idx, thread))
|
|
|
|
return -1;
|
|
|
|
|
2019-09-03 11:19:56 +02:00
|
|
|
ret = ioctl(fd, PERF_EVENT_IOC_ID, &id);
|
|
|
|
if (!ret)
|
|
|
|
goto add;
|
|
|
|
|
|
|
|
if (errno != ENOTTY)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Legacy way to get event id.. All hail to old kernels! */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This way does not work with group format read, so bail
|
|
|
|
* out in that case.
|
|
|
|
*/
|
|
|
|
if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
|
|
|
|
read(fd, &read_data, sizeof(read_data)) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
|
|
|
|
++id_idx;
|
|
|
|
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
|
|
|
|
++id_idx;
|
|
|
|
|
|
|
|
id = read_data[id_idx];
|
|
|
|
|
|
|
|
add:
|
2024-02-28 23:07:57 -08:00
|
|
|
perf_evlist__id_add(evlist, evsel, cpu_map_idx, thread, id);
|
2019-09-03 11:19:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2019-08-06 13:21:53 +02:00
|
|
|
|
|
|
|
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
|
|
|
{
|
2022-05-24 10:54:30 +03:00
|
|
|
int nr_cpus = perf_cpu_map__nr(evlist->all_cpus);
|
2019-08-06 13:21:53 +02:00
|
|
|
int nr_threads = perf_thread_map__nr(evlist->threads);
|
|
|
|
int nfds = 0;
|
|
|
|
struct perf_evsel *evsel;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry(evlist, evsel) {
|
|
|
|
if (evsel->system_wide)
|
|
|
|
nfds += nr_cpus;
|
|
|
|
else
|
|
|
|
nfds += nr_cpus * nr_threads;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fdarray__available_entries(&evlist->pollfd) < nfds &&
|
|
|
|
fdarray__grow(&evlist->pollfd, nfds) < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-16 16:00:45 +02:00
|
|
|
|
|
|
|
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
|
2020-07-17 09:59:45 +03:00
|
|
|
void *ptr, short revent, enum fdarray_flags flags)
|
2019-08-16 16:00:45 +02:00
|
|
|
{
|
2020-07-17 09:59:45 +03:00
|
|
|
int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP, flags);
|
2019-08-16 16:00:45 +02:00
|
|
|
|
|
|
|
if (pos >= 0) {
|
|
|
|
evlist->pollfd.priv[pos].ptr = ptr;
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
2019-08-31 22:48:33 +02:00
|
|
|
|
2019-10-07 14:53:34 +02:00
|
|
|
static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd,
|
|
|
|
void *arg __maybe_unused)
|
|
|
|
{
|
|
|
|
struct perf_mmap *map = fda->priv[fd].ptr;
|
|
|
|
|
|
|
|
if (map)
|
|
|
|
perf_mmap__put(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask)
|
|
|
|
{
|
|
|
|
return fdarray__filter(&evlist->pollfd, revents_and_mask,
|
|
|
|
perf_evlist__munmap_filtered, NULL);
|
|
|
|
}
|
|
|
|
|
2019-08-31 22:48:33 +02:00
|
|
|
int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
|
|
|
|
{
|
|
|
|
return fdarray__poll(&evlist->pollfd, timeout);
|
|
|
|
}
|
2019-10-07 14:53:21 +02:00
|
|
|
|
|
|
|
static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, bool overwrite)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct perf_mmap *map;
|
|
|
|
|
|
|
|
map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
|
|
|
|
if (!map)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < evlist->nr_mmaps; i++) {
|
2019-10-17 12:59:09 +02:00
|
|
|
struct perf_mmap *prev = i ? &map[i - 1] : NULL;
|
|
|
|
|
2019-10-07 14:53:21 +02:00
|
|
|
/*
|
|
|
|
* When the perf_mmap() call is made we grab one refcount, plus
|
|
|
|
* one extra to let perf_mmap__consume() get the last
|
|
|
|
* events after all real references (perf_mmap__get()) are
|
|
|
|
* dropped.
|
|
|
|
*
|
|
|
|
* Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and
|
|
|
|
* thus does perf_mmap__get() on it.
|
|
|
|
*/
|
2019-10-17 12:59:09 +02:00
|
|
|
perf_mmap__init(&map[i], prev, overwrite, NULL);
|
2019-10-07 14:53:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2021-01-21 14:54:46 +02:00
|
|
|
static void perf_evsel__set_sid_idx(struct perf_evsel *evsel, int idx, int cpu, int thread)
|
2019-10-07 14:53:21 +02:00
|
|
|
{
|
|
|
|
struct perf_sample_id *sid = SID(evsel, cpu, thread);
|
|
|
|
|
|
|
|
sid->idx = idx;
|
2021-01-21 14:54:46 +02:00
|
|
|
sid->cpu = perf_cpu_map__cpu(evsel->cpus, cpu);
|
|
|
|
sid->tid = perf_thread_map__pid(evsel->threads, thread);
|
2019-10-07 14:53:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct perf_mmap*
|
2019-10-07 14:53:24 +02:00
|
|
|
perf_evlist__mmap_cb_get(struct perf_evlist *evlist, bool overwrite, int idx)
|
2019-10-07 14:53:21 +02:00
|
|
|
{
|
2019-10-17 12:59:10 +02:00
|
|
|
struct perf_mmap *maps;
|
2019-10-07 14:53:21 +02:00
|
|
|
|
2019-10-17 12:59:10 +02:00
|
|
|
maps = overwrite ? evlist->mmap_ovw : evlist->mmap;
|
|
|
|
|
|
|
|
if (!maps) {
|
|
|
|
maps = perf_evlist__alloc_mmap(evlist, overwrite);
|
|
|
|
if (!maps)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (overwrite)
|
|
|
|
evlist->mmap_ovw = maps;
|
|
|
|
else
|
|
|
|
evlist->mmap = maps;
|
2019-10-07 14:53:21 +02:00
|
|
|
}
|
|
|
|
|
2019-10-17 12:59:10 +02:00
|
|
|
return &maps[idx];
|
2019-10-07 14:53:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y))
|
|
|
|
|
2019-10-07 14:53:25 +02:00
|
|
|
static int
|
|
|
|
perf_evlist__mmap_cb_mmap(struct perf_mmap *map, struct perf_mmap_param *mp,
|
2022-01-04 22:13:51 -08:00
|
|
|
int output, struct perf_cpu cpu)
|
2019-10-07 14:53:25 +02:00
|
|
|
{
|
|
|
|
return perf_mmap__mmap(map, mp, output, cpu);
|
|
|
|
}
|
|
|
|
|
2019-10-17 12:59:09 +02:00
|
|
|
static void perf_evlist__set_mmap_first(struct perf_evlist *evlist, struct perf_mmap *map,
|
|
|
|
bool overwrite)
|
|
|
|
{
|
|
|
|
if (overwrite)
|
|
|
|
evlist->mmap_ovw_first = map;
|
|
|
|
else
|
|
|
|
evlist->mmap_first = map;
|
|
|
|
}
|
|
|
|
|
2019-10-07 14:53:21 +02:00
|
|
|
static int
|
2019-10-07 14:53:24 +02:00
|
|
|
mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops,
|
|
|
|
int idx, struct perf_mmap_param *mp, int cpu_idx,
|
2022-05-24 10:54:32 +03:00
|
|
|
int thread, int *_output, int *_output_overwrite, int *nr_mmaps)
|
2019-10-07 14:53:21 +02:00
|
|
|
{
|
2022-05-24 10:54:30 +03:00
|
|
|
struct perf_cpu evlist_cpu = perf_cpu_map__cpu(evlist->all_cpus, cpu_idx);
|
2019-10-07 14:53:21 +02:00
|
|
|
struct perf_evsel *evsel;
|
|
|
|
int revent;
|
|
|
|
|
|
|
|
perf_evlist__for_each_entry(evlist, evsel) {
|
|
|
|
bool overwrite = evsel->attr.write_backward;
|
2022-09-15 15:26:12 +03:00
|
|
|
enum fdarray_flags flgs;
|
2019-10-07 14:53:21 +02:00
|
|
|
struct perf_mmap *map;
|
|
|
|
int *output, fd, cpu;
|
|
|
|
|
|
|
|
if (evsel->system_wide && thread)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cpu = perf_cpu_map__idx(evsel->cpus, evlist_cpu);
|
|
|
|
if (cpu == -1)
|
|
|
|
continue;
|
|
|
|
|
2019-10-07 14:53:24 +02:00
|
|
|
map = ops->get(evlist, overwrite, idx);
|
2019-10-07 14:53:21 +02:00
|
|
|
if (map == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (overwrite) {
|
|
|
|
mp->prot = PROT_READ;
|
|
|
|
output = _output_overwrite;
|
|
|
|
} else {
|
|
|
|
mp->prot = PROT_READ | PROT_WRITE;
|
|
|
|
output = _output;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = FD(evsel, cpu, thread);
|
|
|
|
|
|
|
|
if (*output == -1) {
|
|
|
|
*output = fd;
|
|
|
|
|
2019-10-07 14:53:30 +02:00
|
|
|
/*
|
|
|
|
* The last one will be done at perf_mmap__consume(), so that we
|
|
|
|
* make sure we don't prevent tools from consuming every last event in
|
|
|
|
* the ring buffer.
|
|
|
|
*
|
|
|
|
* I.e. we can get the POLLHUP meaning that the fd doesn't exist
|
|
|
|
* anymore, but the last events for it are still in the ring buffer,
|
|
|
|
* waiting to be consumed.
|
|
|
|
*
|
|
|
|
* Tools can chose to ignore this at their own discretion, but the
|
|
|
|
* evlist layer can't just drop it when filtering events in
|
|
|
|
* perf_evlist__filter_pollfd().
|
|
|
|
*/
|
|
|
|
refcount_set(&map->refcnt, 2);
|
|
|
|
|
2022-05-06 15:25:45 +03:00
|
|
|
if (ops->idx)
|
2022-05-06 15:25:46 +03:00
|
|
|
ops->idx(evlist, evsel, mp, idx);
|
2022-05-06 15:25:45 +03:00
|
|
|
|
2022-09-12 11:34:11 +03:00
|
|
|
/* Debug message used by test scripts */
|
2022-09-05 14:42:09 +03:00
|
|
|
pr_debug("idx %d: mmapping fd %d\n", idx, *output);
|
2019-10-07 14:53:25 +02:00
|
|
|
if (ops->mmap(map, mp, *output, evlist_cpu) < 0)
|
2019-10-07 14:53:21 +02:00
|
|
|
return -1;
|
2019-10-17 12:59:09 +02:00
|
|
|
|
2022-05-24 10:54:32 +03:00
|
|
|
*nr_mmaps += 1;
|
|
|
|
|
2019-10-17 12:59:09 +02:00
|
|
|
if (!idx)
|
|
|
|
perf_evlist__set_mmap_first(evlist, map, overwrite);
|
2019-10-07 14:53:21 +02:00
|
|
|
} else {
|
2022-09-12 11:34:11 +03:00
|
|
|
/* Debug message used by test scripts */
|
2022-09-05 14:42:09 +03:00
|
|
|
pr_debug("idx %d: set output fd %d -> %d\n", idx, fd, *output);
|
2019-10-07 14:53:21 +02:00
|
|
|
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
perf_mmap__get(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
revent = !overwrite ? POLLIN : 0;
|
|
|
|
|
2022-09-15 15:26:12 +03:00
|
|
|
flgs = evsel->system_wide ? fdarray_flag__nonfilterable : fdarray_flag__default;
|
|
|
|
if (perf_evlist__add_pollfd(evlist, fd, map, revent, flgs) < 0) {
|
2019-10-07 14:53:21 +02:00
|
|
|
perf_mmap__put(map);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (evsel->attr.read_format & PERF_FORMAT_ID) {
|
|
|
|
if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
|
|
|
|
fd) < 0)
|
|
|
|
return -1;
|
2021-01-21 14:54:46 +02:00
|
|
|
perf_evsel__set_sid_idx(evsel, idx, cpu, thread);
|
2019-10-07 14:53:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-09-05 14:42:09 +03:00
|
|
|
static int
|
|
|
|
mmap_per_thread(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops,
|
|
|
|
struct perf_mmap_param *mp)
|
|
|
|
{
|
|
|
|
int nr_threads = perf_thread_map__nr(evlist->threads);
|
|
|
|
int nr_cpus = perf_cpu_map__nr(evlist->all_cpus);
|
|
|
|
int cpu, thread, idx = 0;
|
|
|
|
int nr_mmaps = 0;
|
|
|
|
|
|
|
|
pr_debug("%s: nr cpu values (may include -1) %d nr threads %d\n",
|
|
|
|
__func__, nr_cpus, nr_threads);
|
|
|
|
|
|
|
|
/* per-thread mmaps */
|
|
|
|
for (thread = 0; thread < nr_threads; thread++, idx++) {
|
|
|
|
int output = -1;
|
|
|
|
int output_overwrite = -1;
|
|
|
|
|
|
|
|
if (mmap_per_evsel(evlist, ops, idx, mp, 0, thread, &output,
|
|
|
|
&output_overwrite, &nr_mmaps))
|
|
|
|
goto out_unmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* system-wide mmaps i.e. per-cpu */
|
|
|
|
for (cpu = 1; cpu < nr_cpus; cpu++, idx++) {
|
|
|
|
int output = -1;
|
|
|
|
int output_overwrite = -1;
|
|
|
|
|
|
|
|
if (mmap_per_evsel(evlist, ops, idx, mp, cpu, 0, &output,
|
|
|
|
&output_overwrite, &nr_mmaps))
|
|
|
|
goto out_unmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nr_mmaps != evlist->nr_mmaps)
|
|
|
|
pr_err("Miscounted nr_mmaps %d vs %d\n", nr_mmaps, evlist->nr_mmaps);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_unmap:
|
|
|
|
perf_evlist__munmap(evlist);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-10-07 14:53:21 +02:00
|
|
|
static int
|
2019-10-07 14:53:23 +02:00
|
|
|
mmap_per_cpu(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops,
|
|
|
|
struct perf_mmap_param *mp)
|
2019-10-07 14:53:21 +02:00
|
|
|
{
|
|
|
|
int nr_threads = perf_thread_map__nr(evlist->threads);
|
2022-05-24 10:54:30 +03:00
|
|
|
int nr_cpus = perf_cpu_map__nr(evlist->all_cpus);
|
2022-05-24 10:54:32 +03:00
|
|
|
int nr_mmaps = 0;
|
2019-10-07 14:53:21 +02:00
|
|
|
int cpu, thread;
|
|
|
|
|
2022-09-05 14:42:09 +03:00
|
|
|
pr_debug("%s: nr cpu values %d nr threads %d\n", __func__, nr_cpus, nr_threads);
|
|
|
|
|
2019-10-07 14:53:21 +02:00
|
|
|
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
|
|
|
int output = -1;
|
|
|
|
int output_overwrite = -1;
|
|
|
|
|
|
|
|
for (thread = 0; thread < nr_threads; thread++) {
|
2019-10-07 14:53:24 +02:00
|
|
|
if (mmap_per_evsel(evlist, ops, cpu, mp, cpu,
|
2022-05-24 10:54:32 +03:00
|
|
|
thread, &output, &output_overwrite, &nr_mmaps))
|
2019-10-07 14:53:21 +02:00
|
|
|
goto out_unmap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-24 10:54:32 +03:00
|
|
|
if (nr_mmaps != evlist->nr_mmaps)
|
|
|
|
pr_err("Miscounted nr_mmaps %d vs %d\n", nr_mmaps, evlist->nr_mmaps);
|
|
|
|
|
2019-10-07 14:53:21 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_unmap:
|
|
|
|
perf_evlist__munmap(evlist);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-10-17 12:59:10 +02:00
|
|
|
static int perf_evlist__nr_mmaps(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
int nr_mmaps;
|
|
|
|
|
2022-05-24 10:54:31 +03:00
|
|
|
/* One for each CPU */
|
2022-05-24 10:54:30 +03:00
|
|
|
nr_mmaps = perf_cpu_map__nr(evlist->all_cpus);
|
2023-11-28 22:02:00 -08:00
|
|
|
if (perf_cpu_map__has_any_cpu_or_is_empty(evlist->all_cpus)) {
|
2022-05-24 10:54:31 +03:00
|
|
|
/* Plus one for each thread */
|
|
|
|
nr_mmaps += perf_thread_map__nr(evlist->threads);
|
|
|
|
/* Minus the per-thread CPU (-1) */
|
|
|
|
nr_mmaps -= 1;
|
|
|
|
}
|
2019-10-17 12:59:10 +02:00
|
|
|
|
|
|
|
return nr_mmaps;
|
|
|
|
}
|
|
|
|
|
2019-10-07 14:53:22 +02:00
|
|
|
int perf_evlist__mmap_ops(struct perf_evlist *evlist,
|
|
|
|
struct perf_evlist_mmap_ops *ops,
|
|
|
|
struct perf_mmap_param *mp)
|
2019-10-07 14:53:21 +02:00
|
|
|
{
|
2022-09-05 14:42:09 +03:00
|
|
|
const struct perf_cpu_map *cpus = evlist->all_cpus;
|
2019-10-07 14:53:21 +02:00
|
|
|
struct perf_evsel *evsel;
|
2019-10-07 14:53:22 +02:00
|
|
|
|
2019-10-07 14:53:25 +02:00
|
|
|
if (!ops || !ops->get || !ops->mmap)
|
2019-10-07 14:53:22 +02:00
|
|
|
return -EINVAL;
|
2019-10-07 14:53:21 +02:00
|
|
|
|
2019-10-17 12:59:11 +02:00
|
|
|
mp->mask = evlist->mmap_len - page_size - 1;
|
|
|
|
|
2019-10-17 12:59:10 +02:00
|
|
|
evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist);
|
2019-10-07 14:53:21 +02:00
|
|
|
|
|
|
|
perf_evlist__for_each_entry(evlist, evsel) {
|
|
|
|
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
|
|
|
|
evsel->sample_id == NULL &&
|
2022-04-13 14:42:32 +03:00
|
|
|
perf_evsel__alloc_id(evsel, evsel->fd->max_x, evsel->fd->max_y) < 0)
|
2019-10-07 14:53:21 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2019-10-07 14:53:31 +02:00
|
|
|
if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-11-28 22:02:00 -08:00
|
|
|
if (perf_cpu_map__has_any_cpu_or_is_empty(cpus))
|
2022-09-05 14:42:09 +03:00
|
|
|
return mmap_per_thread(evlist, ops, mp);
|
|
|
|
|
2019-10-07 14:53:23 +02:00
|
|
|
return mmap_per_cpu(evlist, ops, mp);
|
2019-10-07 14:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int perf_evlist__mmap(struct perf_evlist *evlist, int pages)
|
|
|
|
{
|
|
|
|
struct perf_mmap_param mp;
|
2019-10-07 14:53:24 +02:00
|
|
|
struct perf_evlist_mmap_ops ops = {
|
2019-10-07 14:53:25 +02:00
|
|
|
.get = perf_evlist__mmap_cb_get,
|
|
|
|
.mmap = perf_evlist__mmap_cb_mmap,
|
2019-10-07 14:53:24 +02:00
|
|
|
};
|
2019-10-07 14:53:22 +02:00
|
|
|
|
2019-10-07 14:53:21 +02:00
|
|
|
evlist->mmap_len = (pages + 1) * page_size;
|
|
|
|
|
2019-10-07 14:53:22 +02:00
|
|
|
return perf_evlist__mmap_ops(evlist, &ops, &mp);
|
2019-10-07 14:53:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void perf_evlist__munmap(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (evlist->mmap) {
|
|
|
|
for (i = 0; i < evlist->nr_mmaps; i++)
|
|
|
|
perf_mmap__munmap(&evlist->mmap[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (evlist->mmap_ovw) {
|
|
|
|
for (i = 0; i < evlist->nr_mmaps; i++)
|
|
|
|
perf_mmap__munmap(&evlist->mmap_ovw[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
zfree(&evlist->mmap);
|
|
|
|
zfree(&evlist->mmap_ovw);
|
|
|
|
}
|
2019-10-17 12:59:09 +02:00
|
|
|
|
|
|
|
struct perf_mmap*
|
|
|
|
perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map,
|
|
|
|
bool overwrite)
|
|
|
|
{
|
|
|
|
if (map)
|
|
|
|
return map->next;
|
|
|
|
|
|
|
|
return overwrite ? evlist->mmap_ovw_first : evlist->mmap_first;
|
|
|
|
}
|
2021-07-06 17:17:02 +02:00
|
|
|
|
2021-11-30 09:49:44 -08:00
|
|
|
void __perf_evlist__set_leader(struct list_head *list, struct perf_evsel *leader)
|
2021-07-06 17:17:02 +02:00
|
|
|
{
|
2023-03-11 18:15:33 -08:00
|
|
|
struct perf_evsel *evsel;
|
|
|
|
int n = 0;
|
2021-07-06 17:17:02 +02:00
|
|
|
|
2023-03-11 18:15:33 -08:00
|
|
|
__perf_evlist__for_each_entry(list, evsel) {
|
2021-07-06 17:17:02 +02:00
|
|
|
evsel->leader = leader;
|
2023-03-11 18:15:33 -08:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
leader->nr_members = n;
|
2021-07-06 17:17:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void perf_evlist__set_leader(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
if (evlist->nr_entries) {
|
2021-11-30 09:49:44 -08:00
|
|
|
struct perf_evsel *first = list_entry(evlist->entries.next,
|
|
|
|
struct perf_evsel, node);
|
|
|
|
|
|
|
|
__perf_evlist__set_leader(&evlist->entries, first);
|
2021-07-06 17:17:02 +02:00
|
|
|
}
|
|
|
|
}
|
2023-03-11 18:15:42 -08:00
|
|
|
|
|
|
|
int perf_evlist__nr_groups(struct perf_evlist *evlist)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel;
|
|
|
|
int nr_groups = 0;
|
|
|
|
|
|
|
|
perf_evlist__for_each_evsel(evlist, evsel) {
|
|
|
|
/*
|
|
|
|
* evsels by default have a nr_members of 1, and they are their
|
|
|
|
* own leader. If the nr_members is >1 then this is an
|
|
|
|
* indication of a group.
|
|
|
|
*/
|
|
|
|
if (evsel->leader == evsel && evsel->nr_members > 1)
|
|
|
|
nr_groups++;
|
|
|
|
}
|
|
|
|
return nr_groups;
|
|
|
|
}
|
2023-09-04 02:33:35 +00:00
|
|
|
|
|
|
|
void perf_evlist__go_system_wide(struct perf_evlist *evlist, struct perf_evsel *evsel)
|
|
|
|
{
|
|
|
|
if (!evsel->system_wide) {
|
|
|
|
evsel->system_wide = true;
|
|
|
|
if (evlist->needs_map_propagation)
|
|
|
|
__perf_evlist__propagate_maps(evlist, evsel);
|
|
|
|
}
|
|
|
|
}
|