~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/scripts/python/flamegraph.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

Diff markup

Differences between /tools/perf/scripts/python/flamegraph.py (Version linux-6.12-rc7) and /tools/perf/scripts/python/flamegraph.py (Version linux-6.5.13)


  1 # flamegraph.py - create flame graphs from per      1 # flamegraph.py - create flame graphs from perf samples
  2 # SPDX-License-Identifier: GPL-2.0                  2 # SPDX-License-Identifier: GPL-2.0
  3 #                                                   3 #
  4 # Usage:                                            4 # Usage:
  5 #                                                   5 #
  6 #     perf record -a -g -F 99 sleep 60              6 #     perf record -a -g -F 99 sleep 60
  7 #     perf script report flamegraph                 7 #     perf script report flamegraph
  8 #                                                   8 #
  9 # Combined:                                         9 # Combined:
 10 #                                                  10 #
 11 #     perf script flamegraph -a -F 99 sleep 60     11 #     perf script flamegraph -a -F 99 sleep 60
 12 #                                                  12 #
 13 # Written by Andreas Gerstmayr <agerstmayr@redh     13 # Written by Andreas Gerstmayr <agerstmayr@redhat.com>
 14 # Flame Graphs invented by Brendan Gregg <bgreg     14 # Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
 15 # Works in tandem with d3-flame-graph by Marti<     15 # Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
 16 #                                                  16 #
 17 # pylint: disable=missing-module-docstring         17 # pylint: disable=missing-module-docstring
 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              21 from __future__ import print_function
 22 import argparse                                    22 import argparse
 23 import hashlib                                     23 import hashlib
 24 import io                                          24 import io
 25 import json                                        25 import json
 26 import os                                          26 import os
 27 import subprocess                                  27 import subprocess
 28 import sys                                         28 import sys
 29 import urllib.request                              29 import urllib.request
 30                                                    30 
 31 minimal_html = """<head>                           31 minimal_html = """<head>
 32   <https://cdn.jsdelivr.net/npm/d3-flame-graph@     32   <https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css"">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>
 35   <div id="chart"></div>                           35   <div id="chart"></div>
 36   <script type="text/javascript" src="https://     36   <script type="text/javascript" src="https://d3js.org/d3.v7.js"></script>
 37   <https://cdn.jsdelivr.net/npm/d3-flame-graph@     37   <https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.min.js"></script">script type="text/javascript" src="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.min.js"></script>
 38   <script type="text/javascript">                  38   <script type="text/javascript">
 39   const stacks = [/** @flamegraph_json **/];       39   const stacks = [/** @flamegraph_json **/];
 40   // Note, options is unused.                      40   // Note, options is unused.
 41   const options = [/** @options_json **/];         41   const options = [/** @options_json **/];
 42                                                    42 
 43   var chart = flamegraph();                        43   var chart = flamegraph();
 44   d3.select("#chart")                              44   d3.select("#chart")
 45         .datum(stacks[0])                          45         .datum(stacks[0])
 46         .call(chart);                              46         .call(chart);
 47   </script>                                        47   </script>
 48 </body>                                            48 </body>
 49 """                                                49 """
 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, libtype):
 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 = 0
 59         self.children = []                         59         self.children = []
 60                                                    60 
 61     def to_json(self):                             61     def to_json(self):
 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": self.children
 67         }                                          67         }
 68                                                    68 
 69                                                    69 
 70 class FlameGraphCLI:                               70 class FlameGraphCLI:
 71     def __init__(self, args):                      71     def __init__(self, args):
 72         self.args = args                           72         self.args = args
 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):
 77         """                                        77         """
 78         when kernel-debuginfo is installed,        78         when kernel-debuginfo is installed,
 79         dso points to /usr/lib/debug/lib/modul     79         dso points to /usr/lib/debug/lib/modules/*/vmlinux
 80         """                                        80         """
 81         if dso and (dso == "[kernel.kallsyms]"     81         if dso and (dso == "[kernel.kallsyms]" or dso.endswith("/vmlinux")):
 82             return "kernel"                        82             return "kernel"
 83                                                    83 
 84         return ""                                  84         return ""
 85                                                    85 
 86     @staticmethod                                  86     @staticmethod
 87     def find_or_create_node(node, name, libtyp     87     def find_or_create_node(node, name, libtype):
 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
 91                                                    91 
 92         child = Node(name, libtype)                92         child = Node(name, libtype)
 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):
 97         pid = event.get("sample", {}).get("pid     97         pid = event.get("sample", {}).get("pid", 0)
 98         # event["dso"] sometimes contains /usr     98         # event["dso"] sometimes contains /usr/lib/debug/lib/modules/*/vmlinux
 99         # for user-space processes; let's use      99         # for user-space processes; let's use pid for kernel or user-space distinction
