linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] perf script: add flamegraph.py script
@ 2020-02-21 17:55 Andreas Gerstmayr
  2020-02-25 19:54 ` Jiri Olsa
  0 siblings, 1 reply; 6+ messages in thread
From: Andreas Gerstmayr @ 2020-02-21 17:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: kabbott, skozina, mpetlan, nathans, mgoodwin, linux-perf-users,
	bgregg, mspier, Andreas Gerstmayr, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Namhyung Kim

This script works in tandem with d3-flame-graph to generate flame graphs
from perf. It supports two output formats: JSON and HTML (the default).
The HTML format will look for a standalone d3-flame-graph template file in
/usr/share/d3-flame-graph/template.html and fill in the collected stacks.

Usage:

    perf script flamegraph -a -F 99 sleep 60

Alternative usage:

    perf record -a -g -F 99 sleep 60
    perf script report flamegraph

Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---

I'm currently preparing packages for d3-flame-graph. For Fedora, the copr
at
https://copr.fedorainfracloud.org/coprs/agerstmayr/reviews/package/js-d3-flame-graph/
can be installed, or alternatively the prebuilt standalone d3-flame-graph
template can be downloaded from
https://raw.githubusercontent.com/andreasgerstmayr/specs/master/reviews/js-d3-flame-graph/template.html
and moved into /usr/share/d3-flame-graph/template.html

 .../perf/scripts/python/bin/flamegraph-record |   2 +
 .../perf/scripts/python/bin/flamegraph-report |   3 +
 tools/perf/scripts/python/flamegraph.py       | 117 ++++++++++++++++++
 3 files changed, 122 insertions(+)
 create mode 100755 tools/perf/scripts/python/bin/flamegraph-record
 create mode 100755 tools/perf/scripts/python/bin/flamegraph-report
 create mode 100755 tools/perf/scripts/python/flamegraph.py

diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/scripts/python/bin/flamegraph-record
new file mode 100755
index 000000000000..725d66e71570
--- /dev/null
+++ b/tools/perf/scripts/python/bin/flamegraph-record
@@ -0,0 +1,2 @@
+#!/usr/bin/sh
+perf record -g "$@"
diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
new file mode 100755
index 000000000000..b1a79afd903b
--- /dev/null
+++ b/tools/perf/scripts/python/bin/flamegraph-report
@@ -0,0 +1,3 @@
+#!/usr/bin/sh
+# description: create flame graphs
+perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
new file mode 100755
index 000000000000..2e9139ef2c4a
--- /dev/null
+++ b/tools/perf/scripts/python/flamegraph.py
@@ -0,0 +1,117 @@
+# flamegraph.py - create flame graphs from perf samples
+# SPDX-License-Identifier: GPL-2.0
+#
+# Usage:
+#
+#  perf record -a -g -F 99 sleep 60
+#  perf script report flamegraph
+#
+# Combined data collection and flamegraph generation:
+#
+#  perf script flamegraph -a -F 99 sleep 60
+#
+# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
+# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
+# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
+
+import sys
+import os
+import argparse
+import json
+
+
+class Node:
+    def __init__(self, name, libtype=""):
+        self.name = name
+        self.libtype = libtype
+        self.value = 0
+        self.children = []
+
+
+class FlameGraphCLI:
+    def __init__(self, args):
+        self.args = args
+        self.stack = Node("root")
+
+        if self.args.format == "html" and \
+                not os.path.isfile(self.args.template):
+            print(f"Flame Graph template '{self.args.template}' does not " +
+                  f"exist. Please install the d3-flame-graph package, " +
+                  f"specify an existing flame graph template " +
+                  f"(--template PATH) or another output format " +
+                  f"(--format FORMAT).", file=sys.stderr)
+            sys.exit(1)
+
+    def find_or_create_node(self, node, name, dso):
+        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
+        if name is None:
+            name = "[unknown]"
+
+        for child in node.children:
+            if child.name == name and child.libtype == libtype:
+                return child
+
+        child = Node(name, libtype)
+        node.children.append(child)
+        return child
+
+    def process_event(self, event):
+        node = self.find_or_create_node(self.stack, event["comm"], None)
+        if "callchain" in event:
+            for entry in reversed(event['callchain']):
+                node = self.find_or_create_node(
+                    node, entry.get("sym", {}).get("name"), event.get("dso"))
+        else:
+            node = self.find_or_create_node(
+                node, entry.get("symbol"), event.get("dso"))
+        node.value += 1
+
+    def trace_end(self):
+        def encoder(x): return x.__dict__
+        json_str = json.dumps(self.stack, default=encoder,
+                              indent=self.args.indent)
+
+        if self.args.format == "html":
+            try:
+                with open(self.args.template) as f:
+                    output_str = f.read().replace("/** @flamegraph_params **/",
+                                                  json_str)
+            except IOError as e:
+                print(f"Error reading template file: {e}", file=sys.stderr)
+                sys.exit(1)
+            output_fn = self.args.output or "flamegraph.html"
+        else:
+            output_str = json_str
+            output_fn = self.args.output or "stacks.json"
+
+        if output_fn == "-":
+            sys.stdout.write(output_str)
+        else:
+            try:
+                with open(output_fn, "w") as out:
+                    out.write(output_str)
+            except IOError as e:
+                print(f"Error writing output file: {e}", file=sys.stderr)
+                sys.exit(1)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Create flame graphs.")
+    parser.add_argument("-F", "--format",
+                        default="html", choices=["json", "html"],
+                        help="output file format")
+    parser.add_argument("-o", "--output",
+                        help="output file name")
+    parser.add_argument("--indent",
+                        type=int, help="JSON indentation")
+    parser.add_argument("--template",
+                        default="/usr/share/d3-flame-graph/template.html",
+                        help="path to flamegraph HTML template")
+    parser.add_argument("-i", "--input",
+                        help=argparse.SUPPRESS)
+
+    args = parser.parse_args()
+    cli = FlameGraphCLI(args)
+
+    process_event = cli.process_event
+    trace_end = cli.trace_end
-- 
2.24.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC] perf script: add flamegraph.py script
  2020-02-21 17:55 [RFC] perf script: add flamegraph.py script Andreas Gerstmayr
@ 2020-02-25 19:54 ` Jiri Olsa
  2020-02-25 20:03   ` Andreas Gerstmayr
  0 siblings, 1 reply; 6+ messages in thread
From: Jiri Olsa @ 2020-02-25 19:54 UTC (permalink / raw)
  To: Andreas Gerstmayr
  Cc: linux-kernel, kabbott, skozina, mpetlan, nathans, mgoodwin,
	linux-perf-users, bgregg, mspier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Namhyung Kim

On Fri, Feb 21, 2020 at 06:55:01PM +0100, Andreas Gerstmayr wrote:
> This script works in tandem with d3-flame-graph to generate flame graphs
> from perf. It supports two output formats: JSON and HTML (the default).
> The HTML format will look for a standalone d3-flame-graph template file in
> /usr/share/d3-flame-graph/template.html and fill in the collected stacks.
> 
> Usage:
> 
>     perf script flamegraph -a -F 99 sleep 60
> 
> Alternative usage:
> 
>     perf record -a -g -F 99 sleep 60
>     perf script report flamegraph

nice, could this output the output file, like:

     # perf script report flamegraph --output krava.html
     dumping data to krava.html

or something in that sense

other than that it looks good to me

thanks,
jirka


> 
> Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> Cc: Jiri Olsa <jolsa@redhat.com>
> Cc: Namhyung Kim <namhyung@kernel.org>
> ---
> 
> I'm currently preparing packages for d3-flame-graph. For Fedora, the copr
> at
> https://copr.fedorainfracloud.org/coprs/agerstmayr/reviews/package/js-d3-flame-graph/
> can be installed, or alternatively the prebuilt standalone d3-flame-graph
> template can be downloaded from
> https://raw.githubusercontent.com/andreasgerstmayr/specs/master/reviews/js-d3-flame-graph/template.html
> and moved into /usr/share/d3-flame-graph/template.html
> 
>  .../perf/scripts/python/bin/flamegraph-record |   2 +
>  .../perf/scripts/python/bin/flamegraph-report |   3 +
>  tools/perf/scripts/python/flamegraph.py       | 117 ++++++++++++++++++
>  3 files changed, 122 insertions(+)
>  create mode 100755 tools/perf/scripts/python/bin/flamegraph-record
>  create mode 100755 tools/perf/scripts/python/bin/flamegraph-report
>  create mode 100755 tools/perf/scripts/python/flamegraph.py
> 
> diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/scripts/python/bin/flamegraph-record
> new file mode 100755
> index 000000000000..725d66e71570
> --- /dev/null
> +++ b/tools/perf/scripts/python/bin/flamegraph-record
> @@ -0,0 +1,2 @@
> +#!/usr/bin/sh
> +perf record -g "$@"
> diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
> new file mode 100755
> index 000000000000..b1a79afd903b
> --- /dev/null
> +++ b/tools/perf/scripts/python/bin/flamegraph-report
> @@ -0,0 +1,3 @@
> +#!/usr/bin/sh
> +# description: create flame graphs
> +perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
> diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
> new file mode 100755
> index 000000000000..2e9139ef2c4a
> --- /dev/null
> +++ b/tools/perf/scripts/python/flamegraph.py
> @@ -0,0 +1,117 @@
> +# flamegraph.py - create flame graphs from perf samples
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Usage:
> +#
> +#  perf record -a -g -F 99 sleep 60
> +#  perf script report flamegraph
> +#
> +# Combined data collection and flamegraph generation:
> +#
> +#  perf script flamegraph -a -F 99 sleep 60
> +#
> +# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
> +# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
> +# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
> +
> +import sys
> +import os
> +import argparse
> +import json
> +
> +
> +class Node:
> +    def __init__(self, name, libtype=""):
> +        self.name = name
> +        self.libtype = libtype
> +        self.value = 0
> +        self.children = []
> +
> +
> +class FlameGraphCLI:
> +    def __init__(self, args):
> +        self.args = args
> +        self.stack = Node("root")
> +
> +        if self.args.format == "html" and \
> +                not os.path.isfile(self.args.template):
> +            print(f"Flame Graph template '{self.args.template}' does not " +
> +                  f"exist. Please install the d3-flame-graph package, " +
> +                  f"specify an existing flame graph template " +
> +                  f"(--template PATH) or another output format " +
> +                  f"(--format FORMAT).", file=sys.stderr)
> +            sys.exit(1)
> +
> +    def find_or_create_node(self, node, name, dso):
> +        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
> +        if name is None:
> +            name = "[unknown]"
> +
> +        for child in node.children:
> +            if child.name == name and child.libtype == libtype:
> +                return child
> +
> +        child = Node(name, libtype)
> +        node.children.append(child)
> +        return child
> +
> +    def process_event(self, event):
> +        node = self.find_or_create_node(self.stack, event["comm"], None)
> +        if "callchain" in event:
> +            for entry in reversed(event['callchain']):
> +                node = self.find_or_create_node(
> +                    node, entry.get("sym", {}).get("name"), event.get("dso"))
> +        else:
> +            node = self.find_or_create_node(
> +                node, entry.get("symbol"), event.get("dso"))
> +        node.value += 1
> +
> +    def trace_end(self):
> +        def encoder(x): return x.__dict__
> +        json_str = json.dumps(self.stack, default=encoder,
> +                              indent=self.args.indent)
> +
> +        if self.args.format == "html":
> +            try:
> +                with open(self.args.template) as f:
> +                    output_str = f.read().replace("/** @flamegraph_params **/",
> +                                                  json_str)
> +            except IOError as e:
> +                print(f"Error reading template file: {e}", file=sys.stderr)
> +                sys.exit(1)
> +            output_fn = self.args.output or "flamegraph.html"
> +        else:
> +            output_str = json_str
> +            output_fn = self.args.output or "stacks.json"
> +
> +        if output_fn == "-":
> +            sys.stdout.write(output_str)
> +        else:
> +            try:
> +                with open(output_fn, "w") as out:
> +                    out.write(output_str)
> +            except IOError as e:
> +                print(f"Error writing output file: {e}", file=sys.stderr)
> +                sys.exit(1)
> +
> +
> +if __name__ == "__main__":
> +    parser = argparse.ArgumentParser(description="Create flame graphs.")
> +    parser.add_argument("-F", "--format",
> +                        default="html", choices=["json", "html"],
> +                        help="output file format")
> +    parser.add_argument("-o", "--output",
> +                        help="output file name")
> +    parser.add_argument("--indent",
> +                        type=int, help="JSON indentation")
> +    parser.add_argument("--template",
> +                        default="/usr/share/d3-flame-graph/template.html",
> +                        help="path to flamegraph HTML template")
> +    parser.add_argument("-i", "--input",
> +                        help=argparse.SUPPRESS)
> +
> +    args = parser.parse_args()
> +    cli = FlameGraphCLI(args)
> +
> +    process_event = cli.process_event
> +    trace_end = cli.trace_end
> -- 
> 2.24.1
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] perf script: add flamegraph.py script
  2020-02-25 19:54 ` Jiri Olsa
