mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
Tool PMU events (duration_time, user_time, system_time) currently
count from when the event is opened to when it is read. This causes
issues with features like the delay option (-D) or control fd, where
events are opened but should not start counting immediately.
Make these events behave more like regular counters by implementing
proper enable and disable support. Add accumulated_time to struct
evsel to track time while enabled, and implement enable/disable CPU
callbacks to start/stop counting.
Also generalize userspace PMU mixed group handling. Userspace synthetic
PMUs (type > PERF_PMU_TYPE_PE_END) do not have kernel implementations and
cannot be grouped in the kernel (opened with group_fd = -1), and are
skipped by kernel enable/disable calls. Iterate over group members in
userspace and manually enable/disable any members if the leader or the
member is a non-perf-event open PMU, and synchronize their disabled flags.
Closes: https://lore.kernel.org/linux-perf-users/20260517093650.2540920-1-nigro.fra@gmail.com/
Fixes: b71f46a6a7 ("perf stat: Remove hard coded shadow metrics")
Reported-by: Francesco Nigro <nigro.fra@gmail.com>
Assisted-by: Antigravity:gemini-3-flash
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
724 lines
17 KiB
C
724 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
#include "cgroup.h"
|
|
#include "counts.h"
|
|
#include "cputopo.h"
|
|
#include "evsel.h"
|
|
#include "pmu.h"
|
|
#include "print-events.h"
|
|
#include "smt.h"
|
|
#include "stat.h"
|
|
#include "time-utils.h"
|
|
#include "tool_pmu.h"
|
|
#include "tsc.h"
|
|
#include <api/fs/fs.h>
|
|
#include <api/io.h>
|
|
#include <internal/threadmap.h>
|
|
#include <perf/threadmap.h>
|
|
#include <fcntl.h>
|
|
#include <strings.h>
|
|
|
|
#define INVALID_START_TIME ~0ULL
|
|
|
|
static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = {
|
|
NULL,
|
|
"duration_time",
|
|
"user_time",
|
|
"system_time",
|
|
"has_pmem",
|
|
"num_cores",
|
|
"num_cpus",
|
|
"num_cpus_online",
|
|
"num_dies",
|
|
"num_packages",
|
|
"slots",
|
|
"smt_on",
|
|
"system_tsc_freq",
|
|
"core_wide",
|
|
"target_cpu",
|
|
};
|
|
|
|
bool tool_pmu__skip_event(const char *name __maybe_unused)
|
|
{
|
|
#if !defined(__aarch64__)
|
|
/* The slots event should only appear on arm64. */
|
|
if (strcasecmp(name, "slots") == 0)
|
|
return true;
|
|
#endif
|
|
#if !defined(__i386__) && !defined(__x86_64__)
|
|
/* The system_tsc_freq event should only appear on x86. */
|
|
if (strcasecmp(name, "system_tsc_freq") == 0)
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
int tool_pmu__num_skip_events(void)
|
|
{
|
|
int num = 0;
|
|
|
|
#if !defined(__aarch64__)
|
|
num++;
|
|
#endif
|
|
#if !defined(__i386__) && !defined(__x86_64__)
|
|
num++;
|
|
#endif
|
|
return num;
|
|
}
|
|
|
|
const char *tool_pmu__event_to_str(enum tool_pmu_event ev)
|
|
{
|
|
if ((ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX) &&
|
|
!tool_pmu__skip_event(tool_pmu__event_names[ev]))
|
|
return tool_pmu__event_names[ev];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
enum tool_pmu_event tool_pmu__str_to_event(const char *str)
|
|
{
|
|
int i;
|
|
|
|
if (tool_pmu__skip_event(str))
|
|
return TOOL_PMU__EVENT_NONE;
|
|
|
|
tool_pmu__for_each_event(i) {
|
|
if (!strcasecmp(str, tool_pmu__event_names[i]))
|
|
return i;
|
|
}
|
|
return TOOL_PMU__EVENT_NONE;
|
|
}
|
|
|
|
bool perf_pmu__is_tool(const struct perf_pmu *pmu)
|
|
{
|
|
return pmu && pmu->type == PERF_PMU_TYPE_TOOL;
|
|
}
|
|
|
|
bool evsel__is_tool(const struct evsel *evsel)
|
|
{
|
|
return perf_pmu__is_tool(evsel->pmu);
|
|
}
|
|
|
|
enum tool_pmu_event evsel__tool_event(const struct evsel *evsel)
|
|
{
|
|
if (!evsel__is_tool(evsel))
|
|
return TOOL_PMU__EVENT_NONE;
|
|
|
|
return (enum tool_pmu_event)evsel->core.attr.config;
|
|
}
|
|
|
|
const char *evsel__tool_pmu_event_name(const struct evsel *evsel)
|
|
{
|
|
return tool_pmu__event_to_str(evsel->core.attr.config);
|
|
}
|
|
|
|
static bool read_until_char(struct io *io, char e)
|
|
{
|
|
int c;
|
|
|
|
do {
|
|
c = io__get_char(io);
|
|
if (c == -1)
|
|
return false;
|
|
} while (c != e);
|
|
return true;
|
|
}
|
|
|
|
static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val)
|
|
{
|
|
char buf[256];
|
|
struct io io;
|
|
int i;
|
|
|
|
io__init(&io, fd, buf, sizeof(buf));
|
|
|
|
/* Skip lines to relevant CPU. */
|
|
for (i = -1; i < cpu.cpu; i++) {
|
|
if (!read_until_char(&io, '\n'))
|
|
return -EINVAL;
|
|
}
|
|
/* Skip to "cpu". */
|
|
if (io__get_char(&io) != 'c') return -EINVAL;
|
|
if (io__get_char(&io) != 'p') return -EINVAL;
|
|
if (io__get_char(&io) != 'u') return -EINVAL;
|
|
|
|
/* Skip N of cpuN. */
|
|
if (!read_until_char(&io, ' '))
|
|
return -EINVAL;
|
|
|
|
i = 1;
|
|
while (true) {
|
|
if (io__get_dec(&io, val) != ' ')
|
|
break;
|
|
if (field == i)
|
|
return 0;
|
|
i++;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int read_pid_stat_field(int fd, int field, __u64 *val)
|
|
{
|
|
char buf[256];
|
|
struct io io;
|
|
int c, i;
|
|
|
|
io__init(&io, fd, buf, sizeof(buf));
|
|
if (io__get_dec(&io, val) != ' ')
|
|
return -EINVAL;
|
|
if (field == 1)
|
|
return 0;
|
|
|
|
/* Skip comm. */
|
|
if (io__get_char(&io) != '(' || !read_until_char(&io, ')'))
|
|
return -EINVAL;
|
|
if (field == 2)
|
|
return -EINVAL; /* String can't be returned. */
|
|
|
|
/* Skip state */
|
|
if (io__get_char(&io) != ' ' || io__get_char(&io) == -1)
|
|
return -EINVAL;
|
|
if (field == 3)
|
|
return -EINVAL; /* String can't be returned. */
|
|
|
|
/* Loop over numeric fields*/
|
|
if (io__get_char(&io) != ' ')
|
|
return -EINVAL;
|
|
|
|
i = 4;
|
|
while (true) {
|
|
c = io__get_dec(&io, val);
|
|
if (c == -1)
|
|
return -EINVAL;
|
|
if (c == -2) {
|
|
/* Assume a -ve was read */
|
|
c = io__get_dec(&io, val);
|
|
*val *= -1;
|
|
}
|
|
if (c != ' ')
|
|
return -EINVAL;
|
|
if (field == i)
|
|
return 0;
|
|
i++;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
int evsel__tool_pmu_prepare_open(struct evsel *evsel,
|
|
struct perf_cpu_map *cpus,
|
|
int nthreads)
|
|
{
|
|
enum tool_pmu_event ev = evsel__tool_event(evsel);
|
|
|
|
if (ev == TOOL_PMU__EVENT_SYSTEM_TIME || ev == TOOL_PMU__EVENT_USER_TIME) {
|
|
if (!evsel->process_time.start_times) {
|
|
evsel->process_time.start_times =
|
|
xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64));
|
|
if (!evsel->process_time.start_times)
|
|
return -ENOMEM;
|
|
}
|
|
if (!evsel->process_time.accumulated_times) {
|
|
evsel->process_time.accumulated_times =
|
|
xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64));
|
|
if (!evsel->process_time.accumulated_times)
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
|
|
|
|
static int tool_pmu__read_stat(struct evsel *evsel, int cpu_map_idx, int thread, __u64 *val)
|
|
{
|
|
enum tool_pmu_event ev = evsel__tool_event(evsel);
|
|
bool system = ev == TOOL_PMU__EVENT_SYSTEM_TIME;
|
|
int fd = FD(evsel, cpu_map_idx, thread);
|
|
int err = 0;
|
|
|
|
if (fd < 0) {
|
|
*val = 0;
|
|
return 0;
|
|
}
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
if (evsel->pid_stat) {
|
|
if (cpu_map_idx == 0)
|
|
err = read_pid_stat_field(fd, system ? 15 : 14, val);
|
|
else
|
|
*val = 0;
|
|
} else {
|
|
if (thread == 0) {
|
|
struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus, cpu_map_idx);
|
|
|
|
err = read_stat_field(fd, cpu, system ? 3 : 1, val);
|
|
} else {
|
|
*val = 0;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int evsel__tool_pmu_open(struct evsel *evsel,
|
|
struct perf_thread_map *threads,
|
|
int start_cpu_map_idx, int end_cpu_map_idx)
|
|
{
|
|
enum tool_pmu_event ev = evsel__tool_event(evsel);
|
|
int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno;
|
|
|
|
if (ev == TOOL_PMU__EVENT_NUM_CPUS)
|
|
return 0;
|
|
|
|
if (ev == TOOL_PMU__EVENT_DURATION_TIME) {
|
|
if (evsel->core.attr.sample_period) /* no sampling */
|
|
return -EINVAL;
|
|
evsel->duration_time.accumulated_time = 0;
|
|
if (evsel->core.attr.disabled) {
|
|
evsel->disabled = true;
|
|
evsel->duration_time.start_time = INVALID_START_TIME;
|
|
} else {
|
|
evsel->disabled = false;
|
|
evsel->duration_time.start_time = rdclock();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (evsel->cgrp)
|
|
pid = evsel->cgrp->fd;
|
|
|
|
nthreads = perf_thread_map__nr(threads);
|
|
for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
|
|
for (thread = 0; thread < nthreads; thread++) {
|
|
if (!evsel->cgrp && !evsel->core.system_wide)
|
|
pid = perf_thread_map__pid(threads, thread);
|
|
|
|
if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) {
|
|
__u64 *start_time = NULL;
|
|
__u64 *accumulated_time = NULL;
|
|
int fd;
|
|
|
|
if (evsel->core.attr.sample_period) {
|
|
/* no sampling */
|
|
err = -EINVAL;
|
|
goto out_close;
|
|
}
|
|
if (pid > -1) {
|
|
char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
|
|
fd = open(buf, O_RDONLY);
|
|
evsel->pid_stat = true;
|
|
} else {
|
|
fd = open("/proc/stat", O_RDONLY);
|
|
}
|
|
FD(evsel, idx, thread) = fd;
|
|
if (fd < 0) {
|
|
err = -errno;
|
|
goto out_close;
|
|
}
|
|
start_time = xyarray__entry(evsel->process_time.start_times, idx,
|
|
thread);
|
|
accumulated_time = xyarray__entry(
|
|
evsel->process_time.accumulated_times, idx, thread);
|
|
*accumulated_time = 0;
|
|
|
|
if (evsel->core.attr.disabled) {
|
|
evsel->disabled = true;
|
|
*start_time = INVALID_START_TIME;
|
|
} else {
|
|
evsel->disabled = false;
|
|
err = tool_pmu__read_stat(evsel, idx, thread, start_time);
|
|
if (err) {
|
|
close(fd);
|
|
FD(evsel, idx, thread) = -1;
|
|
goto out_close;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
out_close:
|
|
if (err)
|
|
threads->err_thread = thread;
|
|
|
|
old_errno = errno;
|
|
do {
|
|
while (--thread >= 0) {
|
|
if (FD(evsel, idx, thread) >= 0)
|
|
close(FD(evsel, idx, thread));
|
|
FD(evsel, idx, thread) = -1;
|
|
}
|
|
thread = nthreads;
|
|
} while (--idx >= 0);
|
|
errno = old_errno;
|
|
return err;
|
|
}
|
|
|
|
#if !defined(__i386__) && !defined(__x86_64__)
|
|
u64 arch_get_tsc_freq(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(__aarch64__)
|
|
u64 tool_pmu__cpu_slots_per_cycle(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static bool has_pmem(void)
|
|
{
|
|
static bool has_pmem, cached;
|
|
const char *sysfs = sysfs__mountpoint();
|
|
char path[PATH_MAX];
|
|
|
|
if (!cached) {
|
|
snprintf(path, sizeof(path), "%s/firmware/acpi/tables/NFIT", sysfs);
|
|
has_pmem = access(path, F_OK) == 0;
|
|
cached = true;
|
|
}
|
|
return has_pmem;
|
|
}
|
|
|
|
bool tool_pmu__read_event(enum tool_pmu_event ev,
|
|
struct evsel *evsel,
|
|
bool system_wide,
|
|
const char *user_requested_cpu_list,
|
|
u64 *result)
|
|
{
|
|
const struct cpu_topology *topology;
|
|
|
|
switch (ev) {
|
|
case TOOL_PMU__EVENT_HAS_PMEM:
|
|
*result = has_pmem() ? 1 : 0;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_NUM_CORES:
|
|
topology = online_topology();
|
|
*result = topology->core_cpus_lists;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_NUM_CPUS:
|
|
if (!evsel || perf_cpu_map__is_empty(evsel->core.cpus)) {
|
|
/* No evsel to be specific to. */
|
|
*result = cpu__max_present_cpu().cpu;
|
|
} else if (!perf_cpu_map__has_any_cpu(evsel->core.cpus)) {
|
|
/* Evsel just has specific CPUs. */
|
|
*result = perf_cpu_map__nr(evsel->core.cpus);
|
|
} else {
|
|
/*
|
|
* "Any CPU" event that can be scheduled on any CPU in
|
|
* the PMU's cpumask. The PMU cpumask should be saved in
|
|
* pmu_cpus. If not present fall back to max.
|
|
*/
|
|
if (!perf_cpu_map__is_empty(evsel->core.pmu_cpus))
|
|
*result = perf_cpu_map__nr(evsel->core.pmu_cpus);
|
|
else
|
|
*result = cpu__max_present_cpu().cpu;
|
|
}
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_NUM_CPUS_ONLINE: {
|
|
struct perf_cpu_map *online = cpu_map__online();
|
|
|
|
if (!online)
|
|
return false;
|
|
|
|
if (!evsel || perf_cpu_map__is_empty(evsel->core.cpus)) {
|
|
/* No evsel to be specific to. */
|
|
*result = perf_cpu_map__nr(online);
|
|
} else if (!perf_cpu_map__has_any_cpu(evsel->core.cpus)) {
|
|
/* Evsel just has specific CPUs. */
|
|
struct perf_cpu_map *tmp =
|
|
perf_cpu_map__intersect(online, evsel->core.cpus);
|
|
|
|
*result = perf_cpu_map__nr(tmp);
|
|
perf_cpu_map__put(tmp);
|
|
} else {
|
|
/*
|
|
* "Any CPU" event that can be scheduled on any CPU in
|
|
* the PMU's cpumask. The PMU cpumask should be saved in
|
|
* pmu_cpus, if not present then just the online cpu
|
|
* mask.
|
|
*/
|
|
if (!perf_cpu_map__is_empty(evsel->core.pmu_cpus)) {
|
|
struct perf_cpu_map *tmp =
|
|
perf_cpu_map__intersect(online, evsel->core.pmu_cpus);
|
|
|
|
*result = perf_cpu_map__nr(tmp);
|
|
perf_cpu_map__put(tmp);
|
|
} else {
|
|
*result = perf_cpu_map__nr(online);
|
|
}
|
|
}
|
|
perf_cpu_map__put(online);
|
|
return true;
|
|
}
|
|
case TOOL_PMU__EVENT_NUM_DIES:
|
|
topology = online_topology();
|
|
*result = topology->die_cpus_lists;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_NUM_PACKAGES:
|
|
topology = online_topology();
|
|
*result = topology->package_cpus_lists;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_SLOTS:
|
|
*result = tool_pmu__cpu_slots_per_cycle();
|
|
return *result ? true : false;
|
|
|
|
case TOOL_PMU__EVENT_SMT_ON:
|
|
*result = smt_on() ? 1 : 0;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_SYSTEM_TSC_FREQ:
|
|
*result = arch_get_tsc_freq();
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_CORE_WIDE:
|
|
*result = core_wide(system_wide, user_requested_cpu_list) ? 1 : 0;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_TARGET_CPU:
|
|
*result = system_wide || (user_requested_cpu_list != NULL) ? 1 : 0;
|
|
return true;
|
|
|
|
case TOOL_PMU__EVENT_NONE:
|
|
case TOOL_PMU__EVENT_DURATION_TIME:
|
|
case TOOL_PMU__EVENT_USER_TIME:
|
|
case TOOL_PMU__EVENT_SYSTEM_TIME:
|
|
case TOOL_PMU__EVENT_MAX:
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void perf_counts__update(struct perf_counts_values *count,
|
|
const struct perf_counts_values *old_count,
|
|
bool raw, u64 val)
|
|
{
|
|
/*
|
|
* The values of enabled and running must make a ratio of 100%. The
|
|
* exact values don't matter as long as they are non-zero to avoid
|
|
* issues with evsel__count_has_error.
|
|
*/
|
|
if (old_count) {
|
|
count->val = raw ? val : old_count->val + val;
|
|
count->run = old_count->run + 1;
|
|
count->ena = old_count->ena + 1;
|
|
count->lost = old_count->lost;
|
|
} else {
|
|
count->val = val;
|
|
count->run++;
|
|
count->ena++;
|
|
count->lost = 0;
|
|
}
|
|
}
|
|
int evsel__tool_pmu_enable_cpu(struct evsel *evsel, int cpu_map_idx)
|
|
{
|
|
enum tool_pmu_event ev = evsel__tool_event(evsel);
|
|
int thread, nthreads;
|
|
|
|
if (!evsel->disabled)
|
|
return 0;
|
|
|
|
if (ev == TOOL_PMU__EVENT_DURATION_TIME) {
|
|
if (cpu_map_idx == 0)
|
|
evsel->duration_time.start_time = rdclock();
|
|
return 0;
|
|
}
|
|
|
|
if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) {
|
|
nthreads = xyarray__max_y(evsel->process_time.start_times);
|
|
for (thread = 0; thread < nthreads; thread++) {
|
|
__u64 *start_time = xyarray__entry(evsel->process_time.start_times,
|
|
cpu_map_idx, thread);
|
|
__u64 val;
|
|
int err;
|
|
|
|
err = tool_pmu__read_stat(evsel, cpu_map_idx, thread, &val);
|
|
if (!err)
|
|
*start_time = val;
|
|
else
|
|
*start_time = INVALID_START_TIME;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int evsel__tool_pmu_enable(struct evsel *evsel)
|
|
{
|
|
unsigned int idx;
|
|
int err = 0;
|
|
|
|
if (!evsel->disabled)
|
|
return 0;
|
|
|
|
for (idx = 0; idx < perf_cpu_map__nr(evsel->core.cpus); idx++) {
|
|
err = evsel__tool_pmu_enable_cpu(evsel, idx);
|
|
if (err)
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int evsel__tool_pmu_disable_cpu(struct evsel *evsel, int cpu_map_idx)
|
|
{
|
|
enum tool_pmu_event ev = evsel__tool_event(evsel);
|
|
int thread, nthreads;
|
|
|
|
if (evsel->disabled)
|
|
return 0;
|
|
|
|
if (ev == TOOL_PMU__EVENT_DURATION_TIME) {
|
|
if (cpu_map_idx == 0) {
|
|
__u64 delta = rdclock() - evsel->duration_time.start_time;
|
|
|
|
evsel->duration_time.accumulated_time += delta;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) {
|
|
nthreads = xyarray__max_y(evsel->process_time.start_times);
|
|
for (thread = 0; thread < nthreads; thread++) {
|
|
__u64 *start_time = xyarray__entry(evsel->process_time.start_times,
|
|
cpu_map_idx, thread);
|
|
__u64 *accumulated_time = xyarray__entry(
|
|
evsel->process_time.accumulated_times, cpu_map_idx, thread);
|
|
__u64 val;
|
|
int err;
|
|
|
|
err = tool_pmu__read_stat(evsel, cpu_map_idx, thread, &val);
|
|
if (!err) {
|
|
if (*start_time != INVALID_START_TIME && val >= *start_time)
|
|
*accumulated_time += (val - *start_time);
|
|
}
|
|
*start_time = INVALID_START_TIME;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int evsel__tool_pmu_disable(struct evsel *evsel)
|
|
{
|
|
unsigned int idx;
|
|
int err = 0;
|
|
|
|
if (evsel->disabled)
|
|
return 0;
|
|
|
|
for (idx = 0; idx < perf_cpu_map__nr(evsel->core.cpus); idx++) {
|
|
err = evsel__tool_pmu_disable_cpu(evsel, idx);
|
|
if (err)
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
|
|
{
|
|
__u64 delta_start = 0;
|
|
int err = 0;
|
|
struct perf_counts_values *count, *old_count = NULL;
|
|
bool adjust = false;
|
|
enum tool_pmu_event ev = evsel__tool_event(evsel);
|
|
|
|
count = perf_counts(evsel->counts, cpu_map_idx, thread);
|
|
if (evsel->prev_raw_counts)
|
|
old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);
|
|
|
|
switch (ev) {
|
|
case TOOL_PMU__EVENT_HAS_PMEM:
|
|
case TOOL_PMU__EVENT_NUM_CORES:
|
|
case TOOL_PMU__EVENT_NUM_CPUS:
|
|
case TOOL_PMU__EVENT_NUM_CPUS_ONLINE:
|
|
case TOOL_PMU__EVENT_NUM_DIES:
|
|
case TOOL_PMU__EVENT_NUM_PACKAGES:
|
|
case TOOL_PMU__EVENT_SLOTS:
|
|
case TOOL_PMU__EVENT_SMT_ON:
|
|
case TOOL_PMU__EVENT_CORE_WIDE:
|
|
case TOOL_PMU__EVENT_TARGET_CPU:
|
|
case TOOL_PMU__EVENT_SYSTEM_TSC_FREQ: {
|
|
u64 val = 0;
|
|
|
|
if (cpu_map_idx == 0 && thread == 0) {
|
|
if (!tool_pmu__read_event(ev, evsel,
|
|
stat_config.system_wide,
|
|
stat_config.user_requested_cpu_list,
|
|
&val)) {
|
|
count->lost++;
|
|
val = 0;
|
|
}
|
|
}
|
|
perf_counts__update(count, old_count, /*raw=*/false, val);
|
|
return 0;
|
|
}
|
|
case TOOL_PMU__EVENT_DURATION_TIME:
|
|
if (cpu_map_idx == 0 && thread == 0) {
|
|
delta_start = evsel->duration_time.accumulated_time;
|
|
if (!evsel->disabled &&
|
|
evsel->duration_time.start_time != INVALID_START_TIME)
|
|
delta_start += (rdclock() - evsel->duration_time.start_time);
|
|
} else {
|
|
delta_start = 0;
|
|
}
|
|
break;
|
|
case TOOL_PMU__EVENT_USER_TIME:
|
|
case TOOL_PMU__EVENT_SYSTEM_TIME: {
|
|
__u64 accumulated = *(__u64 *)xyarray__entry(evsel->process_time.accumulated_times,
|
|
cpu_map_idx, thread);
|
|
|
|
if (evsel->disabled) {
|
|
delta_start = accumulated;
|
|
} else {
|
|
__u64 *start_time = xyarray__entry(evsel->process_time.start_times,
|
|
cpu_map_idx, thread);
|
|
__u64 cur_time;
|
|
|
|
err = tool_pmu__read_stat(evsel, cpu_map_idx, thread, &cur_time);
|
|
if (!err) {
|
|
if (*start_time != INVALID_START_TIME && cur_time >= *start_time)
|
|
delta_start = accumulated + (cur_time - *start_time);
|
|
else
|
|
delta_start = accumulated;
|
|
}
|
|
}
|
|
adjust = true;
|
|
break;
|
|
}
|
|
case TOOL_PMU__EVENT_NONE:
|
|
case TOOL_PMU__EVENT_MAX:
|
|
default:
|
|
err = -EINVAL;
|
|
}
|
|
if (err)
|
|
return err;
|
|
|
|
if (adjust) {
|
|
__u64 ticks_per_sec = sysconf(_SC_CLK_TCK);
|
|
|
|
delta_start *= 1e9 / ticks_per_sec;
|
|
}
|
|
perf_counts__update(count, old_count, /*raw=*/true, delta_start);
|
|
return 0;
|
|
}
|
|
|
|
struct perf_pmu *tool_pmu__new(void)
|
|
{
|
|
struct perf_pmu *tool = zalloc(sizeof(struct perf_pmu));
|
|
|
|
if (!tool)
|
|
return NULL;
|
|
|
|
if (perf_pmu__init(tool, PERF_PMU_TYPE_TOOL, "tool") != 0) {
|
|
perf_pmu__delete(tool);
|
|
return NULL;
|
|
}
|
|
tool->events_table = find_core_events_table("common", "common");
|
|
return tool;
|
|
}
|