100         if pid == 0:                              100         if pid == 0:
101             comm = event["comm"]                  101             comm = event["comm"]
102             libtype = "kernel"                    102             libtype = "kernel"
103         else:                                     103         else:
104             comm = "{} ({})".format(event["com    104             comm = "{} ({})".format(event["comm"], pid)
105             libtype = ""                          105             libtype = ""
106         node = self.find_or_create_node(self.s    106         node = self.find_or_create_node(self.stack, comm, libtype)
107                                                   107 
108         if "callchain" in event:                  108         if "callchain" in event:
109             for entry in reversed(event["callc    109             for entry in reversed(event["callchain"]):
110                 name = entry.get("sym", {}).ge    110                 name = entry.get("sym", {}).get("name", "[unknown]")
111                 libtype = self.get_libtype_fro    111                 libtype = self.get_libtype_from_dso(entry.get("dso"))
112                 node = self.find_or_create_nod    112                 node = self.find_or_create_node(node, name, libtype)
113         else:                                     113         else:
114             name = event.get("symbol", "[unkno    114             name = event.get("symbol", "[unknown]")
115             libtype = self.get_libtype_from_ds    115             libtype = self.get_libtype_from_dso(event.get("dso"))
116             node = self.find_or_create_node(no    116             node = self.find_or_create_node(node, name, libtype)
117         node.value += 1                           117         node.value += 1
118                                                   118 
119     def get_report_header(self):                  119     def get_report_header(self):
120         if self.args.input == "-":                120         if self.args.input == "-":
121             # when this script is invoked with    121             # when this script is invoked with "perf script flamegraph",
122             # no perf.data is created and we c    122             # no perf.data is created and we cannot read the header of it
123             return ""                             123             return ""
124                                                   124 
125         try:                                      125         try:
126             output = subprocess.check_output([    126             output = subprocess.check_output(["perf", "report", "--header-only"])
127             return output.decode("utf-8")         127             return output.decode("utf-8")
128         except Exception as err:  # pylint: di    128         except Exception as err:  # pylint: disable=broad-except
129             print("Error reading report header    129             print("Error reading report header: {}".format(err), file=sys.stderr)
130             return ""                             130             return ""
131                                                   131 
132     def trace_end(self):                          132     def trace_end(self):
133         stacks_json = json.dumps(self.stack, d    133         stacks_json = json.dumps(self.stack, default=lambda x: x.to_json())
134                                                   134 
135         if self.args.format == "html":            135         if self.args.format == "html":
136             report_header = self.get_report_he    136             report_header = self.get_report_header()
137             options = {                           137             options = {
138                 "colorscheme": self.args.color    138                 "colorscheme": self.args.colorscheme,
139                 "context": report_header          139                 "context": report_header
140             }                                     140             }
141             options_json = json.dumps(options)    141             options_json = json.dumps(options)
142                                                   142 
143             template_md5sum = None                143             template_md5sum = None
144             if self.args.format == "html":        144             if self.args.format == "html":
145                 if os.path.isfile(self.args.te    145                 if os.path.isfile(self.args.template):
146                     template = f"file://{self.    146                     template = f"file://{self.args.template}"
147                 else:                             147                 else:
148                     if not self.args.allow_dow    148                     if not self.args.allow_download:
149                         print(f"""Warning: Fla    149                         print(f"""Warning: Flame Graph template '{self.args.template}'
150 does not exist. To avoid this please install a    150 does not exist. To avoid this please install a package such as the
151 js-d3-flame-graph or libjs-d3-flame-graph, spe    151 js-d3-flame-graph or libjs-d3-flame-graph, specify an existing flame
152 graph template (--template PATH) or use anothe    152 graph template (--template PATH) or use another output format (--format
153 FORMAT).""",                                      153 FORMAT).""",
154                               file=sys.stderr)    154                               file=sys.stderr)
155                         if self.args.input ==     155                         if self.args.input == "-":
156                             print("""Not attem    156                             print("""Not attempting to download Flame Graph template as script command line
157 input is disabled due to using live mode. If y    157 input is disabled due to using live mode. If you want to download the
158 template retry without live mode. For example,    158 template retry without live mode. For example, use 'perf record -a -g
159 -F 99 sleep 60' and 'perf script report flameg    159 -F 99 sleep 60' and 'perf script report flamegraph'. Alternatively,
160 download the template from:                       160 download the template from:
161 https://cdn.jsdelivr.net/npm/d3-flame-graph@4.    161 https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html
162 and place it at:                                  162 and place it at:
163 /usr/share/d3-flame-graph/d3-flamegraph-base.h    163 /usr/share/d3-flame-graph/d3-flamegraph-base.html""",
164                                   file=sys.std    164                                   file=sys.stderr)
165                             quit()                165                             quit()
166                         s = None                  166                         s = None
167                         while s != "y" and s !    167                         while s != "y" and s != "n":
168                             s = input("Do you     168                             s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower()
169                         if s == "n":              169                         if s == "n":
170                             quit()                170                             quit()
171                     template = "https://cdn.js    171                     template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html"
172                     template_md5sum = "143e0d0    172                     template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36"
173                                                   173 
174             try:                                  174             try:
175                 with urllib.request.urlopen(te    175                 with urllib.request.urlopen(template) as template:
176                     output_str = "".join([        176                     output_str = "".join([
177                         l.decode("utf-8") for     177                         l.decode("utf-8") for l in template.readlines()
178                     ])                            178                     ])
179             except Exception as err:              179             except Exception as err:
180                 print(f"Error reading template    180                 print(f"Error reading template {template}: {err}\n"
181                       "a minimal flame graph w    181                       "a minimal flame graph will be generated", file=sys.stderr)
182                 output_str = minimal_html         182                 output_str = minimal_html
183                 template_md5sum = None            183                 template_md5sum = None
184                                                   184 
185             if template_md5sum:                   185             if template_md5sum:
186                 download_md5sum = hashlib.md5(    186                 download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()
187                 if download_md5sum != template    187                 if download_md5sum != template_md5sum:
188                     s = None                      188                     s = None
189                     while s != "y" and s != "n    189                     while s != "y" and s != "n":
190                         s = input(f"""Unexpect    190                         s = input(f"""Unexpected template md5sum.
191 {download_md5sum} != {template_md5sum}, for:      191 {download_md5sum} != {template_md5sum}, for:
192 {output_str}                                      192 {output_str}
193 continue?[yn] """).lower()                        193 continue?[yn] """).lower()
194                     if s == "n":                  194                     if s == "n":
195                         quit()                    195                         quit()
196                                                   196 
197             output_str = output_str.replace("/    197             output_str = output_str.replace("/** @options_json **/", options_json)
198             output_str = output_str.replace("/    198             output_str = output_str.replace("/** @flamegraph_json **/", stacks_json)
199                                                   199 
200             output_fn = self.args.output or "f    200             output_fn = self.args.output or "flamegraph.html"
201         else:                                     201         else:
202             output_str = stacks_json              202             output_str = stacks_json
203             output_fn = self.args.output or "s    203             output_fn = self.args.output or "stacks.json"
204                                                   204 
205         if output_fn == "-":                      205         if output_fn == "-":
206             with io.open(sys.stdout.fileno(),     206             with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:
207                 out.write(output_str)             207                 out.write(output_str)
208         else:                                     208         else:
209             print("dumping data to {}".format(    209             print("dumping data to {}".format(output_fn))
210             try:                                  210             try:
211                 with io.open(output_fn, "w", e    211                 with io.open(output_fn, "w", encoding="utf-8") as out:
212                     out.write(output_str)         212                     out.write(output_str)
213             except IOError as err:                213             except IOError as err:
214                 print("Error writing output fi    214                 print("Error writing output file: {}".format(err), file=sys.stderr)
215                 sys.exit(1)                       215                 sys.exit(1)
216                                                   216 
217                                                   217 
218 if __name__ == "__main__":                        218 if __name__ == "__main__":
219     parser = argparse.ArgumentParser(descripti    219     parser = argparse.ArgumentParser(description="Create flame graphs.")
220     parser.add_argument("-f", "--format",         220     parser.add_argument("-f", "--format",
221                         default="html", choice    221                         default="html", choices=["json", "html"],
222                         help="output file form    222                         help="output file format")
223     parser.add_argument("-o", "--output",         223     parser.add_argument("-o", "--output",
224                         help="output file name    224                         help="output file name")
225     parser.add_argument("--template",             225     parser.add_argument("--template",
226                         default="/usr/share/d3    226                         default="/usr/share/d3-flame-graph/d3-flamegraph-base.html",
227                         help="path to flame gr    227                         help="path to flame graph HTML template")
228     parser.add_argument("--colorscheme",          228     parser.add_argument("--colorscheme",
229                         default="blue-green",     229                         default="blue-green",
230                         help="flame graph colo    230                         help="flame graph color scheme",
231                         choices=["blue-green",    231                         choices=["blue-green", "orange"])
232     parser.add_argument("-i", "--input",          232     parser.add_argument("-i", "--input",
233                         help=argparse.SUPPRESS    233                         help=argparse.SUPPRESS)
234     parser.add_argument("--allow-download",       234     parser.add_argument("--allow-download",
235                         default=False,            235                         default=False,
236                         action="store_true",      236                         action="store_true",
237                         help="allow unprompted    237                         help="allow unprompted downloading of HTML template")
238                                                   238 
239     cli_args = parser.parse_args()                239     cli_args = parser.parse_args()
240     cli = FlameGraphCLI(cli_args)                 240     cli = FlameGraphCLI(cli_args)
241                                                   241 
242     process_event = cli.process_event             242     process_event = cli.process_event
243     trace_end = cli.trace_end                     243     trace_end = cli.trace_end
                                                      

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php