xref: /OK3568_Linux_fs/kernel/tools/perf/scripts/python/flamegraph.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# flamegraph.py - create flame graphs from perf samples
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Usage:
5*4882a593Smuzhiyun#
6*4882a593Smuzhiyun#     perf record -a -g -F 99 sleep 60
7*4882a593Smuzhiyun#     perf script report flamegraph
8*4882a593Smuzhiyun#
9*4882a593Smuzhiyun# Combined:
10*4882a593Smuzhiyun#
11*4882a593Smuzhiyun#     perf script flamegraph -a -F 99 sleep 60
12*4882a593Smuzhiyun#
13*4882a593Smuzhiyun# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
14*4882a593Smuzhiyun# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
15*4882a593Smuzhiyun# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyunfrom __future__ import print_function
18*4882a593Smuzhiyunimport sys
19*4882a593Smuzhiyunimport os
20*4882a593Smuzhiyunimport io
21*4882a593Smuzhiyunimport argparse
22*4882a593Smuzhiyunimport json
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun
25*4882a593Smuzhiyunclass Node:
26*4882a593Smuzhiyun    def __init__(self, name, libtype=""):
27*4882a593Smuzhiyun        self.name = name
28*4882a593Smuzhiyun        self.libtype = libtype
29*4882a593Smuzhiyun        self.value = 0
30*4882a593Smuzhiyun        self.children = []
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun    def toJSON(self):
33*4882a593Smuzhiyun        return {
34*4882a593Smuzhiyun            "n": self.name,
35*4882a593Smuzhiyun            "l": self.libtype,
36*4882a593Smuzhiyun            "v": self.value,
37*4882a593Smuzhiyun            "c": self.children
38*4882a593Smuzhiyun        }
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun
41*4882a593Smuzhiyunclass FlameGraphCLI:
42*4882a593Smuzhiyun    def __init__(self, args):
43*4882a593Smuzhiyun        self.args = args
44*4882a593Smuzhiyun        self.stack = Node("root")
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun        if self.args.format == "html" and \
47*4882a593Smuzhiyun                not os.path.isfile(self.args.template):
48*4882a593Smuzhiyun            print("Flame Graph template {} does not exist. Please install "
49*4882a593Smuzhiyun                  "the js-d3-flame-graph (RPM) or libjs-d3-flame-graph (deb) "
50*4882a593Smuzhiyun                  "package, specify an existing flame graph template "
51*4882a593Smuzhiyun                  "(--template PATH) or another output format "
52*4882a593Smuzhiyun                  "(--format FORMAT).".format(self.args.template),
53*4882a593Smuzhiyun                  file=sys.stderr)
54*4882a593Smuzhiyun            sys.exit(1)
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun    def find_or_create_node(self, node, name, dso):
57*4882a593Smuzhiyun        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
58*4882a593Smuzhiyun        if name is None:
59*4882a593Smuzhiyun            name = "[unknown]"
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun        for child in node.children:
62*4882a593Smuzhiyun            if child.name == name and child.libtype == libtype:
63*4882a593Smuzhiyun                return child
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun        child = Node(name, libtype)
66*4882a593Smuzhiyun        node.children.append(child)
67*4882a593Smuzhiyun        return child
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun    def process_event(self, event):
70*4882a593Smuzhiyun        node = self.find_or_create_node(self.stack, event["comm"], None)
71*4882a593Smuzhiyun        if "callchain" in event:
72*4882a593Smuzhiyun            for entry in reversed(event['callchain']):
73*4882a593Smuzhiyun                node = self.find_or_create_node(
74*4882a593Smuzhiyun                    node, entry.get("sym", {}).get("name"), event.get("dso"))
75*4882a593Smuzhiyun        else:
76*4882a593Smuzhiyun            node = self.find_or_create_node(
77*4882a593Smuzhiyun                node, entry.get("symbol"), event.get("dso"))
78*4882a593Smuzhiyun        node.value += 1
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun    def trace_end(self):
81*4882a593Smuzhiyun        json_str = json.dumps(self.stack, default=lambda x: x.toJSON())
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun        if self.args.format == "html":
84*4882a593Smuzhiyun            try:
85*4882a593Smuzhiyun                with io.open(self.args.template, encoding="utf-8") as f:
86*4882a593Smuzhiyun                    output_str = f.read().replace("/** @flamegraph_json **/",
87*4882a593Smuzhiyun                                                  json_str)
88*4882a593Smuzhiyun            except IOError as e:
89*4882a593Smuzhiyun                print("Error reading template file: {}".format(e), file=sys.stderr)
90*4882a593Smuzhiyun                sys.exit(1)
91*4882a593Smuzhiyun            output_fn = self.args.output or "flamegraph.html"
92*4882a593Smuzhiyun        else:
93*4882a593Smuzhiyun            output_str = json_str
94*4882a593Smuzhiyun            output_fn = self.args.output or "stacks.json"
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun        if output_fn == "-":
97*4882a593Smuzhiyun            with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:
98*4882a593Smuzhiyun                out.write(output_str)
99*4882a593Smuzhiyun        else:
100*4882a593Smuzhiyun            print("dumping data to {}".format(output_fn))
101*4882a593Smuzhiyun            try:
102*4882a593Smuzhiyun                with io.open(output_fn, "w", encoding="utf-8") as out:
103*4882a593Smuzhiyun                    out.write(output_str)
104*4882a593Smuzhiyun            except IOError as e:
105*4882a593Smuzhiyun                print("Error writing output file: {}".format(e), file=sys.stderr)
106*4882a593Smuzhiyun                sys.exit(1)
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun
109*4882a593Smuzhiyunif __name__ == "__main__":
110*4882a593Smuzhiyun    parser = argparse.ArgumentParser(description="Create flame graphs.")
111*4882a593Smuzhiyun    parser.add_argument("-f", "--format",
112*4882a593Smuzhiyun                        default="html", choices=["json", "html"],
113*4882a593Smuzhiyun                        help="output file format")
114*4882a593Smuzhiyun    parser.add_argument("-o", "--output",
115*4882a593Smuzhiyun                        help="output file name")
116*4882a593Smuzhiyun    parser.add_argument("--template",
117*4882a593Smuzhiyun                        default="/usr/share/d3-flame-graph/d3-flamegraph-base.html",
118*4882a593Smuzhiyun                        help="path to flamegraph HTML template")
119*4882a593Smuzhiyun    parser.add_argument("-i", "--input",
120*4882a593Smuzhiyun                        help=argparse.SUPPRESS)
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun    args = parser.parse_args()
123*4882a593Smuzhiyun    cli = FlameGraphCLI(args)
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun    process_event = cli.process_event
126*4882a593Smuzhiyun    trace_end = cli.trace_end
127