@ 2020-02-25 20:03   ` Andreas Gerstmayr
  2020-02-25 20:20     ` Jiri Olsa
  0 siblings, 1 reply; 6+ messages in thread
From: Andreas Gerstmayr @ 2020-02-25 20:03 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, kabbott, skozina, mpetlan, nathans, mgoodwin,
	linux-perf-users, bgregg, mspier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Namhyung Kim

On 25.02.20 20:54, Jiri Olsa wrote:
> On Fri, Feb 21, 2020 at 06:55:01PM +0100, Andreas Gerstmayr wrote:
>> This script works in tandem with d3-flame-graph to generate flame graphs
>> from perf. It supports two output formats: JSON and HTML (the default).
>> The HTML format will look for a standalone d3-flame-graph template file in
>> /usr/share/d3-flame-graph/template.html and fill in the collected stacks.
>>
>> Usage:
>>
>>      perf script flamegraph -a -F 99 sleep 60
>>
>> Alternative usage:
>>
>>      perf record -a -g -F 99 sleep 60
>>      perf script report flamegraph
> 
> nice, could this output the output file, like:
> 
>       # perf script report flamegraph --output krava.html
>       dumping data to krava.html
> 
> or something in that sense
> 
> other than that it looks good to me

Yes, it's already implemented.

