Created
June 7, 2017 12:17
-
-
Save eush77/84824039bed14e63d90d72763ecb1720 to your computer and use it in GitHub Desktop.
Execution time splitter for flame graphs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Execution time splitter for flame graphs. | |
The script takes a flame graph SVG [1] and splits the samples by execution | |
time: a sample can be either compile time, or a run time. Useful for profiling | |
VMs employing JIT compilation. | |
Currently, functions with names starting with "llvm::" are compile time, | |
everything else is run time. | |
Usage: etsplit <graph.svg> | |
[1]: https://github.com/brendangregg/FlameGraph | |
""" | |
from collections import namedtuple | |
from xml.dom import minidom | |
import os | |
import re | |
import sys | |
ExecTimeSplit = namedtuple('ExecTimeSplit', ('compile_time', 'run_time')) | |
def is_compile_time_name(name): | |
return name.startswith('llvm::') | |
def gs_ranges(gs): | |
"X-axis ranges for all `g` elements in the input iterable." | |
return ((float(a.get('x').value), | |
float(a.get('x').value) + float(a.get('width').value)) | |
for a in (g.childNodes[2].attributes for g in gs)) | |
def compile_time_ranges(svg): | |
"X-axis ranges for all compile-time functions." | |
return gs_ranges(g | |
for g in svg.getElementsByTagName('g') | |
if is_compile_time_name( | |
g.childNodes[1].firstChild.nodeValue)) | |
def total_length(svg): | |
"Total length of the SVG canvas." | |
x1s, x2s = zip(*gs_ranges(svg.getElementsByTagName('g'))) | |
return max(x2s) - min(x1s) | |
def ranges_length(ranges): | |
"Total length of x-axis covered by ranges." | |
ranges = sorted(ranges) | |
if not ranges: | |
return 0 | |
x, length = ranges[0][0], 0 | |
for x1, x2 in ranges: | |
length += x2 - max(x, x1) | |
x = x2 | |
return length | |
def exec_time_split(svg): | |
total_time = total_length(svg) | |
compile_time = ranges_length(compile_time_ranges(svg)) | |
run_time = total_time - compile_time | |
return ExecTimeSplit(compile_time / total_time, run_time / total_time) | |
def total_samples(svg): | |
"Total amount of samples in the report." | |
all_title = [title | |
for title in (g.childNodes[1].firstChild.nodeValue | |
for g in svg.getElementsByTagName('g')) | |
if title.startswith('all (')] | |
assert(len(all_title) == 1) | |
all_title = all_title[0] | |
return float(re.match('^.*\(([\d,.]+) .*$', all_title) | |
.group(1) | |
.replace(',', '')) | |
if __name__ == '__main__': | |
if len(sys.argv) != 2: | |
sys.stderr.write('Usage: {} <graph.svg>\n'.format(sys.argv[0])) | |
sys.exit(os.EX_USAGE) | |
svg = minidom.parse(sys.argv[1]) | |
compile_time, run_time = exec_time_split(svg) | |
samples = total_samples(svg) | |
print('Compile time:\t{:6.1f} samples ({:5.2f}%)\n' | |
'Run time:\t{:6.1f} samples ({:5.2f}%)'.format( | |
compile_time * samples, | |
compile_time * 100, | |
run_time * samples, | |
run_time * 100)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment