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