$ perf script report flamegraph --output krava.html

writes the output to krava.html

$ perf script report flamegraph --help

shows the supported arguments.

The only gotcha is that you need to have a perf.data in the same 
directory when calling this command, otherwise perf complains about a 
missing perf.data and doesn't call the flamegraph.py script.


Cheers,
Andreas


> 
> thanks,
> jirka
> 
> 
>>
>> Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
>> Cc: Peter Zijlstra <peterz@infradead.org>
>> Cc: Ingo Molnar <mingo@redhat.com>
>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
>> Cc: Jiri Olsa <jolsa@redhat.com>
>> Cc: Namhyung Kim <namhyung@kernel.org>
>> ---
>>
>> I'm currently preparing packages for d3-flame-graph. For Fedora, the copr
>> at
>> https://copr.fedorainfracloud.org/coprs/agerstmayr/reviews/package/js-d3-flame-graph/
>> can be installed, or alternatively the prebuilt standalone d3-flame-graph
>> template can be downloaded from
>> https://raw.githubusercontent.com/andreasgerstmayr/specs/master/reviews/js-d3-flame-graph/template.html
>> and moved into /usr/share/d3-flame-graph/template.html
>>
>>   .../perf/scripts/python/bin/flamegraph-record |   2 +
>>   .../perf/scripts/python/bin/flamegraph-report |   3 +
>>   tools/perf/scripts/python/flamegraph.py       | 117 ++++++++++++++++++
>>   3 files changed, 122 insertions(+)
>>   create mode 100755 tools/perf/scripts/python/bin/flamegraph-record
>>   create mode 100755 tools/perf/scripts/python/bin/flamegraph-report
>>   create mode 100755 tools/perf/scripts/python/flamegraph.py
>>
>> diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/scripts/python/bin/flamegraph-record
>> new file mode 100755
>> index 000000000000..725d66e71570
>> --- /dev/null
>> +++ b/tools/perf/scripts/python/bin/flamegraph-record
>> @@ -0,0 +1,2 @@
>> +#!/usr/bin/sh
>> +perf record -g "$@"
>> diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
>> new file mode 100755
>> index 000000000000..b1a79afd903b
>> --- /dev/null
>> +++ b/tools/perf/scripts/python/bin/flamegraph-report
>> @@ -0,0 +1,3 @@
>> +#!/usr/bin/sh
>> +# description: create flame graphs
>> +perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
>> diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
>> new file mode 100755
>> index 000000000000..2e9139ef2c4a
>> --- /dev/null
>> +++ b/tools/perf/scripts/python/flamegraph.py
>> @@ -0,0 +1,117 @@
>> +# flamegraph.py - create flame graphs from perf samples
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Usage:
>> +#
>> +#  perf record -a -g -F 99 sleep 60
>> +#  perf script report flamegraph
>> +#
>> +# Combined data collection and flamegraph generation:
>> +#
>> +#  perf script flamegraph -a -F 99 sleep 60
>> +#
>> +# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
>> +# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
>> +# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
>> +
>> +import sys
>> +import os
>> +import argparse
>> +import json
>> +
>> +
>> +class Node:
>> +    def __init__(self, name, libtype=""):
>> +        self.name = name
>> +        self.libtype = libtype
>> +        self.value = 0
>> +        self.children = []
>> +
>> +
>> +class FlameGraphCLI:
>> +    def __init__(self, args):
>> +        self.args = args
>> +        self.stack = Node("root")
>> +
>> +        if self.args.format == "html" and \
>> +                not os.path.isfile(self.args.template):
>> +            print(f"Flame Graph template '{self.args.template}' does not " +
>> +                  f"exist. Please install the d3-flame-graph package, " +
>> +                  f"specify an existing flame graph template " +
>> +                  f"(--template PATH) or another output format " +
>> +                  f"(--format FORMAT).", file=sys.stderr)
>> +            sys.exit(1)
>> +
>> +    def find_or_create_node(self, node, name, dso):
>> +        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
>> +        if name is None:
>> +            name = "[unknown]"
>> +
>> +        for child in node.children:
>> +            if child.name == name and child.libtype == libtype:
>> +                return child
>> +
>> +        child = Node(name, libtype)
>> +        node.children.append(child)
>> +        return child
>> +
>> +    def process_event(self, event):
>> +        node = self.find_or_create_node(self.stack, event["comm"], None)
>> +        if "callchain" in event:
>> +            for entry in reversed(event['callchain']):
>> +                node = self.find_or_create_node(
>> +                    node, entry.get("sym", {}).get("name"), event.get("dso"))
>> +        else:
>> +            node = self.find_or_create_node(
>> +                node, entry.get("symbol"), event.get("dso"))
>> +        node.value += 1
>> +
>> +    def trace_end(self):
>> +        def encoder(x): return x.__dict__
>> +        json_str = json.dumps(self.stack, default=encoder,
>> +                              indent=self.args.indent)
>> +
>> +        if self.args.format == "html":
>> +            try:
>> +                with open(self.args.template) as f:
>> +                    output_str = f.read().replace("/** @flamegraph_params **/",
>> +                                                  json_str)
>> +            except IOError as e:
>> +                print(f"Error reading template file: {e}", file=sys.stderr)
>> +                sys.exit(1)
>> +            output_fn = self.args.output or "flamegraph.html"
>> +        else:
>> +            output_str = json_str
>> +            output_fn = self.args.output or "stacks.json"
>> +
>> +        if output_fn == "-":
>> +            sys.stdout.write(output_str)
>> +        else:
>> +            try:
>> +                with open(output_fn, "w") as out:
>> +                    out.write(output_str)
>> +            except IOError as e:
>> +                print(f"Error writing output file: {e}", file=sys.stderr)
>> +                sys.exit(1)
>> +
>> +
>> +if __name__ == "__main__":
>> +    parser = argparse.ArgumentParser(description="Create flame graphs.")
>> +    parser.add_argument("-F", "--format",
>> +                        default="html", choices=["json", "html"],
>> +                        help="output file format")
>> +    parser.add_argument("-o", "--output",
>> +                        help="output file name")
>> +    parser.add_argument("--indent",
>> +                        type=int, help="JSON indentation")
>> +    parser.add_argument("--template",
>> +                        default="/usr/share/d3-flame-graph/template.html",
>> +                        help="path to flamegraph HTML template")
>> +    parser.add_argument("-i", "--input",
>> +                        help=argparse.SUPPRESS)
>> +
>> +    args = parser.parse_args()
>> +    cli = FlameGraphCLI(args)
>> +
>> +    process_event = cli.process_event
>> +    trace_end = cli.trace_end
>> -- 
>> 2.24.1
>>
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] perf script: add flamegraph.py script
  2020-02-25 20:03   ` Andreas Gerstmayr
@ 2020-02-25 20:20     ` Jiri Olsa
  2020-02-25 20:36       ` Andreas Gerstmayr
  0 siblings, 1 reply; 6+ messages in thread
From: Jiri Olsa @ 2020-02-25 20:20 UTC (permalink / raw)
  To: Andreas Gerstmayr
  Cc: linux-kernel, kabbott, skozina, mpetlan, nathans, mgoodwin,
	linux-perf-users, bgregg, mspier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Namhyung Kim

On Tue, Feb 25, 2020 at 09:03:19PM +0100, Andreas Gerstmayr wrote:
> On 25.02.20 20:54, Jiri Olsa wrote:
> > On Fri, Feb 21, 2020 at 06:55:01PM +0100, Andreas Gerstmayr wrote:
> > > This script works in tandem with d3-flame-graph to generate flame graphs
> > > from perf. It supports two output formats: JSON and HTML (the default).
> > > The HTML format will look for a standalone d3-flame-graph template file in
> > > /usr/share/d3-flame-graph/template.html and fill in the collected stacks.
> > > 
> > > Usage:
> > > 
> > >      perf script flamegraph -a -F 99 sleep 60
> > > 
> > > Alternative usage:
> > > 
> > >      perf record -a -g -F 99 sleep 60
> > >      perf script report flamegraph
> > 
> > nice, could this output the output file, like:
> > 
> >       # perf script report flamegraph --output krava.html
> >       dumping data to krava.html

I meant the actual line ^^^^, saying that it's writing to the file

thanks,
jirka

> > 
> > or something in that sense
> > 
> > other than that it looks good to me
> 
> Yes, it's already implemented.
> 
> $ perf script report flamegraph --output krava.html
> 
> writes the output to krava.html
> 
> $ perf script report flamegraph --help
> 
> shows the supported arguments.
> 
> The only gotcha is that you need to have a perf.data in the same directory
> when calling this command, otherwise perf complains about a missing
> perf.data and doesn't call the flamegraph.py script.
> 
> 
> Cheers,
> Andreas
> 
> 
> > 
> > thanks,
> > jirka
> > 
> > 
> > > 
> > > Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
> > > Cc: Peter Zijlstra <peterz@infradead.org>
> > > Cc: Ingo Molnar <mingo@redhat.com>
> > > Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
> > > Cc: Mark Rutland <mark.rutland@arm.com>
> > > Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> > > Cc: Jiri Olsa <jolsa@redhat.com>
> > > Cc: Namhyung Kim <namhyung@kernel.org>
> > > ---
> > > 
> > > I'm currently preparing packages for d3-flame-graph. For Fedora, the copr
> > > at
> > > https://copr.fedorainfracloud.org/coprs/agerstmayr/reviews/package/js-d3-flame-graph/
> > > can be installed, or alternatively the prebuilt standalone d3-flame-graph
> > > template can be downloaded from
> > > https://raw.githubusercontent.com/andreasgerstmayr/specs/master/reviews/js-d3-flame-graph/template.html
> > > and moved into /usr/share/d3-flame-graph/template.html
> > > 
> > >   .../perf/scripts/python/bin/flamegraph-record |   2 +
> > >   .../perf/scripts/python/bin/flamegraph-report |   3 +
> > >   tools/perf/scripts/python/flamegraph.py       | 117 ++++++++++++++++++
> > >   3 files changed, 122 insertions(+)
> > >   create mode 100755 tools/perf/scripts/python/bin/flamegraph-record
> > >   create mode 100755 tools/perf/scripts/python/bin/flamegraph-report
> > >   create mode 100755 tools/perf/scripts/python/flamegraph.py
> > > 
> > > diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/scripts/python/bin/flamegraph-record
> > > new file mode 100755
> > > index 000000000000..725d66e71570
> > > --- /dev/null
> > > +++ b/tools/perf/scripts/python/bin/flamegraph-record
> > > @@ -0,0 +1,2 @@
> > > +#!/usr/bin/sh
> > > +perf record -g "$@"
> > > diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
> > > new file mode 100755
> > > index 000000000000..b1a79afd903b
> > > --- /dev/null
> > > +++ b/tools/perf/scripts/python/bin/flamegraph-report
> > > @@ -0,0 +1,3 @@
> > > +#!/usr/bin/sh
> > > +# description: create flame graphs
> > > +perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
> > > diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
> > > new file mode 100755
> > > index 000000000000..2e9139ef2c4a
> > > --- /dev/null
> > > +++ b/tools/perf/scripts/python/flamegraph.py
> > > @@ -0,0 +1,117 @@
> > > +# flamegraph.py - create flame graphs from perf samples
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +#
> > > +# Usage:
> > > +#
> > > +#  perf record -a -g -F 99 sleep 60
> > > +#  perf script report flamegraph
> > > +#
> > > +# Combined data collection and flamegraph generation:
> > > +#
> > > +#  perf script flamegraph -a -F 99 sleep 60
> > > +#
> > > +# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
> > > +# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
> > > +# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
> > > +
> > > +import sys
> > > +import os
> > > +import argparse
> > > +import json
> > > +
> > > +
> > > +class Node:
> > > +    def __init__(self, name, libtype=""):
> > > +        self.name = name
> > > +        self.libtype = libtype
> > > +        self.value = 0
> > > +        self.children = []
> > > +
> > > +
> > > +class FlameGraphCLI:
> > > +    def __init__(self, args):
> > > +        self.args = args
> > > +        self.stack = Node("root")
> > > +
> > > +        if self.args.format == "html" and \
> > > +                not os.path.isfile(self.args.template):
> > > +            print(f"Flame Graph template '{self.args.template}' does not " +
> > > +                  f"exist. Please install the d3-flame-graph package, " +
> > > +                  f"specify an existing flame graph template " +
> > > +                  f"(--template PATH) or another output format " +
> > > +                  f"(--format FORMAT).", file=sys.stderr)
> > > +            sys.exit(1)
> > > +
> > > +    def find_or_create_node(self, node, name, dso):
> > > +        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
> > > +        if name is None:
> > > +            name = "[unknown]"
> > > +
> > > +        for child in node.children:
> > > +            if child.name == name and child.libtype == libtype:
> > > +                return child
> > > +
> > > +        child = Node(name, libtype)
> > > +        node.children.append(child)
> > > +        return child
> > > +
> > > +    def process_event(self, event):
> > > +        node = self.find_or_create_node(self.stack, event["comm"], None)
> > > +        if "callchain" in event:
> > > +            for entry in reversed(event['callchain']):
> > > +                node = self.find_or_create_node(
> > > +                    node, entry.get("sym", {}).get("name"), event.get("dso"))
> > > +        else:
> > > +            node = self.find_or_create_node(
> > > +                node, entry.get("symbol"), event.get("dso"))
> > > +        node.value += 1
> > > +
> > > +    def trace_end(self):
> > > +        def encoder(x): return x.__dict__
> > > +        json_str = json.dumps(self.stack, default=encoder,
> > > +                              indent=self.args.indent)
> > > +
> > > +        if self.args.format == "html":
> > > +            try:
> > > +                with open(self.args.template) as f:
> > > +                    output_str = f.read().replace("/** @flamegraph_params **/",
> > > +                                                  json_str)
> > > +            except IOError as e:
> > > +                print(f"Error reading template file: {e}", file=sys.stderr)
> > > +                sys.exit(1)
> > > +            output_fn = self.args.output or "flamegraph.html"
> > > +        else:
> > > +            output_str = json_str
> > > +            output_fn = self.args.output or "stacks.json"
> > > +
> > > +        if output_fn == "-":
> > > +            sys.stdout.write(output_str)
> > > +        else:
> > > +            try:
> > > +                with open(output_fn, "w") as out:
> > > +                    out.write(output_str)
> > > +            except IOError as e:
> > > +                print(f"Error writing output file: {e}", file=sys.stderr)
> > > +                sys.exit(1)
> > > +
> > > +
> > > +if __name__ == "__main__":
> > > +    parser = argparse.ArgumentParser(description="Create flame graphs.")
> > > +    parser.add_argument("-F", "--format",
> > > +                        default="html", choices=["json", "html"],
> > > +                        help="output file format")
> > > +    parser.add_argument("-o", "--output",
> > > +                        help="output file name")
> > > +    parser.add_argument("--indent",
> > > +                        type=int, help="JSON indentation")
> > > +    parser.add_argument("--template",
> > > +                        default="/usr/share/d3-flame-graph/template.html",
> > > +                        help="path to flamegraph HTML template")
> > > +    parser.add_argument("-i", "--input",
> > > +                        help=argparse.SUPPRESS)
> > > +
> > > +    args = parser.parse_args()
> > > +    cli = FlameGraphCLI(args)
> > > +
> > > +    process_event = cli.process_event
> > > +    trace_end = cli.trace_end
> > > -- 
> > > 2.24.1
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] perf script: add flamegraph.py script
  2020-02-25 20:20     ` Jiri Olsa
