Skip to content

Commit

Permalink
Adds a switch to select different methods of calculating total time.
Browse files Browse the repository at this point in the history
  • Loading branch information
David Flater authored and jrfonseca committed Apr 9, 2013
1 parent 7ff7647 commit f557ddc
Showing 1 changed file with 45 additions and 3 deletions.
48 changes: 45 additions & 3 deletions gprof2dot.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ def format(self, val):
SAMPLES = Event("Samples", 0, add)
SAMPLES2 = Event("Samples", 0, add)

# Count of samples where a given function was either executing or on the stack.
# This is used to calculate the total time ratio according to the
# straightforward method described in Mike Dunlavey's answer to
# stackoverflow.com/questions/1777556/alternatives-to-gprof, item 4 (the myth
# "that recursion is a tricky confusing issue"), last edited 2012-08-30: it's
# just the ratio of TOTAL_SAMPLES over the number of samples in the profile.
#
# Used only when total == callstacks
TOTAL_SAMPLES = Event("Samples", 0, add)

TIME = Event("Time", 0.0, add, lambda x: '(' + str(x) + ')')
TIME_RATIO = Event("Time ratio", 0.0, add, lambda x: '(' + percentage(x) + ')')
TOTAL_TIME = Event("Total time", 0.0, fail)
Expand Down Expand Up @@ -1740,9 +1750,10 @@ class PerfParser(LineParser):
perf script | gprof2dot.py --format=perf
"""

def __init__(self, infile):
def __init__(self, infile, total):
LineParser.__init__(self, infile)
self.profile = Profile()
self.total = total

def readline(self):
# Override LineParser.readline to ignore comment lines
Expand All @@ -1765,7 +1776,21 @@ def parse(self):
profile.find_cycles()
profile.ratio(TIME_RATIO, SAMPLES)
profile.call_ratios(SAMPLES2)
profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
if self.total == "callratios":
# Heuristic approach. TOTAL_SAMPLES is unused.
profile.integrate(TOTAL_TIME_RATIO, TIME_RATIO)
elif self.total == "callstacks":
# Use the actual call chains for functions.
profile[TOTAL_SAMPLES] = profile[SAMPLES]
profile.ratio(TOTAL_TIME_RATIO, TOTAL_SAMPLES)
# Then propagate that total time to the calls.
for function in profile.functions.itervalues():
for call in function.calls.itervalues():
if call.ratio is not None:
callee = profile.functions[call.callee_id]
call[TOTAL_TIME_RATIO] = call.ratio * callee[TOTAL_TIME_RATIO];
else:
assert False

return profile

Expand Down Expand Up @@ -1796,6 +1821,11 @@ def parse_event(self):

callee = caller

# Increment TOTAL_SAMPLES only once on each function.
stack = set(callchain)
for function in stack:
function[TOTAL_SAMPLES] += 1

def parse_callchain(self):
callchain = []
while self.lookahead():
Expand Down Expand Up @@ -1830,6 +1860,7 @@ def parse_call(self):
function = Function(function_id, function_name)
function.module = os.path.basename(module)
function[SAMPLES] = 0
function[TOTAL_SAMPLES] = 0
self.profile.add_function(function)

return function
Expand Down Expand Up @@ -3115,6 +3146,11 @@ def main(self):
type="choice", choices=('prof', 'axe', 'callgrind', 'perf', 'oprofile', 'hprof', 'sysprof', 'pstats', 'shark', 'sleepy', 'aqtime', 'xperf'),
dest="format", default="prof",
help="profile format: prof, callgrind, oprofile, hprof, sysprof, shark, sleepy, aqtime, pstats, axe, perf, or xperf [default: %default]")
parser.add_option(
'--total',
type="choice", choices=('callratios', 'callstacks'),
dest="total", default="callratios",
help="preferred method of calculating total time: callratios or callstacks (currently affects only perf format) [default: %default]")
parser.add_option(
'-c', '--colormap',
type="choice", choices=('color', 'pink', 'gray', 'bw'),
Expand Down Expand Up @@ -3178,7 +3214,13 @@ def main(self):
fp = sys.stdin
else:
fp = open(self.args[0], 'rt')
parser = stdinFormats[self.options.format](fp)
# For now, make an exception for perf as it is the only parser
# that supports the total parameter. When there are more, modify
# the signature of Parser.
if self.options.format == "perf":
parser = PerfParser(fp,self.options.total)
else:
parser = stdinFormats[self.options.format](fp)
elif self.options.format == 'pstats':
if not self.args:
parser.error('at least a file must be specified for pstats input')
Expand Down

0 comments on commit f557ddc

Please sign in to comment.