mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
perf script flamegraph: Avoid d3-flame-graph package dependency
Currently flame graph generation requires a d3-flame-graph template to be installed. Unfortunately this is hard to come by for things like Debian [1]. If the template isn't installed then ask if it should be downloaded from jsdelivr CDN. The downloaded HTML file is validated against an md5sum. If the download fails, generate a minimal flame graph with the javascript coming from links to jsdelivr CDN. v3. Adds a warning message and quits before download in live mode. v2. Change the warning to a prompt about downloading and add the --allow-download command line flag. Add an md5sum check for the downloaded HTML. [1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=996839 Reviewed-by: Andreas Gerstmayr <agerstmayr@redhat.com> Signed-off-by: Ian Rogers <irogers@google.com> Cc: 996839@bugs.debian.org Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Brendan Gregg <brendan@intel.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Martin Spier <spiermar@gmail.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://lore.kernel.org/r/20230118072409.147786-1-irogers@google.com # v3 discussion Link: https://lore.kernel.org/r/20230112220024.32709-1-irogers@google.com # v2 discussion Link: https://lore.kernel.org/r/CAP-5=fXi_9zdhTAoYApiFQoLURAvpEatFzU3uL23o3zs=z25ZQ@mail.gmail.com # v1 discussion Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
7287904c87
commit
b430d24367
1 changed files with 85 additions and 22 deletions
|
@ -19,12 +19,34 @@
|
|||
# pylint: disable=missing-function-docstring
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import os
|
||||
import io
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
minimal_html = """<head>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart"></div>
|
||||
<script type="text/javascript" src="https://d3js.org/d3.v7.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
const stacks = [/** @flamegraph_json **/];
|
||||
// Note, options is unused.
|
||||
const options = [/** @options_json **/];
|
||||
|
||||
var chart = flamegraph();
|
||||
d3.select("#chart")
|
||||
.datum(stacks[0])
|
||||
.call(chart);
|
||||
</script>
|
||||
</body>
|
||||
"""
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class Node:
|
||||
|
@ -50,16 +72,6 @@ class FlameGraphCLI:
|
|||
self.args = args
|
||||
self.stack = Node("all", "root")
|
||||
|
||||
if self.args.format == "html" and \
|
||||
not os.path.isfile(self.args.template):
|
||||
print("Flame Graph template {} does not exist. Please install "
|
||||
"the js-d3-flame-graph (RPM) or libjs-d3-flame-graph (deb) "
|
||||
"package, specify an existing flame graph template "
|
||||
"(--template PATH) or another output format "
|
||||
"(--format FORMAT).".format(self.args.template),
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
@staticmethod
|
||||
def get_libtype_from_dso(dso):
|
||||
"""
|
||||
|
@ -128,16 +140,63 @@ class FlameGraphCLI:
|
|||
}
|
||||
options_json = json.dumps(options)
|
||||
|
||||
template_md5sum = None
|
||||
if self.args.format == "html":
|
||||
if os.path.isfile(self.args.template):
|
||||
template = f"file://{self.args.template}"
|
||||
else:
|
||||
if not self.args.allow_download:
|
||||
print(f"""Warning: Flame Graph template '{self.args.template}'
|
||||
does not exist. To avoid this please install a package such as the
|
||||
js-d3-flame-graph or libjs-d3-flame-graph, specify an existing flame
|
||||
graph template (--template PATH) or use another output format (--format
|
||||
FORMAT).""",
|
||||
file=sys.stderr)
|
||||
if self.args.input == "-":
|
||||
print("""Not attempting to download Flame Graph template as script command line
|
||||
input is disabled due to using live mode. If you want to download the
|
||||
template retry without live mode. For example, use 'perf record -a -g
|
||||
-F 99 sleep 60' and 'perf script report flamegraph'. Alternatively,
|
||||
download the template from:
|
||||
https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html
|
||||
and place it at:
|
||||
/usr/share/d3-flame-graph/d3-flamegraph-base.html""",
|
||||
file=sys.stderr)
|
||||
quit()
|
||||
s = None
|
||||
while s != "y" and s != "n":
|
||||
s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower()
|
||||
if s == "n":
|
||||
quit()
|
||||
template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html"
|
||||
template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36"
|
||||
|
||||
try:
|
||||
with io.open(self.args.template, encoding="utf-8") as template:
|
||||
output_str = (
|
||||
template.read()
|
||||
.replace("/** @options_json **/", options_json)
|
||||
.replace("/** @flamegraph_json **/", stacks_json)
|
||||
)
|
||||
except IOError as err:
|
||||
print("Error reading template file: {}".format(err), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
with urllib.request.urlopen(template) as template:
|
||||
output_str = "".join([
|
||||
l.decode("utf-8") for l in template.readlines()
|
||||
])
|
||||
except Exception as err:
|
||||
print(f"Error reading template {template}: {err}\n"
|
||||
"a minimal flame graph will be generated", file=sys.stderr)
|
||||
output_str = minimal_html
|
||||
template_md5sum = None
|
||||
|
||||
if template_md5sum:
|
||||
download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()
|
||||
if download_md5sum != template_md5sum:
|
||||
s = None
|
||||
while s != "y" and s != "n":
|
||||
s = input(f"""Unexpected template md5sum.
|
||||
{download_md5sum} != {template_md5sum}, for:
|
||||
{output_str}
|
||||
continue?[yn] """).lower()
|
||||
if s == "n":
|
||||
quit()
|
||||
|
||||
output_str = output_str.replace("/** @options_json **/", options_json)
|
||||
output_str = output_str.replace("/** @flamegraph_json **/", stacks_json)
|
||||
|
||||
output_fn = self.args.output or "flamegraph.html"
|
||||
else:
|
||||
output_str = stacks_json
|
||||
|
@ -172,6 +231,10 @@ if __name__ == "__main__":
|
|||
choices=["blue-green", "orange"])
|
||||
parser.add_argument("-i", "--input",
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument("--allow-download",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="allow unprompted downloading of HTML template")
|
||||
|
||||
cli_args = parser.parse_args()
|
||||
cli = FlameGraphCLI(cli_args)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue