History

perf flamegraph: Fix minor pylint/type hint issues

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.

Ian Rogers committed 1 year ago

Commit 95d692f1 parent 8db1d771 file · +33 −28Browse files

Showing the changes to tools/perf/scripts/python (1 of 1 file). Show all

Unified · Split

tools/perf/scripts/python/flamegraph.py+33−28
@@ -18,7 +18,6 @@ · 17 unmodified lines
18# pylint: disable=missing-class-docstring18# pylint: disable=missing-class-docstring
19# pylint: disable=missing-function-docstring19# pylint: disable=missing-function-docstring
20 20
21from __future__ import print_function
22import argparse21import argparse
23import hashlib22import hashlib
24import io23import io
@@ -26,9 +25,10 @@ import json · 1 unmodified line
26import os25import os
27import subprocess26import subprocess
28import sys27import sys
28from typing import Dict, Optional, Union
29import urllib.request29import urllib.request
30 30
31minimal_html = """<head>31MINIMAL_HTML = """<head>
32 <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css">32 <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css">
33</head>33</head>
34<body>34<body>
@@ -50,20 +50,20 @@ minimal_html = """<head> · 15 unmodified lines
50 50
51# pylint: disable=too-few-public-methods51# pylint: disable=too-few-public-methods
52class Node:52class Node:
53 def __init__(self, name, libtype):53 def __init__(self, name: str, libtype: str):
54 self.name = name54 self.name = name
55 # "root" | "kernel" | ""55 # "root" | "kernel" | ""
56 # "" indicates user space56 # "" indicates user space
57 self.libtype = libtype57 self.libtype = libtype
58 self.value = 058 self.value: int = 0
59 self.children = []59 self.children: list[Node] = []
60 60
61 def to_json(self):61 def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]:
62 return {62 return {
63 "n": self.name,63 "n": self.name,
64 "l": self.libtype,64 "l": self.libtype,
65 "v": self.value,65 "v": self.value,
66 "c": self.children66 "c": [x.to_json() for x in self.children]
67 }67 }
68 68
69 69
@@ -73,7 +73,7 @@ class FlameGraphCLI: · 3 unmodified lines
73 self.stack = Node("all", "root")73 self.stack = Node("all", "root")
74 74
75 @staticmethod75 @staticmethod
76 def get_libtype_from_dso(dso):76 def get_libtype_from_dso(dso: Optional[str]) -> str:
77 """77 """
78 when kernel-debuginfo is installed,78 when kernel-debuginfo is installed,
79 dso points to /usr/lib/debug/lib/modules/*/vmlinux79 dso points to /usr/lib/debug/lib/modules/*/vmlinux
@@ -84,7 +84,7 @@ class FlameGraphCLI: · 4 unmodified lines
84 return ""84 return ""
85 85
86 @staticmethod86 @staticmethod
87 def find_or_create_node(node, name, libtype):87 def find_or_create_node(node: Node, name: str, libtype: str) -> Node:
88 for child in node.children:88 for child in node.children:
89 if child.name == name:89 if child.name == name:
90 return child90 return child
@@ -93,7 +93,7 @@ class FlameGraphCLI: · 2 unmodified lines
93 node.children.append(child)93 node.children.append(child)
94 return child94 return child
95 95
96 def process_event(self, event):96 def process_event(self, event) -> None:
97 # ignore events where the event name does not match97 # ignore events where the event name does not match
98 # the one specified by the user98 # the one specified by the user
99 if self.args.event_name and event.get("ev_name") != self.args.event_name:99 if self.args.event_name and event.get("ev_name") != self.args.event_name:
@@ -106,7 +106,7 @@ class FlameGraphCLI: · 6 unmodified lines
106 comm = event["comm"]106 comm = event["comm"]
107 libtype = "kernel"107 libtype = "kernel"
108 else:108 else:
109 comm = "{} ({})".format(event["comm"], pid)109 comm = f"{event['comm']} ({pid})"
110 libtype = ""110 libtype = ""
111 node = self.find_or_create_node(self.stack, comm, libtype)111 node = self.find_or_create_node(self.stack, comm, libtype)
112 112
@@ -121,7 +121,7 @@ class FlameGraphCLI: · 8 unmodified lines
121 node = self.find_or_create_node(node, name, libtype)121 node = self.find_or_create_node(node, name, libtype)
122 node.value += 1122 node.value += 1
123 123
124 def get_report_header(self):124 def get_report_header(self) -> str:
125 if self.args.input == "-":125 if self.args.input == "-":
126 # when this script is invoked with "perf script flamegraph",126 # when this script is invoked with "perf script flamegraph",
127 # no perf.data is created and we cannot read the header of it127 # no perf.data is created and we cannot read the header of it
@@ -131,7 +131,8 @@ class FlameGraphCLI: · 3 unmodified lines
131 # if the file name other than perf.data is given,131 # if the file name other than perf.data is given,
132 # we read the header of that file132 # we read the header of that file
133 if self.args.input:133 if self.args.input:
134 output = subprocess.check_output(["perf", "report", "--header-only", "-i", self.args.input])134 output = subprocess.check_output(["perf", "report", "--header-only",
135 "-i", self.args.input])
135 else:136 else:
136 output = subprocess.check_output(["perf", "report", "--header-only"])137 output = subprocess.check_output(["perf", "report", "--header-only"])
137 138
@@ -140,10 +141,10 @@ class FlameGraphCLI: · 2 unmodified lines
140 result += "\nFocused event: " + self.args.event_name141 result += "\nFocused event: " + self.args.event_name
141 return result142 return result
142 except Exception as err: # pylint: disable=broad-except143 except Exception as err: # pylint: disable=broad-except
143 print("Error reading report header: {}".format(err), file=sys.stderr)144 print(f"Error reading report header: {err}", file=sys.stderr)
144 return ""145 return ""
145 146
146 def trace_end(self):147 def trace_end(self) -> None:
147 stacks_json = json.dumps(self.stack, default=lambda x: x.to_json())148 stacks_json = json.dumps(self.stack, default=lambda x: x.to_json())
148 149
149 if self.args.format == "html":150 if self.args.format == "html":
@@ -167,7 +168,8 @@ graph template (--template PATH) or use another output format (--format · 17 unmodified lines
167FORMAT).""",168FORMAT).""",
168 file=sys.stderr)169 file=sys.stderr)
169 if self.args.input == "-":170 if self.args.input == "-":
170 print("""Not attempting to download Flame Graph template as script command line171 print(
172"""Not attempting to download Flame Graph template as script command line
171input is disabled due to using live mode. If you want to download the173input is disabled due to using live mode. If you want to download the
172template retry without live mode. For example, use 'perf record -a -g174template retry without live mode. For example, use 'perf record -a -g
173-F 99 sleep 60' and 'perf script report flamegraph'. Alternatively,175-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/dist/templates/d3-flamegraph-b · 2 unmodified lines
176and place it at:178and place it at:
177/usr/share/d3-flame-graph/d3-flamegraph-base.html""",179/usr/share/d3-flame-graph/d3-flamegraph-base.html""",
178 file=sys.stderr)180 file=sys.stderr)
179 quit()181 sys.exit(1)
180 s = None182 s = None
181 while s != "y" and s != "n":183 while s not in ["y", "n"]:
182 s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower()184 s = input("Do you wish to download a template from cdn.jsdelivr.net?" +
185 "(this warning can be suppressed with --allow-download) [yn] "
186 ).lower()
183 if s == "n":187 if s == "n":
184 quit()188 sys.exit(1)
185 template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html"189 template = ("https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/"
190 "d3-flamegraph-base.html")
186 template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36"191 template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36"
187 192
188 try:193 try:
189 with urllib.request.urlopen(template) as template:194 with urllib.request.urlopen(template) as url_template:
190 output_str = "".join([195 output_str = "".join([
191 l.decode("utf-8") for l in template.readlines()196 l.decode("utf-8") for l in url_template.readlines()
192 ])197 ])
193 except Exception as err:198 except Exception as err:
194 print(f"Error reading template {template}: {err}\n"199 print(f"Error reading template {template}: {err}\n"
195 "a minimal flame graph will be generated", file=sys.stderr)200 "a minimal flame graph will be generated", file=sys.stderr)
196 output_str = minimal_html201 output_str = MINIMAL_HTML
197 template_md5sum = None202 template_md5sum = None
198 203
199 if template_md5sum:204 if template_md5sum:
200 download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()205 download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()
201 if download_md5sum != template_md5sum:206 if download_md5sum != template_md5sum:
202 s = None207 s = None
203 while s != "y" and s != "n":208 while s not in ["y", "n"]:
204 s = input(f"""Unexpected template md5sum.209 s = input(f"""Unexpected template md5sum.
205{download_md5sum} != {template_md5sum}, for:210{download_md5sum} != {template_md5sum}, for:
206{output_str}211{output_str}
207continue?[yn] """).lower()212continue?[yn] """).lower()
208 if s == "n":213 if s == "n":
209 quit()214 sys.exit(1)
210 215
211 output_str = output_str.replace("/** @options_json **/", options_json)216 output_str = output_str.replace("/** @options_json **/", options_json)
212 output_str = output_str.replace("/** @flamegraph_json **/", stacks_json)217 output_str = output_str.replace("/** @flamegraph_json **/", stacks_json)
@@ -220,12 +225,12 @@ continue?[yn] """).lower() · 7 unmodified lines
220 with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:225 with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:
221 out.write(output_str)226 out.write(output_str)
222 else:227 else:
223 print("dumping data to {}".format(output_fn))228 print(f"dumping data to {output_fn}")
224 try:229 try:
225 with io.open(output_fn, "w", encoding="utf-8") as out:230 with io.open(output_fn, "w", encoding="utf-8") as out:
226 out.write(output_str)231 out.write(output_str)
227 except IOError as err:232 except IOError as err:
228 print("Error writing output file: {}".format(err), file=sys.stderr)233 print(f"Error writing output file: {err}", file=sys.stderr)
229 sys.exit(1)234 sys.exit(1)
230 235
231 236

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t