From nobody Mon May 25 04:33:58 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A1B5A2F7F01 for ; Tue, 19 May 2026 01:41:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779154896; cv=none; b=m1VvmsQRS8MozfXyK2UhCuMs9cAUrGyDkYEJVlNUcLaLLLtUR+Si5qwr1gfmhZDTh0UsNLVTd/wvRYRB6RxADPkHtGjS16swXL8mB5Sg2fNDum+6GeyzmdD8YhxfQIx79BNxL+wO3Zlomk3Goto1eE2VngbVTEhq0wISYJkKSyE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779154896; c=relaxed/simple; bh=5k84pslMb8NrEcdO2kSLiCWlRusEvKRpz1qDwCFPj3A=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=V3zKqW3gIrILpfU+A7xhrkhbDK+3RN80FXlXhch78B3YUmpohjwn1QzqeSHmgSmA0Ne3oo8iK+GLOAElScFquJmRARj40U20t+Fz4gYtV5PD/eUvM+ZV/PQolOk6QQsW0PvjuG5fQ9X6GrYtIOHpjvcL8tYla+iQG6DTTtCKxIo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=LZiwoSqZ; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="LZiwoSqZ" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2f3eb8f3419so1586842eec.1 for ; Mon, 18 May 2026 18:41:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779154894; x=1779759694; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=FOE29DGtQ0d+tfimCwLxe/PsFfFtpHc9lU56F7WmtFM=; b=LZiwoSqZHKa4dlYqIlOUsU3iFinEtl70mo4iWopYBO/U6G+QdTthoN1yUdDPEavV6U HCh4FB8ubRqm/+iXXHlB+9M2dCC/F0GK+WQZMK3pCPhtkb1Wip4IGQ6+rfApjEmU6J4W NI65tyOjm8jZQixW6zX1QcVvV6WU2kq4xLa93f4YRozUIMmQuXIsEHiT5485+Dh9rG72 7x8bEtF/3zHaNDIBeybIK+3R65AIuJXu5v7tXgO3ha8JVaRMgxRgYtgQJvmISzvnqXow 49drwi8AOkfbtq8MO0/MeSUggU8gqBm+kfcWbhZAcjOEZVDvfy5RCdAgvmjsSJwDOF6V PFrQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779154894; x=1779759694; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FOE29DGtQ0d+tfimCwLxe/PsFfFtpHc9lU56F7WmtFM=; b=d5HhZ17B6Mv7y/yepZnZBG6xHTGiVNN3Kfuc6YU9aGqO6gMH9zlWPevR529GNwEVmv 2jVWHjFkFbPutF2639nGmJcM3IXK1x6FMgWVrgBBrS9i3Pk5XM8Op8DW3hyuVApf8GzL A7yOBOPetQTo7RlTP7SsyT+xx5P++Rui4SVyyQR7qXHKhT2MdzQ0ydL349EyV1vxgRA4 1uv0dFw/+0UH5LRgkLcVU4MpOwpz/eeMaSCVwI2KU/WSgDjT8gORLx97a7Az/sMu4MD+ UmvaXtNfF0wFjmDlcAESJiPBYWEQxZLEUN3HB6wsQ0yEmMrX43P6IHZJRzH81qqob/LL 3slw== X-Forwarded-Encrypted: i=1; AFNElJ9jzbCu+dFR7FwvOKsqao0QjHnLLFJSo4wY27cz4XD2hJ0bzpaL3hjs+EBy2vQ2hXvg+LglS0C/XKiKriw=@vger.kernel.org X-Gm-Message-State: AOJu0YxFNF/yg5E6CWubJwVz5trgCDRxJzjj0G6nOdaSPUCKd91eEBL8 x18KlLlDCyGDrzZPrVsmrcYKLq7ClimowsjC87WaGa/qz+Ql7nqQ7zi7s/6oI4cfqBxq2A+Rgxi /jGITGsJoGg== X-Received: from dlbdt19.prod.google.com ([2002:a05:7022:2593:b0:135:db69:f5db]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:e430:b0:2f1:3aa8:f2c with SMTP id 5a478bee46e88-303981914bdmr8044930eec.4.1779154893511; Mon, 18 May 2026 18:41:33 -0700 (PDT) Date: Mon, 18 May 2026 18:41:05 -0700 In-Reply-To: <20260519014106.3089452-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260518223733.3034897-1-irogers@google.com> <20260519014106.3089452-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.631.ge1b05301d1-goog Message-ID: <20260519014106.3089452-2-irogers@google.com> Subject: [PATCH v4 1/2] perf tool_pmu: Make tool PMU events respect enable/disable From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, nigro.fra@gmail.com, peterz@infradead.org, tmricht@linux.ibm.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 =3D -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. Fixes: b71f46a6a708 ("perf stat: Remove hard coded shadow metrics") Reported-by: Francesco Nigro Closes: https://lore.kernel.org/linux-perf-users/20260517093650.2540920-1-n= igro.fra@gmail.com/ Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3-flash --- tools/perf/util/evlist.c | 10 +- tools/perf/util/evsel.c | 197 ++++++++++++++++++++++------- tools/perf/util/evsel.h | 15 ++- tools/perf/util/tool_pmu.c | 250 +++++++++++++++++++++++++++++-------- tools/perf/util/tool_pmu.h | 4 + 5 files changed, 377 insertions(+), 99 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ee971d15b3c6..1a238b245b3a 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -529,7 +529,7 @@ static int evlist__is_enabled(struct evlist *evlist) =20 static void __evlist__disable(struct evlist *evlist, char *evsel_name, boo= l excl_dummy) { - struct evsel *pos; + struct evsel *pos, *member; struct evlist_cpu_iterator evlist_cpu_itr; bool has_imm =3D false; =20 @@ -561,6 +561,9 @@ static void __evlist__disable(struct evlist *evlist, ch= ar *evsel_name, bool excl if (excl_dummy && evsel__is_dummy_event(pos)) continue; pos->disabled =3D true; + + for_each_group_member(member, pos) + member->disabled =3D true; } =20 /* @@ -590,7 +593,7 @@ void evlist__disable_evsel(struct evlist *evlist, char = *evsel_name) =20 static void __evlist__enable(struct evlist *evlist, char *evsel_name, bool= excl_dummy) { - struct evsel *pos; + struct evsel *pos, *member; struct evlist_cpu_iterator evlist_cpu_itr; =20 evlist__for_each_cpu(evlist_cpu_itr, evlist) { @@ -611,6 +614,9 @@ static void __evlist__enable(struct evlist *evlist, cha= r *evsel_name, bool excl_ if (excl_dummy && evsel__is_dummy_event(pos)) continue; pos->disabled =3D false; + + for_each_group_member(member, pos) + member->disabled =3D false; } =20 /* diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2ee87fd84d3e..8a80d2e15f5c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -11,68 +11,71 @@ */ #define __SANE_USERSPACE_TYPES__ =20 -#include +#include "evsel.h" + #include #include +#include + +#include #include -#include -#include -#include -#include #include +#include #include +#include +#include #include #include #include #include #include -#include -#include + +#include +#include +#include +#include +#include +#include +#include #include + +#include "../perf-sys.h" #include "asm/bug.h" +#include "bpf-filter.h" #include "bpf_counter.h" #include "callchain.h" #include "cgroup.h" #include "counts.h" +#include "debug.h" +#include "drm_pmu.h" #include "dwarf-regs.h" +#include "env.h" #include "event.h" -#include "evsel.h" -#include "time-utils.h" -#include "util/env.h" -#include "util/evsel_config.h" -#include "util/evsel_fprintf.h" #include "evlist.h" -#include -#include "thread_map.h" -#include "target.h" +#include "evsel_config.h" +#include "evsel_fprintf.h" +#include "hashmap.h" +#include "hist.h" +#include "hwmon_pmu.h" +#include "intel-tpebs.h" +#include "memswap.h" +#include "off_cpu.h" +#include "parse-branch-options.h" #include "perf_regs.h" +#include "pmu.h" +#include "pmus.h" #include "record.h" -#include "debug.h" -#include "trace-event.h" +#include "rlimit.h" #include "session.h" #include "stat.h" #include "string2.h" -#include "memswap.h" -#include "util.h" -#include "util/hashmap.h" -#include "off_cpu.h" -#include "pmu.h" -#include "pmus.h" -#include "drm_pmu.h" -#include "hwmon_pmu.h" +#include "target.h" +#include "thread_map.h" +#include "time-utils.h" #include "tool_pmu.h" #include "tp_pmu.h" -#include "rlimit.h" -#include "../perf-sys.h" -#include "util/parse-branch-options.h" -#include "util/bpf-filter.h" -#include "util/hist.h" -#include -#include -#include -#include "util/intel-tpebs.h" - -#include +#include "trace-event.h" +#include "util.h" =20 #ifdef HAVE_LIBTRACEEVENT #include @@ -1795,27 +1798,114 @@ int evsel__append_addr_filter(struct evsel *evsel,= const char *filter) /* Caller has to clear disabled after going through all CPUs. */ int evsel__enable_cpu(struct evsel *evsel, int cpu_map_idx) { - return perf_evsel__enable_cpu(&evsel->core, cpu_map_idx); + int err; + + if (evsel__is_tool(evsel)) + err =3D evsel__tool_pmu_enable_cpu(evsel, cpu_map_idx); + else + err =3D perf_evsel__enable_cpu(&evsel->core, cpu_map_idx); + + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel (opened with group_fd =3D -1) + * and are skipped by the kernel when enabling the + * group leader. We must manually enable them in + * userspace. + */ + int mem_err =3D evsel__enable_cpu(member, cpu_map_idx); + + if (mem_err) + return mem_err; + } + } + } + return err; } =20 int evsel__enable(struct evsel *evsel) { - int err =3D perf_evsel__enable(&evsel->core); + int err; + + if (evsel__is_tool(evsel)) + err =3D evsel__tool_pmu_enable(evsel); + else + err =3D perf_evsel__enable(&evsel->core); =20 if (!err) evsel->disabled =3D false; + + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel (opened with group_fd =3D -1) + * and are skipped by the kernel when enabling the + * group leader. We must manually enable them in + * userspace. + */ + int mem_err =3D evsel__enable(member); + + if (mem_err) + return mem_err; + } + member->disabled =3D false; + } + } + return err; } =20 /* Caller has to set disabled after going through all CPUs. */ int evsel__disable_cpu(struct evsel *evsel, int cpu_map_idx) { - return perf_evsel__disable_cpu(&evsel->core, cpu_map_idx); + int err; + + if (evsel__is_tool(evsel)) + err =3D evsel__tool_pmu_disable_cpu(evsel, cpu_map_idx); + else + err =3D perf_evsel__disable_cpu(&evsel->core, cpu_map_idx); + + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel and are skipped by the + * kernel when disabling the group leader. We must + * manually disable them in userspace. + */ + int mem_err =3D evsel__disable_cpu(member, cpu_map_idx); + + if (mem_err) + return mem_err; + } + } + } + return err; } =20 int evsel__disable(struct evsel *evsel) { - int err =3D perf_evsel__disable(&evsel->core); + int err; + + if (evsel__is_tool(evsel)) + err =3D evsel__tool_pmu_disable(evsel); + else + err =3D perf_evsel__disable(&evsel->core); + /* * We mark it disabled here so that tools that disable a event can * ignore events after they disable it. I.e. the ring buffer may have @@ -1825,6 +1915,27 @@ int evsel__disable(struct evsel *evsel) if (!err) evsel->disabled =3D true; =20 + if (!err && evsel__is_group_leader(evsel)) { + struct evsel *member; + + for_each_group_member(member, evsel) { + if (evsel__is_non_perf_event_open_pmu(evsel) || + evsel__is_non_perf_event_open_pmu(member)) { + /* + * In a mixed PMU group, userspace PMUs are not + * grouped in the kernel and are skipped by the + * kernel when disabling the group leader. We must + * manually disable them in userspace. + */ + int mem_err =3D evsel__disable(member); + + if (mem_err) + return mem_err; + } + member->disabled =3D true; + } + } + return err; } =20 @@ -1885,8 +1996,10 @@ void evsel__exit(struct evsel *evsel) evsel__priv_destructor(evsel->priv); perf_evsel__object.fini(evsel); if (evsel__tool_event(evsel) =3D=3D TOOL_PMU__EVENT_SYSTEM_TIME || - evsel__tool_event(evsel) =3D=3D TOOL_PMU__EVENT_USER_TIME) - xyarray__delete(evsel->start_times); + evsel__tool_event(evsel) =3D=3D TOOL_PMU__EVENT_USER_TIME) { + xyarray__delete(evsel->process_time.start_times); + xyarray__delete(evsel->process_time.accumulated_times); + } } =20 void evsel__delete(struct evsel *evsel) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 339b5c08a33d..7e3746480269 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -190,12 +190,18 @@ struct evsel { double max; } retirement_latency; /* duration_time is a single global time. */ - __u64 start_time; + struct { + __u64 start_time; + __u64 accumulated_time; + } duration_time; /* * user_time and system_time read an initial value potentially * per-CPU or per-pid. */ - struct xyarray *start_times; + struct { + struct xyarray *start_times; + struct xyarray *accumulated_times; + } process_time; }; /* Is the tool's fd for /proc/pid/stat or /proc/stat. */ bool pid_stat; @@ -350,6 +356,11 @@ void arch_evsel__apply_ratio_to_prev(struct evsel *evs= el, struct perf_event_attr int evsel__set_filter(struct evsel *evsel, const char *filter); int evsel__append_tp_filter(struct evsel *evsel, const char *filter); int evsel__append_addr_filter(struct evsel *evsel, const char *filter); +static inline bool evsel__is_non_perf_event_open_pmu(const struct evsel *e= vsel) +{ + return evsel->pmu && evsel->pmu->type > PERF_PMU_TYPE_PE_END; +} + int evsel__enable_cpu(struct evsel *evsel, int cpu_map_idx); int evsel__enable(struct evsel *evsel); int evsel__disable(struct evsel *evsel); diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c index 6a9df3dc0e07..5c30854b4644 100644 --- a/tools/perf/util/tool_pmu.c +++ b/tools/perf/util/tool_pmu.c @@ -17,6 +17,8 @@ #include #include =20 +#define INVALID_START_TIME ~0ULL + static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] =3D { NULL, "duration_time", @@ -205,20 +207,57 @@ int evsel__tool_pmu_prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, int nthreads) { - if ((evsel__tool_event(evsel) =3D=3D TOOL_PMU__EVENT_SYSTEM_TIME || - evsel__tool_event(evsel) =3D=3D TOOL_PMU__EVENT_USER_TIME) && - !evsel->start_times) { - evsel->start_times =3D xyarray__new(perf_cpu_map__nr(cpus), - nthreads, - sizeof(__u64)); - if (!evsel->start_times) - return -ENOMEM; + enum tool_pmu_event ev =3D evsel__tool_event(evsel); + + if (ev =3D=3D TOOL_PMU__EVENT_SYSTEM_TIME || ev =3D=3D TOOL_PMU__EVENT_US= ER_TIME) { + if (!evsel->process_time.start_times) { + evsel->process_time.start_times =3D + 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 =3D + xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64)); + if (!evsel->process_time.accumulated_times) + return -ENOMEM; + } } return 0; } =20 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) =20 +static int tool_pmu__read_stat(struct evsel *evsel, int cpu_map_idx, int t= hread, __u64 *val) +{ + enum tool_pmu_event ev =3D evsel__tool_event(evsel); + bool system =3D ev =3D=3D TOOL_PMU__EVENT_SYSTEM_TIME; + int fd =3D FD(evsel, cpu_map_idx, thread); + int err =3D 0; + + if (fd < 0) { + *val =3D 0; + return 0; + } + + lseek(fd, 0, SEEK_SET); + if (evsel->pid_stat) { + if (cpu_map_idx =3D=3D 0) + err =3D read_pid_stat_field(fd, system ? 15 : 14, val); + else + *val =3D 0; + } else { + if (thread =3D=3D 0) { + struct perf_cpu cpu =3D perf_cpu_map__cpu(evsel->core.cpus, cpu_map_idx= ); + + err =3D read_stat_field(fd, cpu, system ? 3 : 1, val); + } else { + *val =3D 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) @@ -232,7 +271,14 @@ int evsel__tool_pmu_open(struct evsel *evsel, if (ev =3D=3D TOOL_PMU__EVENT_DURATION_TIME) { if (evsel->core.attr.sample_period) /* no sampling */ return -EINVAL; - evsel->start_time =3D rdclock(); + evsel->duration_time.accumulated_time =3D 0; + if (evsel->core.attr.disabled) { + evsel->disabled =3D true; + evsel->duration_time.start_time =3D INVALID_START_TIME; + } else { + evsel->disabled =3D false; + evsel->duration_time.start_time =3D rdclock(); + } return 0; } =20 @@ -246,8 +292,8 @@ int evsel__tool_pmu_open(struct evsel *evsel, pid =3D perf_thread_map__pid(threads, thread); =20 if (ev =3D=3D TOOL_PMU__EVENT_USER_TIME || ev =3D=3D TOOL_PMU__EVENT_SY= STEM_TIME) { - bool system =3D ev =3D=3D TOOL_PMU__EVENT_SYSTEM_TIME; __u64 *start_time =3D NULL; + __u64 *accumulated_time =3D NULL; int fd; =20 if (evsel->core.attr.sample_period) { @@ -269,21 +315,25 @@ int evsel__tool_pmu_open(struct evsel *evsel, err =3D -errno; goto out_close; } - start_time =3D xyarray__entry(evsel->start_times, idx, thread); - if (pid > -1) { - err =3D read_pid_stat_field(fd, system ? 15 : 14, - start_time); + start_time =3D xyarray__entry(evsel->process_time.start_times, idx, + thread); + accumulated_time =3D xyarray__entry( + evsel->process_time.accumulated_times, idx, thread); + *accumulated_time =3D 0; + + if (evsel->core.attr.disabled) { + evsel->disabled =3D true; + *start_time =3D INVALID_START_TIME; } else { - struct perf_cpu cpu; - - cpu =3D perf_cpu_map__cpu(evsel->core.cpus, idx); - err =3D read_stat_field(fd, cpu, system ? 3 : 1, - start_time); + evsel->disabled =3D false; + err =3D tool_pmu__read_stat(evsel, idx, thread, start_time); + if (err) { + close(fd); + FD(evsel, idx, thread) =3D -1; + goto out_close; + } } - if (err) - goto out_close; } - } } return 0; @@ -467,10 +517,111 @@ static void perf_counts__update(struct perf_counts_v= alues *count, count->lost =3D 0; } } +int evsel__tool_pmu_enable_cpu(struct evsel *evsel, int cpu_map_idx) +{ + enum tool_pmu_event ev =3D evsel__tool_event(evsel); + int thread, nthreads; + + if (!evsel->disabled) + return 0; + + if (ev =3D=3D TOOL_PMU__EVENT_DURATION_TIME) { + if (cpu_map_idx =3D=3D 0) + evsel->duration_time.start_time =3D rdclock(); + return 0; + } + + if (ev =3D=3D TOOL_PMU__EVENT_USER_TIME || ev =3D=3D TOOL_PMU__EVENT_SYST= EM_TIME) { + nthreads =3D xyarray__max_y(evsel->process_time.start_times); + for (thread =3D 0; thread < nthreads; thread++) { + __u64 *start_time =3D xyarray__entry(evsel->process_time.start_times, + cpu_map_idx, thread); + __u64 val; + int err; + + err =3D tool_pmu__read_stat(evsel, cpu_map_idx, thread, &val); + if (!err) + *start_time =3D val; + else + *start_time =3D INVALID_START_TIME; + } + } + return 0; +} + +int evsel__tool_pmu_enable(struct evsel *evsel) +{ + unsigned int idx; + int err =3D 0; + + if (!evsel->disabled) + return 0; + + for (idx =3D 0; idx < perf_cpu_map__nr(evsel->core.cpus); idx++) { + err =3D 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 =3D evsel__tool_event(evsel); + int thread, nthreads; + + if (evsel->disabled) + return 0; + + if (ev =3D=3D TOOL_PMU__EVENT_DURATION_TIME) { + if (cpu_map_idx =3D=3D 0) { + __u64 delta =3D rdclock() - evsel->duration_time.start_time; + + evsel->duration_time.accumulated_time +=3D delta; + } + return 0; + } + + if (ev =3D=3D TOOL_PMU__EVENT_USER_TIME || ev =3D=3D TOOL_PMU__EVENT_SYST= EM_TIME) { + nthreads =3D xyarray__max_y(evsel->process_time.start_times); + for (thread =3D 0; thread < nthreads; thread++) { + __u64 *start_time =3D xyarray__entry(evsel->process_time.start_times, + cpu_map_idx, thread); + __u64 *accumulated_time =3D xyarray__entry( + evsel->process_time.accumulated_times, cpu_map_idx, thread); + __u64 val; + int err; + + err =3D tool_pmu__read_stat(evsel, cpu_map_idx, thread, &val); + if (!err) { + if (*start_time !=3D INVALID_START_TIME && val >=3D *start_time) + *accumulated_time +=3D (val - *start_time); + } + *start_time =3D INVALID_START_TIME; + } + } + return 0; +} + +int evsel__tool_pmu_disable(struct evsel *evsel) +{ + unsigned int idx; + int err =3D 0; + + if (evsel->disabled) + return 0; + + for (idx =3D 0; idx < perf_cpu_map__nr(evsel->core.cpus); idx++) { + err =3D evsel__tool_pmu_disable_cpu(evsel, idx); + if (err) + break; + } + return err; +} =20 int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) { - __u64 *start_time, cur_time, delta_start; + __u64 delta_start =3D 0; int err =3D 0; struct perf_counts_values *count, *old_count =3D NULL; bool adjust =3D false; @@ -507,39 +658,33 @@ int evsel__tool_pmu_read(struct evsel *evsel, int cpu= _map_idx, int thread) return 0; } case TOOL_PMU__EVENT_DURATION_TIME: - /* - * Pretend duration_time is only on the first CPU and thread, or - * else aggregation will scale duration_time by the number of - * CPUs/threads. - */ - start_time =3D &evsel->start_time; - if (cpu_map_idx =3D=3D 0 && thread =3D=3D 0) - cur_time =3D rdclock(); - else - cur_time =3D *start_time; + if (cpu_map_idx =3D=3D 0 && thread =3D=3D 0) { + delta_start =3D evsel->duration_time.accumulated_time; + if (!evsel->disabled && + evsel->duration_time.start_time !=3D INVALID_START_TIME) + delta_start +=3D (rdclock() - evsel->duration_time.start_time); + } else { + delta_start =3D 0; + } break; case TOOL_PMU__EVENT_USER_TIME: case TOOL_PMU__EVENT_SYSTEM_TIME: { - bool system =3D evsel__tool_event(evsel) =3D=3D TOOL_PMU__EVENT_SYSTEM_T= IME; - int fd =3D FD(evsel, cpu_map_idx, thread); - - start_time =3D xyarray__entry(evsel->start_times, cpu_map_idx, thread); - lseek(fd, SEEK_SET, 0); - if (evsel->pid_stat) { - /* The event exists solely on 1 CPU. */ - if (cpu_map_idx =3D=3D 0) - err =3D read_pid_stat_field(fd, system ? 15 : 14, &cur_time); - else - cur_time =3D 0; - } else { - /* The event is for all threads. */ - if (thread =3D=3D 0) { - struct perf_cpu cpu =3D perf_cpu_map__cpu(evsel->core.cpus, - cpu_map_idx); + __u64 accumulated =3D *(__u64 *)xyarray__entry(evsel->process_time.accum= ulated_times, + cpu_map_idx, thread); =20 - err =3D read_stat_field(fd, cpu, system ? 3 : 1, &cur_time); - } else { - cur_time =3D 0; + if (evsel->disabled) { + delta_start =3D accumulated; + } else { + __u64 *start_time =3D xyarray__entry(evsel->process_time.start_times, + cpu_map_idx, thread); + __u64 cur_time; + + err =3D tool_pmu__read_stat(evsel, cpu_map_idx, thread, &cur_time); + if (!err) { + if (*start_time !=3D INVALID_START_TIME && cur_time >=3D *start_time) + delta_start =3D accumulated + (cur_time - *start_time); + else + delta_start =3D accumulated; } } adjust =3D true; @@ -553,7 +698,6 @@ int evsel__tool_pmu_read(struct evsel *evsel, int cpu_m= ap_idx, int thread) if (err) return err; =20 - delta_start =3D cur_time - *start_time; if (adjust) { __u64 ticks_per_sec =3D sysconf(_SC_CLK_TCK); =20 diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h index f1714001bc1d..f66d24cf3502 100644 --- a/tools/perf/util/tool_pmu.h +++ b/tools/perf/util/tool_pmu.h @@ -56,6 +56,10 @@ int evsel__tool_pmu_prepare_open(struct evsel *evsel, int evsel__tool_pmu_open(struct evsel *evsel, struct perf_thread_map *threads, int start_cpu_map_idx, int end_cpu_map_idx); +int evsel__tool_pmu_enable_cpu(struct evsel *evsel, int cpu_map_idx); +int evsel__tool_pmu_enable(struct evsel *evsel); +int evsel__tool_pmu_disable_cpu(struct evsel *evsel, int cpu_map_idx); +int evsel__tool_pmu_disable(struct evsel *evsel); int evsel__tool_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread); =20 struct perf_pmu *tool_pmu__new(void); --=20 2.54.0.631.ge1b05301d1-goog From nobody Mon May 25 04:33:58 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA1A72FCC0E for ; Tue, 19 May 2026 01:41:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779154898; cv=none; b=NugX/27eIyb1KvNcJczhvBWsu1qOCVZkcaA4qFpWehu29pHiGTrK8kB3FRgC9vDFCmkJNsujl1Lg+7FN5Oyf5CAxi0B9OA/5vg03cHA9h4uTNnLBg0IKV6u0dE2VKrE++2SFjXBT4gNgE8yoUUK1YpBzQScUwYe9f2eR8f6sqgU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779154898; c=relaxed/simple; bh=Y0A7uIYm0/hTwz3uOvIKTQjqsYkyO72j5ZJc+bz7kWA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jb5WdUdnE6+azc9VSfhShFDKWexJ/V0VhEG55Uhw7+B6UaB+29Exz30QZIOZw9ALBucBkYpX/PpVMtJt+Dk4NHBnUZHLjrlEhY0zBh7o/+ujP6u7VhRCR/4wZ6aXrhHMZlxJyC3r44udcCDW7gdbnvkfdkDNP1isjB383mmGvZI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vT201cPB; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vT201cPB" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-13312be8a31so10735655c88.1 for ; Mon, 18 May 2026 18:41:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779154896; x=1779759696; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+FLbhfS6dmKPZbmfI47Elwx0wCEuwKnKMsF58wjgvdo=; b=vT201cPBu6XeLmxNzP1ZN0p1GTQQbJAFl68ekgJBgx+o53AKPqIdPmVJFBLGGNNRyl XDhqy4/obrpGef2kjdx83FF7IKfK7QI9rrYa+yGIXYTgoNW+C2WD8HU2ksAnuAsDDoy7 0bw3XWKdIs/QAs9nz2KZaMOSIIEXN0Q5E410EtEsI1ptfs4Abw6k4nwCslG4dXjfbdbN MBu2UuUivwpWvQiyGdBAb/V9O4St92/8s9nh82WdqR1h5urudfO41lwJvD5VuUi2nsLM rOtRCHq8nrkdqkTGcT+QZsZGhG8In8mpsAoZ4XuImyCTc0xysiPwHTbiNgav1a5EaWTr FeEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779154896; x=1779759696; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+FLbhfS6dmKPZbmfI47Elwx0wCEuwKnKMsF58wjgvdo=; b=snAE+CwR53NWWvCfz+7mkM6YaffSQf6rl1/QgDhWAidk4Y2uZowsxiljGmvKN1qc0J q1ttd/NxKDwfDyLdSbls3x9aVstnbaNvmiL9qIA4SJ+6bnwpUWeeh8fzrKhJT+GnhLMQ hHQQ2HAQdfgCVMku9IE87y91WCocDdEMzAfPeV6PuaRHnxi87Xw6vrIyeDPZmt/rOyS8 zlFwzbhayEW+Fj9ELE6wPH7I7m0Ourzo4OsI3+WHAQ73HUpkOIrMyTaF8v/NGOQF7Hgi UdL91wqUZYXWJMVxJxLk45XLXz/Z4x/kT3F0PYmzSBLewfIyGqZd7Jr1TW2rmwrjqd+x xrAg== X-Forwarded-Encrypted: i=1; AFNElJ/wgzaw42JNoxRxdZ73Urukj4E3PeLIK+Jicshqs7IZqReUY70i6NhKe02xBDeRpvjFU+NKqQBMlJEzSrk=@vger.kernel.org X-Gm-Message-State: AOJu0YzXwPxauWT7IlHRHTwaG0/+xZjxGIRQnIrqxIwemDOAVs57Y4tZ jrDF+SilsS7If4rwoMXFX88/HpzVdqqCLpbxfZNY9vhB68Qyb6D6DGYuc3jOGAa1EJqoYi3d23t PZ4VaBSK4Mw== X-Received: from dlbcj24.prod.google.com ([2002:a05:7022:6998:b0:12c:20df:7530]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:1a85:b0:12d:d972:b96e with SMTP id a92af1059eb24-1350542e8e5mr7867042c88.20.1779154895600; Mon, 18 May 2026 18:41:35 -0700 (PDT) Date: Mon, 18 May 2026 18:41:06 -0700 In-Reply-To: <20260519014106.3089452-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260518223733.3034897-1-irogers@google.com> <20260519014106.3089452-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.631.ge1b05301d1-goog Message-ID: <20260519014106.3089452-3-irogers@google.com> Subject: [PATCH v4 2/2] perf tests: Add test for stat delay option with duration_time From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, nigro.fra@gmail.com, peterz@infradead.org, tmricht@linux.ibm.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a new test case `test_stat_delay` to `stat.sh` to verify that `duration_time` correctly excludes the delay period when using the delay option (-D). The test runs `perf stat -D 1000 -e duration_time sleep 2` and verifies that `duration_time` is ~1s (excluding the 1s delay), not ~2s. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3-flash --- tools/perf/tests/shell/stat.sh | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tools/perf/tests/shell/stat.sh b/tools/perf/tests/shell/stat.sh index 4edb04039036..1e17bee026bd 100755 --- a/tools/perf/tests/shell/stat.sh +++ b/tools/perf/tests/shell/stat.sh @@ -483,6 +483,58 @@ test_stat_pid() { wait $pid 2>/dev/null || true } =20 +test_stat_delay() { + echo "stat -D test" + if ! env LC_ALL=3DC perf stat -D 1000 -e duration_time sleep 2 > "${stat= _output}" 2>&1 + then + echo "stat -D test [Failed - command failed]" + cat "${stat_output}" + err=3D1 + return + fi + + duration=3D$(grep "duration_time" "${stat_output}" | awk '{print $1}' | = tr -d ',') + elapsed=3D$(grep "seconds time elapsed" "${stat_output}" | awk '{print $= 1}') + + if [ -z "$duration" ] || [ -z "$elapsed" ] + then + echo "stat -D test [Failed - failed to find duration_time or time elap= sed in output]" + cat "${stat_output}" + err=3D1 + return + fi + + # Compare duration (ns) and elapsed (s) using awk to handle float and al= low tolerance. + if ! awk -v d=3D"$duration" -v e=3D"$elapsed" ' + BEGIN { + diff =3D d - (e * 1e9); + if (diff < 0) diff =3D -diff; + # Allow 200ms tolerance (200,000,000 ns) for loaded CI machines. + if (diff > 200000000) { + printf "Fail: duration (%d ns) and elapsed (%f s) mismatch (diff %= d ns)\n", d, e, diff; + exit 1; + } + # Lower bound check: must be at least 0.5s. + if (d < 500000000) { + printf "Fail: duration (%d ns) is abnormally small\n", d; + exit 1; + } + # Upper bound check: must be strictly less than 1.7s (proving delay = was excluded). + if (d > 1700000000) { + printf "Fail: duration (%d ns) is too large (delay might not be ex= cluded)\n", d; + exit 1; + } + exit 0; + }' + then + echo "stat -D test [Failed - validation failed]" + cat "${stat_output}" + err=3D1 + return + fi + echo "stat -D test [Success]" +} + test_default_stat test_null_stat test_offline_cpu_stat @@ -498,6 +550,7 @@ test_stat_no_aggr test_stat_detailed test_stat_repeat test_stat_pid +test_stat_delay =20 cleanup exit $err --=20 2.54.0.631.ge1b05301d1-goog