From nobody Fri Dec 19 10:42:21 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.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 00A7916C850 for ; Wed, 26 Mar 2025 03:51:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961070; cv=none; b=ETDA/Yg3h+76DIx+d8p9jCW0WIGhItPJ6k2x4zK5sATvVdVwM0ghFkdfNcfj03uJHmSdVZ1rvqbYc8HEKj8YMYpfgV2yy0JsFD54aqjuRmQQ9CIXjsV3PezRspNUeuB0qvhYdO0yLo4gCM9DSS4yG8SA7n2vZeaDP7pJ/pUHVa4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961070; c=relaxed/simple; bh=3jxraLjE69gnQLWEysFoeBUKLHVIso6rk3eWNHc+GA4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=NdxkbSj/9ClGpVA5jvnTF2l778xLG7MLqDLkSX/ahcuDy/mV5ekQkD4qfRv+Pz+fQ6RLBXamR0YbgevjIlkAaZif+8goWhRzcsE3HsN8ZZ/YFLYOteNDzVKbDMSEmtyoOPYWTf7CPfqdF0eQcbVRBrajKpmce5PaWlbZZGWtS2U= 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=4sCmJBYS; arc=none smtp.client-ip=209.85.214.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="4sCmJBYS" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-2242ca2a4a5so77706535ad.2 for ; Tue, 25 Mar 2025 20:51:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1742961068; x=1743565868; 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=uCVS+DhgJOLHYroH5VuWGGnd2W5CTNd7YHsjO54Adcw=; b=4sCmJBYSBM9f8ses29SkX7L/YfCuEuYdJ9qk/wiqtMwQK/+ZGKA8RSKhf1k42btH4U pW+lahVBnT1QCi3Q0fHi5l3sUO/a0oF18pkSOp2Ww9VicfoAbH82GQo+7apVDBMueQZU tO5v2PupokHp/tdribX6mXMRN4GbZge+kb21yUNdEpBPmzzCaFeqV6Y3zggammponV49 BigkFNd4ovyRxOsBxBKL35TCW/mHEyo2XNO33hh5/txzk+9ThVlxYgk3k+c4jNjfTatI 5LxK6SDJLML9Mg1HOmmJWBu3a9NKskSJuNsZU6RTJqOb8pFblb5LwDrHolX7Wi5OHrNW iT8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742961068; x=1743565868; 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=uCVS+DhgJOLHYroH5VuWGGnd2W5CTNd7YHsjO54Adcw=; b=VSSi2FqDMggNifqocGmmv/3MYqI+JyGh+NzRJ+YMBnSzIEEysGjB0OAFtrKWAcGGOO b7aEbo3Lcyiguuoy/LYEaLGwhIOUKbD0vrVSQhkGe1faPwYQA0Mym4BORGnpT1/XZwDe EHXHjNQPXrqfy/7xnDrp/EcuLqk7SW9t1vJqj9TVDId0E96cKeUlZ3e4WDMlcz067KmH TXya/0hDNIJc6QHQfMUguKL9grmMwIJO9+azEv3IlQD4prk6+gnxa0NF+KVKceUrmBmj B/mbRTb4ROGuAK40ax5PU7n7M9ZzUDBMjgPBm5RVFPNDv7wN+ElJPlP9yjWOvwfqNT8/ CMOA== X-Forwarded-Encrypted: i=1; AJvYcCVrDufSp/QeoD4TsWeegxh6TkPA6XqoGAUx/SXUj5COLL2RGZN0BG4Th3rH8rMPihKav9Nk/sCXfeLMaM4=@vger.kernel.org X-Gm-Message-State: AOJu0YxMaFpEPTrXPLlyJRqg156svbFGhYYJbhLkmunJLadUCcIYTHrJ B0j4QhKoldszGTjeVCA1gjbemUyM2A7htrLeL0mt7XQkmCqke0TFygH3QGi2sv+B32yu4rQG8yn vjbm3Dg== X-Google-Smtp-Source: AGHT+IFVfYoE7t8L3UJr15qQGlHKncIQsy7nPhLY5dK19DZs1BWjoNZ2pWij3AjrrsHCyI9ogBO2Nzy6GO4k X-Received: from pfiy3.prod.google.com ([2002:a05:6a00:1903:b0:736:8727:67fd]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:80e:b0:736:6279:ca25 with SMTP id d2e1a72fcca58-73905a27ae5mr34688941b3a.24.1742961067760; Tue, 25 Mar 2025 20:51:07 -0700 (PDT) Date: Tue, 25 Mar 2025 20:50:42 -0700 In-Reply-To: <20250326035045.129440-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: <20250326035045.129440-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.395.g12beb8f557-goog Message-ID: <20250326035045.129440-2-irogers@google.com> Subject: [PATCH v2 1/4] perf parse-events: Avoid scanning PMUs that can't contain events From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add perf_pmus__scan_for_event that only reads sysfs for pmus that could contain a given event. Signed-off-by: Ian Rogers --- tools/perf/util/parse-events.c | 4 ++-- tools/perf/util/pmus.c | 35 ++++++++++++++++++++++++++++++++++ tools/perf/util/pmus.h | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5152fd5a6ead..d77af1d24985 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -434,7 +434,7 @@ int parse_events_add_cache(struct list_head *list, int = *idx, const char *name, const char *config_name =3D get_config_name(parsed_terms); const char *metric_id =3D get_config_metric_id(parsed_terms); =20 - while ((pmu =3D perf_pmus__scan(pmu)) !=3D NULL) { + while ((pmu =3D perf_pmus__scan_for_event(pmu, name)) !=3D NULL) { LIST_HEAD(config_terms); struct perf_event_attr attr; int ret; @@ -1585,7 +1585,7 @@ int parse_events_multi_pmu_add(struct parse_events_st= ate *parse_state, =20 INIT_LIST_HEAD(list); =20 - while ((pmu =3D perf_pmus__scan(pmu)) !=3D NULL) { + while ((pmu =3D perf_pmus__scan_for_event(pmu, event_name)) !=3D NULL) { bool auto_merge_stats; =20 if (parse_events__filter_pmu(parse_state, pmu)) diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index b99292de7669..f2706c395509 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -350,6 +350,41 @@ struct perf_pmu *perf_pmus__scan_core(struct perf_pmu = *pmu) return NULL; } =20 +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const cha= r *event) +{ + bool use_core_pmus =3D !pmu || pmu->is_core; + + if (!pmu) { + /* Hwmon filename values that aren't used. */ + enum hwmon_type type; + int number; + /* + * Core PMUs, other sysfs PMUs and tool PMU can take all event + * types or aren't wother optimizing for. + */ + unsigned int to_read_pmus =3D PERF_TOOL_PMU_TYPE_PE_CORE_MASK | + PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | + PERF_TOOL_PMU_TYPE_TOOL_MASK; + + /* Could the event be a hwmon event? */ + if (parse_hwmon_filename(event, &type, &number, /*item=3D*/NULL, /*alarm= =3D*/NULL)) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu =3D list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) + return pmu; + + pmu =3D NULL; + pmu =3D list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) + return pmu; + return NULL; +} + static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *p= mu) { bool use_core_pmus =3D !pmu || pmu->is_core; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 8def20e615ad..213ee65306d6 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -19,6 +19,7 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int typ= e); =20 struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const cha= r *event); =20 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); =20 --=20 2.49.0.395.g12beb8f557-goog From nobody Fri Dec 19 10:42:21 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.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 3701817A2F2 for ; Wed, 26 Mar 2025 03:51:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961072; cv=none; b=kOVGSrZ4E3nRsWDJymoW80EuD6R5n48KYfPKUolsQk6LOQAgzOZeJvXfeaTgYAcd51tDJ2AUruC7D+B6k5paam9wOGsHYKEoqIN0jJwoJOZjS3ok6nalBCMkbW40QuDXnMH6rbNtT5WlTc52umjlg3/HJkx9ROsCL/jUQpyMGos= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961072; c=relaxed/simple; bh=6LrIdPRj1HxPT+PbcMIt9Tvx3WbxHdl1wI4+3h2doPs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=PGrH69Sof+mWTNILmLNuHnDgXlvD13rLJiSl/FLeI/8xO+wDC/CXaPVILYCTvwQWs4gl9eUKASoZ8+SR6qdbqEH2+K1yTPrSrAGhoRHC35GfbDkDnVP/yAp6AluXSUzRcpSXf1e+go5H07BPtEY/39iwgfplxTKKc9MzzW2053M= 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=3oSGHeT6; arc=none smtp.client-ip=209.85.216.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="3oSGHeT6" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-2ff62f96b10so852359a91.0 for ; Tue, 25 Mar 2025 20:51:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1742961070; x=1743565870; 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=VDGbOYIOj15E+JsmdSzGj6CuNWcI9S7Zmd8zr8fj0vs=; b=3oSGHeT6Q3St/nECgKitBHqETjUD7jFTqgkoApGJuWI32h58cG9ViRnO9VjG0TVGmV urOTaar1R+cG7dGTzHaM+m+QeP8GdKag+MDSu0Q0NP0do+5dg8wnDs1rgP1dsBx5OZWP mOINAf+pfZ2GIu0y83Wfb9XCxCSgwukrBR+jF1Mmq9diCCEIF5Cq1VrHfwzE/V6Nuy5l LMWqeJgApGmLLsipIwG9nyWHYEDQHQETn8PZzVD7AUEjoGQMMwUioqUZOF4O6cRCfkvB 1vuyWnLtdZM/XjbKogTw8XtC85WanDT8vJ4wieRt4jGWps+WTmyJBP7X9l57RlfJleow MzYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742961070; x=1743565870; 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=VDGbOYIOj15E+JsmdSzGj6CuNWcI9S7Zmd8zr8fj0vs=; b=hj4xlIgRyk3jvM4LRxW/itTlGM3YUrDWvZUW0jyclGud5o9s5hOwrI/VkrY+ODQkVx vWszU99lQeB7PBQfygQjYtXfTMKD4sXT9Y43NAa0sbccyjVBaRlJVF3vOlzipS5RLMjn NQwwqDPPHMJEBw7K2IZyw0K3q6tw1I7YuYq+nq20sisj98qAKmT4NXXktoJ5e5BYyeF6 LB6CalJ7lSOrLekHKLv9gT44HgTykU4VtzvPkNogEBj2gZlin1sAVmXn9TreCVr+m4bh hgbP23Je8mz8upkewSgX0RulrZUTov5M9Ps2xr7wCJuhD4BjtJ0XNhAf5jGqK9rwVgap 9o/A== X-Forwarded-Encrypted: i=1; AJvYcCVXKziYi9qMoQT1R3xr/ulyW/rJAcWNMv4owktWrBShuVGdR40w6x3ALNgh6XXVJYcofS1saW8OeH7WulM=@vger.kernel.org X-Gm-Message-State: AOJu0Yz0FJsJ2yesOZS1UTvn+Z/YOnA3Gs8R123Nu/JgrfdWlr9qpcrW s+w8CFs85Bfj8N9b6ZmQBj7XdVSAOFMRBf9FPfRuHSTJdaKA4mFmUeAcGjR47BN7P8AJCrS76h2 5ElZfLQ== X-Google-Smtp-Source: AGHT+IGG8kf/JIk38ELOkHk95ox9JB8hK5eqE/sVI+IvZADNpn7vVcFc/26jk3ntBtqUIe/wSk//NLgpdJV8 X-Received: from pgnn25.prod.google.com ([2002:a05:6a02:4f19:b0:af2:2c5c:55e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:32a3:b0:1ee:ba29:d8fa with SMTP id adf61e73a8af0-1fe9380d489mr3594205637.6.1742961070224; Tue, 25 Mar 2025 20:51:10 -0700 (PDT) Date: Tue, 25 Mar 2025 20:50:43 -0700 In-Reply-To: <20250326035045.129440-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: <20250326035045.129440-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.395.g12beb8f557-goog Message-ID: <20250326035045.129440-3-irogers@google.com> Subject: [PATCH v2 2/4] perf parse-events: Avoid scanning PMUs that can't match a wildcard From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add perf_pmus__scan_matching_wildcard that only reads sysfs for pmus that could wildcard match. For example, if searching for the event 'inst_retired.any/period=3D1000000/' first 'inst_retired.any' will try to be directly looked up as a PMU, then 'inst_retired.any' will try to be wildcard matched against all PMUs, finally it is assumed 'inst_retired.any' is an event name and opened on all PMUs avoiding reading PMUs that can't have such an event. However, before this patch such a case would read all PMUs for the wildcard matching. Now if the wildcard can't be a hwmon PMU wildcard, reading hwmon PMUs is avoided. On my laptop this reduces 'perf stat -e inst_retired.any true' from performing 253 openat system calls down to 234. Signed-off-by: Ian Rogers --- tools/perf/util/parse-events.c | 26 +++++++++++---------- tools/perf/util/pmus.c | 42 ++++++++++++++++++++++++++++++++++ tools/perf/util/pmus.h | 1 + 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d77af1d24985..19caf1ed9f5d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1664,18 +1664,20 @@ int parse_events_multi_pmu_add_or_add_pmu(struct pa= rse_events_state *parse_state =20 pmu =3D NULL; /* Failed to add, try wildcard expansion of event_or_pmu as a PMU name. */ - while ((pmu =3D perf_pmus__scan(pmu)) !=3D NULL) { - if (!parse_events__filter_pmu(parse_state, pmu) && - perf_pmu__wildcard_match(pmu, event_or_pmu)) { - bool auto_merge_stats =3D perf_pmu__auto_merge_stats(pmu); - - if (!parse_events_add_pmu(parse_state, *listp, pmu, - const_parsed_terms, - auto_merge_stats, - /*alternate_hw_config=3D*/PERF_COUNT_HW_MAX)) { - ok++; - parse_state->wild_card_pmus =3D true; - } + while ((pmu =3D perf_pmus__scan_matching_wildcard(pmu, event_or_pmu)) != =3D NULL) { + bool auto_merge_stats; + + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + + auto_merge_stats =3D perf_pmu__auto_merge_stats(pmu); + + if (!parse_events_add_pmu(parse_state, *listp, pmu, + const_parsed_terms, + auto_merge_stats, + /*alternate_hw_config=3D*/PERF_COUNT_HW_MAX)) { + ok++; + parse_state->wild_card_pmus =3D true; } } if (ok) diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index f2706c395509..93d5db1c89b4 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -19,6 +19,7 @@ #include "tool_pmu.h" #include "print-events.h" #include "strbuf.h" +#include "string2.h" =20 /* * core_pmus: A PMU belongs to core_pmus if it's name is "cpu" or it's sy= sfs @@ -385,6 +386,47 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf= _pmu *pmu, const char *eve return NULL; } =20 +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, c= onst char *wildcard) +{ + bool use_core_pmus =3D !pmu || pmu->is_core; + + if (!pmu) { + /* + * Core PMUs, other sysfs PMUs and tool PMU can have any name or + * aren't wother optimizing for. + */ + unsigned int to_read_pmus =3D PERF_TOOL_PMU_TYPE_PE_CORE_MASK | + PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | + PERF_TOOL_PMU_TYPE_TOOL_MASK; + + /* + * Hwmon PMUs have an alias from a sysfs name like hwmon0, + * hwmon1, etc. or have a name of hwmon_. They therefore + * can only have a wildcard match if the wildcard begins with + * "hwmon". + */ + if (strisglob(wildcard) || + (strlen(wildcard) >=3D 5 && strncmp("hwmon", wildcard, 5) =3D=3D 0)) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu =3D list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) { + if (perf_pmu__wildcard_match(pmu, wildcard)) + return pmu; + } + pmu =3D NULL; + pmu =3D list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) { + if (perf_pmu__wildcard_match(pmu, wildcard)) + return pmu; + } + return NULL; +} + static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *p= mu) { bool use_core_pmus =3D !pmu || pmu->is_core; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 213ee65306d6..2794d8c3a466 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -20,6 +20,7 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int typ= e); struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const cha= r *event); +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, c= onst char *wildcard); =20 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); =20 --=20 2.49.0.395.g12beb8f557-goog From nobody Fri Dec 19 10:42:21 2025 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (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 05B7517A31C for ; Wed, 26 Mar 2025 03:51:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961075; cv=none; b=qxDK4VcQV1HdU4YbOvMQ/AfyhBPxYOYOsTwx13IfhVdPibfcJrik9KRxRUjO93Rsi0bK29yBpDJKjSTPgkVYnyRw74HXDRvEw+xLs7hqEk1Vk+MJ72J641J9Kz4WGq9xPBpLC6sG1ADwBClNv41M1LHtybzBgawaH9ML8iGF9UA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961075; c=relaxed/simple; bh=Zx59AKp0yj5ziJODBNA/3oaZ8vQA+6fZr3gfERgthyU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=qGTSjO140ozTzNgJpbXT04JSu5kcw0IVOuYIjdUSlSGi24GycRvAcRPYBFLDJyOYGrzvhUxXQMSfpqJwm07yrQZKRQ7NHwtNrWogJ4WkT+IySiwDT7NCFRu+Kg4mJkQoEJUMWGXge89gSmSRneU+Wk2f2mhRu/HYhbytcqAJsxk= 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=eCbro2SI; arc=none smtp.client-ip=209.85.214.201 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="eCbro2SI" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2264e5b2b7cso96547025ad.2 for ; Tue, 25 Mar 2025 20:51:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1742961072; x=1743565872; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=LsmdxWrBvRSn9Ajow3Tiw59LqmVZF1ZvltY9Jo36BNw=; b=eCbro2SIjGsEkCzTxmdtgi9EyQq/tQfzCdlJImoCnV7jY561ebMgqH5zH7tlqmI9e7 Xb9JeUvEYy9VKt2TY3Lzopn/q7q+UUYgX3+kX1tm+7NkcCxwktO3KRwJq1AQfqhETT3G 2FFd0EyluzQuq1iSaeGS3oargVE2OCegXroEwrunfEJzcYCBKff+EQA8tlPhxh87PS8s 1fbEJ0wuFfXvpePKFOwX/LGRTku3oHVo2KAcnjkA+xTC068WK5t//Z7ToGhwQFFxGM6x jnkSg3/PvvlaIqaBnyoSTWBWtliGvEgncFHOSjm8faApMMoDIu6/KThM1xBJ2msMc/ru YWaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742961072; x=1743565872; h=content-transfer-encoding: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=LsmdxWrBvRSn9Ajow3Tiw59LqmVZF1ZvltY9Jo36BNw=; b=MALoEPEjsjNUQ1CAijeUpNAwc0cLvH1tfbnL9imIkLzkSAQZZy2+DcJ9ZoXMbirhZU oMZB8SIQN//xmZStYvaTUtn8gGQTy3lKYvRh2bUWKIlAXtqNhRmbaESC/NXWkiWNmkof ESdpZGSzgaZ7pu/qm5gzZSRkH98PkQ9Fl0IWSTPvsoKB/hm5nmdqtQy/XDCYR2aUoUie V/9dAD4AEqPClVcHWbnF4sjLfxFmSjCmtdno5C0S06tTB9BSQnP+/Ge5sSYlF/EgFT5E yi1D32eesE5MRVfP0cSIoH+LwRts7UJdPNd7BfPhvOQm5AynJl4pzRyW7YxgUxMDAstD yWWg== X-Forwarded-Encrypted: i=1; AJvYcCVi54R3jMPcSdGUZ+jTn+jhXeqN8viz1FLnPy+8tQR2Coyp7Df7a6aAKaXPt0fYHLCitAd5tuJeIQrQRZ8=@vger.kernel.org X-Gm-Message-State: AOJu0YwVstLkC/As8iMlEhs+lFbjm0y6OBCZ8ykl3aua7Mk3gfPyf3zT BxyJrHN+he7K4+ca+NkjwBtd3yQWD8bJQIKiO8hgvONE7U039DEb8Sh/0JIJMyCn7Gvf/LBS09m Wc7faaw== X-Google-Smtp-Source: AGHT+IGdfvtyW96JXtS6Bugfi/XpazUz8yM1YYsg4qFrJrPfuflKOikHB8QR3r8Png3zvsKT4SiHVVjAI54g X-Received: from pfbna8.prod.google.com ([2002:a05:6a00:3e08:b0:734:cc8:a107]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:aa7:8892:0:b0:728:e2cc:bfd6 with SMTP id d2e1a72fcca58-73905a3b9d0mr30060630b3a.18.1742961072198; Tue, 25 Mar 2025 20:51:12 -0700 (PDT) Date: Tue, 25 Mar 2025 20:50:44 -0700 In-Reply-To: <20250326035045.129440-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: <20250326035045.129440-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.395.g12beb8f557-goog Message-ID: <20250326035045.129440-4-irogers@google.com> Subject: [PATCH v2 3/4] perf drm_pmu: Add a tool like PMU to expose DRM information From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" DRM clients expose information through usage stats as documented in Documentation/gpu/drm-usage-stats.rst (available online at https://docs.kernel.org/gpu/drm-usage-stats.html). Add a tool like PMU, similar to the hwmon PMU, that exposes DRM information. For example on a tigerlake laptop: ``` $ perf list drm List of pre-defined events (to be used in -e or -M): drm: drm-active-stolen-system0 [Total memory active in one or more engines. Unit: drm_i915] drm-active-system0 [Total memory active in one or more engines. Unit: drm_i915] drm-engine-capacity-video [Engine capacity. Unit: drm_i915] drm-engine-copy [Utilization in ns. Unit: drm_i915] drm-engine-render [Utilization in ns. Unit: drm_i915] drm-engine-video [Utilization in ns. Unit: drm_i915] drm-engine-video-enhance [Utilization in ns. Unit: drm_i915] drm-purgeable-stolen-system0 [Size of resident and purgeable memory bufers. Unit: drm_i915] drm-purgeable-system0 [Size of resident and purgeable memory bufers. Unit: drm_i915] drm-resident-stolen-system0 [Size of resident memory bufers. Unit: drm_i915] drm-resident-system0 [Size of resident memory bufers. Unit: drm_i915] drm-shared-stolen-system0 [Size of shared memory bufers. Unit: drm_i915] drm-shared-system0 [Size of shared memory bufers. Unit: drm_i915] drm-total-stolen-system0 [Size of shared and private memory. Unit: drm_i915] drm-total-system0 [Size of shared and private memory. Unit: drm_i915] ``` System wide data can be gathered: ``` $ perf stat -x, -I 1000 -e drm-active-stolen-system0,drm-active-system0,drm= -engine-capacity-video,drm-engine-copy,drm-engine-render,drm-engine-video,d= rm-engine-video-enhance,drm-purgeable-stolen-system0,drm-purgeable-system0,= drm-resident-stolen-system0,drm-resident-system0,drm-shared-stolen-system0,= drm-shared-system0,drm-total-stolen-system0,drm-total-system0 1.000904910,0,bytes,drm-active-stolen-system0,1,100.00,, 1.000904910,0,bytes,drm-active-system0,1,100.00,, 1.000904910,36,capacity,drm-engine-capacity-video,1,100.00,, 1.000904910,0,ns,drm-engine-copy,1,100.00,, 1.000904910,1472970566175,ns,drm-engine-render,1,100.00,, 1.000904910,0,ns,drm-engine-video,1,100.00,, 1.000904910,0,ns,drm-engine-video-enhance,1,100.00,, 1.000904910,0,bytes,drm-purgeable-stolen-system0,1,100.00,, 1.000904910,38199296,bytes,drm-purgeable-system0,1,100.00,, 1.000904910,0,bytes,drm-resident-stolen-system0,1,100.00,, 1.000904910,4643196928,bytes,drm-resident-system0,1,100.00,, 1.000904910,0,bytes,drm-shared-stolen-system0,1,100.00,, 1.000904910,1886871552,bytes,drm-shared-system0,1,100.00,, 1.000904910,0,bytes,drm-total-stolen-system0,1,100.00,, 1.000904910,4643196928,bytes,drm-total-system0,1,100.00,, 2.264426839,0,bytes,drm-active-stolen-system0,1,100.00,, ``` Or for a particular process: ``` $ perf stat -x, -I 1000 -e drm-active-stolen-system0,drm-active-system0,drm= -engine-capacity-video,drm-engine-copy,drm-engine-render,drm-engine-video,d= rm-engine-video-enhance,drm-purgeable-stolen-system0,drm-purgeable-system0,= drm-resident-stolen-system0,drm-resident-system0,drm-shared-stolen-system0,= drm-shared-system0,drm-total-stolen-system0,drm-total-system0 -p 200027 1.001040274,0,bytes,drm-active-stolen-system0,6,100.00,, 1.001040274,0,bytes,drm-active-system0,6,100.00,, 1.001040274,12,capacity,drm-engine-capacity-video,6,100.00,, 1.001040274,0,ns,drm-engine-copy,6,100.00,, 1.001040274,1542300,ns,drm-engine-render,6,100.00,, 1.001040274,0,ns,drm-engine-video,6,100.00,, 1.001040274,0,ns,drm-engine-video-enhance,6,100.00,, 1.001040274,0,bytes,drm-purgeable-stolen-system0,6,100.00,, 1.001040274,13516800,bytes,drm-purgeable-system0,6,100.00,, 1.001040274,0,bytes,drm-resident-stolen-system0,6,100.00,, 1.001040274,27746304,bytes,drm-resident-system0,6,100.00,, 1.001040274,0,bytes,drm-shared-stolen-system0,6,100.00,, 1.001040274,0,bytes,drm-shared-system0,6,100.00,, 1.001040274,0,bytes,drm-total-stolen-system0,6,100.00,, 1.001040274,27746304,bytes,drm-total-system0,6,100.00,, 2.016629075,0,bytes,drm-active-stolen-system0,6,100.00,, ``` As with the hwmon PMU, high numbered PMU types are used to encode multiple possible "DRM" PMUs. The appropriate fdinfo is found by scanning /proc and filtering which fdinfos to read with stat. To avoid some unneeding scanning, events not starting with "drm-" are ignored. The patch builds on commit 57e13264dcea ("perf pmus: Restructure pmu_read_sysfs to scan fewer PMUs") and later so that only if full wild carding is being done, the PMU starts with "drm_" or the event starts with "drm-" will /proc be scanned. That is there should be little to no cost in this PMU unless DRM events are requested. Signed-off-by: Ian Rogers --- tools/perf/util/Build | 1 + tools/perf/util/drm_pmu.c | 689 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/drm_pmu.h | 39 +++ tools/perf/util/evsel.c | 9 + tools/perf/util/pmu.c | 15 + tools/perf/util/pmu.h | 4 +- tools/perf/util/pmus.c | 30 +- 7 files changed, 782 insertions(+), 5 deletions(-) create mode 100644 tools/perf/util/drm_pmu.c create mode 100644 tools/perf/util/drm_pmu.h diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 034a6603d5a8..f153816f6fe1 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -84,6 +84,7 @@ perf-util-y +=3D pmu.o perf-util-y +=3D pmus.o perf-util-y +=3D pmu-flex.o perf-util-y +=3D pmu-bison.o +perf-util-y +=3D drm_pmu.o perf-util-y +=3D hwmon_pmu.o perf-util-y +=3D tool_pmu.o perf-util-y +=3D svghelper.o diff --git a/tools/perf/util/drm_pmu.c b/tools/perf/util/drm_pmu.c new file mode 100644 index 000000000000..a76bba4fe47f --- /dev/null +++ b/tools/perf/util/drm_pmu.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "drm_pmu.h" +#include "counts.h" +#include "cpumap.h" +#include "debug.h" +#include "evsel.h" +#include "pmu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum drm_pmu_unit { + DRM_PMU_UNIT_BYTES, + DRM_PMU_UNIT_CAPACITY, + DRM_PMU_UNIT_CYCLES, + DRM_PMU_UNIT_HZ, + DRM_PMU_UNIT_NS, + + DRM_PMU_UNIT_MAX, +}; + +struct drm_pmu_event { + const char *name; + const char *desc; + enum drm_pmu_unit unit; +}; + +struct drm_pmu { + struct perf_pmu pmu; + struct drm_pmu_event *events; + int num_events; +}; + +static const char * const drm_pmu_unit_strs[DRM_PMU_UNIT_MAX] =3D { + "bytes", + "capacity", + "cycles", + "hz", + "ns", +}; + +static const char * const drm_pmu_scale_unit_strs[DRM_PMU_UNIT_MAX] =3D { + "1bytes", + "1capacity", + "1cycles", + "1hz", + "1ns", +}; + +bool perf_pmu__is_drm(const struct perf_pmu *pmu) +{ + return pmu && pmu->type >=3D PERF_PMU_TYPE_DRM_START && + pmu->type <=3D PERF_PMU_TYPE_DRM_END; +} + +bool evsel__is_drm(const struct evsel *evsel) +{ + return perf_pmu__is_drm(evsel->pmu); +} + +static struct drm_pmu *add_drm_pmu(struct list_head *pmus, char *line, siz= e_t line_len) +{ + struct drm_pmu *drm; + struct perf_pmu *pmu; + const char *name; + __u32 max_drm_pmu_type =3D 0; + int i =3D 12; + + if (line[line_len - 1] =3D=3D '\n') + line[line_len - 1] =3D '\0'; + while (isspace(line[i])) + i++; + + line[--i] =3D '_'; + line[--i] =3D 'm'; + line[--i] =3D 'r'; + line[--i] =3D 'd'; + name =3D &line[i]; + + list_for_each_entry(pmu, pmus, list) { + if (!perf_pmu__is_drm(pmu)) + continue; + if (pmu->type > max_drm_pmu_type) + max_drm_pmu_type =3D pmu->type; + if (!strcmp(pmu->name, name)) { + /* PMU already exists. */ + return NULL; + } + } + + drm =3D zalloc(sizeof(*drm)); + if (!drm) + return NULL; + + if (max_drm_pmu_type !=3D 0) + drm->pmu.type =3D max_drm_pmu_type + 1; + else + drm->pmu.type =3D PERF_PMU_TYPE_DRM_START; + + if (drm->pmu.type > PERF_PMU_TYPE_DRM_END) { + zfree(&drm); + pr_err("Unable to encode DRM PMU type for %s\n", name); + return NULL; + } + drm->pmu.name =3D strdup(name); + if (!drm->pmu.name) { + zfree(&drm); + return NULL; + } + drm->pmu.cpus =3D perf_cpu_map__new("0"); + if (!drm->pmu.cpus) { + zfree(&drm->pmu.name); + zfree(&drm); + return NULL; + } + INIT_LIST_HEAD(&drm->pmu.format); + INIT_LIST_HEAD(&drm->pmu.aliases); + INIT_LIST_HEAD(&drm->pmu.caps); + return drm; +} + + +static bool starts_with(const char *str, const char *prefix) +{ + return !strncmp(prefix, str, strlen(prefix)); +} + +static int add_event(struct drm_pmu_event **events, int *num_events, + const char *line, enum drm_pmu_unit unit, const char *desc) +{ + const char *colon =3D strchr(line, ':'); + struct drm_pmu_event *tmp; + + if (!colon) + return -EINVAL; + + tmp =3D reallocarray(*events, *num_events + 1, sizeof(struct drm_pmu_even= t)); + if (!tmp) + return -ENOMEM; + tmp[*num_events].unit =3D unit; + tmp[*num_events].desc =3D desc; + tmp[*num_events].name =3D strndup(line, colon - line); + if (!tmp[*num_events].name) + return -ENOMEM; + (*num_events)++; + *events =3D tmp; + return 0; +} + +static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_= name) +{ + struct list_head *pmus =3D args; + char buf[640]; + struct io io; + char *line =3D NULL; + size_t line_len; + struct drm_pmu *drm =3D NULL; + struct drm_pmu_event *events =3D NULL; + int num_events =3D 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd =3D=3D -1) { + /* Failed to open file, ignore. */ + return 0; + } + + while (io__getline(&io, &line, &line_len) > 0) { + if (starts_with(line, "drm-driver:")) { + drm =3D add_drm_pmu(pmus, line, line_len); + if (!drm) + break; + continue; + } + /* + * Note the string matching below is alphabetical, with more + * specific matches appearing before less specific. + */ + if (starts_with(line, "drm-active-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Total memory active in one or more engines"); + continue; + } + if (starts_with(line, "drm-cycles-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_CYCLES, + "Busy cycles"); + continue; + } + if (starts_with(line, "drm-engine-capacity-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_CAPACITY, + "Engine capacity"); + continue; + } + if (starts_with(line, "drm-engine-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_NS, + "Utilization in ns"); + continue; + } + if (starts_with(line, "drm-maxfreq-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_HZ, + "Maximum frequency"); + continue; + } + if (starts_with(line, "drm-purgeable-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of resident and purgeable memory bufers"); + continue; + } + if (starts_with(line, "drm-resident-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of resident memory bufers"); + continue; + } + if (starts_with(line, "drm-shared-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of shared memory bufers"); + continue; + } + if (starts_with(line, "drm-total-cycles-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Total busy cycles"); + continue; + } + if (starts_with(line, "drm-total-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of shared and private memory"); + continue; + } + if (verbose > 1 && starts_with(line, "drm-") && + !starts_with(line, "drm-client-id:") && + !starts_with(line, "drm-pdev:")) + pr_debug("Unhandled DRM PMU fdinfo line match '%s'\n", line); + } + if (drm) { + drm->events =3D events; + drm->num_events =3D num_events; + list_add_tail(&drm->pmu.list, pmus); + } + free(line); + if (io.fd !=3D -1) + close(io.fd); + return 0; +} + +void drm_pmu__exit(struct perf_pmu *pmu) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + free(drm->events); +} + +bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + if (!starts_with(name, "drm-")) + return false; + + for (int i =3D 0; i < drm->num_events; i++) { + if (!strcasecmp(drm->events[i].name, name)) + return true; + } + return false; +} + +int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_e= vent_callback cb) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + for (int i =3D 0; i < drm->num_events; i++) { + char encoding_buf[128]; + struct pmu_event_info info =3D { + .pmu =3D pmu, + .name =3D drm->events[i].name, + .alias =3D NULL, + .scale_unit =3D drm_pmu_scale_unit_strs[drm->events[i].unit], + .desc =3D drm->events[i].desc, + .long_desc =3D NULL, + .encoding_desc =3D encoding_buf, + .topic =3D "drm", + .pmu_name =3D pmu->name, + .event_type_desc =3D "DRM event", + }; + int ret; + + snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=3D0x%x/", pmu->n= ame, i); + + ret =3D cb(state, &info); + if (ret) + return ret; + } + return 0; +} + +size_t drm_pmu__num_events(const struct perf_pmu *pmu) +{ + const struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + return drm->num_events; +} + +static int drm_pmu__index_for_event(const struct drm_pmu *drm, const char = *name) +{ + for (int i =3D 0; i < drm->num_events; i++) { + if (!strcmp(drm->events[i].name, name)) + return i; + } + return -1; +} + +static int drm_pmu__config_term(const struct drm_pmu *drm, + struct perf_event_attr *attr, + struct parse_events_term *term, + struct parse_events_error *err) +{ + if (term->type_term =3D=3D PARSE_EVENTS__TERM_TYPE_USER) { + int i =3D drm_pmu__index_for_event(drm, term->config); + + if (i >=3D 0) { + attr->config =3D i; + return 0; + } + } + if (err) { + char *err_str; + + parse_events_error__handle(err, term->err_val, + asprintf(&err_str, + "unexpected drm event term (%s) %s", + parse_events__term_type_str(term->type_term), + term->config) < 0 + ? strdup("unexpected drm event term") + : err_str, + NULL); + } + return -EINVAL; +} + +int drm_pmu__config_terms(const struct perf_pmu *pmu, + struct perf_event_attr *attr, + struct parse_events_terms *terms, + struct parse_events_error *err) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term; + + list_for_each_entry(term, &terms->terms, list) { + if (drm_pmu__config_term(drm, attr, term, err)) + return -EINVAL; + } + + return 0; +} + +int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_t= erms *terms, + struct perf_pmu_info *info, struct parse_events_error *err) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term =3D + list_first_entry(&terms->terms, struct parse_events_term, list); + + if (term->type_term =3D=3D PARSE_EVENTS__TERM_TYPE_USER) { + int i =3D drm_pmu__index_for_event(drm, term->config); + + if (i >=3D 0) { + info->unit =3D drm_pmu_unit_strs[drm->events[i].unit]; + info->scale =3D 1; + return 0; + } + } + if (err) { + char *err_str; + + parse_events_error__handle(err, term->err_val, + asprintf(&err_str, + "unexpected drm event term (%s) %s", + parse_events__term_type_str(term->type_term), + term->config) < 0 + ? strdup("unexpected drm event term") + : err_str, + NULL); + } + return -EINVAL; +} + +struct minor_info { + unsigned int *minors; + int minors_num, minors_len; +}; + +static int for_each_drm_fdinfo_in_dir(int (*cb)(void *args, int fdinfo_dir= _fd, const char *fd_name), + void *args, int proc_dir, const char *pid_name, + struct minor_info *minors) +{ + char buf[256]; + DIR *fd_dir; + struct dirent *fd_entry; + int fd_dir_fd, fdinfo_dir_fd =3D -1; + + + scnprintf(buf, sizeof(buf), "%s/fd", pid_name); + fd_dir_fd =3D openat(proc_dir, buf, O_DIRECTORY); + if (fd_dir_fd =3D=3D -1) + return 0; /* Presumably lost race to open. */ + fd_dir =3D fdopendir(fd_dir_fd); + if (!fd_dir) { + close(fd_dir_fd); + return -ENOMEM; + } + while ((fd_entry =3D readdir(fd_dir)) !=3D NULL) { + struct stat stat; + unsigned int minor; + bool is_dup =3D false; + int ret; + + if (fd_entry->d_type !=3D DT_LNK) + continue; + + if (fstatat(fd_dir_fd, fd_entry->d_name, &stat, 0) !=3D 0) + continue; + + if ((stat.st_mode & S_IFMT) !=3D S_IFCHR || major(stat.st_rdev) !=3D 226) + continue; + + minor =3D minor(stat.st_rdev); + for (int i =3D 0; i < minors->minors_num; i++) { + if (minor(stat.st_rdev) =3D=3D minors->minors[i]) { + is_dup =3D true; + break; + } + } + if (is_dup) + continue; + + if (minors->minors_num =3D=3D minors->minors_len) { + unsigned int *tmp =3D reallocarray(minors->minors, minors->minors_len += 4, + sizeof(unsigned int)); + + if (tmp) { + minors->minors =3D tmp; + minors->minors_len +=3D 4; + } + } + minors->minors[minors->minors_num++] =3D minor; + if (fdinfo_dir_fd =3D=3D -1) { + /* Open fdinfo dir if we have a DRM fd. */ + scnprintf(buf, sizeof(buf), "%s/fdinfo", pid_name); + fdinfo_dir_fd =3D openat(proc_dir, buf, O_DIRECTORY); + if (fdinfo_dir_fd =3D=3D -1) + continue; + } + ret =3D cb(args, fdinfo_dir_fd, fd_entry->d_name); + if (ret) + return ret; + } + if (fdinfo_dir_fd !=3D -1) + close(fdinfo_dir_fd); + closedir(fd_dir); + return 0; +} + +static int for_each_drm_fdinfo(bool skip_all_duplicates, + int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name), + void *args) +{ + DIR *proc_dir; + struct dirent *proc_entry; + int ret; + /* + * minors maintains an array of DRM minor device numbers seen for a pid, + * or for all pids if skip_all_duplicates is true, so that duplicates + * are ignored. + */ + struct minor_info minors =3D { + .minors =3D NULL, + .minors_num =3D 0, + .minors_len =3D 0, + }; + + proc_dir =3D opendir(procfs__mountpoint()); + if (!proc_dir) + return 0; + + /* Walk through the /proc directory. */ + while ((proc_entry =3D readdir(proc_dir)) !=3D NULL) { + if (proc_entry->d_type !=3D DT_DIR || + !isdigit(proc_entry->d_name[0])) + continue; + if (!skip_all_duplicates) { + /* Reset the seen minor numbers for each pid. */ + minors.minors_num =3D 0; + } + ret =3D for_each_drm_fdinfo_in_dir(cb, args, + dirfd(proc_dir), proc_entry->d_name, + &minors); + if (ret) + break; + } + free(minors.minors); + closedir(proc_dir); + return ret; +} + +int perf_pmus__read_drm_pmus(struct list_head *pmus) +{ + return for_each_drm_fdinfo(/*skip_all_duplicates=3D*/true, read_drm_pmus_= cb, pmus); +} + +int evsel__drm_pmu_open(struct evsel *evsel, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx) +{ + (void)evsel; + (void)threads; + (void)start_cpu_map_idx; + (void)end_cpu_map_idx; + return 0; +} + +static uint64_t read_count_and_apply_unit(const char *count_and_unit, enum= drm_pmu_unit unit) +{ + char *unit_ptr =3D NULL; + uint64_t count =3D strtoul(count_and_unit, &unit_ptr, 10); + + if (!unit_ptr) + return 0; + + while (isblank(*unit_ptr)) + unit_ptr++; + + switch (unit) { + case DRM_PMU_UNIT_BYTES: + if (*unit_ptr =3D=3D '\0') + assert(count =3D=3D 0); /* Generally undocumented, happens for 0. */ + else if (!strcmp(unit_ptr, "KiB")) + count *=3D 1024; + else if (!strcmp(unit_ptr, "MiB")) + count *=3D 1024 * 1024; + else + pr_err("Unexpected bytes unit '%s'\n", unit_ptr); + break; + case DRM_PMU_UNIT_CAPACITY: + /* No units expected. */ + break; + case DRM_PMU_UNIT_CYCLES: + /* No units expected. */ + break; + case DRM_PMU_UNIT_HZ: + if (!strcmp(unit_ptr, "Hz")) + count *=3D 1; + else if (!strcmp(unit_ptr, "KHz")) + count *=3D 1000; + else if (!strcmp(unit_ptr, "MHz")) + count *=3D 1000000; + else + pr_err("Unexpected hz unit '%s'\n", unit_ptr); + break; + case DRM_PMU_UNIT_NS: + /* Only unit ns expected. */ + break; + case DRM_PMU_UNIT_MAX: + default: + break; + } + return count; +} + +static uint64_t read_drm_event(int fdinfo_dir_fd, const char *fd_name, + const char *match, enum drm_pmu_unit unit) +{ + char buf[640]; + struct io io; + char *line =3D NULL; + size_t line_len; + uint64_t count =3D 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd =3D=3D -1) { + /* Failed to open file, ignore. */ + return 0; + } + while (io__getline(&io, &line, &line_len) > 0) { + size_t i =3D strlen(match); + + if (strncmp(line, match, i)) + continue; + if (line[i] !=3D ':') + continue; + while (isblank(line[++i])) + ; + if (line[line_len - 1] =3D=3D '\n') + line[line_len - 1] =3D '\0'; + count =3D read_count_and_apply_unit(&line[i], unit); + break; + } + free(line); + close(io.fd); + return count; +} + +struct read_drm_event_cb_args { + const char *match; + uint64_t count; + enum drm_pmu_unit unit; +}; + +static int read_drm_event_cb(void *vargs, int fdinfo_dir_fd, const char *f= d_name) +{ + struct read_drm_event_cb_args *args =3D vargs; + + args->count +=3D read_drm_event(fdinfo_dir_fd, fd_name, args->match, args= ->unit); + return 0; +} + +static uint64_t drm_pmu__read_system_wide(struct drm_pmu *drm, struct evse= l *evsel) +{ + struct read_drm_event_cb_args args =3D { + .count =3D 0, + .match =3D drm->events[evsel->core.attr.config].name, + .unit =3D drm->events[evsel->core.attr.config].unit, + }; + + for_each_drm_fdinfo(/*skip_all_duplicates=3D*/false, read_drm_event_cb, &= args); + return args.count; +} + +static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *e= vsel, int pid) +{ + struct read_drm_event_cb_args args =3D { + .count =3D 0, + .match =3D drm->events[evsel->core.attr.config].name, + .unit =3D drm->events[evsel->core.attr.config].unit, + }; + struct minor_info minors =3D { + .minors =3D NULL, + .minors_num =3D 0, + .minors_len =3D 0, + }; + int proc_dir =3D open(procfs__mountpoint(), O_DIRECTORY); + char pid_name[12]; + int ret; + + if (proc_dir < 0) + return 0; + + snprintf(pid_name, sizeof(pid_name), "%d", pid); + ret =3D for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pi= d_name, &minors); + free(minors.minors); + close(proc_dir); + return ret =3D=3D 0 ? args.count : 0; +} + +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) +{ + struct drm_pmu *drm =3D container_of(evsel->pmu, struct drm_pmu, pmu); + struct perf_counts_values *count, *old_count =3D NULL; + int pid =3D perf_thread_map__pid(evsel->core.threads, thread); + uint64_t counter; + + if (pid !=3D -1) + counter =3D drm_pmu__read_for_pid(drm, evsel, pid); + else + counter =3D drm_pmu__read_system_wide(drm, evsel); + + if (evsel->prev_raw_counts) + old_count =3D perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); + + count =3D perf_counts(evsel->counts, cpu_map_idx, thread); + if (old_count) { + count->val =3D old_count->val + counter; + count->run =3D old_count->run + 1; + count->ena =3D old_count->ena + 1; + } else { + count->val =3D counter; + count->run++; + count->ena++; + } + return 0; +} diff --git a/tools/perf/util/drm_pmu.h b/tools/perf/util/drm_pmu.h new file mode 100644 index 000000000000..e7f366fca8a4 --- /dev/null +++ b/tools/perf/util/drm_pmu.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __DRM_PMU_H +#define __DRM_PMU_H +/* + * Linux DRM clients expose information through usage stats as documented = in + * Documentation/gpu/drm-usage-stats.rst (available online at + * https://docs.kernel.org/gpu/drm-usage-stats.html). This is a tool like = PMU + * that exposes DRM information. + */ + +#include "pmu.h" +#include + +struct list_head; +struct perf_thread_map; + +void drm_pmu__exit(struct perf_pmu *pmu); +bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name); +int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_e= vent_callback cb); +size_t drm_pmu__num_events(const struct perf_pmu *pmu); +int drm_pmu__config_terms(const struct perf_pmu *pmu, + struct perf_event_attr *attr, + struct parse_events_terms *terms, + struct parse_events_error *err); +int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_t= erms *terms, + struct perf_pmu_info *info, struct parse_events_error *err); + + +bool perf_pmu__is_drm(const struct perf_pmu *pmu); +bool evsel__is_drm(const struct evsel *evsel); + +int perf_pmus__read_drm_pmus(struct list_head *pmus); + +int evsel__drm_pmu_open(struct evsel *evsel, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx); +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread); + +#endif /* __DRM_PMU_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1974395492d7..6722fbdbc72b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -56,6 +56,7 @@ #include "off_cpu.h" #include "pmu.h" #include "pmus.h" +#include "drm_pmu.h" #include "hwmon_pmu.h" #include "tool_pmu.h" #include "rlimit.h" @@ -1888,6 +1889,9 @@ int evsel__read_counter(struct evsel *evsel, int cpu_= map_idx, int thread) if (evsel__is_hwmon(evsel)) return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread); =20 + if (evsel__is_drm(evsel)) + return evsel__drm_pmu_read(evsel, cpu_map_idx, thread); + if (evsel__is_retire_lat(evsel)) return evsel__read_retire_lat(evsel, cpu_map_idx, thread); =20 @@ -2628,6 +2632,11 @@ static int evsel__open_cpu(struct evsel *evsel, stru= ct perf_cpu_map *cpus, start_cpu_map_idx, end_cpu_map_idx); } + if (evsel__is_drm(evsel)) { + return evsel__drm_pmu_open(evsel, threads, + start_cpu_map_idx, + end_cpu_map_idx); + } =20 for (idx =3D start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { cpu =3D perf_cpu_map__cpu(cpus, idx); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index b7ebac5ab1d1..6a2561f8b153 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -20,6 +20,7 @@ #include "debug.h" #include "evsel.h" #include "pmu.h" +#include "drm_pmu.h" #include "hwmon_pmu.h" #include "pmus.h" #include "tool_pmu.h" @@ -1546,6 +1547,8 @@ int perf_pmu__config_terms(const struct perf_pmu *pmu, =20 if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__config_terms(pmu, attr, terms, err); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__config_terms(pmu, attr, terms, err); =20 list_for_each_entry(term, &terms->terms, list) { if (pmu_config_term(pmu, attr, term, terms, zero, apply_hardcoded, err)) @@ -1683,6 +1686,10 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, stru= ct parse_events_terms *head_ ret =3D hwmon_pmu__check_alias(head_terms, info, err); goto out; } + if (perf_pmu__is_drm(pmu)) { + ret =3D drm_pmu__check_alias(pmu, head_terms, info, err); + goto out; + } =20 /* Fake PMU doesn't rewrite terms. */ if (perf_pmu__is_fake(pmu)) @@ -1860,6 +1867,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const= char *name) return false; if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__have_event(pmu, name); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__have_event(pmu, name); if (perf_pmu__find_alias(pmu, name, /*load=3D*/ true) !=3D NULL) return true; if (pmu->cpu_aliases_added || !pmu->events_table) @@ -1873,6 +1882,8 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu) =20 if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__num_events(pmu); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__num_events(pmu); =20 pmu_aliases_parse(pmu); nr =3D pmu->sysfs_aliases + pmu->sys_json_aliases; @@ -1940,6 +1951,8 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bo= ol skip_duplicate_pmus, =20 if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__for_each_event(pmu, state, cb); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__for_each_event(pmu, state, cb); =20 strbuf_init(&sb, /*hint=3D*/ 0); pmu_aliases_parse(pmu); @@ -2403,6 +2416,8 @@ void perf_pmu__delete(struct perf_pmu *pmu) { if (perf_pmu__is_hwmon(pmu)) hwmon_pmu__exit(pmu); + else if (perf_pmu__is_drm(pmu)) + drm_pmu__exit(pmu); =20 perf_pmu__del_formats(&pmu->format); perf_pmu__del_aliases(pmu); diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index b93014cc3670..7bc5ac61e93a 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -38,7 +38,9 @@ struct perf_pmu_caps { =20 enum { PERF_PMU_TYPE_PE_START =3D 0, - PERF_PMU_TYPE_PE_END =3D 0xFFFEFFFF, + PERF_PMU_TYPE_PE_END =3D 0xFFFDFFFF, + PERF_PMU_TYPE_DRM_START =3D 0xFFFE0000, + PERF_PMU_TYPE_DRM_END =3D 0xFFFEFFFF, PERF_PMU_TYPE_HWMON_START =3D 0xFFFF0000, PERF_PMU_TYPE_HWMON_END =3D 0xFFFFFFFD, PERF_PMU_TYPE_TOOL =3D 0xFFFFFFFE, diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 93d5db1c89b4..f7782d6c396a 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -12,6 +12,7 @@ #include #include "cpumap.h" #include "debug.h" +#include "drm_pmu.h" #include "evsel.h" #include "pmus.h" #include "pmu.h" @@ -43,16 +44,19 @@ enum perf_tool_pmu_type { PERF_TOOL_PMU_TYPE_PE_OTHER, PERF_TOOL_PMU_TYPE_TOOL, PERF_TOOL_PMU_TYPE_HWMON, + PERF_TOOL_PMU_TYPE_DRM, =20 #define PERF_TOOL_PMU_TYPE_PE_CORE_MASK (1 << PERF_TOOL_PMU_TYPE_PE_CORE) #define PERF_TOOL_PMU_TYPE_PE_OTHER_MASK (1 << PERF_TOOL_PMU_TYPE_PE_OTHER) #define PERF_TOOL_PMU_TYPE_TOOL_MASK (1 << PERF_TOOL_PMU_TYPE_TOOL) #define PERF_TOOL_PMU_TYPE_HWMON_MASK (1 << PERF_TOOL_PMU_TYPE_HWMON) +#define PERF_TOOL_PMU_TYPE_DRM_MASK (1 << PERF_TOOL_PMU_TYPE_DRM) =20 #define PERF_TOOL_PMU_TYPE_ALL_MASK (PERF_TOOL_PMU_TYPE_PE_CORE_MASK | \ PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | \ PERF_TOOL_PMU_TYPE_TOOL_MASK | \ - PERF_TOOL_PMU_TYPE_HWMON_MASK) + PERF_TOOL_PMU_TYPE_HWMON_MASK | \ + PERF_TOOL_PMU_TYPE_DRM_MASK) }; static unsigned int read_pmu_types; =20 @@ -173,6 +177,8 @@ struct perf_pmu *perf_pmus__find(const char *name) /* Looking up an individual perf event PMU failed, check if a tool PMU sh= ould be read. */ if (!strncmp(name, "hwmon_", 6)) to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + else if (!strncmp(name, "drm_", 4)) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_DRM_MASK; else if (!strcmp(name, "tool")) to_read_pmus |=3D PERF_TOOL_PMU_TYPE_TOOL_MASK; =20 @@ -273,6 +279,10 @@ static void pmu_read_sysfs(unsigned int to_read_types) (read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) =3D=3D 0) perf_pmus__read_hwmon_pmus(&other_pmus); =20 + if ((to_read_types & PERF_TOOL_PMU_TYPE_DRM_MASK) !=3D 0 && + (read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) =3D=3D 0) + perf_pmus__read_drm_pmus(&other_pmus); + list_sort(NULL, &other_pmus, pmus_cmp); =20 read_pmu_types |=3D to_read_types; @@ -305,6 +315,8 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int t= ype) if (type >=3D PERF_PMU_TYPE_PE_START && type <=3D PERF_PMU_TYPE_PE_END) { to_read_pmus =3D PERF_TOOL_PMU_TYPE_PE_CORE_MASK | PERF_TOOL_PMU_TYPE_PE_OTHER_MASK; + } else if (type >=3D PERF_PMU_TYPE_DRM_START && type <=3D PERF_PMU_TYPE_D= RM_END) { + to_read_pmus =3D PERF_TOOL_PMU_TYPE_DRM_MASK; } else if (type >=3D PERF_PMU_TYPE_HWMON_START && type <=3D PERF_PMU_TYPE= _HWMON_END) { to_read_pmus =3D PERF_TOOL_PMU_TYPE_HWMON_MASK; } else { @@ -371,6 +383,10 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf= _pmu *pmu, const char *eve if (parse_hwmon_filename(event, &type, &number, /*item=3D*/NULL, /*alarm= =3D*/NULL)) to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; =20 + /* Could the event be a DRM event? */ + if (strlen(event) > 4 && strncmp("drm-", event, 4) =3D=3D 0) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_DRM_MASK; + pmu_read_sysfs(to_read_pmus); pmu =3D list_prepare_entry(pmu, &core_pmus, list); } @@ -403,11 +419,17 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(st= ruct perf_pmu *pmu, const c * Hwmon PMUs have an alias from a sysfs name like hwmon0, * hwmon1, etc. or have a name of hwmon_. They therefore * can only have a wildcard match if the wildcard begins with - * "hwmon". + * "hwmon". Similarly drm PMUs must start "drm_", avoid reading + * such events unless the PMU could match. */ - if (strisglob(wildcard) || - (strlen(wildcard) >=3D 5 && strncmp("hwmon", wildcard, 5) =3D=3D 0)) + if (strisglob(wildcard)) { + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK | + PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >=3D 4 && strncmp("drm_", wildcard, 4) =3D= =3D 0) { + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >=3D 5 && strncmp("hwmon", wildcard, 5) =3D= =3D 0) { to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + } =20 pmu_read_sysfs(to_read_pmus); pmu =3D list_prepare_entry(pmu, &core_pmus, list); --=20 2.49.0.395.g12beb8f557-goog From nobody Fri Dec 19 10:42:21 2025 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.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 353F1185920 for ; Wed, 26 Mar 2025 03:51:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961076; cv=none; b=YH6Oyy5t8/iEBTRED7qfxHA00E8n/QhphsIcz4oMnE0X9Xu0x8oSazmfj/l8NoYpkLi8MpU2jlZegvCjq3xzjEZKIK3LHoysMPiJAoIB956FNimaur9ODi5JlCdTC1Z4nXmAEPK5t8ScveW87DGyPLVJ6mCBSboHZQv25M7HtBQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1742961076; c=relaxed/simple; bh=x9WzSkt15367O1g9MyuIlk/pYCokLWkTCrAxR8G2AK4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=R54hBOaEaWPanGpZax8NNRrp9HkQr5B6xaInfoYoPtzelKtrByjUCHWgrgHS/fXJyJyq6bJfKDz0HXnL62QH6Tlp+KjA4tZqNz+hWu4EatRMxMTDGGTp81IgJV0k087u1O/fxnc4Kow/XA93aKnwjCxK2CkAw78k+fyzwz96Nt0= 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=Z3YUBf9w; arc=none smtp.client-ip=209.85.214.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="Z3YUBf9w" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-2242f3fd213so100264355ad.1 for ; Tue, 25 Mar 2025 20:51:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1742961074; x=1743565874; 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=ctVsz7hgWpIU/uH3VF5e2aMQbQTrsjnfwBUoye3blFY=; b=Z3YUBf9wvuiAEOasRR3uljAmbTAUq66iDM162PqykaM3A9uiMHOfPOyR4z7OIOawAh EEbtk217oJJidcKO7n+ouk/b21atcdK1zIc5PbupAmU8rYxrI77N9KkR7iWH3TENxrdm cBUex3eMjPO0D0L3HwxlZC4t9M7bswIe8AAC/4po763mCntP3HdkViI8pjhqLqzdSEkv kiKlOuK1A7OtqmedLkG5ORqyfbj0ZxR2V8kG7A8WwvqRhfd5Zrj7GtYZ9V27ysgdJkk7 3a9Kx/TW0oWh2+Bn4wAvb/CsUsrXzRugFhOGjdORjTgFfQeUVeBTpy+89NENRdVfumlC 5UVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742961074; x=1743565874; 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=ctVsz7hgWpIU/uH3VF5e2aMQbQTrsjnfwBUoye3blFY=; b=MnMDJdtAmQ2QtjcIhi9ywbQV6XSQ5qZTSQ0STkPnN5r77QCce3bDwIENnW55LfFXor dpXebJMTpEE5b/8mavuivJn5TDnj1K3kMLqqMVTq409nB/DpIOtJb7jViBMJ+bhOTIR4 706RVLvZLR+1aVMz1zhol14DcmEkljo+XUZXQfofv3A4VjxzlBUXn/mEy1iutmYVRI0P FBkS6XdiUiY+hZclyhPEwobuSyUJaiR8U0XF24/gH396p4wkI23gG6ZYaXqAnBIPb5qd AUwmlkz0usys6upwLnE+uE6xSyKp7ge0Zn6QumkIV0N8KxAwZN1kokLjUwbszsRxJVwa 5JtQ== X-Forwarded-Encrypted: i=1; AJvYcCU4woepN9Mcfiq0MVkxDeohb8GBHh4nY2anmFLsJVvh9shj6RxAMWqhqSpFyJR2hSW9ul1caWAI2VXVtwM=@vger.kernel.org X-Gm-Message-State: AOJu0YzBhyU4uv/siX4zdBW8W5RwyuCW48X2bEyVMtWX8o4fqYxbbSKT +vKeCRpJqpLatKEdDMYkKaUlAn4WvCOUohn1so1gmP0h1ojRv4z/NicNCHDzEdaBIXKf2jBNmeW uUpLZ7g== X-Google-Smtp-Source: AGHT+IFuIYPt5fZK8YyYW50wNwsUW0E0QHMb5IXd5j8+kU/hV6iPRt1mTz1isNYFhwUfLx1ZPXqwltN8EATv X-Received: from pffx4.prod.google.com ([2002:aa7:93a4:0:b0:736:47b8:9b89]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:aa7:888a:0:b0:736:3c2f:acdd with SMTP id d2e1a72fcca58-739059c1873mr29119546b3a.14.1742961074328; Tue, 25 Mar 2025 20:51:14 -0700 (PDT) Date: Tue, 25 Mar 2025 20:50:45 -0700 In-Reply-To: <20250326035045.129440-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: <20250326035045.129440-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.395.g12beb8f557-goog Message-ID: <20250326035045.129440-5-irogers@google.com> Subject: [PATCH v2 4/4] perf tests: Add a DRM PMU test From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The test opens any DRM devices so that the shell has fdinfo files containing the DRM data. The test then uses perf stat to make sure the events can be read. Signed-off-by: Ian Rogers --- tools/perf/tests/shell/drm_pmu.sh | 77 +++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 tools/perf/tests/shell/drm_pmu.sh diff --git a/tools/perf/tests/shell/drm_pmu.sh b/tools/perf/tests/shell/drm= _pmu.sh new file mode 100755 index 000000000000..55477603a80f --- /dev/null +++ b/tools/perf/tests/shell/drm_pmu.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# DRM PMU +# SPDX-License-Identifier: GPL-2.0 + +set -e + +output=3D$(mktemp /tmp/perf.drm_pmu.XXXXXX.txt) + +cleanup() { + rm -f "${output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +# Array to store file descriptors and device names +declare -A device_fds + +# Open all devices and store file descriptors. Opening the device will cre= ate a +# /proc/$$/fdinfo file containing the DRM statistics. +fd_count=3D3 # Start with file descriptor 3 +for device in /dev/dri/* +do + if [[ ! -c "$device" ]] + then + continue + fi + major=3D$(stat -c "%Hr" "$device") + if [[ "$major" !=3D 226 ]] + then + continue + fi + echo "Opening $device" + eval "exec $fd_count<\"$device\"" + echo "fdinfo for: $device (FD: $fd_count)" + cat "/proc/$$/fdinfo/$fd_count" + echo + device_fds["$device"]=3D"$fd_count" + fd_count=3D$((fd_count + 1)) +done + +if [[ ${#device_fds[@]} -eq 0 ]] +then + echo "No DRM devices found [Skip]" + exit 2 +fi + +# For each DRM event +err=3D0 +for p in $(perf list --raw-dump drm-) +do + echo -n "Testing perf stat of $p. " + perf stat -e "$p" --pid=3D$$ true > "$output" 2>&1 + if ! grep -q "$p" "$output" + then + echo "Missing DRM event in: [Failed]" + cat "$output" + err=3D1 + else + echo "[OK]" + fi +done + +# Close all file descriptors +for fd in "${device_fds[@]}"; do + eval "exec $fd<&-" +done + +# Finished +cleanup +exit $err --=20 2.49.0.395.g12beb8f557-goog