@ 2020-02-25 20:36       ` Andreas Gerstmayr
  2020-02-27 20:16         ` Andreas Gerstmayr
  0 siblings, 1 reply; 6+ messages in thread
From: Andreas Gerstmayr @ 2020-02-25 20:36 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, kabbott, skozina, mpetlan, nathans, mgoodwin,
	linux-perf-users, bgregg, mspier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Namhyung Kim



On 25.02.20 21:20, Jiri Olsa wrote:
> On Tue, Feb 25, 2020 at 09:03:19PM +0100, Andreas Gerstmayr wrote:
>> On 25.02.20 20:54, Jiri Olsa wrote:
>>> On Fri, Feb 21, 2020 at 06:55:01PM +0100, Andreas Gerstmayr wrote:
>>>> This script works in tandem with d3-flame-graph to generate flame graphs
>>>> from perf. It supports two output formats: JSON and HTML (the default).
>>>> The HTML format will look for a standalone d3-flame-graph template file in
>>>> /usr/share/d3-flame-graph/template.html and fill in the collected stacks.
>>>>
>>>> Usage:
>>>>
>>>>       perf script flamegraph -a -F 99 sleep 60
>>>>
>>>> Alternative usage:
>>>>
>>>>       perf record -a -g -F 99 sleep 60
>>>>       perf script report flamegraph
>>>
>>> nice, could this output the output file, like:
>>>
>>>        # perf script report flamegraph --output krava.html
>>>        dumping data to krava.html
> 
> I meant the actual line ^^^^, saying that it's writing to the file

