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.
Showing the changes to tools/perf/scripts/python (1 of 1 file). Show all
Unified · Split
| @@ -18,7 +18,6 @@ · 17 unmodified lines | |||
| 18 | # pylint: disable=missing-class-docstring | 18 | # pylint: disable=missing-class-docstring |
| 19 | # pylint: disable=missing-function-docstring | 19 | # pylint: disable=missing-function-docstring |
| 20 | 20 | ||
| 21 | from __future__ import print_function | ||
| 22 | import argparse | 21 | import argparse |
| 23 | import hashlib | 22 | import hashlib |
| 24 | import io | 23 | import io |
| @@ -26,9 +25,10 @@ import json · 1 unmodified line | |||
| 26 | import os | 25 | import os |
| 27 | import subprocess | 26 | import subprocess |
| 28 | import sys | 27 | import sys |
| 28 | from typing import Dict, Optional, Union | ||
| 29 | import urllib.request | 29 | import urllib.request |
| 30 | 30 | ||
| 31 | minimal_html = """<head> | 31 | MINIMAL_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-methods | 51 | # pylint: disable=too-few-public-methods |
| 52 | class Node: | 52 | class Node: |
| 53 | def __init__(self, name, libtype): | 53 | def __init__(self, name: str, libtype: str): |
| 54 | self.name = name | 54 | self.name = name |
| 55 | # "root" | "kernel" | "" | 55 | # "root" | "kernel" | "" |
| 56 | # "" indicates user space | 56 | # "" indicates user space |
| 57 | self.libtype = libtype | 57 | self.libtype = libtype |
| 58 | self.value = 0 | 58 | 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.children | 66 | "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 | @staticmethod | 75 | @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/*/vmlinux | 79 | 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 | @staticmethod | 86 | @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 child | 90 | return child |
| @@ -93,7 +93,7 @@ class FlameGraphCLI: · 2 unmodified lines | |||
| 93 | node.children.append(child) | 93 | node.children.append(child) |
| 94 | return child | 94 | 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 match | 97 | # ignore events where the event name does not match |
| 98 | # the one specified by the user | 98 | # 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 += 1 | 122 | 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 it | 127 | # 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 file | 132 | # 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_name | 141 | result += "\nFocused event: " + self.args.event_name |
| 141 | return result | 142 | return result |
| 142 | except Exception as err: # pylint: disable=broad-except | 143 | 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 | |||
| 167 | FORMAT).""", | 168 | FORMAT).""", |
| 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 line | 171 | print( |
| 172 | """Not attempting to download Flame Graph template as script command line | ||
| 171 | input is disabled due to using live mode. If you want to download the | 173 | input is disabled due to using live mode. If you want to download the |
| 172 | template retry without live mode. For example, use 'perf record -a -g | 174 | template 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 | |||
| 176 | and place it at: | 178 | and 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 = None | 182 | 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_html | 201 | output_str = MINIMAL_HTML |
| 197 | template_md5sum = None | 202 | 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 = None | 207 | 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} |
| 207 | continue?[yn] """).lower() | 212 | continue?[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 | ||