From nobody Tue Oct 7 00:22:39 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 D5D382B9BA for ; Wed, 16 Jul 2025 00:46:40 +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=1752626802; cv=none; b=Yi5lUpiSVRqIw2XlLXkADl74eh0Sl+SXi+tMKNjT0glH+G2RwV2cQOAxHjHueqwWAE+uJwWNuCsk/7fsyw6o//anQjJsQKwkG9WMxtDdwsXXwcO1IB3NvZuP8zJ9BF8azJh32CHHLNXo5suROyGVtgCTKqpDVMWex9Os4ZHdmwU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752626802; c=relaxed/simple; bh=A84GfCjcITi8AiOzIpygW57B+h5ZDq5fbeJel7bZmD8=; h=Date:Mime-Version:Message-ID:Subject:From:To:Content-Type; b=OnmIuJwOmOZahlrCxPHAPggTkxjn+0p7MJynBFjKAs8RU6VU8lV1bVyzaQ8VlCXCzOQxGauiZ89qtypf3F4aD7pRF5MBlprpIjO1tszFKeau7fMmdQ+Npth7338ajJBKkN2Wtx6skhRFUIHye9nAwrb4iC9ZfvyBZgxb8jdHw98= 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=BzQHO2zb; 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="BzQHO2zb" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-235089528a0so3286025ad.1 for ; Tue, 15 Jul 2025 17:46:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1752626800; x=1753231600; darn=vger.kernel.org; h=to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=FSiMDXfM4cpD5u4hTNtqX5LKR2sTa7aLbN2a0UDDlHE=; b=BzQHO2zbORHhpo9tuxW4Wm18yAcXWkLz26wsBc7shtSIuIaJjU/9iSWF6b+1ppnE1Y 6kAnaTJ2apPvo8s1TWO4I4XRiW4kK6A1bO9EQqgkiU3KwZ3xF2hIsx1iSvBQAIHf7C95 5an8fld60OMnTJvZT+NRR/bHZo7gDBw2DkrUZdqOaLgZcsuGQMcBWEJTbzgwzO6akmWq iPln1xwGz6m3ZoO6zBbdSqivKidxH10TT6znd/k4yuNZOx5yfHa+7Vc9jnfAeUlzL+Ll KVusTwukniMxYY0D+sr9CbVipZ7y+cYode+feacLsGtfB3QlrGDikT34tdsn50bgOLR3 99AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752626800; x=1753231600; h=to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=FSiMDXfM4cpD5u4hTNtqX5LKR2sTa7aLbN2a0UDDlHE=; b=gAsMy0Ai48MyPlYror6Sv74aJG1vHmuI5N7syarIMds1CrFYm/dejWM1DslggkOk36 zExoIPKBqkNC/kNfMWwHHYMdrukquUxtFh9Lchu37DHTYXnWFNQk2vkyGgbcK5Jkdsv7 Ft3cF/PDoKkGX82XB6QjVAL/gSNqcCCGHxCzlqUJbJ4Lzs0hsrCqIRGX+pmJgz4ynPE4 nCJxujO+jIvKv8kjGzsFykPVm0frg1J5D3/2tc8xaO9I7XWaUhWdznJ0knjmCjZugjuF BQ7y/5UpT7gyskYBBlJHcCEGAej42agAb0mSvj3YLbGcaWAmJnokb64OZR4q9kVwNDDz dcEQ== X-Forwarded-Encrypted: i=1; AJvYcCW+7xnh5jyh2kWMY4w6FKEdZAytJIKRhFY72Bz/G8GlB8CnN3gvwLgvkwoENLAuwGjAkfsoUCQkZTnbULg=@vger.kernel.org X-Gm-Message-State: AOJu0YwbyfL5WYg6Hi11zv5e7YwChMaRQ6OVLH9+VREcF9Ep1+P67gBC G1QDL8aRGjSWP5O6CxdjVXX9DRKw4/+7oRwEekXvjtq69zbE2rTGg0ef6NkHyS/ONByKHdGFiZy Uq6FkISrJjQ== X-Google-Smtp-Source: AGHT+IERW0Jkpu6iHZi02K60bdWT88CcoXfpHjLAzQFOMyGMZ+LYLQwDV4fHXEaIISA1euoseJmK3f6ddsP4 X-Received: from pjxx3.prod.google.com ([2002:a17:90b:58c3:b0:313:2d44:397b]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:18a:b0:234:9fe1:8fc6 with SMTP id d9443c01a7336-23e1a4c6791mr84890435ad.18.1752626800112; Tue, 15 Jul 2025 17:46:40 -0700 (PDT) Date: Tue, 15 Jul 2025 17:46:35 -0700 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Mailer: git-send-email 2.50.0.727.gbf7dc18ff4-goog Message-ID: <20250716004635.31161-1-irogers@google.com> Subject: [PATCH v1] perf flamegraph: Fix minor pylint/type hint issues 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 , Wangyang Guo , Tim Chen , Zhiguo Zhou , Tianyou Li , linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Switch to assuming python3. Fix minor pylint issues on line length, repeated compares, not using f-strings and variable case. Add type hints and check with mypy. Signed-off-by: Ian Rogers Tested-by: Namhyung Kim --- tools/perf/scripts/python/flamegraph.py | 61 +++++++++++++------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/p= ython/flamegraph.py index e49ff242b779..ad735990c5be 100755 --- a/tools/perf/scripts/python/flamegraph.py +++ b/tools/perf/scripts/python/flamegraph.py @@ -18,7 +18,6 @@ # pylint: disable=3Dmissing-class-docstring # pylint: disable=3Dmissing-function-docstring =20 -from __future__ import print_function import argparse import hashlib import io @@ -26,9 +25,10 @@ import json import os import subprocess import sys +from typing import Dict, Optional, Union import urllib.request =20 -minimal_html =3D """ +MINIMAL_HTML =3D """ @@ -50,20 +50,20 @@ minimal_html =3D """ =20 # pylint: disable=3Dtoo-few-public-methods class Node: - def __init__(self, name, libtype): + def __init__(self, name: str, libtype: str): self.name =3D name # "root" | "kernel" | "" # "" indicates user space self.libtype =3D libtype - self.value =3D 0 - self.children =3D [] + self.value: int =3D 0 + self.children: list[Node] =3D [] =20 - def to_json(self): + def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]: return { "n": self.name, "l": self.libtype, "v": self.value, - "c": self.children + "c": [x.to_json() for x in self.children] } =20 =20 @@ -73,7 +73,7 @@ class FlameGraphCLI: self.stack =3D Node("all", "root") =20 @staticmethod - def get_libtype_from_dso(dso): + def get_libtype_from_dso(dso: Optional[str]) -> str: """ when kernel-debuginfo is installed, dso points to /usr/lib/debug/lib/modules/*/vmlinux @@ -84,7 +84,7 @@ class FlameGraphCLI: return "" =20 @staticmethod - def find_or_create_node(node, name, libtype): + def find_or_create_node(node: Node, name: str, libtype: str) -> Node: for child in node.children: if child.name =3D=3D name: return child @@ -93,7 +93,7 @@ class FlameGraphCLI: node.children.append(child) return child =20 - def process_event(self, event): + def process_event(self, event) -> None: # ignore events where the event name does not match # the one specified by the user if self.args.event_name and event.get("ev_name") !=3D self.args.ev= ent_name: @@ -106,7 +106,7 @@ class FlameGraphCLI: comm =3D event["comm"] libtype =3D "kernel" else: - comm =3D "{} ({})".format(event["comm"], pid) + comm =3D f"{event['comm']} ({pid})" libtype =3D "" node =3D self.find_or_create_node(self.stack, comm, libtype) =20 @@ -121,7 +121,7 @@ class FlameGraphCLI: node =3D self.find_or_create_node(node, name, libtype) node.value +=3D 1 =20 - def get_report_header(self): + def get_report_header(self) -> str: if self.args.input =3D=3D "-": # when this script is invoked with "perf script flamegraph", # no perf.data is created and we cannot read the header of it @@ -131,7 +131,8 @@ class FlameGraphCLI: # if the file name other than perf.data is given, # we read the header of that file if self.args.input: - output =3D subprocess.check_output(["perf", "report", "--h= eader-only", "-i", self.args.input]) + output =3D subprocess.check_output(["perf", "report", "--h= eader-only", + "-i", self.args.input]) else: output =3D subprocess.check_output(["perf", "report", "--h= eader-only"]) =20 @@ -140,10 +141,10 @@ class FlameGraphCLI: result +=3D "\nFocused event: " + self.args.event_name return result except Exception as err: # pylint: disable=3Dbroad-except - print("Error reading report header: {}".format(err), file=3Dsy= s.stderr) + print(f"Error reading report header: {err}", file=3Dsys.stderr) return "" =20 - def trace_end(self): + def trace_end(self) -> None: stacks_json =3D json.dumps(self.stack, default=3Dlambda x: x.to_js= on()) =20 if self.args.format =3D=3D "html": @@ -167,7 +168,8 @@ graph template (--template PATH) or use another output = format (--format FORMAT).""", file=3Dsys.stderr) if self.args.input =3D=3D "-": - print("""Not attempting to download Flame Grap= h template as script command line + print( +"""Not attempting to download Flame Graph template as script command line input is disabled due to using live mode. If you want to download the template retry without live mode. For example, use 'perf record -a -g -F 99 sleep 60' and 'perf script report flamegraph'. Alternatively, @@ -176,37 +178,40 @@ https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dis= t/templates/d3-flamegraph-b and place it at: /usr/share/d3-flame-graph/d3-flamegraph-base.html""", file=3Dsys.stderr) - quit() + sys.exit(1) s =3D None - while s !=3D "y" and s !=3D "n": - s =3D input("Do you wish to download a templat= e from cdn.jsdelivr.net? (this warning can be suppressed with --allow-downl= oad) [yn] ").lower() + while s not in ["y", "n"]: + s =3D input("Do you wish to download a templat= e from cdn.jsdelivr.net?" + + "(this warning can be suppressed wit= h --allow-download) [yn] " + ).lower() if s =3D=3D "n": - quit() - template =3D "https://cdn.jsdelivr.net/npm/d3-flame-gr= aph@4.1.3/dist/templates/d3-flamegraph-base.html" + sys.exit(1) + template =3D ("https://cdn.jsdelivr.net/npm/d3-flame-g= raph@4.1.3/dist/templates/" + "d3-flamegraph-base.html") template_md5sum =3D "143e0d06ba69b8370b9848dcd6ae3f36" =20 try: - with urllib.request.urlopen(template) as template: + with urllib.request.urlopen(template) as url_template: output_str =3D "".join([ - l.decode("utf-8") for l in template.readlines() + l.decode("utf-8") for l in url_template.readlines() ]) except Exception as err: print(f"Error reading template {template}: {err}\n" "a minimal flame graph will be generated", file=3Dsy= s.stderr) - output_str =3D minimal_html + output_str =3D MINIMAL_HTML template_md5sum =3D None =20 if template_md5sum: download_md5sum =3D hashlib.md5(output_str.encode("utf-8")= ).hexdigest() if download_md5sum !=3D template_md5sum: s =3D None - while s !=3D "y" and s !=3D "n": + while s not in ["y", "n"]: s =3D input(f"""Unexpected template md5sum. {download_md5sum} !=3D {template_md5sum}, for: {output_str} continue?[yn] """).lower() if s =3D=3D "n": - quit() + sys.exit(1) =20 output_str =3D output_str.replace("/** @options_json **/", opt= ions_json) output_str =3D output_str.replace("/** @flamegraph_json **/", = stacks_json) @@ -220,12 +225,12 @@ continue?[yn] """).lower() with io.open(sys.stdout.fileno(), "w", encoding=3D"utf-8", clo= sefd=3DFalse) as out: out.write(output_str) else: - print("dumping data to {}".format(output_fn)) + print(f"dumping data to {output_fn}") try: with io.open(output_fn, "w", encoding=3D"utf-8") as out: out.write(output_str) except IOError as err: - print("Error writing output file: {}".format(err), file=3D= sys.stderr) + print(f"Error writing output file: {err}", file=3Dsys.stde= rr) sys.exit(1) =20 =20 --=20 2.50.0.727.gbf7dc18ff4-goog