Ah! Sorry, I misunderstood.
Yep, sure, I can add that.

I also have one other change lined up to reduce the JSON output, and I'm 
testing it with huge flamegraphs right now. Will send an update this week.


Cheers,
Andreas


> 
> thanks,
> jirka
> 
>>>
>>> or something in that sense
>>>
>>> other than that it looks good to me
>>
>> Yes, it's already implemented.
>>
>> $ perf script report flamegraph --output krava.html
>>
>> writes the output to krava.html
>>
>> $ perf script report flamegraph --help
>>
>> shows the supported arguments.
>>
>> The only gotcha is that you need to have a perf.data in the same directory
>> when calling this command, otherwise perf complains about a missing
>> perf.data and doesn't call the flamegraph.py script.
>>
>>
>> Cheers,
>> Andreas
>>
>>
>>>
>>> thanks,
>>> jirka
>>>
>>>
>>>>
>>>> Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
>>>> Cc: Peter Zijlstra <peterz@infradead.org>
>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>>> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
>>>> Cc: Jiri Olsa <jolsa@redhat.com>
>>>> Cc: Namhyung Kim <namhyung@kernel.org>
>>>> ---
>>>>
>>>> I'm currently preparing packages for d3-flame-graph. For Fedora, the copr
>>>> at
>>>> https://copr.fedorainfracloud.org/coprs/agerstmayr/reviews/package/js-d3-flame-graph/
>>>> can be installed, or alternatively the prebuilt standalone d3-flame-graph
>>>> template can be downloaded from
>>>> https://raw.githubusercontent.com/andreasgerstmayr/specs/master/reviews/js-d3-flame-graph/template.html
>>>> and moved into /usr/share/d3-flame-graph/template.html
>>>>
>>>>    .../perf/scripts/python/bin/flamegraph-record |   2 +
>>>>    .../perf/scripts/python/bin/flamegraph-report |   3 +
>>>>    tools/perf/scripts/python/flamegraph.py       | 117 ++++++++++++++++++
>>>>    3 files changed, 122 insertions(+)
>>>>    create mode 100755 tools/perf/scripts/python/bin/flamegraph-record
>>>>    create mode 100755 tools/perf/scripts/python/bin/flamegraph-report
>>>>    create mode 100755 tools/perf/scripts/python/flamegraph.py
>>>>
>>>> diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/scripts/python/bin/flamegraph-record
>>>> new file mode 100755
>>>> index 000000000000..725d66e71570
>>>> --- /dev/null
>>>> +++ b/tools/perf/scripts/python/bin/flamegraph-record
>>>> @@ -0,0 +1,2 @@
>>>> +#!/usr/bin/sh
>>>> +perf record -g "$@"
>>>> diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
>>>> new file mode 100755
>>>> index 000000000000..b1a79afd903b
>>>> --- /dev/null
>>>> +++ b/tools/perf/scripts/python/bin/flamegraph-report
>>>> @@ -0,0 +1,3 @@
>>>> +#!/usr/bin/sh
>>>> +# description: create flame graphs
>>>> +perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
>>>> diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
>>>> new file mode 100755
>>>> index 000000000000..2e9139ef2c4a
>>>> --- /dev/null
>>>> +++ b/tools/perf/scripts/python/flamegraph.py
>>>> @@ -0,0 +1,117 @@
>>>> +# flamegraph.py - create flame graphs from perf samples
>>>> +# SPDX-License-Identifier: GPL-2.0
>>>> +#
>>>> +# Usage:
>>>> +#
>>>> +#  perf record -a -g -F 99 sleep 60
>>>> +#  perf script report flamegraph
>>>> +#
>>>> +# Combined data collection and flamegraph generation:
>>>> +#
>>>> +#  perf script flamegraph -a -F 99 sleep 60
>>>> +#
>>>> +# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
>>>> +# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
>>>> +# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
>>>> +
>>>> +import sys
>>>> +import os
>>>> +import argparse
>>>> +import json
>>>> +
>>>> +
>>>> +class Node:
>>>> +    def __init__(self, name, libtype=""):
>>>> +        self.name = name
>>>> +        self.libtype = libtype
>>>> +        self.value = 0
>>>> +        self.children = []
>>>> +
>>>> +
>>>> +class FlameGraphCLI:
>>>> +    def __init__(self, args):
>>>> +        self.args = args
>>>> +        self.stack = Node("root")
>>>> +
>>>> +        if self.args.format == "html" and \
>>>> +                not os.path.isfile(self.args.template):
>>>> +            print(f"Flame Graph template '{self.args.template}' does not " +
>>>> +                  f"exist. Please install the d3-flame-graph package, " +
>>>> +                  f"specify an existing flame graph template " +
>>>> +                  f"(--template PATH) or another output format " +
>>>> +                  f"(--format FORMAT).", file=sys.stderr)
>>>> +            sys.exit(1)
>>>> +
>>>> +    def find_or_create_node(self, node, name, dso):
>>>> +        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
>>>> +        if name is None:
>>>> +            name = "[unknown]"
>>>> +
>>>> +        for child in node.children:
>>>> +            if child.name == name and child.libtype == libtype:
>>>> +                return child
>>>> +
>>>> +        child = Node(name, libtype)
>>>> +        node.children.append(child)
>>>> +        return child
>>>> +
>>>> +    def process_event(self, event):
>>>> +        node = self.find_or_create_node(self.stack, event["comm"], None)
>>>> +        if "callchain" in event:
>>>> +            for entry in reversed(event['callchain']):
>>>> +                node = self.find_or_create_node(
>>>> +                    node, entry.get("sym", {}).get("name"), event.get("dso"))
>>>> +        else:
>>>> +            node = self.find_or_create_node(
>>>> +                node, entry.get("symbol"), event.get("dso"))
>>>> +        node.value += 1
>>>> +
>>>> +    def trace_end(self):
>>>> +        def encoder(x): return x.__dict__
>>>> +        json_str = json.dumps(self.stack, default=encoder,
>>>> +                              indent=self.args.indent)
>>>> +
>>>> +        if self.args.format == "html":
>>>> +            try:
>>>> +                with open(self.args.template) as f:
>>>> +                    output_str = f.read().replace("/** @flamegraph_params **/",
>>>> +                                                  json_str)
>>>> +            except IOError as e:
>>>> +                print(f"Error reading template file: {e}", file=sys.stderr)
>>>> +                sys.exit(1)
>>>> +            output_fn = self.args.output or "flamegraph.html"
>>>> +        else:
>>>> +            output_str = json_str
>>>> +            output_fn = self.args.output or "stacks.json"
>>>> +
>>>> +        if output_fn == "-":
>>>> +            sys.stdout.write(output_str)
>>>> +        else:
>>>> +            try:
>>>> +                with open(output_fn, "w") as out:
>>>> +                    out.write(output_str)
>>>> +            except IOError as e:
>>>> +                print(f"Error writing output file: {e}", file=sys.stderr)
>>>> +                sys.exit(1)
>>>> +
>>>> +
>>>> +if __name__ == "__main__":
>>>> +    parser = argparse.ArgumentParser(description="Create flame graphs.")
>>>> +    parser.add_argument("-F", "--format",
>>>> +                        default="html", choices=["json", "html"],
>>>> +                        help="output file format")
>>>> +    parser.add_argument("-o", "--output",
>>>> +                        help="output file name")
>>>> +    parser.add_argument("--indent",
>>>> +                        type=int, help="JSON indentation")
>>>> +    parser.add_argument("--template",
>>>> +                        default="/usr/share/d3-flame-graph/template.html",
>>>> +                        help="path to flamegraph HTML template")
>>>> +    parser.add_argument("-i", "--input",
>>>> +                        help=argparse.SUPPRESS)
>>>> +
>>>> +    args = parser.parse_args()
>>>> +    cli = FlameGraphCLI(args)
>>>> +
>>>> +    process_event = cli.process_event
>>>> +    trace_end = cli.trace_end
>>>> -- 
>>>> 2.24.1
>>>>
>>>
>>
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] perf script: add flamegraph.py script
  2020-02-25 20:36       ` Andreas Gerstmayr
@ 2020-02-27 20:16         ` Andreas Gerstmayr
  0 siblings, 0 replies; 6+ messages in thread
