1*523fa7a6SAndroid Build Coastguard Worker# Copyright (c) Meta Platforms, Inc. and affiliates. 2*523fa7a6SAndroid Build Coastguard Worker# All rights reserved. 3*523fa7a6SAndroid Build Coastguard Worker# 4*523fa7a6SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the 5*523fa7a6SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree. 6*523fa7a6SAndroid Build Coastguard Worker 7*523fa7a6SAndroid Build Coastguard Workerimport cProfile 8*523fa7a6SAndroid Build Coastguard Workerimport io 9*523fa7a6SAndroid Build Coastguard Workerimport logging 10*523fa7a6SAndroid Build Coastguard Workerimport os 11*523fa7a6SAndroid Build Coastguard Workerimport pstats 12*523fa7a6SAndroid Build Coastguard Workerimport re 13*523fa7a6SAndroid Build Coastguard Workerfrom pstats import Stats 14*523fa7a6SAndroid Build Coastguard Worker 15*523fa7a6SAndroid Build Coastguard Workerfrom snakeviz.stats import json_stats, table_rows 16*523fa7a6SAndroid Build Coastguard Workerfrom tornado import template 17*523fa7a6SAndroid Build Coastguard Worker 18*523fa7a6SAndroid Build Coastguard Workermodule_found = True 19*523fa7a6SAndroid Build Coastguard Workertry: 20*523fa7a6SAndroid Build Coastguard Worker import snakeviz 21*523fa7a6SAndroid Build Coastguard Workerexcept ImportError: 22*523fa7a6SAndroid Build Coastguard Worker module_found = False 23*523fa7a6SAndroid Build Coastguard Worker 24*523fa7a6SAndroid Build Coastguard Workersnakeviz_dir = os.path.dirname(os.path.abspath(snakeviz.__file__)) 25*523fa7a6SAndroid Build Coastguard Workersnakeviz_templates_dir = os.path.join(snakeviz_dir, "templates") 26*523fa7a6SAndroid Build Coastguard Worker 27*523fa7a6SAndroid Build Coastguard Worker 28*523fa7a6SAndroid Build Coastguard Workerdef _from_pstat_to_static_html(stats: Stats, html_filename: str): 29*523fa7a6SAndroid Build Coastguard Worker """ 30*523fa7a6SAndroid Build Coastguard Worker Parses pstats data and populates viz.html template stored under templates dir. 31*523fa7a6SAndroid Build Coastguard Worker This utility allows to export html file without kicking off webserver. 32*523fa7a6SAndroid Build Coastguard Worker 33*523fa7a6SAndroid Build Coastguard Worker Note that it relies js scripts stored at rawgit cdn. This is not super 34*523fa7a6SAndroid Build Coastguard Worker reliable, however it does allow one to not have to rely on webserver and 35*523fa7a6SAndroid Build Coastguard Worker local rendering. On the other hand, for local rendering please follow 36*523fa7a6SAndroid Build Coastguard Worker the main snakeviz tutorial 37*523fa7a6SAndroid Build Coastguard Worker 38*523fa7a6SAndroid Build Coastguard Worker Inspiration for this util is from https://gist.github.com/jiffyclub/6b5e0f0f05ab487ff607. 39*523fa7a6SAndroid Build Coastguard Worker 40*523fa7a6SAndroid Build Coastguard Worker Args: 41*523fa7a6SAndroid Build Coastguard Worker stats: Stats generated from cProfile data 42*523fa7a6SAndroid Build Coastguard Worker html_filename: Output filename in which populated template is rendered 43*523fa7a6SAndroid Build Coastguard Worker """ 44*523fa7a6SAndroid Build Coastguard Worker RESTR = r'(?<!] \+ ")/static/' 45*523fa7a6SAndroid Build Coastguard Worker REPLACE_WITH = "https://cdn.rawgit.com/jiffyclub/snakeviz/v0.4.2/snakeviz/static/" 46*523fa7a6SAndroid Build Coastguard Worker 47*523fa7a6SAndroid Build Coastguard Worker if not isinstance(html_filename, str): 48*523fa7a6SAndroid Build Coastguard Worker raise ValueError("A valid file name must be provided.") 49*523fa7a6SAndroid Build Coastguard Worker 50*523fa7a6SAndroid Build Coastguard Worker viz_html_loader = template.Loader(snakeviz_templates_dir) 51*523fa7a6SAndroid Build Coastguard Worker html_bytes_renderer = viz_html_loader.load("viz.html") 52*523fa7a6SAndroid Build Coastguard Worker file_split = html_filename.split(".") 53*523fa7a6SAndroid Build Coastguard Worker if len(file_split) < 2: 54*523fa7a6SAndroid Build Coastguard Worker raise ValueError( 55*523fa7a6SAndroid Build Coastguard Worker f"\033[0;32;40m Provided filename \033[0;31;47m {html_filename} \033[0;32;40m does not contain . separator." 56*523fa7a6SAndroid Build Coastguard Worker ) 57*523fa7a6SAndroid Build Coastguard Worker profile_name = file_split[0] 58*523fa7a6SAndroid Build Coastguard Worker html_bytes = html_bytes_renderer.generate( 59*523fa7a6SAndroid Build Coastguard Worker profile_name=profile_name, 60*523fa7a6SAndroid Build Coastguard Worker table_rows=table_rows(stats), 61*523fa7a6SAndroid Build Coastguard Worker callees=json_stats(stats), 62*523fa7a6SAndroid Build Coastguard Worker ) 63*523fa7a6SAndroid Build Coastguard Worker html_string = html_bytes.decode("utf-8") 64*523fa7a6SAndroid Build Coastguard Worker html_string = re.sub(RESTR, REPLACE_WITH, html_string) 65*523fa7a6SAndroid Build Coastguard Worker with open(html_filename, "w") as f: 66*523fa7a6SAndroid Build Coastguard Worker f.write(html_string) 67*523fa7a6SAndroid Build Coastguard Worker 68*523fa7a6SAndroid Build Coastguard Worker 69*523fa7a6SAndroid Build Coastguard Workerclass CProfilerFlameGraph: 70*523fa7a6SAndroid Build Coastguard Worker def __init__(self, filename: str): 71*523fa7a6SAndroid Build Coastguard Worker if not module_found: 72*523fa7a6SAndroid Build Coastguard Worker raise Exception( 73*523fa7a6SAndroid Build Coastguard Worker "Please install snakeviz to use CProfilerFlameGraph. Follow cprofiler_flamegraph.md for more information." 74*523fa7a6SAndroid Build Coastguard Worker ) 75*523fa7a6SAndroid Build Coastguard Worker self.filename = filename 76*523fa7a6SAndroid Build Coastguard Worker 77*523fa7a6SAndroid Build Coastguard Worker def __enter__(self): 78*523fa7a6SAndroid Build Coastguard Worker self.pr = cProfile.Profile() 79*523fa7a6SAndroid Build Coastguard Worker self.pr.enable() 80*523fa7a6SAndroid Build Coastguard Worker 81*523fa7a6SAndroid Build Coastguard Worker def __exit__(self, exc_type, exc_val, exc_tb): 82*523fa7a6SAndroid Build Coastguard Worker if exc_type is not None: 83*523fa7a6SAndroid Build Coastguard Worker logging.error("Exception occurred", exc_info=(exc_type, exc_val, exc_tb)) 84*523fa7a6SAndroid Build Coastguard Worker 85*523fa7a6SAndroid Build Coastguard Worker self.pr.disable() 86*523fa7a6SAndroid Build Coastguard Worker s = io.StringIO() 87*523fa7a6SAndroid Build Coastguard Worker ps = pstats.Stats(self.pr, stream=s) 88*523fa7a6SAndroid Build Coastguard Worker _from_pstat_to_static_html(ps, self.filename) 89