Received: by 2002:a05:6358:a55:b0:ec:fcf4:3ecf with SMTP id 21csp1610005rwb; Thu, 19 Jan 2023 13:00:42 -0800 (PST) X-Google-Smtp-Source: AMrXdXtRpRBjIqmmtj6ou5SZyLgvBGNkmCKEzJYI2jHcXPPWkELNeDVbnP6j5OiZHCYww/Kf6rgI X-Received: by 2002:a17:906:6814:b0:870:ef77:e186 with SMTP id k20-20020a170906681400b00870ef77e186mr12044258ejr.49.1674162042263; Thu, 19 Jan 2023 13:00:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1674162042; cv=none; d=google.com; s=arc-20160816; b=IV/q1aj6T3NW0TSePyxSgmNxwpTMBhUhJocfvg9HlkcRie04PXhIvPxpmhIZ4wvcmc RheD+vUnZxDVOQxy5UWBasY0Ct/Z4jbqNqs+4O7p/1axjFOGVMA6kj5twm4/Aopynvg8 xqztYMz6Ixsm3y+fhrGrKLhcpNg+VhDYqEZzamSSJL6ipL79S3CwATb1QaVLeVytsvR9 jqb2zBbz9encB6OBdZBk6BJa9BumFwMzYGKGJbOv2oseGLm67CVX7XHX/rD1wL7MPP5L SbJR7opdaWf4lZZNUY7MLfnsoXbLgtAZl02ZPttIA8lQo1xBtyrhsw0+6q+23ECLYXtX D2dg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=bagGf68ry1JpnwUnvUmcScyJ7gubFGzB5rLy+GIW+8o=; b=GNjcv/Wv1K9hh9YuUM9LhH3i0qkJAbcfD4hKxA9DxxWKgqqeTuNzdVEysigFOqQcBE Jc0a7I6acwwV688hY2lGdEsJbs3RFkkihbXFXzrBDJi2vDy6PkA7HsjYMPG7SQwyjAGd 1cRqm7x6pl6KhJNFI3zp2eCC2UTzi/JJdGaI318Yy2OEgBo15A7E2vCwj8w8ZGqGMocR GE6NafvMvIOXchZ74bNA+Dz6UwKj9ag2dx6p3sfnY5PeLMe+CdrZMC7vTxfAn7bUTzDq etT2Zzq7maqQBOSw76N7dofGDp2NH/LRMon/UWB1wpbHeVS/u/ROgkbHn7II4oUw8eEY 31BA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=Lng8WB8m; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id ds9-20020a170907724900b008778a5b6c10si665368ejc.112.2023.01.19.13.00.31; Thu, 19 Jan 2023 13:00:42 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=Lng8WB8m; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230344AbjASTFN (ORCPT + 46 others); Thu, 19 Jan 2023 14:05:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47390 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231303AbjASTFE (ORCPT ); Thu, 19 Jan 2023 14:05:04 -0500 Received: from mail-wr1-x434.google.com (mail-wr1-x434.google.com [IPv6:2a00:1450:4864:20::434]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 16048658F for ; Thu, 19 Jan 2023 11:04:14 -0800 (PST) Received: by mail-wr1-x434.google.com with SMTP id d2so2790449wrp.8 for ; Thu, 19 Jan 2023 11:04:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=bagGf68ry1JpnwUnvUmcScyJ7gubFGzB5rLy+GIW+8o=; b=Lng8WB8mNVBoxfvrWFqe/ikPrxiqLnUVY+IlWZrUeFSnrEwGf8PQ2OSEZTbUoOIxIE yp5GE7dz1OlvzCPE9TS5VjLyoxAwaMZCEIoP0SZg1kMHCCa/10c+q6CwO5wq8UNYkUYh wFyujVatb6VulfkDnKiTp//G4JCu3dunSo1DaeIA50eCsOqBLJ0jdNRSbG9BNVwNQOP/ AGq1pS6ktFJQQWoB7HYUQGvtjxkNQBrJO8aF97LWtZJsc076PW9jvie+lTQZ9fXGMjDS zxMxRNcyvHCAJgg0eUyoKBl5LHOTZSOnU93hmqE9TrMPOyVUWv/CcfMTLhJoLNvHfVXW 2FbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=bagGf68ry1JpnwUnvUmcScyJ7gubFGzB5rLy+GIW+8o=; b=UoXW3ufMeZ51snwMLZYFq+e0zU77TMYHCQ5R4ko3NKqqrZY6uFn7p2QsMT7Jy0xQ7f H/91bsYhOLS+KCPWc4UwTYstqxlr/IN6wem9tbFtB7CbqjR6jok8ZZYrf8IlZ5A3G8WS Hv0GsXkUgZQFAxW6wa5jYvqv6SW2dIfYypepeipAgI32q3c1o3JK+++Pqxz/7ROUBVv9 uPvs9pHsVfTpfb9S4krgyLaPBb2MQPYIPFNSm23yTbGvS7k5B6O73WB45mcRnBpn+XNM i3QBQSGKwvR/OIcSb3LlrjUUZfNB43HsnL2KWrMKsRiQM3BdHSvWM24OoWndjTAwMYvG 4GSg== X-Gm-Message-State: AFqh2krvQyHADp5nRxNL/Q4CHNE0Uyewsm7Q3ruEIpttx9KaI7iJhW0t jFYAk4eXLNTFAmDUbHwMQKyvXctV+aObGJ1+bDs31Q== X-Received: by 2002:adf:e2cd:0:b0:2bd:f8da:9be0 with SMTP id d13-20020adfe2cd000000b002bdf8da9be0mr407328wrj.375.1674154963570; Thu, 19 Jan 2023 11:02:43 -0800 (PST) MIME-Version: 1.0 References: <20230119183118.126387-1-irogers@google.com> In-Reply-To: <20230119183118.126387-1-irogers@google.com> From: Ian Rogers Date: Thu, 19 Jan 2023 11:02:31 -0800 Message-ID: Subject: Re: [PATCH v3] perf script flamegraph: Avoid d3-flame-graph package dependency To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Connor OBrien , Nathan Chancellor , Ian Rogers , Kumar Kartikeya Dwivedi , bpf@vger.kernel.org, linux-kernel@vger.kernel.org Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-17.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, ENV_AND_HDR_SPF_MATCH,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS, USER_IN_DEF_DKIM_WL,USER_IN_DEF_SPF_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, Jan 19, 2023 at 10:31 AM Ian Rogers wrote: > > 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 > > Signed-off-by: Ian Rogers Apologies, please ignore this patch. I accidentally picked this merged change up with a wild card. Thanks, Ian > --- > tools/perf/scripts/python/flamegraph.py | 107 +++++++++++++++++++----- > 1 file changed, 85 insertions(+), 22 deletions(-) > > diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py > index b6af1dd5f816..cf7ce8229a6c 100755 > --- a/tools/perf/scripts/python/flamegraph.py > +++ b/tools/perf/scripts/python/flamegraph.py > @@ -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 = """ > + > + > + > +
> + > + > + > + > +""" > > # 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) > -- > 2.39.0.314.g84b9a713c41-goog >