From: Andreas Gerstmayr @ 2020-02-27 20:16 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: linux-kernel, kabbott, skozina, mpetlan, nathans, mgoodwin,
	linux-perf-users, bgregg, mspier, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Namhyung Kim

On 25.02.20 21:36, Andreas Gerstmayr wrote:
> On 25.02.20 21:20, Jiri Olsa wrote:
>> On Tue, Feb 25, 2020 at 09:03:19PM +0100, Andreas Gerstmayr wrote:
>>> On 25.02.20 20:54, Jiri Olsa wrote:
>>>> On Fri, Feb 21, 2020 at 06:55:01PM +0100, Andreas Gerstmayr wrote:
>>>>> Usage:
>>>>>
>>>>>       perf script flamegraph -a -F 99 sleep 60
>>>>>
>>>>> Alternative usage:
>>>>>
>>>>>       perf record -a -g -F 99 sleep 60
>>>>>       perf script report flamegraph
>>>>
>>>> nice, could this output the output file, like:
>>>>
>>>>        # perf script report flamegraph --output krava.html
>>>>        dumping data to krava.html
>>
>> I meant the actual line ^^^^, saying that it's writing to the file
> 
> Ah! Sorry, I misunderstood.
> Yep, sure, I can add that.
> 
> I also have one other change lined up to reduce the JSON output, and I'm 
> testing it with huge flamegraphs right now. Will send an update this week.

