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