From nobody Sat Oct 4 08:05:09 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 1D229258EEE for ; Tue, 19 Aug 2025 01:40:18 +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=1755567620; cv=none; b=Oij6VZXKnHBcjxrwg1ou+fSFwBpoT/D0gBDkubuwOKupsIWbLrjoBNQ1uDwQIs41s7m8jRUefH/xOqfthhye7WTzCuzdOY9/4DtNf88mZvGRH0N96eE1Fg1ghyLzYo0zLRv0RXLiqhzvgm+1rJvJDWpruoUt6f23n46HJ6NP8/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567620; c=relaxed/simple; bh=A1O7CYuDIwfrjpf5HTHYJpAdjlrhkvSWHPKIw6QihHM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=L95NDG5zMoi74A2yazejeIrvyNfM8DwuMocbjYmxNVtS6zY/lM57dkZ/DnHw+iv6ftN+DGB6VeY/6WSX2XE50e8tfvP028xbPujyX6PNOAvtwbkoqjdwkyHAbFlYgVDAyOO1pFRjtp0WxgZ9hoKTHTT2m9snfWgUl3TAxZK1lEo= 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=aTaR1MKG; 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="aTaR1MKG" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-2445806dc88so119778545ad.1 for ; Mon, 18 Aug 2025 18:40:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567618; x=1756172418; 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=mxi/JCK2BLKzi0/ZyhjF6rPP5ww+d440SIzuUKydzxE=; b=aTaR1MKGj+9xp9Zz15ryyFUTDa0jv6U+JNcXnn68JspVpvd39rdUcaR49Z3HnQsrKM dAKQ2Xo1XCEjTGKjq63+m4Imt0374ALfp8AAih8VqREwGLxhQEtseCRU7Q4Yco5FvyiO kEd1zThaXSwFUdVxmNO0kVtPpwtijEpAlTlftJtjH4eENNZyTyA7G+kDCh6c5NZNUM1W pzF2lAljph2HCMExtKgOdOR1+jsyHxa3sZZFFm5KobSatS1XJ14jL85w7ZVTIDYA4YE6 Meq9r6VpxEWh2VbUxdPF+LfCk/wNP5pKRsDunMqbz8zDsMVF+c9ES9nwmP4TNBrPZ1YU dlSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567618; x=1756172418; 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=mxi/JCK2BLKzi0/ZyhjF6rPP5ww+d440SIzuUKydzxE=; b=sp7uRrWjO6sF+i4u+0LXtrlKg4kiuENuyQuXm7b97X3NnrmDBzpDWJduGczH/OqG4A MqosQnHJCgugagBnJvYEl47shOn1uHXk8DNeB1ZspTBZysYPq9P9iK+lhCqqxPReObbL tF7gLswrFE4asIcfcW2dQv1ap1zrwZ5LGvhRL9BVwTZPkTBPTjCBmNBVZGucyTGer/U6 OCNYyKTYlL+BECqluYPYX3CLWP3/ZgXfFqKx5svYQ0gS26O7YvLh6LP9GsKjmBkLeRUu 6h7sMOmpZCdaEvYYVSnflcnL5FsFy1ZksFaJuefvnl6QrQ/8//3ctiXU9pm7NTAM3bZ6 PMEg== X-Forwarded-Encrypted: i=1; AJvYcCWn+nVN6QvotcugiTEt/78RrcB0Bft0b5UCAZPO3jdx6C2pLrU3htmKQEPlGcSDv6yNCyHuIc2BuHQnqoM=@vger.kernel.org X-Gm-Message-State: AOJu0YzsaKDEFbQ0cgrAay7SgOInvir+89uW5QHkjjqQqaecj9cLHAuc /kJTYgsF+o4RpjN4IAwX+iS5/paqe1S57O1VAKStVSD+WK1wAkgOWqCYW8vv4ZPkfN5I48g0hUZ 3ro6eNj7SSw== X-Google-Smtp-Source: AGHT+IF9CeNDcjJLIvg9GhN428Ve6XepqE0wjzKqZf+rotfr+pXMiqOqhylx332A3+FEYRDbtxPeRMI5Z64H X-Received: from plij13.prod.google.com ([2002:a17:903:380d:b0:242:7de4:ef6e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1447:b0:240:79d5:8dc7 with SMTP id d9443c01a7336-245e04d50eamr11330215ad.46.1755567618413; Mon, 18 Aug 2025 18:40:18 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:31 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-2-irogers@google.com> Subject: [PATCH v10 01/11] perf python: Add more exceptions on error paths 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Returning NULL will cause the python interpreter to fail but not report an error. If none wants to be returned then Py_None needs returning. Set the error for the cases returning NULL so that more meaningful interpreter behavior is had. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index ea77bea0306f..d47cbc1c2257 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -485,13 +485,19 @@ static PyObject *pyrf_event__new(const union perf_eve= nt *event) if ((event->header.type < PERF_RECORD_MMAP || event->header.type > PERF_RECORD_SAMPLE) && !(event->header.type =3D=3D PERF_RECORD_SWITCH || - event->header.type =3D=3D PERF_RECORD_SWITCH_CPU_WIDE)) + event->header.type =3D=3D PERF_RECORD_SWITCH_CPU_WIDE)) { + PyErr_Format(PyExc_TypeError, "Unexpected header type %u", + event->header.type); return NULL; + } =20 // FIXME this better be dynamic or we need to parse everything // before calling perf_mmap__consume(), including tracepoint fields. - if (sizeof(pevent->event) < event->header.size) + if (sizeof(pevent->event) < event->header.size) { + PyErr_Format(PyExc_TypeError, "Unexpected event size: %zd < %u", + sizeof(pevent->event), event->header.size); return NULL; + } =20 ptype =3D pyrf_event__type[event->header.type]; pevent =3D PyObject_New(struct pyrf_event, ptype); @@ -1209,8 +1215,10 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyr= f_evlist *pevlist, return NULL; =20 md =3D get_md(evlist, cpu); - if (!md) + if (!md) { + PyErr_Format(PyExc_TypeError, "Unknown CPU '%d'", cpu); return NULL; + } =20 if (perf_mmap__read_init(&md->core) < 0) goto end; --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:09 2025 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.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 24911261B76 for ; Tue, 19 Aug 2025 01:40:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567622; cv=none; b=Nqjvqatb44+zdcSJ7Q6snERDHBLqvi46+MZdvJrUXXX6OkMJbSDzn5QHi3vInQctWLM/8k7D+0a12ayfr8Wg08PLzphC9/jZL3lYoOEP3KZutoaXmRb9AqDk1+2w5EzGaurrPl3UNOVuqIIHRE2sC7mjtvbIXVJyOnjX82/c2Fk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567622; c=relaxed/simple; bh=khs4S/7IxVCuql9myiBmwSw6UVebkoy5hCfsOWhQDjQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=btZpUaj/f7kFzsuaQDwY3m2d9bvMKjxg0NTVHSsHLsvFI1YFkWrtv4r4CVvaYgdlFtNYeFG7MuJrzQ9oIYLfeFTusKEE5bD3mpQcMmlUszB7lQ+RKiyuXkRpSuj3sFOeypRZ0q8cNoMc63+zL6kQVxlcSEcID6JOazEZ0LPCKI8= 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=H7N+yrp5; arc=none smtp.client-ip=209.85.215.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="H7N+yrp5" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-b47253319b8so3232314a12.3 for ; Mon, 18 Aug 2025 18:40:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567620; x=1756172420; 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=Jh50Dmb4zL5UKBnMYgwYnwoepImIAnKG7fDAfETvJtE=; b=H7N+yrp59lN3p1S6A2GqSwh9DIy9RKNwcsIrY/K+E0+3WhXWFbH5XW1urKRE+Lamd+ k3pMZu25oXx8Xi/HX9xgUdPO0kkr3DPvSDktC/pIHdIDdxutzpDg2nwsOkIbAbPoO4UC Yt6M6DN54coHL/2ek1TY3gGEgiYMlYG8njMBiQb3wPogDgfmOc09wCn9UCeHIGUPwC9h CdVGfCbdA6QMu/0RrDpEak38hdZcMGRnmbSTcCf2+fk8bLecH1xUWYJ2CNuHeRZNyjb/ O+cThg1Vm+17hHh6EMdIBDKNv+GZXP7QH0HfzzDnnk11MMZ11Td8qD99NaOIIIb/nN4q 3Msw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567620; x=1756172420; 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=Jh50Dmb4zL5UKBnMYgwYnwoepImIAnKG7fDAfETvJtE=; b=MG4p38kU53zNA+01ua2gl74QW/5YsmEJLyN8ppT63EvWpSvIc/dC3RoaR9gBWa3XFC Qwnrce+4TVkF34nUmIZdEhyFFgWpkQLLjUi1aANCb7Pjfc5yPTYxUC+yCj+LNunsZy7x /AI6DmxVfZz7FjB6cdEZESmwdpdpoa+9da2ZXgkxYDps00s5B6vj2K499cwiHFfj4dBr uIONyLDUvNGd5MsL1dyp/LCKkrK/xGTXDqvZ1pe/6MKFRNCnb2E0XubflN3p96KDyQ9U hV1pJlUeg756OvW9qKDVWXK8/smnVwzF7bfUto8vypmnq/btw8n9kXNHD/vVuNNBh5BY TH2w== X-Forwarded-Encrypted: i=1; AJvYcCU2zfaJDjPrmSWT+UivzCIYgygVRzmPdDyrZzlRmVT3f4djMxKnzKQd7n56mQbbjkAudBJ9+XHCUY6HwU0=@vger.kernel.org X-Gm-Message-State: AOJu0YyyjWoZ2sJUgNCnx7tyJq7kJQebo8S5z6tcqYULgJb5tQ/rG4C6 hyKUhO/0xuFtPEf74UqA+AP7py6Cz0qqfWkcfQd2xcYmQvsSunVyoczHRi6UQWwnVFSDbcWM3qc k8o0T0K8+TQ== X-Google-Smtp-Source: AGHT+IHhO25EIfvqplWW5D7diRrlzK6/WU28ojDclYhVCxL/0RuiRvqPMqemYCaiuJ90EthLaFXcPZ46iV4a X-Received: from plrp4.prod.google.com ([2002:a17:902:b084:b0:240:770f:72d2]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:c410:b0:240:3f43:25f with SMTP id d9443c01a7336-245e041d9e0mr9070415ad.23.1755567620423; Mon, 18 Aug 2025 18:40:20 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:32 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-3-irogers@google.com> Subject: [PATCH v10 02/11] perf python: Improve the tracepoint function if no libtraceevent 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The tracepoint function just returns the tracepoint id, this doesn't require libtraceevent which is only used for parsing the event format data. Implement the function using the id function in tp_pmu. No current code in perf is using this, the previous code migrated to perf.parse_events, but it feels good to have less ifdef HAVE_LIBTRACEEVENT. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index d47cbc1c2257..127934af4828 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -18,6 +18,7 @@ #include "record.h" #include "strbuf.h" #include "thread_map.h" +#include "tp_pmu.h" #include "trace-event.h" #include "metricgroup.h" #include "mmap.h" @@ -1554,10 +1555,6 @@ static const struct perf_constant perf__constants[] = =3D { static PyObject *pyrf__tracepoint(struct pyrf_evsel *pevsel, PyObject *args, PyObject *kwargs) { -#ifndef HAVE_LIBTRACEEVENT - return NULL; -#else - struct tep_event *tp_format; static char *kwlist[] =3D { "sys", "name", NULL }; char *sys =3D NULL; char *name =3D NULL; @@ -1566,12 +1563,7 @@ static PyObject *pyrf__tracepoint(struct pyrf_evsel = *pevsel, &sys, &name)) return NULL; =20 - tp_format =3D trace_event__tp_format(sys, name); - if (IS_ERR(tp_format)) - return PyLong_FromLong(-1); - - return PyLong_FromLong(tp_format->id); -#endif // HAVE_LIBTRACEEVENT + return PyLong_FromLong(tp_pmu__id(sys, name)); } =20 static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:09 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 3E62F26F28B for ; Tue, 19 Aug 2025 01:40:23 +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=1755567624; cv=none; b=eewQ0fux1iM7xLn3vU4DOa3jsTUHZ5KLYgBdBtrk5h1k3Qv1hl4iskestbpaRcBPdXLHM0cFZR0T3IKiPsuWSN0+XLy85nBG3t+ykTKeM8Esq8kWA4zUXzGxdRiEPFo4b8fzZAEZq9SCWTEQF2/wGII8BRITWbVIC+I3HHlO8rc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567624; c=relaxed/simple; bh=T1elWucuGMMiywBQriuEk5+XEmE5bHmcFYClf0QygjU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=iyNwT5UuEI4jwPlkTM8pG6GgXUyVDT10htzNt5tgiK3z413CnnRL61dHzjxYvqguUFbszFL8Yoe7sx5beZCj8QnV2ClKdlKrDBf7tK/CCw9GF3qUfY7s35KrAf/6Zm+E6TxKqH6Fap4gDdUn3pMK8mQEisbppYvp/yDZ9eSI8g4= 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=T8qKWbCR; 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="T8qKWbCR" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-32326e017efso4855481a91.3 for ; Mon, 18 Aug 2025 18:40:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567623; x=1756172423; 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=MQF3nN0QauEUkhCRy8Eabiox2XnlWSfk2RO5PcO3ql4=; b=T8qKWbCRH5aJfh/Rp86tZEX8KtwELVYejFZdxdFR0uIYxm4BxtQW7Uav+LiaKbKpmz qbI84ljx8R0oI2E1MVdfFmYQ31nD4oB+IuD/WhMAk2GGtqO4248kqxzcCO9jvbG6ImMd IP8WqZsW+Y3AQBirUPyijBe3GzczJ0f6TvO1dyeJEoINFiXgDU1a04c/XoaDGRQEBGvG A0f0Y3ExkVbQf1/XJJ1jCNkQQfR/WK9bnVxGDKY8/b6k9Ww7NA3CI5AAFEbLh7IVbCjv d3uPKRnwkTdmBKf3Zyy9+v9e4IXS+zmA08GpXOXKXHqKte9fJhiTJWCbvdO7DphZyAYx i0+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567623; x=1756172423; 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=MQF3nN0QauEUkhCRy8Eabiox2XnlWSfk2RO5PcO3ql4=; b=rfID3NwoJdbZu5EN02u9Caw9jtaRi6cvpt1FzyKDKG2owmNOfIqfJ2nxorrSxvJq72 FHH4HP9yy+fm/c+L2rESQGymHI0ChHVdf+Jq3YTl2HXDFClGZOmdlOhylviT+0WTgIen jqqAps/4coWddnuoz56wZxMJrNU9WMazh81A4V16AI2RRXhcG8jW74gAgdVDWwthzgUX rUkR0RKBIyDhtzdJ2amLDxpW06Ip3MxfWeN4fGfd9/RF/xemc6Safzq3wHwLKNvLfJDn tPhte16Q/Dig9AU4XKAMruuXiNRjBX0AGxHl4cR+rqg6SXbDsWDoEvQuq36iRlZhARRo Sk8w== X-Forwarded-Encrypted: i=1; AJvYcCW8JGMO+qVO23dSbYZWXo8pjuIQ2W1gDTeQtSYIp9HN2KCRyWHfCb/wmoWfWQzsL05P42xlG5vmd7XbQco=@vger.kernel.org X-Gm-Message-State: AOJu0YzGYUPRKCOY87J1mini/dhd/GghXnwf7IqYih2Y/h7nawH0X3sz fCKA6rkstqKhS67iJYZMuSbqhcyR7vcM9zOIiv4Z/zH74H4ddlLUjiCwzukUlHreb27ylpMkSia k2BHX7lA98g== X-Google-Smtp-Source: AGHT+IHYaW94GeIGsQ8WsWTvn0g4KTmdGfV8Cs4z9ozPJ1LK11cFfbC9bH5R++PXeY3kD6SATY84vYN5zbEH X-Received: from pjd4.prod.google.com ([2002:a17:90b:54c4:b0:311:485b:d057]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2584:b0:311:d05c:936 with SMTP id 98e67ed59e1d1-32476b009e2mr1303198a91.17.1755567622505; Mon, 18 Aug 2025 18:40:22 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:33 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-4-irogers@google.com> Subject: [PATCH v10 03/11] perf python: Add basic PMU abstraction and pmus sequence 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add an ability to iterate over PMUs and a basic PMU type then can just show the PMU's name. An example usage: ``` $ python Python 3.12.9 (main, Feb 5 2025, 01:31:18) [GCC 14.2.0] on linux >>> import perf >>> list(perf.pmus()) [pmu(cpu), pmu(breakpoint), pmu(cstate_core), pmu(cstate_pkg), pmu(hwmon_acpitz), pmu(hwmon_ac), pmu(hwmon_bat0), pmu(hwmon_coretemp), pmu(hwmon_iwlwifi_1), pmu(hwmon_nvme), pmu(hwmon_thinkpad), pmu(hwmon_ucsi_source_psy_usbc000_0), pmu(hwmon_ucsi_source_psy_usbc000_0), pmu(i915), pmu(intel_bts), pmu(intel_pt), pmu(kprobe), pmu(msr), pmu(power), pmu(software), pmu(tool), pmu(tracepoint), pmu(uncore_arb), pmu(uncore_cbox_0), pmu(uncore_cbox_1), pmu(uncore_cbox_2), pmu(uncore_cbox_3), pmu(uncore_cbox_4), pmu(uncore_cbox_5), pmu(uncore_cbox_6), pmu(uncore_cbox_7), pmu(uncore_clock), pmu(uncore_imc_free_running_0), pmu(uncore_imc_free_running_1), pmu(uprobe)] ``` Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 140 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 127934af4828..6f9728d365ae 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -649,6 +649,138 @@ static int pyrf_thread_map__setup_types(void) return PyType_Ready(&pyrf_thread_map__type); } =20 +/** + * A python wrapper for perf_pmus that are globally owned by the pmus.c co= de. + */ +struct pyrf_pmu { + PyObject_HEAD + + struct perf_pmu *pmu; +}; + +static void pyrf_pmu__delete(struct pyrf_pmu *ppmu) +{ + Py_TYPE(ppmu)->tp_free((PyObject *)ppmu); +} + +static PyObject *pyrf_pmu__name(PyObject *self) +{ + struct pyrf_pmu *ppmu =3D (void *)self; + + return PyUnicode_FromString(ppmu->pmu->name); +} + +static PyObject *pyrf_pmu__repr(PyObject *self) +{ + struct pyrf_pmu *ppmu =3D (void *)self; + + return PyUnicode_FromFormat("pmu(%s)", ppmu->pmu->name); +} + +static const char pyrf_pmu__doc[] =3D PyDoc_STR("perf Performance Monitori= ng Unit (PMU) object."); + +static PyMethodDef pyrf_pmu__methods[] =3D { + { + .ml_name =3D "name", + .ml_meth =3D (PyCFunction)pyrf_pmu__name, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("Name of the PMU including suffixes.") + }, + { .ml_name =3D NULL, } +}; + +/** The python type for a perf.pmu. */ +static PyTypeObject pyrf_pmu__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "perf.pmu", + .tp_basicsize =3D sizeof(struct pyrf_pmu), + .tp_dealloc =3D (destructor)pyrf_pmu__delete, + .tp_flags =3D Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc =3D pyrf_pmu__doc, + .tp_methods =3D pyrf_pmu__methods, + .tp_str =3D pyrf_pmu__name, + .tp_repr =3D pyrf_pmu__repr, +}; + +static int pyrf_pmu__setup_types(void) +{ + pyrf_pmu__type.tp_new =3D PyType_GenericNew; + return PyType_Ready(&pyrf_pmu__type); +} + + +/** A python iterator for pmus that has no equivalent in the C code. */ +struct pyrf_pmu_iterator { + PyObject_HEAD + struct perf_pmu *pmu; +}; + +static void pyrf_pmu_iterator__dealloc(struct pyrf_pmu_iterator *self) +{ + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject *pyrf_pmu_iterator__new(PyTypeObject *type, PyObject *args= __maybe_unused, + PyObject *kwds __maybe_unused) +{ + struct pyrf_pmu_iterator *itr =3D (void *)type->tp_alloc(type, 0); + + if (itr !=3D NULL) + itr->pmu =3D perf_pmus__scan(/*pmu=3D*/NULL); + + return (PyObject *) itr; +} + +static PyObject *pyrf_pmu_iterator__iter(PyObject *self) +{ + Py_INCREF(self); + return self; +} + +static PyObject *pyrf_pmu_iterator__iternext(PyObject *self) +{ + struct pyrf_pmu_iterator *itr =3D (void *)self; + struct pyrf_pmu *ppmu; + + if (itr->pmu =3D=3D NULL) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + // Create object to return. + ppmu =3D PyObject_New(struct pyrf_pmu, &pyrf_pmu__type); + if (ppmu) { + ppmu->pmu =3D itr->pmu; + // Advance iterator. + itr->pmu =3D perf_pmus__scan(itr->pmu); + } + return (PyObject *)ppmu; +} + +/** The python type for the PMU iterator. */ +static PyTypeObject pyrf_pmu_iterator__type =3D { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name =3D "pmus.iterator", + .tp_doc =3D "Iterator for the pmus string sequence.", + .tp_basicsize =3D sizeof(struct pyrf_pmu_iterator), + .tp_itemsize =3D 0, + .tp_flags =3D Py_TPFLAGS_DEFAULT, + .tp_new =3D pyrf_pmu_iterator__new, + .tp_dealloc =3D (destructor) pyrf_pmu_iterator__dealloc, + .tp_iter =3D pyrf_pmu_iterator__iter, + .tp_iternext =3D pyrf_pmu_iterator__iternext, +}; + +static int pyrf_pmu_iterator__setup_types(void) +{ + return PyType_Ready(&pyrf_pmu_iterator__type); +} + +static PyObject *pyrf__pmus(PyObject *self, PyObject *args) +{ + // Calling the class creates an instance of the iterator. + return PyObject_CallObject((PyObject *) &pyrf_pmu_iterator__type, /*args= =3D*/NULL); +} + struct pyrf_counts_values { PyObject_HEAD =20 @@ -1701,6 +1833,12 @@ static PyMethodDef perf__methods[] =3D { .ml_flags =3D METH_VARARGS, .ml_doc =3D PyDoc_STR("Parse a string of events and return an evlist.") }, + { + .ml_name =3D "pmus", + .ml_meth =3D (PyCFunction) pyrf__pmus, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("Returns a sequence of pmus.") + }, { .ml_name =3D NULL, } }; =20 @@ -1728,6 +1866,8 @@ PyMODINIT_FUNC PyInit_perf(void) pyrf_evsel__setup_types() < 0 || pyrf_thread_map__setup_types() < 0 || pyrf_cpu_map__setup_types() < 0 || + pyrf_pmu_iterator__setup_types() < 0 || + pyrf_pmu__setup_types() < 0 || pyrf_counts_values__setup_types() < 0) return module; =20 --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:09 2025 Received: from mail-pf1-f201.google.com (mail-pf1-f201.google.com [209.85.210.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 6CA3D272E43 for ; Tue, 19 Aug 2025 01:40:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567627; cv=none; b=S4cTne/1++w+cjXAsfVpOWcXgCjHW28BbmsTlJcUVQHu0IL3zwZTc2SYo3DhHMr4rTXjV1PjEol5VzW+5pzsny1+o23gVBZ7Gpu3x+wNsAGmAjq2D40p+tX49+94QbecaGMzxIAq9JbdFRpXiz94R9BnNec/kiMlJQ52OY5Vq9U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567627; c=relaxed/simple; bh=Ot2Dr6PSv8pxCvtkjTQlrv6XbJN1DrZt3H88fFEKjgE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=HWaGtA8iyRWeJ/xEbqMCaqZUS+c+Oj1COUmDLN+c5lNet7RJ/q8WY4PRQ7MxeAJc1c2Df+lvl8Z9GFsDVNDTLEYZijMToyvGcXWpg8IO6wm+C0lVMj/6+pdNXxft9eHVEcXv/9UDQyVu7306rCDo/kJJ7jR18ldqBKEFFP2qzec= 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=ktgQDEaJ; arc=none smtp.client-ip=209.85.210.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="ktgQDEaJ" Received: by mail-pf1-f201.google.com with SMTP id d2e1a72fcca58-76e2eab5baaso3957478b3a.2 for ; Mon, 18 Aug 2025 18:40:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567625; x=1756172425; 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=oj1fRnWH+PUgQsyGSCdXclzhy5JmUhLP1R7WLpzdAGU=; b=ktgQDEaJxP6mfMe96z6l7jP7DAUBjTuZwiixGuInPGCbsOfnckmN9KYWrjx5epLHCu fuwjcahWrx+0y1syLb8Iuf3N+RDDs0qbYgs+uX4gPBxZpMOcq4U5wKb3Ck/QF/FhwaaY oqzZsRhrug/09PmAF3SGVcQ8gpJS6ItqVABGLiHCu6krVpRkvdep+MpOI9RuiNJrJpBc nGOFqbZkt4nAhQe2c0y7eCm9mvfYF1CmQA80SoKP2hOicEcluQ9kDUEfGcdbKnb9L29V IINMgmeCG1USEV+mgzX7plBdEIhfpHqM6U05pUkN0FzEiAyoTZdauMPaLJdtyCFMvOTM 5bsQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567625; x=1756172425; 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=oj1fRnWH+PUgQsyGSCdXclzhy5JmUhLP1R7WLpzdAGU=; b=xHhLpszxptyoyi/vgEvOHLFIYD88YcKgYnOKrQLRSr9IsUlyzdw0jF8kFNkZ3vxPO+ nhRXFXy59ahH6DPuU7SuZVR2iGoB2OEUDtuGO15XVboHebQUKIMYSlkBdgplFy04z5s1 L3AYW+TGOyzb4xfLKHHnbJLDWJrrJ/SnhpqJ6mR1aZfSf1PdR3arisdLP5w1JKmc1SLv L1tY7r+1SLljdpgkRSAjLykCF9r4oQQ1xOoEDtsCPrC/HvoEW/RGmcM6UZ1wd4Qa983P QdF21Z2jPpUCAlsykaeJsHZJ0chFHdG92u/7Fw7VRf6T/0ErX2GKg0Nvuv6K+1cujpwK PTvA== X-Forwarded-Encrypted: i=1; AJvYcCV9piBZYuW8BaJceiOAbiNNcYItIfBJbjLlFUaGNYC9977niLFItxQcGMoZ+zkNuXdd8dm6o51ZKn1ytjc=@vger.kernel.org X-Gm-Message-State: AOJu0YxBdpB6RHRJ5zAxm18bp6bCQ6H7CE0wKZxOqCMqX8PRaHzqs1Ux XfoOs3dxlA0M44c5JF3fx1QTKHmVJOsEJZ2nyaP5bboEEZjolcXNkIt4cv6TW95CXNhriijYtJH L5f0yk4hgqQ== X-Google-Smtp-Source: AGHT+IGBB/9CxgsoUv0P1HhdY5gqY248ujpEmyp6WGrPPHvd4bT5y/9JA+erEo1R9vmuvSbJZrYYKgFYk3q6 X-Received: from pjtd1.prod.google.com ([2002:a17:90b:41:b0:31c:2fe4:33b6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:a11a:b0:240:1e63:2dfd with SMTP id adf61e73a8af0-2430d42ccb1mr1040643637.29.1755567624752; Mon, 18 Aug 2025 18:40:24 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:34 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-5-irogers@google.com> Subject: [PATCH v10 04/11] perf python: Add function returning dictionary of all events on a PMU 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Allow all events on a PMU to be gathered, similar to how perf list gathers event information. An example usage: ``` $ python Python 3.12.9 (main, Feb 5 2025, 01:31:18) [GCC 14.2.0] on linux >>> import perf >>> for pmu in perf.pmus(): ... print(pmu.events()) ... [{'name': 'mem_load_retired.l3_hit', 'desc': 'Retired load instructions... ``` Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 6f9728d365ae..cf1128435022 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -670,6 +670,71 @@ static PyObject *pyrf_pmu__name(PyObject *self) return PyUnicode_FromString(ppmu->pmu->name); } =20 +static bool add_to_dict(PyObject *dict, const char *key, const char *value) +{ + PyObject *pkey, *pvalue; + bool ret; + + if (value =3D=3D NULL) + return true; + + pkey =3D PyUnicode_FromString(key); + pvalue =3D PyUnicode_FromString(value); + + ret =3D pkey && pvalue && PyDict_SetItem(dict, pkey, pvalue) =3D=3D 0; + Py_XDECREF(pkey); + Py_XDECREF(pvalue); + return ret; +} + +static int pyrf_pmu__events_cb(void *state, struct pmu_event_info *info) +{ + PyObject *py_list =3D state; + PyObject *dict =3D PyDict_New(); + + if (!dict) + return -ENOMEM; + + if (!add_to_dict(dict, "name", info->name) || + !add_to_dict(dict, "alias", info->alias) || + !add_to_dict(dict, "scale_unit", info->scale_unit) || + !add_to_dict(dict, "desc", info->desc) || + !add_to_dict(dict, "long_desc", info->long_desc) || + !add_to_dict(dict, "encoding_desc", info->encoding_desc) || + !add_to_dict(dict, "topic", info->topic) || + !add_to_dict(dict, "event_type_desc", info->event_type_desc) || + !add_to_dict(dict, "str", info->str) || + !add_to_dict(dict, "deprecated", info->deprecated ? "deprecated" : NU= LL) || + PyList_Append(py_list, dict) !=3D 0) { + Py_DECREF(dict); + return -ENOMEM; + } + Py_DECREF(dict); + return 0; +} + +static PyObject *pyrf_pmu__events(PyObject *self) +{ + struct pyrf_pmu *ppmu =3D (void *)self; + PyObject *py_list =3D PyList_New(0); + int ret; + + if (!py_list) + return NULL; + + ret =3D perf_pmu__for_each_event(ppmu->pmu, + /*skip_duplicate_pmus=3D*/false, + py_list, + pyrf_pmu__events_cb); + if (ret) { + Py_DECREF(py_list); + errno =3D -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return py_list; +} + static PyObject *pyrf_pmu__repr(PyObject *self) { struct pyrf_pmu *ppmu =3D (void *)self; @@ -680,6 +745,12 @@ static PyObject *pyrf_pmu__repr(PyObject *self) static const char pyrf_pmu__doc[] =3D PyDoc_STR("perf Performance Monitori= ng Unit (PMU) object."); =20 static PyMethodDef pyrf_pmu__methods[] =3D { + { + .ml_name =3D "events", + .ml_meth =3D (PyCFunction)pyrf_pmu__events, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("Returns a sequence of events encoded as a dicti= onaries.") + }, { .ml_name =3D "name", .ml_meth =3D (PyCFunction)pyrf_pmu__name, --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:09 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 61AC4274B26 for ; Tue, 19 Aug 2025 01:40:27 +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=1755567629; cv=none; b=XS0TYylBSj37cE8DRJkUggVGdUxsTpHzbms4YJJmkA88gg+wxrjsIkcuA+zxE0o8Uyddq+dtLM58jWyByBbMi88FFBD6L/0lT2pDjqak+xipQCfGKA/meqrztGGQZ8OCtbfEHaZaOhNtqvQnrKuJpXdUL6b1ck4oy157r0ZG7qI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567629; c=relaxed/simple; bh=Pn9B1v56yJpBRvRrIQkXWfg+7FJnfgn7QwjdJUcM7d0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pMHJDfjgAqleiHQwd7Pb8MYYLF3L5GXYsPNT4pAyBtXaGlwfnRAafVb2Jkb5FeOOLN93kecoQzy4ggM/A97yiag1O7AM/n0DG2ZTFusTbOJCOmcWyX9vQtTi802DE+fjZPRh6p4L4pAvWkeZ9EQTa8aye2zskFDOwZyeU9n3AlU= 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=fbtyAdEW; 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="fbtyAdEW" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-32326e72dfbso9291789a91.3 for ; Mon, 18 Aug 2025 18:40:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567627; x=1756172427; 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=fJxuUhIDwKl+0KEoN8iFKO37qmhXMdDCRYJkEgT5pzo=; b=fbtyAdEWhfXW+ybIB7CWTYiqXkPu+8O/bP8VR3g6C3hcLSlFy83xfVtB7isZvW24hv 80qHcIiKXj/yFpm+n/U/t7lPWkZK5R3arNObfQ9wC0NKu3DvFCm093ftsoyFKh8gySKd fQdqtmKR7No6n6ZiqbvOhP4Zrt+O0ZefC+fogRL/O1UipDncubUPAWq0uZsgCW3S3IHB Tu1WjGxXndpe4XJXaG8/fV+OX6gM0f1BkBDPrnhaqpOOGuZnsWG6VjoPrIWxc9Y257km SUkuuP97MGv99LTl+s0NOrGbI46/zABMSHETnUn10zrhtiUpoyI/0JgO55Ooh+L5uund 0/Dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567627; x=1756172427; 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=fJxuUhIDwKl+0KEoN8iFKO37qmhXMdDCRYJkEgT5pzo=; b=kAq8Cv28wTP6i51OaXbo8liSdCCp/8WKNGett35q1YTQZ7ksxmsRCOFNZrfP0NItgP GBz79Ey8lfRv9eD3h98fMjeE+A9cMdB1IhvflvPke/AgS/oOEqizF8WREuNo3MY+fNa8 Cd7y3bDjp2OTAW6SA9svTuXkAAloLDCIMhK7nCZ9JgnEEYjuoSeDlwnLVHTtTGYMy3RR /u7x/ZjbxRVMtsvXMPzPrGHc9GqwyobIgNdrNSgPLfHtA6wORCUaHymR5Y2ipkcge8p4 dvryzNNkDEHXtGYxRL6WHQkSWPe7dKZ8c8wk/4MajC5nhcjk3blYDe3l4RUTL9pJaZIK QXiw== X-Forwarded-Encrypted: i=1; AJvYcCUKg/raooR04GwDlZDTqX343tN+SIEtLKEc7BswMKIxt4JettWuHk5tKWtIh95JhhaAWcado6sRTJKXOG0=@vger.kernel.org X-Gm-Message-State: AOJu0YzG9kJlS6cp3YwIvdoNqtunECo7i0DqN97RMB3D1SC+hMjBvUVO VIG10v/bk/mef2ZzoGs65cw6UQCohIZabq71hDeR0HugBGPOV/gEr/ukygtxCU9SHhbEwUENTFi Tx4nRFHMYiA== X-Google-Smtp-Source: AGHT+IG3Yrk24yuDekxh+xkzAIf3TG6/yz0bvuWOEA1HQaNeo2f0Sh+Isvd8alLaUgiueGWyvw8Gk3qBGUQs X-Received: from pjj16.prod.google.com ([2002:a17:90b:5550:b0:320:e3e2:6877]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:180d:b0:321:156f:5c00 with SMTP id 98e67ed59e1d1-32476a1d746mr1268855a91.1.1755567626728; Mon, 18 Aug 2025 18:40:26 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:35 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-6-irogers@google.com> Subject: [PATCH v10 05/11] perf ilist: Add new python ilist command 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The perf ilist command is a textual app [1] similar to perf list. In the top-left pane a tree of PMUs is displayed. Selecting a PMU expands the events within it. Selecting an event displays the `perf list` style event information in the top-right pane. When an event is selected it is opened and the counters on each CPU the event is for are periodically read. The bottom of the screen contains a scrollable set of sparklines showing the events in total and on each CPU. Scrolling below the sparklines shows the same data as raw counts. The sparklines are small graphs where the height of the bar is in relation to maximum of the other counts in the graph. By default the counts are read with an interval of 0.1 seconds (10 times per second). A -I/--interval command line option allows the interval to be changed. The oldest read counts are dropped when the counts fill the line causing the sparkline to move from right to left. A search box can be pulled up with the 's' key. 'n' and 'p' iterate through the search results. As some PMUs have hundreds of events a 'c' key will collapse the events in the current PMU to make navigating the PMUs easier. [1] https://textual.textualize.io/ Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/python/ilist.py | 385 +++++++++++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100755 tools/perf/python/ilist.py diff --git a/tools/perf/python/ilist.py b/tools/perf/python/ilist.py new file mode 100755 index 000000000000..22c70a8b31f3 --- /dev/null +++ b/tools/perf/python/ilist.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +"""Interactive perf list.""" + +import argparse +from typing import Any, Dict, Optional, Tuple +import perf +from textual import on +from textual.app import App, ComposeResult +from textual.binding import Binding +from textual.containers import Horizontal, HorizontalGroup, Vertical, Vert= icalScroll +from textual.command import SearchIcon +from textual.screen import ModalScreen +from textual.widgets import Button, Footer, Header, Input, Label, Sparklin= e, Static, Tree +from textual.widgets.tree import TreeNode + + +class ErrorScreen(ModalScreen[bool]): + """Pop up dialog for errors.""" + + CSS =3D """ + ErrorScreen { + align: center middle; + } + """ + + def __init__(self, error: str): + self.error =3D error + super().__init__() + + def compose(self) -> ComposeResult: + yield Button(f"Error: {self.error}", variant=3D"primary", id=3D"er= ror") + + def on_button_pressed(self, event: Button.Pressed) -> None: + self.dismiss(True) + + +class SearchScreen(ModalScreen[str]): + """Pop up dialog for search.""" + + CSS =3D """ + SearchScreen Horizontal { + align: center middle; + margin-top: 1; + } + SearchScreen Input { + width: 1fr; + } + """ + + def compose(self) -> ComposeResult: + yield Horizontal(SearchIcon(), Input(placeholder=3D"Event name")) + + def on_input_submitted(self, event: Input.Submitted) -> None: + """Handle the user pressing Enter in the input field.""" + self.dismiss(event.value) + + +class Counter(HorizontalGroup): + """Two labels for a CPU and its counter value.""" + + CSS =3D """ + Label { + gutter: 1; + } + """ + + def __init__(self, cpu: int) -> None: + self.cpu =3D cpu + super().__init__() + + def compose(self) -> ComposeResult: + label =3D f"cpu{self.cpu}" if self.cpu >=3D 0 else "total" + yield Label(label + " ") + yield Label("0", id=3Df"counter_{label}") + + +class CounterSparkline(HorizontalGroup): + """A Sparkline for a performance counter.""" + + def __init__(self, cpu: int) -> None: + self.cpu =3D cpu + super().__init__() + + def compose(self) -> ComposeResult: + label =3D f"cpu{self.cpu}" if self.cpu >=3D 0 else "total" + yield Label(label) + yield Sparkline([], summary_function=3Dmax, id=3Df"sparkline_{labe= l}") + + +class IListApp(App): + TITLE =3D "Interactive Perf List" + + BINDINGS =3D [ + Binding(key=3D"s", action=3D"search", description=3D"Search", + tooltip=3D"Search events and PMUs"), + Binding(key=3D"n", action=3D"next", description=3D"Next", + tooltip=3D"Next search result or item"), + Binding(key=3D"p", action=3D"prev", description=3D"Previous", + tooltip=3D"Previous search result or item"), + Binding(key=3D"c", action=3D"collapse", description=3D"Collapse", + tooltip=3D"Collapse the current PMU"), + Binding(key=3D"^q", action=3D"quit", description=3D"Quit", + tooltip=3D"Quit the app"), + ] + + CSS =3D """ + /* Make the 'total' sparkline a different color. */ + #sparkline_total > .sparkline--min-color { + color: $accent; + } + #sparkline_total > .sparkline--max-color { + color: $accent 30%; + } + /* + * Make the active_search initially not displayed with the text in + * the middle of the line. + */ + #active_search { + display: none; + width: 100%; + text-align: center; + } + """ + + def __init__(self, interval: float) -> None: + self.interval =3D interval + self.evlist =3D None + self.search_results: list[TreeNode[str]] =3D [] + self.cur_search_result: TreeNode[str] | None =3D None + super().__init__() + + def expand_and_select(self, node: TreeNode[Any]) -> None: + """Expand select a node in the tree.""" + if node.parent: + node.parent.expand() + if node.parent.parent: + node.parent.parent.expand() + node.expand() + node.tree.select_node(node) + node.tree.scroll_to_node(node) + + def set_searched_tree_node(self, previous: bool) -> None: + """Set the cur_search_result node to either the next or previous."= "" + l =3D len(self.search_results) + + if l < 1: + tree: Tree[str] =3D self.query_one("#pmus", Tree) + if previous: + tree.action_cursor_up() + else: + tree.action_cursor_down() + return + + if self.cur_search_result: + idx =3D self.search_results.index(self.cur_search_result) + if previous: + idx =3D idx - 1 if idx > 0 else l - 1 + else: + idx =3D idx + 1 if idx < l - 1 else 0 + else: + idx =3D l - 1 if previous else 0 + + node =3D self.search_results[idx] + if node =3D=3D self.cur_search_result: + return + + self.cur_search_result =3D node + self.expand_and_select(node) + + def action_search(self) -> None: + """Search was chosen.""" + def set_initial_focus(event: str | None) -> None: + """Sets the focus after the SearchScreen is dismissed.""" + + search_label =3D self.query_one("#active_search", Label) + search_label.display =3D True if event else False + if not event: + return + event =3D event.lower() + search_label.update(f'Searching for events matching "{event}"') + + tree: Tree[str] =3D self.query_one("#pmus", Tree) + + def find_search_results(event: str, node: TreeNode[str], + cursor_seen: bool =3D False, + match_after_cursor: Optional[TreeNode[= str]] =3D None + ) -> Tuple[bool, Optional[TreeNode[str= ]]]: + """Find nodes that match the search remembering the one af= ter the cursor.""" + if not cursor_seen and node =3D=3D tree.cursor_node: + cursor_seen =3D True + if node.data and event in node.data: + if cursor_seen and not match_after_cursor: + match_after_cursor =3D node + self.search_results.append(node) + + if node.children: + for child in node.children: + (cursor_seen, match_after_cursor) =3D \ + find_search_results(event, child, cursor_seen,= match_after_cursor) + return (cursor_seen, match_after_cursor) + + self.search_results.clear() + (_, self.cur_search_result) =3D find_search_results(event, tre= e.root) + if len(self.search_results) < 1: + self.push_screen(ErrorScreen(f"Failed to find pmu/event {e= vent}")) + search_label.display =3D False + elif self.cur_search_result: + self.expand_and_select(self.cur_search_result) + else: + self.set_searched_tree_node(previous=3DFalse) + + self.push_screen(SearchScreen(), set_initial_focus) + + def action_next(self) -> None: + """Next was chosen.""" + self.set_searched_tree_node(previous=3DFalse) + + def action_prev(self) -> None: + """Previous was chosen.""" + self.set_searched_tree_node(previous=3DTrue) + + def action_collapse(self) -> None: + """Collapse the potentially large number of events under a PMU.""" + tree: Tree[str] =3D self.query_one("#pmus", Tree) + node =3D tree.cursor_node + if node and node.parent and node.parent.parent: + node.parent.collapse_all() + node.tree.scroll_to_node(node.parent) + + def update_counts(self) -> None: + """Called every interval to update counts.""" + if not self.evlist: + return + + def update_count(cpu: int, count: int): + # Update the raw count display. + counter: Label =3D self.query(f"#counter_cpu{cpu}" if cpu >=3D= 0 else "#counter_total") + if not counter: + return + counter =3D counter.first(Label) + counter.update(str(count)) + + # Update the sparkline. + line: Sparkline =3D self.query(f"#sparkline_cpu{cpu}" if cpu >= =3D 0 else "#sparkline_total") + if not line: + return + line =3D line.first(Sparkline) + # If there are more events than the width, remove the front ev= ent. + if len(line.data) > line.size.width: + line.data.pop(0) + line.data.append(count) + line.mutate_reactive(Sparkline.data) + + # Update the total and each CPU counts, assume there's just 1 evse= l. + total =3D 0 + self.evlist.disable() + for evsel in self.evlist: + for cpu in evsel.cpus(): + aggr =3D 0 + for thread in evsel.threads(): + counts =3D evsel.read(cpu, thread) + aggr +=3D counts.val + update_count(cpu, aggr) + total +=3D aggr + update_count(-1, total) + self.evlist.enable() + + def on_mount(self) -> None: + """When App starts set up periodic event updating.""" + self.update_counts() + self.set_interval(self.interval, self.update_counts) + + def set_pmu_and_event(self, pmu: str, event: str) -> None: + """Updates the event/description and starts the counters.""" + # Remove previous event information. + if self.evlist: + self.evlist.disable() + self.evlist.close() + lines =3D self.query(CounterSparkline) + for line in lines: + line.remove() + lines =3D self.query(Counter) + for line in lines: + line.remove() + + def pmu_event_description(pmu: str, event: str) -> str: + """Find and format event description for {pmu}/{event}/.""" + def get_info(info: Dict[str, str], key: str): + return (info[key] + "\n") if key in info else "" + + for p in perf.pmus(): + if p.name() !=3D pmu: + continue + for info in p.events(): + if "name" not in info or info["name"] !=3D event: + continue + + desc =3D get_info(info, "topic") + desc +=3D get_info(info, "event_type_desc") + desc +=3D get_info(info, "desc") + desc +=3D get_info(info, "long_desc") + desc +=3D get_info(info, "encoding_desc") + return desc + return "description" + + # Parse event, update event text and description. + full_name =3D event if event.startswith(pmu) or ':' in event else = f"{pmu}/{event}/" + self.query_one("#event_name", Label).update(full_name) + self.query_one("#event_description", Static).update(pmu_event_desc= ription(pmu, event)) + + # Open the event. + try: + self.evlist =3D perf.parse_events(full_name) + if self.evlist: + self.evlist.open() + self.evlist.enable() + except: + self.evlist =3D None + + if not self.evlist: + self.push_screen(ErrorScreen(f"Failed to open {full_name}")) + return + + # Add spark lines for all the CPUs. Note, must be done after + # open so that the evlist CPUs have been computed by propagate + # maps. + lines =3D self.query_one("#lines") + line =3D CounterSparkline(cpu=3D-1) + lines.mount(line) + for cpu in self.evlist.all_cpus(): + line =3D CounterSparkline(cpu) + lines.mount(line) + line =3D Counter(cpu=3D-1) + lines.mount(line) + for cpu in self.evlist.all_cpus(): + line =3D Counter(cpu) + lines.mount(line) + + def compose(self) -> ComposeResult: + """Draws the app.""" + def pmu_event_tree() -> Tree: + """Create tree of PMUs with events under.""" + tree: Tree[str] =3D Tree("PMUs", id=3D"pmus") + tree.root.expand() + for pmu in perf.pmus(): + pmu_name =3D pmu.name().lower() + pmu_node =3D tree.root.add(pmu_name, data=3Dpmu_name) + try: + for event in sorted(pmu.events(), key=3Dlambda x: x["n= ame"]): + if "name" in event: + e =3D event["name"].lower() + if "alias" in event: + pmu_node.add_leaf(f'{e} ({event["alias"]})= ', data=3De) + else: + pmu_node.add_leaf(e, data=3De) + except: + # Reading events may fail with EPERM, ignore. + pass + return tree + + yield Header(id=3D"header") + yield Horizontal(Vertical(pmu_event_tree(), id=3D"events"), + Vertical(Label("event name", id=3D"event_name"), + Static("description", markup=3DFalse, id= =3D"event_description"), + )) + yield Label(id=3D"active_search") + yield VerticalScroll(id=3D"lines") + yield Footer(id=3D"footer") + + @on(Tree.NodeSelected) + def on_tree_node_selected(self, event: Tree.NodeSelected[str]) -> None: + """Called when a tree node is selected, selecting the event.""" + if event.node.parent and event.node.parent.parent: + assert event.node.parent.data is not None + assert event.node.data is not None + self.set_pmu_and_event(event.node.parent.data, event.node.data) + + +if __name__ =3D=3D "__main__": + ap =3D argparse.ArgumentParser() + ap.add_argument('-I', '--interval', help=3D"Counter update interval in= seconds", default=3D0.1) + args =3D ap.parse_args() + app =3D IListApp(float(args.interval)) + app.run() --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:10 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 C2DEF277008 for ; Tue, 19 Aug 2025 01:40:29 +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=1755567631; cv=none; b=WVUuudLvJ32ZzcptqtL11h3w1IOusvdzjTwSWFYlWf5Rf1oksnu0Mwpw4/YEGFENbRJa8ePWR4RB6s6v4rBhf4Emq7XkhzQCAtZmzzzWG7nUAWDs7nu/wTxoAMsyXpOsyXrcZAM4XaYhOAIocoPrvTIr7mh+u3jJI6gWN6oJu08= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567631; c=relaxed/simple; bh=bMogRmk2v/hpy/Se06GD5oraPWc0hCml5uLqL0OQKyM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=tIlYO5DKBIOm/RlNJwJJM4lJUAL5K5REp+rJ1o7/tl/X9JixY7pzIsSghvLVgUXKys/MwdoNt7s+5Yb3T8mhTnWl7lO/gibRunmOVe/qwhSMRjtVGFGV81CE+XLSWTga27P0c8I6KJLzteLm2+8hZktAKkwnhBng1FnLDxDF2MU= 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=PtGqpq13; 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="PtGqpq13" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-24457ef983fso94151255ad.0 for ; Mon, 18 Aug 2025 18:40:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567629; x=1756172429; 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=g9s37lLgOScEp8T9bbq+nm4+bsOcVBIeJ4ZJysGLqso=; b=PtGqpq13z2SmZT2vvlPtEChZaQWslFM2FXAO8N0xi0A5K5xwriLPV977EXWLCAoF66 jJm04aEywfFj15Ixh2wJE1+pXrjQwaz2nPEozQCeBh2M3+8/6OJyzI0fAYKLXEQqCchb qBn9KDKdz4ti/N7IGApYjDnPSOk5vEmcy15JneX6rihBZk9RuxA6Gsyd5NRIdHr5h3y7 AYcbY0V3siSrRl46cgUygzkTouXONV6anGHIWfHQy8jJGP1dzbBKZ4tHu0apVIpOeMT5 7vmRyOUXfoJ9KyqXuR1x8qvZxikmGz32Su4jrE56r3e4O6lCVeKZ1eWFVa8AK32zvF7y sDnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567629; x=1756172429; 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=g9s37lLgOScEp8T9bbq+nm4+bsOcVBIeJ4ZJysGLqso=; b=S8zeyQMD1uP3pDWa+NLr8XhCaGf80ee3Ep8kWvdCajA7TKds9nrAj8qM6HEjvg94zc LqwB91YutRXSEo5QV3tu6pF4w87n1JBg5fzW+S2vdA/WelHtd6I1FSRL6n+K+tL1Od6Q GG/cxvONXY/90jB7F7qT30BM2ltegWptkM+6/ZJqErvhpfLwjeFGTPynOsgq+zs5tqvq 5lvW/bokJ2KW1HwOjvTiry/PVxBOn1HAFOFnejqx0quc8b+wGsWzzc73YZ3EInFUAqrn 0pI1XQxF0JXnYrAayY1OIARuZC+5V0c023AvfLdQTgFJE972CGvhlibrUzdsmXO0DSpg CJsA== X-Forwarded-Encrypted: i=1; AJvYcCW5wWBUviPIJIGQJp7UneKhEvbTv+TyzkKT3EGvu15S+6i+ZZcPj8HtCM/QG5uGHkKgABhvCuND6KgKNrU=@vger.kernel.org X-Gm-Message-State: AOJu0YxwluPoD7+el+LsTkwyA72yI1R5SXSGJYwdr1qoaHWIxxr5pJt1 IGlfSbmm3nRGhXbGaCCxo44GQghFd2WcQRcAeNxzpMlW/Odw+Ue2Q8I99OvWhnyrNWzIRSbkl0G SDlm7/p9AIA== X-Google-Smtp-Source: AGHT+IEIIVLT8RvPny3ldxOSOzYu2i+t0KStGenB8Zlxi31UoojIJ+9B54z5+AojHycbg81m1BXFqNuAJvIK X-Received: from pldw5.prod.google.com ([2002:a17:902:ca05:b0:23f:fa41:1de3]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:1251:b0:240:96a:b81d with SMTP id d9443c01a7336-245e02a4fd7mr11046085ad.5.1755567628966; Mon, 18 Aug 2025 18:40:28 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:36 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-7-irogers@google.com> Subject: [PATCH v10 06/11] perf python: Add parse_metrics function 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add parse_metrics function that takes a string of metrics and/or metric groups and returns the evlist containing the events and metrics. For example: ``` >>> import perf >>> perf.parse_metrics("TopdownL1") evlist([cpu/TOPDOWN.SLOTS/,cpu/topdown-retiring/,cpu/topdown-fe-bound/, cpu/topdown-be-bound/,cpu/topdown-bad-spec/,cpu/INT_MISC.CLEARS_COUNT/, cpu/INT_MISC.UOP_DROPPING/]) ``` Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index cf1128435022..48308ed4e1c7 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1891,6 +1891,40 @@ static PyObject *pyrf__parse_events(PyObject *self, = PyObject *args) return result; } =20 +static PyObject *pyrf__parse_metrics(PyObject *self, PyObject *args) +{ + const char *input; + struct evlist evlist =3D {}; + PyObject *result; + PyObject *pcpus =3D NULL, *pthreads =3D NULL; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; + int ret; + + if (!PyArg_ParseTuple(args, "s|OO", &input, &pcpus, &pthreads)) + return NULL; + + threads =3D pthreads ? ((struct pyrf_thread_map *)pthreads)->threads : NU= LL; + cpus =3D pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; + + evlist__init(&evlist, cpus, threads); + ret =3D metricgroup__parse_groups(&evlist, /*pmu=3D*/"all", input, + /*metric_no_group=3D*/ false, + /*metric_no_merge=3D*/ false, + /*metric_no_threshold=3D*/ true, + /*user_requested_cpu_list=3D*/ NULL, + /*system_wide=3D*/true, + /*hardware_aware_grouping=3D*/ false); + if (ret) { + errno =3D -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + result =3D pyrf_evlist__from_evlist(&evlist); + evlist__exit(&evlist); + return result; +} + static PyMethodDef perf__methods[] =3D { { .ml_name =3D "tracepoint", @@ -1904,6 +1938,13 @@ static PyMethodDef perf__methods[] =3D { .ml_flags =3D METH_VARARGS, .ml_doc =3D PyDoc_STR("Parse a string of events and return an evlist.") }, + { + .ml_name =3D "parse_metrics", + .ml_meth =3D (PyCFunction) pyrf__parse_metrics, + .ml_flags =3D METH_VARARGS, + .ml_doc =3D PyDoc_STR( + "Parse a string of metics or metric groups and return an evlist.") + }, { .ml_name =3D "pmus", .ml_meth =3D (PyCFunction) pyrf__pmus, --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:10 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 E2EE427A456 for ; Tue, 19 Aug 2025 01:40:31 +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=1755567633; cv=none; b=lNA7sET1CK2Xi/5txT+KJ6ZnicGL8jHN9zJ6ISP+MK5C5phgS3WALCnPDrGbuLDYpocn+8h4n9uJFP24ojW/tbE1tjcVOz8EMdnQ5gZ+eV67Xxr0QFumJeX69685152zZaqU6r8AfjRoXA7go1Uu89Q0eIsb/+FGLVOFLW1vjng= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567633; c=relaxed/simple; bh=9m3NIbf6G+NKc23GI7ySDayLKzAPlzW0J8oYNOOwiYg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=lEpqtyyJoDDiL3++GIK5umSaoQKAC2SXl0kAP1NzmortPVcFnwdSg4NTqqZCUoydAs8h+WYA2wIebinDWPo7Cvb0FAb6rFprIWBLNh9FOkEtH1BY5T26sWJc0fF2DZ4rLocGNVGtzTnm46qaLPkaLWqjv74qJbZwFmfv9BumZQU= 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=YFXb/5Rf; 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="YFXb/5Rf" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-24456ebed7bso56249855ad.0 for ; Mon, 18 Aug 2025 18:40:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567631; x=1756172431; 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=bLttyQpmtQ9ns5JcMBOEWKXtf65aFs4f20L8xnCqtm8=; b=YFXb/5Rf7oBPJ+f65jUrnJI6l6J7HzKiWofJpgJY60YxYY0T0tn2Lp+7xbPiblZpT1 ukOUuq58jGtbb+5QjOv9jsA1FVJcj52yJccSC9XXVCjSBVpDXsf1U3VV3qpBbttz2X2W XX5eCAcBoafVt7JxQBjwQqDqxzYryIROyuRORb63rO8V3aExIENPZcdPRaSdMN1rnOlC cKOc+omwFumoURFhzM/vAYltH8QiY5O5s5CiD0lHPQiCsQcic/FSVfNjF13E+mf7Hm0q bHzd7jG9J2MlhfXAZqkhP4QW970PHltDtFQl2LxMZ09HKLWqvBxJ9Y6aMKyLvnZPXyS/ UZpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567631; x=1756172431; 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=bLttyQpmtQ9ns5JcMBOEWKXtf65aFs4f20L8xnCqtm8=; b=KdGoYuUpXAIB0VNPG+C5qZZpWkjEpYDM9Uw3zpuekpmrFdUbkAUJlpV56iXrcBli9e dkjw4vhoUFms6WTKxT5+aQd/2ErvC67XBF2fqLeqAd9SlAfLZLXI9L8N4Q01BV9gsCff WPpuDKZAlrJdJYIvAVsE5kWk6YIWPyEnDi+VGiSj+28750A6ov8ZrQb5afb7+uh1AVZp vQkSvdTVT16DjEGSR209KTbq46jYOegIvLgunsnuoqd/qGvXk4qfT3ClFvJ2czfQQ/I8 OTclQTcOFh05vU1MlzFZkfRoYo1dv2NHb+N8zaWXGWyWABJIjhEya4xwwFoaD7inU/xX Opgg== X-Forwarded-Encrypted: i=1; AJvYcCW1vE5GNf2Q46XkbmAUJ493z05c46u/zhoFN2XlDfHUUUv5LsusRe7c6Mexk/MTSNH3Ii9NFh2ND0gbGJU=@vger.kernel.org X-Gm-Message-State: AOJu0YxBEvxovGem2xQEg8l6/IWsGAELW/Vy8ufH0s3eF53uD1BzMsoe NhvOgmpLg304ikQlIjobuH7dQGfZhd8xJtaIH0ReP5+REGp3lya0Hw2HnKIuG6R8lIDUXuYmfEV cEeK+7a1BcA== X-Google-Smtp-Source: AGHT+IEBnLBRfX+O18qzMr8TEjJTaUqakya01MzrAg/MQihzbOmYI0CPTllurPyMzJs1Wj5L618BZe69bcdP X-Received: from plwp11.prod.google.com ([2002:a17:903:248b:b0:235:ee71:80d5]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:90f:b0:235:f091:11e5 with SMTP id d9443c01a7336-245e0e98b88mr7165465ad.10.1755567631203; Mon, 18 Aug 2025 18:40:31 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:37 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-8-irogers@google.com> Subject: [PATCH v10 07/11] perf python: Add evlist metrics function 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The function returns a list of the names of metrics within the evlist. For example: ``` >>> import perf >>> perf.parse_metrics("TopdownL1").metrics() ['tma_bad_speculation', 'tma_frontend_bound', 'tma_backend_bound', 'tma_ret= iring'] ``` Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 48308ed4e1c7..31089f8e5519 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1303,6 +1303,33 @@ static PyObject *pyrf_evlist__all_cpus(struct pyrf_e= vlist *pevlist) return (PyObject *)pcpu_map; } =20 +static PyObject *pyrf_evlist__metrics(struct pyrf_evlist *pevlist) +{ + PyObject *list =3D PyList_New(/*len=3D*/0); + struct rb_node *node; + + if (!list) + return NULL; + + for (node =3D rb_first_cached(&pevlist->evlist.metric_events.entries); no= de; + node =3D rb_next(node)) { + struct metric_event *me =3D container_of(node, struct metric_event, nd); + struct list_head *pos; + + list_for_each(pos, &me->head) { + struct metric_expr *expr =3D container_of(pos, struct metric_expr, nd); + PyObject *str =3D PyUnicode_FromString(expr->metric_name); + + if (!str || PyList_Append(list, str) !=3D 0) { + Py_DECREF(list); + return NULL; + } + Py_DECREF(str); + } + } + return list; +} + static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { @@ -1531,6 +1558,12 @@ static PyMethodDef pyrf_evlist__methods[] =3D { .ml_flags =3D METH_NOARGS, .ml_doc =3D PyDoc_STR("CPU map union of all evsel CPU maps.") }, + { + .ml_name =3D "metrics", + .ml_meth =3D (PyCFunction)pyrf_evlist__metrics, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR("List of metric names within the evlist.") + }, { .ml_name =3D "mmap", .ml_meth =3D (PyCFunction)pyrf_evlist__mmap, --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:10 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 467B127B505 for ; Tue, 19 Aug 2025 01:40:33 +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=1755567636; cv=none; b=RgX4Cx883IF1eWkJ7T4wiVi4ker6brZ5FeY+5f5x8lmbrTGEezMT5pRbqTBgbX6LRgvC3+xRComkjQwn7pT6c+LfDIbyzI+FqmWhFKgmf/5jIxmqWRRQs/y6iePFLvmPN9n7JEH02RF/SPS/DlWin59JmucZolYHVDbdP9Aw3UQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567636; c=relaxed/simple; bh=IReQ/gDzDf181XNqMbXCjWaObwn2+z97Mfoo3hhvrIo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WLH9JY6z6ZkkdAJc5NbR7uDsq2ZJN/fsQbcALkIBvyj4xVDCNLt6e3DETpdrs2Ypp5x/Xq4bPpROoZ6C5sddvKpTpo61nPEmdVP2lojloMeQHHAu69xEs4cYuotqf6vxfkIfgVRevzoD29+IELFEs0TD+VGxEdB3SD32IflYH0M= 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=pSszm8Yx; 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="pSszm8Yx" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-244581ce13aso99400605ad.2 for ; Mon, 18 Aug 2025 18:40:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567633; x=1756172433; 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=r6OQKtyJgp17iiv1Y1ZPyP7AI0CwVjct8ec+x0FCacg=; b=pSszm8Yx2OZhFwXGGSdsAhGRwlkzk7/6hivvrNQa+Q7Ql+jLoA32uumyhyktP0R0VB jxOmkmiL/9B2mXcmnBvuJer1kzkvqwDyBTx57b3ugnxqz2VIH1903g66ZSa9mCGnHlgs f5mI2k9hXPKTC7G3Z78TJRqR+5kEvdX8v8202XEXUfISx4KGJCzborQP5ax8Hodue4ON arSYtUgMqydCRUzZdPEjVZLIcQ7C5KzKFXTZz4ScIg/IPhZTY69IKPHu7u4Z0EOVwaEB LUMmMRgtq9F3K6kpAXT2OTIx9sV7IVP9xPBaFP//tAkxdlPVMD0wqMuoqD0Xlg2TIG3m hW4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567633; x=1756172433; 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=r6OQKtyJgp17iiv1Y1ZPyP7AI0CwVjct8ec+x0FCacg=; b=PY0NzB8wxTjJK4h/jwNaZSK6lqiG1Fm1D3V/YK+8bC0wbUAIkmtPxtBdHPyvsfzl6u XEoMtQzYkpmD6kzPPf0sj6zJplkiLqdwek50kdGzL8aqzi1ANv4OnSPzXQ5AYdPs1imV 8374A6cqQt1j5RkwssvLrLkNqONcA3oSuEjjwj5phBKyo4SyhdIazxuhGLW5Dac0N/rT 44VeGgVXCxwXQiL8Lglmlh5EXN99tWgScvedVZFLBx75Gna+giRST0G5i48Nwzryxds7 gxE9eIdCYWFMve5rS5smxaKd73/EtvPyQbrftUaDuFVAsYEl7ems/AhESIsmPfh036jV 71AQ== X-Forwarded-Encrypted: i=1; AJvYcCUsN2bBTFBghp6dYkEw/RyKytW6Ivs4W87bwxiszwGdqMDVUMkSudt38nQuxlRexcmC5QLTfwFmRvW3wkU=@vger.kernel.org X-Gm-Message-State: AOJu0YwCEn4hiS7YiOy60VeXaVz2TVOnMGX7cI3L6A7kJ8hKgnJBfjQj ckvcfUeZf6J++ybJcvMhRkvt7ebAq969bYOgCZg1hIUrdoYBO7GNdfN8ZoIVVdGaPCS03COhjQ2 5G8vlKjzc6A== X-Google-Smtp-Source: AGHT+IGB0qnLyrcD6hO1Jw97YHzv6tq7Xzuf4h17QXCNH+lxWlw/lRt8SYFEkMyo/+Eur40pWUYWjVycAEBq X-Received: from plbkc6.prod.google.com ([2002:a17:903:33c6:b0:240:7619:64aa]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:2443:b0:242:8a7:6a6c with SMTP id d9443c01a7336-245e02d7629mr10950045ad.17.1755567633423; Mon, 18 Aug 2025 18:40:33 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:38 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-9-irogers@google.com> Subject: [PATCH v10 08/11] perf python: Add evlist compute_metric 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a compute_metric function that computes a metric double value for a given evlist, metric name, CPU and thread. For example: ``` >>> import perf >>> x =3D perf.parse_metrics("TopdownL1") >>> x.open() >>> x.enable() >>> x.disable() >>> x.metrics() ['tma_bad_speculation', 'tma_frontend_bound', 'tma_backend_bound', 'tma_ret= iring'] >>> x.compute_metric('tma_bad_speculation', 0, -1) 0.08605342847131037 ``` Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 125 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 31089f8e5519..e0769538b8d9 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -14,6 +14,7 @@ #include "evlist.h" #include "evsel.h" #include "event.h" +#include "expr.h" #include "print_binary.h" #include "record.h" #include "strbuf.h" @@ -1330,6 +1331,124 @@ static PyObject *pyrf_evlist__metrics(struct pyrf_e= vlist *pevlist) return list; } =20 +static int prepare_metric(const struct metric_expr *mexp, + const struct evsel *evsel, + struct expr_parse_ctx *pctx, + int cpu_idx, int thread_idx) +{ + struct evsel * const *metric_events =3D mexp->metric_events; + struct metric_ref *metric_refs =3D mexp->metric_refs; + + for (int i =3D 0; metric_events[i]; i++) { + char *n =3D strdup(evsel__metric_id(metric_events[i])); + double val, ena, run; + int source_count =3D evsel__source_count(metric_events[i]); + int ret; + struct perf_counts_values *old_count, *new_count; + + if (!n) + return -ENOMEM; + + if (source_count =3D=3D 0) + source_count =3D 1; + + ret =3D evsel__ensure_counts(metric_events[i]); + if (ret) + return ret; + + /* Set up pointers to the old and newly read counter values. */ + old_count =3D perf_counts(metric_events[i]->prev_raw_counts, cpu_idx, th= read_idx); + new_count =3D perf_counts(metric_events[i]->counts, cpu_idx, thread_idx); + /* Update the value in metric_events[i]->counts. */ + evsel__read_counter(metric_events[i], cpu_idx, thread_idx); + + val =3D new_count->val - old_count->val; + ena =3D new_count->ena - old_count->ena; + run =3D new_count->run - old_count->run; + + if (ena !=3D run && run !=3D 0) + val =3D val * ena / run; + ret =3D expr__add_id_val_source_count(pctx, n, val, source_count); + if (ret) + return ret; + } + + for (int i =3D 0; metric_refs && metric_refs[i].metric_name; i++) { + int ret =3D expr__add_ref(pctx, &metric_refs[i]); + + if (ret) + return ret; + } + + return 0; +} + +static PyObject *pyrf_evlist__compute_metric(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs) +{ + int ret, cpu =3D 0, cpu_idx, thread =3D 0, thread_idx; + const char *metric; + struct rb_node *node; + struct metric_expr *mexp =3D NULL; + struct expr_parse_ctx *pctx; + double result =3D 0; + + if (!PyArg_ParseTuple(args, "sii", &metric, &cpu, &thread)) + return NULL; + + for (node =3D rb_first_cached(&pevlist->evlist.metric_events.entries); + mexp =3D=3D NULL && node; + node =3D rb_next(node)) { + struct metric_event *me =3D container_of(node, struct metric_event, nd); + struct list_head *pos; + + list_for_each(pos, &me->head) { + struct metric_expr *e =3D container_of(pos, struct metric_expr, nd); + + if (strcmp(e->metric_name, metric)) + continue; + + if (e->metric_events[0] =3D=3D NULL) + continue; + + cpu_idx =3D perf_cpu_map__idx(e->metric_events[0]->core.cpus, + (struct perf_cpu){.cpu =3D cpu}); + if (cpu_idx < 0) + continue; + + thread_idx =3D perf_thread_map__idx(e->metric_events[0]->core.threads, + thread); + if (thread_idx < 0) + continue; + + mexp =3D e; + break; + } + } + if (!mexp) { + PyErr_Format(PyExc_TypeError, "Unknown metric '%s' for CPU '%d' and thre= ad '%d'", + metric, cpu, thread); + return NULL; + } + + pctx =3D expr__ctx_new(); + if (!pctx) + return PyErr_NoMemory(); + + ret =3D prepare_metric(mexp, mexp->metric_events[0], pctx, cpu_idx, threa= d_idx); + if (ret) { + expr__ctx_free(pctx); + errno =3D -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (expr__parse(&result, pctx, mexp->metric_expr)) + result =3D 0.0; + + expr__ctx_free(pctx); + return PyFloat_FromDouble(result); +} + static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { @@ -1564,6 +1683,12 @@ static PyMethodDef pyrf_evlist__methods[] =3D { .ml_flags =3D METH_NOARGS, .ml_doc =3D PyDoc_STR("List of metric names within the evlist.") }, + { + .ml_name =3D "compute_metric", + .ml_meth =3D (PyCFunction)pyrf_evlist__compute_metric, + .ml_flags =3D METH_VARARGS | METH_KEYWORDS, + .ml_doc =3D PyDoc_STR("compute metric for given name, cpu and thread") + }, { .ml_name =3D "mmap", .ml_meth =3D (PyCFunction)pyrf_evlist__mmap, --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:10 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 E25CB27CB04 for ; Tue, 19 Aug 2025 01:40:35 +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=1755567637; cv=none; b=LwAJHwda+YACoV9q+mkYqKI+EgBBFnzD4ZKYCO0dSA2Ao4hDUtWmhm6QVZ7ePqwG8Wyxp+6P8e8rjhaqy9uRLEZ3xHng8MyT/DTSovIhR1a4dxwJQkCura8zyiwc9OT+GIbJHmgyvIOl/3VNSXMuJwJrsJHr3Idux134/PPf3zE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567637; c=relaxed/simple; bh=54O4qwPl3pYABWv4bHwl6c0YHU2Dn9GO8qnhYsTvTU8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ZGD4z2mNJnTVlk/hXBrg7gnCP7lAYCMWQdmTw55CSv+3bjQTTFujlQYgZQkAE1PO0aWlWpMJxxErraFZMAYWkz1hUiM49b6ptp5vaZ2ysxtzFirvkLHZzbCqk/fipelwY//yvRZ75d6Q+0yPUr457mZ/S8rCTM3o/mxKQopSEog= 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=r31LgyI1; 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="r31LgyI1" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-32326e09c5fso4751249a91.2 for ; Mon, 18 Aug 2025 18:40:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567635; x=1756172435; 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=rmibaETcnJ3KysxuOmPon6EiiVF0b4+yUjRDx5iYegU=; b=r31LgyI1QvWI/uR4+CkHhOQyw6peNq2/+uQ8SfsVjh4qcetoGnjuMBVkSzWbEgwUGK aY8Hg3EzOFQr/Cec8boLXtJ4QWvkJy8/fmnpdWyoDa5efO60KNEnibkqYLZ1qcbt4An+ efxY3X8HCAftmQ3mQ6mYNzvvWyeuWDWi3upqnIOzblU0beHlvXseUWfzGg8/R8prIorc 8ipwosiOqOobDCL93562Fn2WexXGNnvWf7mayR48GXCFqQTCBbZYf0UmGzvbHJG6ZI3v Vfq6Z5I5GTwFniEimAWCMcPUR6PinzEfKXMqS6TGLBPh3pTZeyGIbsu8/8EDpAz8zr1o VPzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567635; x=1756172435; 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=rmibaETcnJ3KysxuOmPon6EiiVF0b4+yUjRDx5iYegU=; b=MDTtF4ysRUUBpSckTseP/VAu4Ok4q2DXoB5vIMur/ERthInViJqM1J/NQKG/4aexOz SiuQRDIg0TmZMkzB0jVKmJYpHZ7SPizTDgoTOrVjJVwRQmKG9DWNYqFkKa4f4WqtE+w7 xsX6gqMBVzqABYO7l233nXlK9Mj7NIb0QLquH8QFpZ2Y0QKj25yCOtCUb5wW9SrYlO6r VyCoYkYaoh4DZhts7ujQ6RNqhJivP124URWBV2SvVtT5HmUkc+a5nLzI/47h/tyr8ylY SABsJFMWcY2RC6ADohLGzk+UkMl7G9U8dtxbVUlJyM7SYWueLhVhnr9bTGgmCEAoiKzg 0Vgw== X-Forwarded-Encrypted: i=1; AJvYcCUEukToQeXyiwyK47rRpvJOFSNE3gOhIUe3gI27Yl4ljAZRyB9GVMr3PBjGr74UVK4LVrxXruW3YwaVKD8=@vger.kernel.org X-Gm-Message-State: AOJu0YxsN/wxoroF4ILOASIVAV4PVWS3fwxRAYghSmaj/0qzE5urNvJX 7Twu6HWoHQAz3v3OOphjrzsozA1Lkn4dR8MAFVjreunCXbdrhfDgq3nfkHFAUQjgChRyXd+tsRL mWJSmf5WPAg== X-Google-Smtp-Source: AGHT+IF8dlH5fhV/I4zHE0ctt3jtWDWmAghBmvFvbGWFwUX1pXP8P4UaRWIlz+JUjNVacfJJ9QXbdTj8JyZv X-Received: from pjee16.prod.google.com ([2002:a17:90b:5790:b0:321:c567:44cf]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:42:b0:313:352f:6620 with SMTP id 98e67ed59e1d1-32476a4a2b6mr1363114a91.4.1755567635430; Mon, 18 Aug 2025 18:40:35 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:39 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-10-irogers@google.com> Subject: [PATCH v10 09/11] perf python: Add metrics function 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The metrics function returns a list dictionaries describing metrics as strings mapping to strings, except for metric groups that are a string mapping to a list of strings. For example: ``` >>> import perf >>> perf.metrics()[0] {'MetricGroup': ['Power'], 'MetricName': 'C10_Pkg_Residency', 'PMU': 'default_core', 'MetricExpr': 'cstate_pkg@c10\\-residency@ / TSC', 'ScaleUnit': '100%', 'BriefDescription': 'C10 residency percent per packag= e'} ``` Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/util/python.c | 86 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index e0769538b8d9..56102034d5b8 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -2083,7 +2083,93 @@ static PyObject *pyrf__parse_metrics(PyObject *self,= PyObject *args) return result; } =20 +static PyObject *pyrf__metrics_groups(const struct pmu_metric *pm) +{ + PyObject *groups =3D PyList_New(/*len=3D*/0); + const char *mg =3D pm->metric_group; + + if (!groups) + return NULL; + + while (mg) { + PyObject *val =3D NULL; + const char *sep =3D strchr(mg, ';'); + size_t len =3D sep ? (size_t)(sep - mg) : strlen(mg); + + if (len > 0) { + val =3D PyUnicode_FromStringAndSize(mg, len); + if (val) + PyList_Append(groups, val); + + Py_XDECREF(val); + } + mg =3D sep ? sep + 1 : NULL; + } + return groups; +} + +static int pyrf__metrics_cb(const struct pmu_metric *pm, + const struct pmu_metrics_table *table __maybe_unused, + void *vdata) +{ + PyObject *py_list =3D vdata; + PyObject *dict =3D PyDict_New(); + PyObject *key =3D dict ? PyUnicode_FromString("MetricGroup") : NULL; + PyObject *value =3D key ? pyrf__metrics_groups(pm) : NULL; + + if (!value || PyDict_SetItem(dict, key, value) !=3D 0) { + Py_XDECREF(key); + Py_XDECREF(value); + Py_XDECREF(dict); + return -ENOMEM; + } + + if (!add_to_dict(dict, "MetricName", pm->metric_name) || + !add_to_dict(dict, "PMU", pm->pmu) || + !add_to_dict(dict, "MetricExpr", pm->metric_expr) || + !add_to_dict(dict, "MetricThreshold", pm->metric_threshold) || + !add_to_dict(dict, "ScaleUnit", pm->unit) || + !add_to_dict(dict, "Compat", pm->compat) || + !add_to_dict(dict, "BriefDescription", pm->desc) || + !add_to_dict(dict, "PublicDescription", pm->long_desc) || + PyList_Append(py_list, dict) !=3D 0) { + Py_DECREF(dict); + return -ENOMEM; + } + Py_DECREF(dict); + return 0; +} + +static PyObject *pyrf__metrics(PyObject *self, PyObject *args) +{ + const struct pmu_metrics_table *table =3D pmu_metrics_table__find(); + PyObject *list =3D PyList_New(/*len=3D*/0); + int ret; + + if (!list) + return NULL; + + ret =3D pmu_metrics_table__for_each_metric(table, pyrf__metrics_cb, list); + if (!ret) + ret =3D pmu_for_each_sys_metric(pyrf__metrics_cb, list); + + if (ret) { + Py_DECREF(list); + errno =3D -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return list; +} + static PyMethodDef perf__methods[] =3D { + { + .ml_name =3D "metrics", + .ml_meth =3D (PyCFunction) pyrf__metrics, + .ml_flags =3D METH_NOARGS, + .ml_doc =3D PyDoc_STR( + "Returns a list of metrics represented as string values in dictionaries= .") + }, { .ml_name =3D "tracepoint", .ml_meth =3D (PyCFunction) pyrf__tracepoint, --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:10 2025 Received: from mail-yb1-f201.google.com (mail-yb1-f201.google.com [209.85.219.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 A983727F00E for ; Tue, 19 Aug 2025 01:40:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567640; cv=none; b=Ww04pU4o9hHFfzDM4RJ9L07Q4cSpKo6x1fI5XRD2uxhBpqaeGTHaOuw83djcEwarheTdbiVoFgJSJ5UJKvEQwvxDG++P1CA9W8OtsL2OgtQeZIOf+QNYY3aXcD83lSY41JgdXBFuRG4FpGnzS28pa2AkcG8TlOPapg6tCp9+r/k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567640; c=relaxed/simple; bh=t4wvV9vwekMMrq2ydrKd6eNCnUoW4JbMeNG6gK6G9V4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=NB9t3EiE9D89W0rVoW3v166CbLIKikfYtzjQdhuvz9Dm8YiaoYf3Pfi3uWMu2qMXxyjmunWtSyBvxFFxzQIKW2wa4EFF7qBlTOI6oQg/pXOoh7BOB9yZMEnCBue3VRHcKQiaU+z/Wdp1lgLSopLQ8VBC//oeqISToUCi4lvHRw8= 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=RGvcmBAM; arc=none smtp.client-ip=209.85.219.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="RGvcmBAM" Received: by mail-yb1-f201.google.com with SMTP id 3f1490d57ef6-e933de385cbso3780735276.2 for ; Mon, 18 Aug 2025 18:40:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567638; x=1756172438; 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=Hl7AyxzldYvZFRTRgIoYqh+Kx2ZHoiUBsCMLQEtAIgk=; b=RGvcmBAMXK9TmiBdMjkygLXxvEZEG7Arx9tAw06Pec4jXYFa22m7n3XZtrSFHZC3pa WbnnpvUcmslLtwudz7/sc2665fFMah7I+tFtyHWyh4sR5jLUYrJ2rEIIzVBm2SfixeZF ysWMzzVRXkgA21d/k9G2gJiuJh/LYBWLHkYBfydLoiGIwsCaTyRenjIoLDDDd1SxCZB9 B9SY2GMxuKWvIvJFsTt8ixTU50Vk9ZtCtmAM8GPdq2ITpeariSz4yhS7VTTPHMriHqa2 dwRbdj7IUt/G5wH2DRRgfg8F70kLExS5i4rMUf3a8mLctq2OB6CtPccxhKWIj9c+BehX FjgA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567638; x=1756172438; 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=Hl7AyxzldYvZFRTRgIoYqh+Kx2ZHoiUBsCMLQEtAIgk=; b=FVJWj9Vcu7WElHbEmjb/l4/VSRrSQwQ6Z50St0Fly59W+rKPv2pLLfnvysfEgccDx+ R5NjNgDq+FGW5Vn3reV0ats3I42e3CE5FMiD86BymuNMxxLP9tG7FHSTGAi2tlqqLeYf UQl+hpmCWoEOs+/WE9e2kBt+TQApwRhhp2BqFOur5lXgDejGmoU1eFuqvXuLu3TU9UmO jJxbBv5+aVvMjm/wE3dj18lKSFOnHLmzPyNPKMQJNhQOyH3+/L7h7WaVGhijtrhnT9wt kX8l0Ezt/uPfJvWFdmqVI4OnlNSEUSt8LoFl3zeEopzew8Ip/ZHJkGlLZuDPGAoq3Muh DfZw== X-Forwarded-Encrypted: i=1; AJvYcCWIDMJGVCKNYwnCYa60HB9VjCp/3VIhDENGiRdR6up8aKtK3ZI7fMysEze7CZ7YNtP4YwqWcRfh+TP8uJE=@vger.kernel.org X-Gm-Message-State: AOJu0YzO6nF78lI3/FrjlJaMzCE2VwDPv4ybhhYKmUr2GaLefJQUmtay eGYknZU2W5qVpLLfzUzHH2D/xqDkFhlh5h2p/NwnJLiVgq43XatATOu7OQg4zs0ppHxo5q+yE4A lo+0Af1YSGA== X-Google-Smtp-Source: AGHT+IGEy55mHjNSF8FticXDHLMT32LWX3I+ztnRk8dOjCjooAEx0eIdkQYy3vXz2DfomggqMRHv/JGZtMkA X-Received: from ybbei5.prod.google.com ([2002:a05:6902:1b85:b0:e93:76f:4c57]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6902:4086:b0:e8f:eb74:24b7 with SMTP id 3f1490d57ef6-e94e63b6330mr1225466276.49.1755567637753; Mon, 18 Aug 2025 18:40:37 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:40 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-11-irogers@google.com> Subject: [PATCH v10 10/11] perf ilist: Add support for metrics 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: Arnaldo Carvalho de Melo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Change tree nodes to having a value of either Metric or PmuEvent, these values have the ability to match searches, be parsed to create evlists and to give a value per CPU and per thread to display. Use perf.metrics to generate a tree of metrics. Most metrics are placed under their metric group, if the metric group name ends with '_group' then the metric group is placed next to the associated metric. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu --- tools/perf/python/ilist.py | 226 +++++++++++++++++++++++++++---------- 1 file changed, 168 insertions(+), 58 deletions(-) diff --git a/tools/perf/python/ilist.py b/tools/perf/python/ilist.py index 22c70a8b31f3..9d6465c60df3 100755 --- a/tools/perf/python/ilist.py +++ b/tools/perf/python/ilist.py @@ -2,19 +2,121 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) """Interactive perf list.""" =20 +from abc import ABC, abstractmethod import argparse +from dataclasses import dataclass +import math from typing import Any, Dict, Optional, Tuple import perf from textual import on from textual.app import App, ComposeResult from textual.binding import Binding from textual.containers import Horizontal, HorizontalGroup, Vertical, Vert= icalScroll +from textual.css.query import NoMatches from textual.command import SearchIcon from textual.screen import ModalScreen from textual.widgets import Button, Footer, Header, Input, Label, Sparklin= e, Static, Tree from textual.widgets.tree import TreeNode =20 =20 +def get_info(info: Dict[str, str], key: str): + return (info[key] + "\n") if key in info else "" + + +class TreeValue(ABC): + """Abstraction for the data of value in the tree.""" + + @abstractmethod + def name(self) -> str: + pass + + @abstractmethod + def description(self) -> str: + pass + + @abstractmethod + def matches(self, query: str) -> bool: + pass + + @abstractmethod + def parse(self) -> perf.evlist: + pass + + @abstractmethod + def value(self, evlist: perf.evlist, evsel: perf.evsel, cpu: int, thre= ad: int) -> float: + pass + + +@dataclass +class Metric(TreeValue): + """A metric in the tree.""" + metric_name: str + + def name(self) -> str: + return self.metric_name + + def description(self) -> str: + """Find and format metric description.""" + for metric in perf.metrics(): + if metric["MetricName"] !=3D self.metric_name: + continue + desc =3D get_info(metric, "BriefDescription") + desc +=3D get_info(metric, "PublicDescription") + desc +=3D get_info(metric, "MetricExpr") + desc +=3D get_info(metric, "MetricThreshold") + return desc + return "description" + + def matches(self, query: str) -> bool: + return query in self.metric_name + + def parse(self) -> perf.evlist: + return perf.parse_metrics(self.metric_name) + + def value(self, evlist: perf.evlist, evsel: perf.evsel, cpu: int, thre= ad: int) -> float: + val =3D evlist.compute_metric(self.metric_name, cpu, thread) + return 0 if math.isnan(val) else val + + +@dataclass +class PmuEvent(TreeValue): + """A PMU and event within the tree.""" + pmu: str + event: str + + def name(self) -> str: + if self.event.startswith(self.pmu) or ':' in self.event: + return self.event + else: + return f"{self.pmu}/{self.event}/" + + def description(self) -> str: + """Find and format event description for {pmu}/{event}/.""" + for p in perf.pmus(): + if p.name() !=3D self.pmu: + continue + for info in p.events(): + if "name" not in info or info["name"] !=3D self.event: + continue + + desc =3D get_info(info, "topic") + desc +=3D get_info(info, "event_type_desc") + desc +=3D get_info(info, "desc") + desc +=3D get_info(info, "long_desc") + desc +=3D get_info(info, "encoding_desc") + return desc + return "description" + + def matches(self, query: str) -> bool: + return query in self.pmu or query in self.event + + def parse(self) -> perf.evlist: + return perf.parse_events(self.name()) + + def value(self, evlist: perf.evlist, evsel: perf.evsel, cpu: int, thre= ad: int) -> float: + return evsel.read(cpu, thread).val + + class ErrorScreen(ModalScreen[bool]): """Pop up dialog for errors.""" =20 @@ -126,8 +228,9 @@ class IListApp(App): def __init__(self, interval: float) -> None: self.interval =3D interval self.evlist =3D None - self.search_results: list[TreeNode[str]] =3D [] - self.cur_search_result: TreeNode[str] | None =3D None + self.selected: Optional[TreeValue] =3D None + self.search_results: list[TreeNode[TreeValue]] =3D [] + self.cur_search_result: TreeNode[TreeValue] | None =3D None super().__init__() =20 def expand_and_select(self, node: TreeNode[Any]) -> None: @@ -145,7 +248,7 @@ class IListApp(App): l =3D len(self.search_results) =20 if l < 1: - tree: Tree[str] =3D self.query_one("#pmus", Tree) + tree: Tree[TreeValue] =3D self.query_one("#root", Tree) if previous: tree.action_cursor_up() else: @@ -180,7 +283,7 @@ class IListApp(App): event =3D event.lower() search_label.update(f'Searching for events matching "{event}"') =20 - tree: Tree[str] =3D self.query_one("#pmus", Tree) + tree: Tree[str] =3D self.query_one("#root", Tree) =20 def find_search_results(event: str, node: TreeNode[str], cursor_seen: bool =3D False, @@ -189,7 +292,7 @@ class IListApp(App): """Find nodes that match the search remembering the one af= ter the cursor.""" if not cursor_seen and node =3D=3D tree.cursor_node: cursor_seen =3D True - if node.data and event in node.data: + if node.data and node.data.matches(event): if cursor_seen and not match_after_cursor: match_after_cursor =3D node self.search_results.append(node) @@ -203,7 +306,7 @@ class IListApp(App): self.search_results.clear() (_, self.cur_search_result) =3D find_search_results(event, tre= e.root) if len(self.search_results) < 1: - self.push_screen(ErrorScreen(f"Failed to find pmu/event {e= vent}")) + self.push_screen(ErrorScreen(f"Failed to find pmu/event or= metric {event}")) search_label.display =3D False elif self.cur_search_result: self.expand_and_select(self.cur_search_result) @@ -221,16 +324,16 @@ class IListApp(App): self.set_searched_tree_node(previous=3DTrue) =20 def action_collapse(self) -> None: - """Collapse the potentially large number of events under a PMU.""" - tree: Tree[str] =3D self.query_one("#pmus", Tree) + """Collapse the part of the tree currently on.""" + tree: Tree[str] =3D self.query_one("#root", Tree) node =3D tree.cursor_node - if node and node.parent and node.parent.parent: + if node and node.parent: node.parent.collapse_all() node.tree.scroll_to_node(node.parent) =20 def update_counts(self) -> None: """Called every interval to update counts.""" - if not self.evlist: + if not self.selected or not self.evlist: return =20 def update_count(cpu: int, count: int): @@ -259,8 +362,7 @@ class IListApp(App): for cpu in evsel.cpus(): aggr =3D 0 for thread in evsel.threads(): - counts =3D evsel.read(cpu, thread) - aggr +=3D counts.val + aggr +=3D self.selected.value(self.evlist, evsel, cpu,= thread) update_count(cpu, aggr) total +=3D aggr update_count(-1, total) @@ -271,47 +373,37 @@ class IListApp(App): self.update_counts() self.set_interval(self.interval, self.update_counts) =20 - def set_pmu_and_event(self, pmu: str, event: str) -> None: + def set_selected(self, value: TreeValue) -> None: """Updates the event/description and starts the counters.""" + try: + label_name =3D self.query_one("#event_name", Label) + event_description =3D self.query_one("#event_description", Sta= tic) + lines =3D self.query_one("#lines") + except NoMatches: + # A race with rendering, ignore the update as we can't + # mount the assumed output widgets. + return + + self.selected =3D value + # Remove previous event information. if self.evlist: self.evlist.disable() self.evlist.close() - lines =3D self.query(CounterSparkline) - for line in lines: - line.remove() - lines =3D self.query(Counter) - for line in lines: + old_lines =3D self.query(CounterSparkline) + for line in old_lines: line.remove() + old_counters =3D self.query(Counter) + for counter in old_counters: + counter.remove() =20 - def pmu_event_description(pmu: str, event: str) -> str: - """Find and format event description for {pmu}/{event}/.""" - def get_info(info: Dict[str, str], key: str): - return (info[key] + "\n") if key in info else "" - - for p in perf.pmus(): - if p.name() !=3D pmu: - continue - for info in p.events(): - if "name" not in info or info["name"] !=3D event: - continue - - desc =3D get_info(info, "topic") - desc +=3D get_info(info, "event_type_desc") - desc +=3D get_info(info, "desc") - desc +=3D get_info(info, "long_desc") - desc +=3D get_info(info, "encoding_desc") - return desc - return "description" - - # Parse event, update event text and description. - full_name =3D event if event.startswith(pmu) or ':' in event else = f"{pmu}/{event}/" - self.query_one("#event_name", Label).update(full_name) - self.query_one("#event_description", Static).update(pmu_event_desc= ription(pmu, event)) + # Update event/metric text and description. + label_name.update(value.name()) + event_description.update(value.description()) =20 # Open the event. try: - self.evlist =3D perf.parse_events(full_name) + self.evlist =3D value.parse() if self.evlist: self.evlist.open() self.evlist.enable() @@ -319,13 +411,12 @@ class IListApp(App): self.evlist =3D None =20 if not self.evlist: - self.push_screen(ErrorScreen(f"Failed to open {full_name}")) + self.push_screen(ErrorScreen(f"Failed to open {value.name()}")) return =20 # Add spark lines for all the CPUs. Note, must be done after # open so that the evlist CPUs have been computed by propagate # maps. - lines =3D self.query_one("#lines") line =3D CounterSparkline(cpu=3D-1) lines.mount(line) for cpu in self.evlist.all_cpus(): @@ -339,28 +430,49 @@ class IListApp(App): =20 def compose(self) -> ComposeResult: """Draws the app.""" - def pmu_event_tree() -> Tree: - """Create tree of PMUs with events under.""" - tree: Tree[str] =3D Tree("PMUs", id=3D"pmus") - tree.root.expand() + def metric_event_tree() -> Tree: + """Create tree of PMUs and metricgroups with events or metrics= under.""" + tree: Tree[TreeValue] =3D Tree("Root", id=3D"root") + pmus =3D tree.root.add("PMUs") for pmu in perf.pmus(): pmu_name =3D pmu.name().lower() - pmu_node =3D tree.root.add(pmu_name, data=3Dpmu_name) + pmu_node =3D pmus.add(pmu_name) try: for event in sorted(pmu.events(), key=3Dlambda x: x["n= ame"]): if "name" in event: e =3D event["name"].lower() if "alias" in event: - pmu_node.add_leaf(f'{e} ({event["alias"]})= ', data=3De) + pmu_node.add_leaf(f'{e} ({event["alias"]})= ', + data=3DPmuEvent(pmu_name= , e)) else: - pmu_node.add_leaf(e, data=3De) + pmu_node.add_leaf(e, data=3DPmuEvent(pmu_n= ame, e)) except: # Reading events may fail with EPERM, ignore. pass + metrics =3D tree.root.add("Metrics") + groups =3D set() + for metric in perf.metrics(): + groups.update(metric["MetricGroup"]) + + def add_metrics_to_tree(node: TreeNode[TreeValue], parent: str= ): + for metric in sorted(perf.metrics(), key=3Dlambda x: x["Me= tricName"]): + if parent in metric["MetricGroup"]: + name =3D metric["MetricName"] + node.add_leaf(name, data=3DMetric(name)) + child_group_name =3D f'{name}_group' + if child_group_name in groups: + add_metrics_to_tree(node.add(child_group_name)= , child_group_name) + + for group in sorted(groups): + if group.endswith('_group'): + continue + add_metrics_to_tree(metrics.add(group), group) + + tree.root.expand() return tree =20 yield Header(id=3D"header") - yield Horizontal(Vertical(pmu_event_tree(), id=3D"events"), + yield Horizontal(Vertical(metric_event_tree(), id=3D"events"), Vertical(Label("event name", id=3D"event_name"), Static("description", markup=3DFalse, id= =3D"event_description"), )) @@ -369,12 +481,10 @@ class IListApp(App): yield Footer(id=3D"footer") =20 @on(Tree.NodeSelected) - def on_tree_node_selected(self, event: Tree.NodeSelected[str]) -> None: + def on_tree_node_selected(self, event: Tree.NodeSelected[TreeValue]) -= > None: """Called when a tree node is selected, selecting the event.""" - if event.node.parent and event.node.parent.parent: - assert event.node.parent.data is not None - assert event.node.data is not None - self.set_pmu_and_event(event.node.parent.data, event.node.data) + if event.node.data: + self.set_selected(event.node.data) =20 =20 if __name__ =3D=3D "__main__": --=20 2.51.0.rc1.167.g924127e9c0-goog From nobody Sat Oct 4 08:05:10 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 B892727F756 for ; Tue, 19 Aug 2025 01:40:40 +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=1755567642; cv=none; b=GbJteY/OuBz2vPi58QgKE9OGCHcqScdhIpHjGBIzmY/6gWpKh8ASqFPzxhhoJ5iQRlMzEKB7J93Xk9Af+PQmZnzYMS1mJ0GG/A5WZwNvQY/m5Jx7ecdDCQWu+UiN/Vo8mlAC3kXI1DHxt2J8fEBQeMxkJHXVczrlPkp83UaEXu4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755567642; c=relaxed/simple; bh=dReP5xDSocTLXuE6lV+2jz+6UPBQqAGaaJLrbMKD+n8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Content-Type; b=IN9fLicjRwY/bIedL0Pah7ilzQMwL2TZNVes2+ow9AR7bImgFRw6yy5V59yCCuFQZZ6Wdc8sh1GafmjJOEjm6c7wLSyzlkr6oIHQQXOH+wj3PFxQeOTBbKPKCd7/esbJShQ6YBt5jIMpAY5rE6WGdb2bsI2rKyryQeS/OoINnUU= 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=VOOMkGGg; 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="VOOMkGGg" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-24457f440f0so51820855ad.0 for ; Mon, 18 Aug 2025 18:40:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1755567640; x=1756172440; darn=vger.kernel.org; h=to:from:subject:message-id:references:mime-version:in-reply-to:date :from:to:cc:subject:date:message-id:reply-to; bh=+fecVTDRHORXkJ+Si5yHb8EKxcHl1U0RUsg9Sl2Llew=; b=VOOMkGGggulRxLX6vQEihLq0JZJIKWUPwZXJ9UfjbdW56FBdjQkXyBH4mxvGZSgYBR 0NDE49XQu/kFsi9z9p/9SfSqITNZhZdGVeBmi89vC0BaV5uLKW3jc7mbyozyCZ0oTkKz sPGNU2weCB2ZqVZSUNTodteAbLrZriRDnwDG51CWpuV26stj02l83jKNq8AJFJ+2+S1V HvIvpqCeynjtBtu6cDmaS6p8xRneOK8lYd2KUfbSt7bMgalgATrASdMKgV3bBmqUUZzq ikX57M/FWJM/2T565AlxXnNrzsSyrxKh3+urIgqVDBAqMgZtmXupB91+FZYkMXOTbg8u xrmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755567640; x=1756172440; h=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=+fecVTDRHORXkJ+Si5yHb8EKxcHl1U0RUsg9Sl2Llew=; b=ScHw954cFQNGpyhlPBGprjC5tM6E9pZ6ZtCvSoYpivJqKVam4Ul0w2wkuAceDhoK5F 1dn0T/JhLkYAYK9UAMPaLxyPNuZSNAhD1oiYck+aN/YtkNzKzqQacre9SReVTS8q2W2M g2Tj//QwxAMvXlPb8rjm1JV8GE0O8/vv8g8O/lG3OQ24pF6vmBNP09WSm+Gc5nuKaxpl X7f2XuOaoHxp2LsjKDosCFtYyM6TECPOp4m696Iaa2143thRdhWMG/kt5kNyTNje8enp GQvI/Ebl9fJgkWibUwcFHoZqQugHmz1BbMsIiip2DOr/uEEGJlT1GB5xvXyv474elIw2 6i+w== X-Forwarded-Encrypted: i=1; AJvYcCWVapRvdQjEXkzKU70iVXQhPX8Hp6Lu/8BGmv1xMhYLWy0pKHpEgbYAXssoBzt7W6LQwPFMLKO4RhA1YeU=@vger.kernel.org X-Gm-Message-State: AOJu0Yx1F9cj2VCxZw/3UiIGobEOGJ++a++4/gAgYiSWO5yCwCDgK+qC Kn5C6pFfrJkaqMHCd7BzJK8p4dR2Zx4WxJjtKuXh4IgaXcSkf59uWhUlLGt9C2YHwrWDsQsYv1Y mQ9vnFEaNDQ== X-Google-Smtp-Source: AGHT+IGMkP+UAP1J1Ztwnt5lueLRrYAM/WwzRYwBr/B+bGN+5hlJ2J50How5Xyv34FCbbgUYfCBsooiLdK1D X-Received: from pllw7.prod.google.com ([2002:a17:902:7b87:b0:234:9673:1d13]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:f86:b0:240:72e9:87bb with SMTP id d9443c01a7336-245e04a417fmr8203565ad.42.1755567639941; Mon, 18 Aug 2025 18:40:39 -0700 (PDT) Date: Mon, 18 Aug 2025 18:39:41 -0700 In-Reply-To: <20250819013941.209033-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: <20250819013941.209033-1-irogers@google.com> X-Mailer: git-send-email 2.51.0.rc1.167.g924127e9c0-goog Message-ID: <20250819013941.209033-12-irogers@google.com> Subject: [PATCH v10 11/11] perf tp_pmu: Remove unnecessary check 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 , James Clark , Xu Yang , "Masami Hiramatsu (Google)" , Collin Funk , Howard Chu , Weilin Wang , Andi Kleen , "Dr. David Alan Gilbert" , Thomas Richter , Tiezhu Yang , Gautam Menghani , Thomas Falcon , Chun-Tse Shao , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The "if" condition is also part of the "while" condition, remove the "if" to reduce the amount of code. Reported-by: Howard Chu Signed-off-by: Ian Rogers --- tools/perf/util/tp_pmu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c index e7534a973247..eddb9807131a 100644 --- a/tools/perf/util/tp_pmu.c +++ b/tools/perf/util/tp_pmu.c @@ -88,8 +88,6 @@ int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback = cb) continue; =20 ret =3D cb(state, events_ent->d_name); - if (ret) - break; } close(events_dir.dirfd); return ret; --=20 2.51.0.rc1.167.g924127e9c0-goog