Ok, I'll need some more time to properly test this with huge flame 
graphs and different browsers. I'll get back to this in ~2 weeks after 
my PTO.


Cheers,
Andreas


> 
> 
> Cheers,
> Andreas
> 
> 
>>
>> thanks,
>> jirka
>>
>>>>
>>>> or something in that sense
>>>>
>>>> other than that it looks good to me
>>>
>>> Yes, it's already implemented.
>>>
>>> $ perf script report flamegraph --output krava.html
>>>
>>> writes the output to krava.html
>>>
>>> $ perf script report flamegraph --help
>>>
>>> shows the supported arguments.
>>>
>>> The only gotcha is that you need to have a perf.data in the same 
>>> directory
>>> when calling this command, otherwise perf complains about a missing
>>> perf.data and doesn't call the flamegraph.py script.
>>>
>>>
>>> Cheers,
>>> Andreas
>>>
>>>
>>>>
>>>> thanks,
>>>> jirka
>>>>
>>>>
>>>>>
>>>>> Signed-off-by: Andreas Gerstmayr <agerstmayr@redhat.com>
>>>>> Cc: Peter Zijlstra <peterz@infradead.org>
>>>>> Cc: Ingo Molnar <mingo@redhat.com>
>>>>> Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
>>>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>>>> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
>>>>> Cc: Jiri Olsa <jolsa@redhat.com>
>>>>> Cc: Namhyung Kim <namhyung@kernel.org>
>>>>> ---
>>>>>
>>>>> I'm currently preparing packages for d3-flame-graph. For Fedora, 
>>>>> the copr
>>>>> at
>>>>> https://copr.fedorainfracloud.org/coprs/agerstmayr/reviews/package/js-d3-flame-graph/ 
>>>>>
>>>>> can be installed, or alternatively the prebuilt standalone 
>>>>> d3-flame-graph
>>>>> template can be downloaded from
>>>>> https://raw.githubusercontent.com/andreasgerstmayr/specs/master/reviews/js-d3-flame-graph/template.html 
>>>>>
>>>>> and moved into /usr/share/d3-flame-graph/template.html
>>>>>
>>>>>    .../perf/scripts/python/bin/flamegraph-record |   2 +
>>>>>    .../perf/scripts/python/bin/flamegraph-report |   3 +
>>>>>    tools/perf/scripts/python/flamegraph.py       | 117 
>>>>> ++++++++++++++++++
>>>>>    3 files changed, 122 insertions(+)
>>>>>    create mode 100755 tools/perf/scripts/python/bin/flamegraph-record
>>>>>    create mode 100755 tools/perf/scripts/python/bin/flamegraph-report
>>>>>    create mode 100755 tools/perf/scripts/python/flamegraph.py
>>>>>
>>>>> diff --git a/tools/perf/scripts/python/bin/flamegraph-record 
>>>>> b/tools/perf/scripts/python/bin/flamegraph-record
>>>>> new file mode 100755
>>>>> index 000000000000..725d66e71570
>>>>> --- /dev/null
>>>>> +++ b/tools/perf/scripts/python/bin/flamegraph-record
>>>>> @@ -0,0 +1,2 @@
>>>>> +#!/usr/bin/sh
>>>>> +perf record -g "$@"
>>>>> diff --git a/tools/perf/scripts/python/bin/flamegraph-report 
>>>>> b/tools/perf/scripts/python/bin/flamegraph-report
>>>>> new file mode 100755
>>>>> index 000000000000..b1a79afd903b
>>>>> --- /dev/null
>>>>> +++ b/tools/perf/scripts/python/bin/flamegraph-report
>>>>> @@ -0,0 +1,3 @@
>>>>> +#!/usr/bin/sh
>>>>> +# description: create flame graphs
>>>>> +perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
>>>>> diff --git a/tools/perf/scripts/python/flamegraph.py 
>>>>> b/tools/perf/scripts/python/flamegraph.py
>>>>> new file mode 100755
>>>>> index 000000000000..2e9139ef2c4a
>>>>> --- /dev/null
>>>>> +++ b/tools/perf/scripts/python/flamegraph.py
>>>>> @@ -0,0 +1,117 @@
>>>>> +# flamegraph.py - create flame graphs from perf samples
>>>>> +# SPDX-License-Identifier: GPL-2.0
>>>>> +#
>>>>> +# Usage:
>>>>> +#
>>>>> +#  perf record -a -g -F 99 sleep 60
>>>>> +#  perf script report flamegraph
>>>>> +#
>>>>> +# Combined data collection and flamegraph generation:
>>>>> +#
>>>>> +#  perf script flamegraph -a -F 99 sleep 60
>>>>> +#
>>>>> +# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
>>>>> +# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
>>>>> +# Works in tandem with d3-flame-graph by Martin Spier 
>>>>> <mspier@netflix.com>
>>>>> +
>>>>> +import sys
>>>>> +import os
>>>>> +import argparse
>>>>> +import json
>>>>> +
>>>>> +
>>>>> +class Node:
>>>>> +    def __init__(self, name, libtype=""):
>>>>> +        self.name = name
>>>>> +        self.libtype = libtype
>>>>> +        self.value = 0
>>>>> +        self.children = []
>>>>> +
>>>>> +
>>>>> +class FlameGraphCLI:
>>>>> +    def __init__(self, args):
>>>>> +        self.args = args
>>>>> +        self.stack = Node("root")
>>>>> +
>>>>> +        if self.args.format == "html" and \
>>>>> +                not os.path.isfile(self.args.template):
>>>>> +            print(f"Flame Graph template '{self.args.template}' 
>>>>> does not " +
>>>>> +                  f"exist. Please install the d3-flame-graph 
>>>>> package, " +
>>>>> +                  f"specify an existing flame graph template " +
>>>>> +                  f"(--template PATH) or another output format " +
>>>>> +                  f"(--format FORMAT).", file=sys.stderr)
>>>>> +            sys.exit(1)
>>>>> +
>>>>> +    def find_or_create_node(self, node, name, dso):
>>>>> +        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
>>>>> +        if name is None:
>>>>> +            name = "[unknown]"
>>>>> +
>>>>> +        for child in node.children:
>>>>> +            if child.name == name and child.libtype == libtype:
>>>>> +                return child
>>>>> +
>>>>> +        child = Node(name, libtype)
>>>>> +        node.children.append(child)
>>>>> +        return child
>>>>> +
>>>>> +    def process_event(self, event):
>>>>> +        node = self.find_or_create_node(self.stack, event["comm"], 
>>>>> None)
>>>>> +        if "callchain" in event:
>>>>> +            for entry in reversed(event['callchain']):
>>>>> +                node = self.find_or_create_node(
>>>>> +                    node, entry.get("sym", {}).get("name"), 
>>>>> event.get("dso"))
>>>>> +        else:
>>>>> +            node = self.find_or_create_node(
>>>>> +                node, entry.get("symbol"), event.get("dso"))
>>>>> +        node.value += 1
>>>>> +
>>>>> +    def trace_end(self):
>>>>> +        def encoder(x): return x.__dict__
>>>>> +        json_str = json.dumps(self.stack, default=encoder,
>>>>> +                              indent=self.args.indent)
>>>>> +
>>>>> +        if self.args.format == "html":
>>>>> +            try:
>>>>> +                with open(self.args.template) as f:
>>>>> +                    output_str = f.read().replace("/** 
>>>>> @flamegraph_params **/",
>>>>> +                                                  json_str)
>>>>> +            except IOError as e:
>>>>> +                print(f"Error reading template file: {e}", 
>>>>> file=sys.stderr)
>>>>> +                sys.exit(1)
>>>>> +            output_fn = self.args.output or "flamegraph.html"
>>>>> +        else:
>>>>> +            output_str = json_str
>>>>> +            output_fn = self.args.output or "stacks.json"
>>>>> +
>>>>> +        if output_fn == "-":
>>>>> +            sys.stdout.write(output_str)
>>>>> +        else:
>>>>> +            try:
>>>>> +                with open(output_fn, "w") as out:
>>>>> +                    out.write(output_str)
>>>>> +            except IOError as e:
>>>>> +                print(f"Error writing output file: {e}", 
>>>>> file=sys.stderr)
>>>>> +                sys.exit(1)
>>>>> +
>>>>> +
>>>>> +if __name__ == "__main__":
>>>>> +    parser = argparse.ArgumentParser(description="Create flame 
>>>>> graphs.")
>>>>> +    parser.add_argument("-F", "--format",
>>>>> +                        default="html", choices=["json", "html"],
>>>>> +                        help="output file format")
>>>>> +    parser.add_argument("-o", "--output",
>>>>> +                        help="output file name")
>>>>> +    parser.add_argument("--indent",
>>>>> +                        type=int, help="JSON indentation")
>>>>> +    parser.add_argument("--template",
>>>>> +                        
>>>>> default="/usr/share/d3-flame-graph/template.html",
>>>>> +                        help="path to flamegraph HTML template")
>>>>> +    parser.add_argument("-i", "--input",
>>>>> +                        help=argparse.SUPPRESS)
>>>>> +
>>>>> +    args = parser.parse_args()
>>>>> +    cli = FlameGraphCLI(args)
>>>>> +
>>>>> +    process_event = cli.process_event
>>>>> +    trace_end = cli.trace_end
>>>>> -- 
>>>>> 2.24.1
>>>>>
>>>>
>>>
>>


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-02-27 20:17 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-21 17:55 [RFC] perf script: add flamegraph.py script Andreas Gerstmayr
2020-02-25 19:54 ` Jiri Olsa
2020-02-25 20:03   ` Andreas Gerstmayr
2020-02-25 20:20     ` Jiri Olsa
2020-02-25 20:36       ` Andreas Gerstmayr
2020-02-27 20:16         ` Andreas Gerstmayr

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).