Skip to content

Instantly share code, notes, and snippets.

@eush77
Created June 7, 2017 12:17
Show Gist options
  • Save eush77/84824039bed14e63d90d72763ecb1720 to your computer and use it in GitHub Desktop.
Save eush77/84824039bed14e63d90d72763ecb1720 to your computer and use it in GitHub Desktop.
Execution time splitter for flame graphs
#!/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