From patchwork Wed Mar 26 03:50:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Rogers X-Patchwork-Id: 14029709 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C22F8C3600B for ; Wed, 26 Mar 2025 03:51:12 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4114E10E2AE; Wed, 26 Mar 2025 03:51:09 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="LlDtjzNF"; dkim-atps=neutral Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) by gabe.freedesktop.org (Postfix) with ESMTPS id 82E3410E2AC for ; Wed, 26 Mar 2025 03:51:08 +0000 (UTC) Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-225107fbdc7so107906405ad.0 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=lists.freedesktop.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=LlDtjzNFHqDktgMReCb8uiRtZvTMqwTplH4mbY3IBQ0wTCTzhHurdz4uneplD8om5q 824dQvWTUzwxwSzRIDDILL0NFsicxTC9irXYBXHts3Am2cVWVxOeQU9xkDxmV6w3mzor KZPz2RBb/zsNjkrk3Qy15NO09YDnovQMT8I4KoUEap7+Q7QkaDMWhsr2DCkhS8UcnRY5 A2RGMmuGoETB/hZ9LZi8PNHP3wHc/gB/Vl+kJUbfM/oPg27kt/P9XgSOsHoKkLtXkMKO XolaeNezDBFYEt8KbSgVQXwbDJm1tGoqkTj6JbNbc/QC8kMl95AuCxwl+OruxQmQKsCW ulcQ== 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=CY7Qiorm62QStasEKQ0PVMg5+JS2r9N+/12mdq7EpyoPT9jTxJIivGdGibNBVg28qQ zcX1h6oS8wuztJaMi53HE5uCIHTWCKd378ecBMbQe8wvIDUzzTyyZqvR+kFOADZOAn4B Cq5iDAnXJuH5W4ISOBtSr8cbe+qT9Q6g0rsJnuUbvwjw8dQ/u7AmL5BAm90W9vfXruK4 6vXLj27nqHaN/2i0z2YVpMlyuwAk6i89PycQicRgXQYKM53R28HCTwb3TidnIbhDmN3O mkBzTRC3StiIiI5EQDmL+qZGvbfpaWUE/ZXpvAh8d1R2CWWXayuL6ajFbnyWp5RkvqKG CqeQ== X-Gm-Message-State: AOJu0YxoiUSiJAGydI3fBdDSIvhBSbeDBnEZbdE5sfwjnDDsvLclcsjF /oUF9LUy9EJuRZZqHZwxJn6ptI7jzdw+wbQhTxu3YCE552LZwg5inSmn7GugXdKHoCwi0U3Ahfa VsyGIAQ== 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> 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 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 = get_config_name(parsed_terms); const char *metric_id = get_config_metric_id(parsed_terms); - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan_for_event(pmu, name)) != 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_state *parse_state, INIT_LIST_HEAD(list); - while ((pmu = perf_pmus__scan(pmu)) != NULL) { + while ((pmu = perf_pmus__scan_for_event(pmu, event_name)) != NULL) { bool auto_merge_stats; 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; } +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *event) +{ + bool use_core_pmus = !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 = 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=*/NULL, /*alarm=*/NULL)) + to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu = list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) + return pmu; + + pmu = NULL; + pmu = 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 *pmu) { bool use_core_pmus = !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 type); 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 char *event); const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); From patchwork Wed Mar 26 03:50:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Rogers X-Patchwork-Id: 14029712 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 92545C28B30 for ; Wed, 26 Mar 2025 03:51:20 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DAB9E10E310; Wed, 26 Mar 2025 03:51:19 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="3uG3sukg"; dkim-atps=neutral Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) by gabe.freedesktop.org (Postfix) with ESMTPS id 4D1DE10E310 for ; Wed, 26 Mar 2025 03:51:15 +0000 (UTC) Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-2ff62f96b10so852361a91.0 for ; Tue, 25 Mar 2025 20:51:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1742961070; x=1743565870; darn=lists.freedesktop.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=3uG3sukgAOA3914ej7NdlXnssEZm33KHXsNdsGC0RT43YCoDCZXeDaTMs5HP+mWkBE y2PiljjK3oIxEV0tImFfF44VC7XBEkgUeiy+9BuBc2On4LxhzBf5EZW1kootKySMjvyh OQZGiOb8Xw+UgeUSbj2z0+CPe/4ciwj5Thtvqa2H+0hcHFGfFFzOa9IrMlYhEOfpHelO x3kodCzMFNeR8jR07KfGBOXHly/+ek4R/jh5KDGqRVgrjqw86fMBIinK2tjsCvZtDmZ6 T1dxbrqc5AgNiOcX5LoG8uzUHNz7VQ5uaWQ0wWix1AgE24sLP4FDwYisUFYrkmZnLuIK Cwxg== 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=vrvV/qtJ1FQTAycvcRpNN7fQ2CNJPNReUErZHfTYIAtlDQII2L8CNfrWFmJr1MVsNj HJVKD3V+ijJ3hYPwBmcgHz3L011Oh9cZ4MbZEVcx0iKv0mWsxuFCw/a4O8HGmkopuyuF d48fYAJo661QZKjCt4vAh8VXH7eML2V/yOh+9VzRDMGp1rNGDHFMCrAl86AdYW2oHjcg kcORSWNWh13c0rWX5R6lx4eCq/sjSWgfdRQAF6aTEIWFxheOm78yVAnrYZE0FNWcu5JH Gxf30dK+wKddfXmr7UmPT8TBfPVxZhBFDtwbn0NcSEdK2P4mBKFCuqR2LFsTGxxU5ICb OR3w== X-Gm-Message-State: AOJu0YztyaN10WsnWgd2MNWp7NTUsyQNXFTQQOzdVs7LOu8fWFUYretz M/JoZlXqmE/el9VfDZ4+/kdQ5apsVjrhwFiHbpsOYPUSNnU27+3zedwWnrD9hoXwyCpgJuRPNct XnpWDfg== 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> 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 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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=1000000/' 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 parse_events_state *parse_state pmu = NULL; /* Failed to add, try wildcard expansion of event_or_pmu as a PMU name. */ - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - if (!parse_events__filter_pmu(parse_state, pmu) && - perf_pmu__wildcard_match(pmu, event_or_pmu)) { - bool auto_merge_stats = perf_pmu__auto_merge_stats(pmu); - - if (!parse_events_add_pmu(parse_state, *listp, pmu, - const_parsed_terms, - auto_merge_stats, - /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) { - ok++; - parse_state->wild_card_pmus = true; - } + while ((pmu = perf_pmus__scan_matching_wildcard(pmu, event_or_pmu)) != NULL) { + bool auto_merge_stats; + + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + + auto_merge_stats = perf_pmu__auto_merge_stats(pmu); + + if (!parse_events_add_pmu(parse_state, *listp, pmu, + const_parsed_terms, + auto_merge_stats, + /*alternate_hw_config=*/PERF_COUNT_HW_MAX)) { + ok++; + parse_state->wild_card_pmus = 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" /* * core_pmus: A PMU belongs to core_pmus if it's name is "cpu" or it's sysfs @@ -385,6 +386,47 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const char *eve return NULL; } +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard) +{ + bool use_core_pmus = !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 = 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) >= 5 && strncmp("hwmon", wildcard, 5) == 0)) + to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu = 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 = NULL; + pmu = 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 *pmu) { bool use_core_pmus = !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 type); 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 char *event); +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, const char *wildcard); const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); From patchwork Wed Mar 26 03:50:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Rogers X-Patchwork-Id: 14029710 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id CF8BEC28B30 for ; Wed, 26 Mar 2025 03:51:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 3A63C10E2AF; Wed, 26 Mar 2025 03:51:15 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="03SUpVta"; dkim-atps=neutral Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) by gabe.freedesktop.org (Postfix) with ESMTPS id B7ED810E2AF for ; Wed, 26 Mar 2025 03:51:12 +0000 (UTC) Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2242ce15cc3so102758775ad.1 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=lists.freedesktop.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=03SUpVtaQgGTg2rdyKBd8iji7TjbvT1EeEh247sn5B396Z3+usyC3/YCCaLqGfxNCU RXCCjsnUAHfSIQenjAcsCpqQhekbpVLFn+YmOAG065neOAs/IYPs/2HzfqlmYS8oLJrG +ymp7DhY208lp7TPrDrgbxMa8FOIBY/ojgzW/Qm3OXsiPK3AD8R37ZKnC1q3vQA6BvcV uVGEefr0f8c2pRiAFUFTEsdhua34ZRms4FuMS78uM6FhztsQXysQ+RItBWApakGhX7Vc D1tAwseHXE1rw3jnsC8KDXrCMBqTSVgg4MVxNcXjsdUnRzHBodxA4yw1LCdJ5JSXj5WW bLQQ== 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=XsuIUASl5bLz/fQkglddEIWr0on0OXoJjAJr+aOsIq5o4ZD2lr5DPEXbc4QDJiwd2e R1ZuKsZllg7iCS4UYNSlJnarP3bhMibGkH9F4Tf+1BnwXShmOhojffRQX9WvRjZ9VSaB Kg9VivZDibjkvdOPjf2rvKYoFzuZa23XCndkcMlrzn16IKbPgMzocKTq1JF1Ay5lJMOV 2CLRHaZXEQ2aWjuz3mpI2YL6Bh8O4A4aUydbXUCu7xs8xvwr0k4JpkPNl0Fm8PSR3RAY YgBbdJ1VJgXXENJSv3gYN730PggiJmOV2+52KwbdIQMVp+8cSNxMXFGUm66n0Rl4XWMq QMUA== X-Gm-Message-State: AOJu0YywGsE7eoGC/07YRd/Zo0+x8DCckmuiynTxsQnWsnTs5ht9BPF6 NRlG9IXo7KIuLwrFausOA3rvTpwq1B3btFBAtehYJ/o4RN2Ebl9d8HdyGHrIckm3sVnGyCp5M9v FRev09g== 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> 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 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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,drm-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,drm-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 += pmu.o perf-util-y += pmus.o perf-util-y += pmu-flex.o perf-util-y += pmu-bison.o +perf-util-y += drm_pmu.o perf-util-y += hwmon_pmu.o perf-util-y += tool_pmu.o perf-util-y += 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] = { + "bytes", + "capacity", + "cycles", + "hz", + "ns", +}; + +static const char * const drm_pmu_scale_unit_strs[DRM_PMU_UNIT_MAX] = { + "1bytes", + "1capacity", + "1cycles", + "1hz", + "1ns", +}; + +bool perf_pmu__is_drm(const struct perf_pmu *pmu) +{ + return pmu && pmu->type >= PERF_PMU_TYPE_DRM_START && + pmu->type <= 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, size_t line_len) +{ + struct drm_pmu *drm; + struct perf_pmu *pmu; + const char *name; + __u32 max_drm_pmu_type = 0; + int i = 12; + + if (line[line_len - 1] == '\n') + line[line_len - 1] = '\0'; + while (isspace(line[i])) + i++; + + line[--i] = '_'; + line[--i] = 'm'; + line[--i] = 'r'; + line[--i] = 'd'; + name = &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 = pmu->type; + if (!strcmp(pmu->name, name)) { + /* PMU already exists. */ + return NULL; + } + } + + drm = zalloc(sizeof(*drm)); + if (!drm) + return NULL; + + if (max_drm_pmu_type != 0) + drm->pmu.type = max_drm_pmu_type + 1; + else + drm->pmu.type = 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 = strdup(name); + if (!drm->pmu.name) { + zfree(&drm); + return NULL; + } + drm->pmu.cpus = 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 = strchr(line, ':'); + struct drm_pmu_event *tmp; + + if (!colon) + return -EINVAL; + + tmp = reallocarray(*events, *num_events + 1, sizeof(struct drm_pmu_event)); + if (!tmp) + return -ENOMEM; + tmp[*num_events].unit = unit; + tmp[*num_events].desc = desc; + tmp[*num_events].name = strndup(line, colon - line); + if (!tmp[*num_events].name) + return -ENOMEM; + (*num_events)++; + *events = tmp; + return 0; +} + +static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_name) +{ + struct list_head *pmus = args; + char buf[640]; + struct io io; + char *line = NULL; + size_t line_len; + struct drm_pmu *drm = NULL; + struct drm_pmu_event *events = NULL; + int num_events = 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd == -1) { + /* Failed to open file, ignore. */ + return 0; + } + + while (io__getline(&io, &line, &line_len) > 0) { + if (starts_with(line, "drm-driver:")) { + drm = 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 = events; + drm->num_events = num_events; + list_add_tail(&drm->pmu.list, pmus); + } + free(line); + if (io.fd != -1) + close(io.fd); + return 0; +} + +void drm_pmu__exit(struct perf_pmu *pmu) +{ + struct drm_pmu *drm = 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 = container_of(pmu, struct drm_pmu, pmu); + + if (!starts_with(name, "drm-")) + return false; + + for (int i = 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_event_callback cb) +{ + struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + + for (int i = 0; i < drm->num_events; i++) { + char encoding_buf[128]; + struct pmu_event_info info = { + .pmu = pmu, + .name = drm->events[i].name, + .alias = NULL, + .scale_unit = drm_pmu_scale_unit_strs[drm->events[i].unit], + .desc = drm->events[i].desc, + .long_desc = NULL, + .encoding_desc = encoding_buf, + .topic = "drm", + .pmu_name = pmu->name, + .event_type_desc = "DRM event", + }; + int ret; + + snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%x/", pmu->name, i); + + ret = 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 = 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 = 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 == PARSE_EVENTS__TERM_TYPE_USER) { + int i = drm_pmu__index_for_event(drm, term->config); + + if (i >= 0) { + attr->config = 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 = 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_terms *terms, + struct perf_pmu_info *info, struct parse_events_error *err) +{ + struct drm_pmu *drm = container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term = + list_first_entry(&terms->terms, struct parse_events_term, list); + + if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { + int i = drm_pmu__index_for_event(drm, term->config); + + if (i >= 0) { + info->unit = drm_pmu_unit_strs[drm->events[i].unit]; + info->scale = 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 = -1; + + + scnprintf(buf, sizeof(buf), "%s/fd", pid_name); + fd_dir_fd = openat(proc_dir, buf, O_DIRECTORY); + if (fd_dir_fd == -1) + return 0; /* Presumably lost race to open. */ + fd_dir = fdopendir(fd_dir_fd); + if (!fd_dir) { + close(fd_dir_fd); + return -ENOMEM; + } + while ((fd_entry = readdir(fd_dir)) != NULL) { + struct stat stat; + unsigned int minor; + bool is_dup = false; + int ret; + + if (fd_entry->d_type != DT_LNK) + continue; + + if (fstatat(fd_dir_fd, fd_entry->d_name, &stat, 0) != 0) + continue; + + if ((stat.st_mode & S_IFMT) != S_IFCHR || major(stat.st_rdev) != 226) + continue; + + minor = minor(stat.st_rdev); + for (int i = 0; i < minors->minors_num; i++) { + if (minor(stat.st_rdev) == minors->minors[i]) { + is_dup = true; + break; + } + } + if (is_dup) + continue; + + if (minors->minors_num == minors->minors_len) { + unsigned int *tmp = reallocarray(minors->minors, minors->minors_len + 4, + sizeof(unsigned int)); + + if (tmp) { + minors->minors = tmp; + minors->minors_len += 4; + } + } + minors->minors[minors->minors_num++] = minor; + if (fdinfo_dir_fd == -1) { + /* Open fdinfo dir if we have a DRM fd. */ + scnprintf(buf, sizeof(buf), "%s/fdinfo", pid_name); + fdinfo_dir_fd = openat(proc_dir, buf, O_DIRECTORY); + if (fdinfo_dir_fd == -1) + continue; + } + ret = cb(args, fdinfo_dir_fd, fd_entry->d_name); + if (ret) + return ret; + } + if (fdinfo_dir_fd != -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 = { + .minors = NULL, + .minors_num = 0, + .minors_len = 0, + }; + + proc_dir = opendir(procfs__mountpoint()); + if (!proc_dir) + return 0; + + /* Walk through the /proc directory. */ + while ((proc_entry = readdir(proc_dir)) != NULL) { + if (proc_entry->d_type != DT_DIR || + !isdigit(proc_entry->d_name[0])) + continue; + if (!skip_all_duplicates) { + /* Reset the seen minor numbers for each pid. */ + minors.minors_num = 0; + } + ret = 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=*/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 = NULL; + uint64_t count = 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 == '\0') + assert(count == 0); /* Generally undocumented, happens for 0. */ + else if (!strcmp(unit_ptr, "KiB")) + count *= 1024; + else if (!strcmp(unit_ptr, "MiB")) + count *= 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 *= 1; + else if (!strcmp(unit_ptr, "KHz")) + count *= 1000; + else if (!strcmp(unit_ptr, "MHz")) + count *= 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 = NULL; + size_t line_len; + uint64_t count = 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd == -1) { + /* Failed to open file, ignore. */ + return 0; + } + while (io__getline(&io, &line, &line_len) > 0) { + size_t i = strlen(match); + + if (strncmp(line, match, i)) + continue; + if (line[i] != ':') + continue; + while (isblank(line[++i])) + ; + if (line[line_len - 1] == '\n') + line[line_len - 1] = '\0'; + count = 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 *fd_name) +{ + struct read_drm_event_cb_args *args = vargs; + + args->count += 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 evsel *evsel) +{ + struct read_drm_event_cb_args args = { + .count = 0, + .match = drm->events[evsel->core.attr.config].name, + .unit = drm->events[evsel->core.attr.config].unit, + }; + + for_each_drm_fdinfo(/*skip_all_duplicates=*/false, read_drm_event_cb, &args); + return args.count; +} + +static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *evsel, int pid) +{ + struct read_drm_event_cb_args args = { + .count = 0, + .match = drm->events[evsel->core.attr.config].name, + .unit = drm->events[evsel->core.attr.config].unit, + }; + struct minor_info minors = { + .minors = NULL, + .minors_num = 0, + .minors_len = 0, + }; + int proc_dir = 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 = for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pid_name, &minors); + free(minors.minors); + close(proc_dir); + return ret == 0 ? args.count : 0; +} + +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) +{ + struct drm_pmu *drm = container_of(evsel->pmu, struct drm_pmu, pmu); + struct perf_counts_values *count, *old_count = NULL; + int pid = perf_thread_map__pid(evsel->core.threads, thread); + uint64_t counter; + + if (pid != -1) + counter = drm_pmu__read_for_pid(drm, evsel, pid); + else + counter = drm_pmu__read_system_wide(drm, evsel); + + if (evsel->prev_raw_counts) + old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); + + count = perf_counts(evsel->counts, cpu_map_idx, thread); + if (old_count) { + count->val = old_count->val + counter; + count->run = old_count->run + 1; + count->ena = old_count->ena + 1; + } else { + count->val = 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_event_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_terms *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); + 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); @@ -2628,6 +2632,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct 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); + } for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { cpu = 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, 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); 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, struct parse_events_terms *head_ ret = hwmon_pmu__check_alias(head_terms, info, err); goto out; } + if (perf_pmu__is_drm(pmu)) { + ret = drm_pmu__check_alias(pmu, head_terms, info, err); + goto out; + } /* 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=*/ true) != 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) if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__num_events(pmu); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__num_events(pmu); pmu_aliases_parse(pmu); nr = pmu->sysfs_aliases + pmu->sys_json_aliases; @@ -1940,6 +1951,8 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, 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); strbuf_init(&sb, /*hint=*/ 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); 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 { enum { PERF_PMU_TYPE_PE_START = 0, - PERF_PMU_TYPE_PE_END = 0xFFFEFFFF, + PERF_PMU_TYPE_PE_END = 0xFFFDFFFF, + PERF_PMU_TYPE_DRM_START = 0xFFFE0000, + PERF_PMU_TYPE_DRM_END = 0xFFFEFFFF, PERF_PMU_TYPE_HWMON_START = 0xFFFF0000, PERF_PMU_TYPE_HWMON_END = 0xFFFFFFFD, PERF_PMU_TYPE_TOOL = 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, #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) #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; @@ -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 should be read. */ if (!strncmp(name, "hwmon_", 6)) to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + else if (!strncmp(name, "drm_", 4)) + to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK; else if (!strcmp(name, "tool")) to_read_pmus |= PERF_TOOL_PMU_TYPE_TOOL_MASK; @@ -273,6 +279,10 @@ static void pmu_read_sysfs(unsigned int to_read_types) (read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) == 0) perf_pmus__read_hwmon_pmus(&other_pmus); + if ((to_read_types & PERF_TOOL_PMU_TYPE_DRM_MASK) != 0 && + (read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) == 0) + perf_pmus__read_drm_pmus(&other_pmus); + list_sort(NULL, &other_pmus, pmus_cmp); read_pmu_types |= to_read_types; @@ -305,6 +315,8 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int type) if (type >= PERF_PMU_TYPE_PE_START && type <= PERF_PMU_TYPE_PE_END) { to_read_pmus = PERF_TOOL_PMU_TYPE_PE_CORE_MASK | PERF_TOOL_PMU_TYPE_PE_OTHER_MASK; + } else if (type >= PERF_PMU_TYPE_DRM_START && type <= PERF_PMU_TYPE_DRM_END) { + to_read_pmus = PERF_TOOL_PMU_TYPE_DRM_MASK; } else if (type >= PERF_PMU_TYPE_HWMON_START && type <= PERF_PMU_TYPE_HWMON_END) { to_read_pmus = 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=*/NULL, /*alarm=*/NULL)) to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + /* Could the event be a DRM event? */ + if (strlen(event) > 4 && strncmp("drm-", event, 4) == 0) + to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK; + pmu_read_sysfs(to_read_pmus); pmu = list_prepare_entry(pmu, &core_pmus, list); } @@ -403,11 +419,17 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(struct 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) >= 5 && strncmp("hwmon", wildcard, 5) == 0)) + if (strisglob(wildcard)) { + to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK | + PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >= 4 && strncmp("drm_", wildcard, 4) == 0) { + to_read_pmus |= PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >= 5 && strncmp("hwmon", wildcard, 5) == 0) { to_read_pmus |= PERF_TOOL_PMU_TYPE_HWMON_MASK; + } pmu_read_sysfs(to_read_pmus); pmu = list_prepare_entry(pmu, &core_pmus, list); From patchwork Wed Mar 26 03:50:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Rogers X-Patchwork-Id: 14029711 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 0A8DBC3600B for ; Wed, 26 Mar 2025 03:51:20 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 704CD10E64D; Wed, 26 Mar 2025 03:51:19 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="KhU1OF4i"; dkim-atps=neutral Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.202]) by gabe.freedesktop.org (Postfix) with ESMTPS id 82B5710E310 for ; Wed, 26 Mar 2025 03:51:14 +0000 (UTC) Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-224364f2492so97643305ad.3 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=lists.freedesktop.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=KhU1OF4is7ACoB5iv3fDnGgA7FoMKjVKROnl6P1iSYSwR7BV53AXBLo7T9IhIwesS1 0PQyRLJfdgZHIzBoe7cWgp2fIfF+2lVDw8yINAE3ED5Q/c2dImOx0LMtvTDmBIBkwaUN NG+C+dp986ePJYeRMS+j3lbm4MiCNJTGXYLzkh/dXbwjw4jWzyHTsw0jaBP17KyNrrkd MjfqvyIiyUuFgqtTt/2NJyBagzCm5B+3ApK0qFIL+vg2+LBaUfEk9/5RyvuJHFOKM0w7 Yy9y26Tbl552gtKeM9jWOCzM1xVTFzi3Xkw9hT2kT/j48bBS0haljF7FrgTlOWi7jq8J Excw== 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=eAv1HkYxvV+5GV63FA+2SjJrk59FQt2g+qNvBBXMGgvOm4sh337V9h7CbUsGyeblmw yOtO36tH0Nl5MytZ74OzAEAoqCYXnnY0Bc/kP/QNRc0v4Q+Utiq74InH4PM/cdTFJ9dn zTGfyICA17HclZQmW25d/EhVPjPfCMPQ03Ntm7ssazd+84at6crbvOvk1JKHpPv5trUz rl1jpdvX30W1VyCcMq/yc/pDvUZ+T9MW93eeto74sNbzFZj0TvePlm89eyQwqcYKSOJY aGxCsJZlDFser2otX66Q+Ells3pdWeFMd2sJAMPRE3VNWfxCAlyT+BsuoaRs+OWmujFc O0Gw== X-Gm-Message-State: AOJu0YwPrQUW6P0NBWgquilRXuMPXedV8dTacJ33fnBU+Q8e5TxggYMs HWW8ogxrVkbRppQTl+T+5Lz22ALnoRMoG6uPZH/4SSXmKKn+Iegt/JBE2J/Ixa/uPOM6lzmYHaf ASzIRDQ== 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> 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 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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=$(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 create a +# /proc/$$/fdinfo file containing the DRM statistics. +fd_count=3 # Start with file descriptor 3 +for device in /dev/dri/* +do + if [[ ! -c "$device" ]] + then + continue + fi + major=$(stat -c "%Hr" "$device") + if [[ "$major" != 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"]="$fd_count" + fd_count=$((fd_count + 1)) +done + +if [[ ${#device_fds[@]} -eq 0 ]] +then + echo "No DRM devices found [Skip]" + exit 2 +fi + +# For each DRM event +err=0 +for p in $(perf list --raw-dump drm-) +do + echo -n "Testing perf stat of $p. " + perf stat -e "$p" --pid=$$ true > "$output" 2>&1 + if ! grep -q "$p" "$output" + then + echo "Missing DRM event in: [Failed]" + cat "$output" + err=1 + else + echo "[OK]" + fi +done + +# Close all file descriptors +for fd in "${device_fds[@]}"; do + eval "exec $fd<&-" +done + +# Finished +cleanup +exit $err