|
@@ -98,7 +98,7 @@ def ratio(numerator, denominator):
|
|
|
|
|
|
class UndefinedEvent(Exception):
|
|
|
"""Raised when attempting to get an event which is undefined."""
|
|
|
-
|
|
|
+
|
|
|
def __init__(self, event):
|
|
|
Exception.__init__(self)
|
|
|
self.event = event
|
|
@@ -130,7 +130,7 @@ class Event(object):
|
|
|
assert val1 is not None
|
|
|
assert val2 is not None
|
|
|
return self._aggregator(val1, val2)
|
|
|
-
|
|
|
+
|
|
|
def format(self, val):
|
|
|
"""Format an event value."""
|
|
|
assert val is not None
|
|
@@ -176,13 +176,13 @@ class Object(object):
|
|
|
|
|
|
def __contains__(self, event):
|
|
|
return event in self.events
|
|
|
-
|
|
|
+
|
|
|
def __getitem__(self, event):
|
|
|
try:
|
|
|
return self.events[event]
|
|
|
except KeyError:
|
|
|
raise UndefinedEvent(event)
|
|
|
-
|
|
|
+
|
|
|
def __setitem__(self, event, value):
|
|
|
if value is None:
|
|
|
if event in self.events:
|
|
@@ -193,7 +193,7 @@ class Object(object):
|
|
|
|
|
|
class Call(Object):
|
|
|
"""A call between functions.
|
|
|
-
|
|
|
+
|
|
|
There should be at most one call object for every pair of functions.
|
|
|
"""
|
|
|
|
|
@@ -217,7 +217,7 @@ class Function(Object):
|
|
|
self.called = None
|
|
|
self.weight = None
|
|
|
self.cycle = None
|
|
|
-
|
|
|
+
|
|
|
def add_call(self, call):
|
|
|
if call.callee_id in self.calls:
|
|
|
sys.stderr.write('warning: overwriting call from function %s to %s\n' % (str(self.id), str(call.callee_id)))
|
|
@@ -258,7 +258,7 @@ class Function(Object):
|
|
|
|
|
|
return name
|
|
|
|
|
|
- # TODO: write utility functions
|
|
|
+ # : write utility functions
|
|
|
|
|
|
def __repr__(self):
|
|
|
return self.name
|
|
@@ -372,7 +372,7 @@ class Profile(Object):
|
|
|
if self.functions[f].name == funcName:
|
|
|
return f
|
|
|
return False
|
|
|
-
|
|
|
+
|
|
|
def _tarjan(self, function, order, stack, orders, lowlinks, visited):
|
|
|
"""Tarjan's strongly connected components algorithm.
|
|
|
|
|
@@ -388,7 +388,7 @@ class Profile(Object):
|
|
|
stack.append(function)
|
|
|
for call in compat_itervalues(function.calls):
|
|
|
callee = self.functions[call.callee_id]
|
|
|
- # TODO: use a set to optimize lookup
|
|
|
+ # : use a set to optimize lookup
|
|
|
if callee not in orders:
|
|
|
order = self._tarjan(callee, order, stack, orders, lowlinks, visited)
|
|
|
lowlinks[function] = min(lowlinks[function], lowlinks[callee])
|
|
@@ -464,7 +464,7 @@ class Profile(Object):
|
|
|
if call.callee_id != function.id:
|
|
|
assert call.ratio is not None
|
|
|
|
|
|
- # Aggregate the input for each cycle
|
|
|
+ # Aggregate the input for each cycle
|
|
|
for cycle in self.cycles:
|
|
|
total = inevent.null()
|
|
|
for function in compat_itervalues(self.functions):
|
|
@@ -489,7 +489,7 @@ class Profile(Object):
|
|
|
total += self._integrate_call(call, outevent, inevent)
|
|
|
function[outevent] = total
|
|
|
return function[outevent]
|
|
|
-
|
|
|
+
|
|
|
def _integrate_call(self, call, outevent, inevent):
|
|
|
assert outevent not in call
|
|
|
assert call.ratio is not None
|
|
@@ -511,7 +511,7 @@ class Profile(Object):
|
|
|
subtotal += self._integrate_call(call, outevent, inevent)
|
|
|
total += subtotal
|
|
|
cycle[outevent] = total
|
|
|
-
|
|
|
+
|
|
|
# Compute the time propagated to callers of this cycle
|
|
|
callees = {}
|
|
|
for function in compat_itervalues(self.functions):
|
|
@@ -523,7 +523,7 @@ class Profile(Object):
|
|
|
callees[callee] += call.ratio
|
|
|
except KeyError:
|
|
|
callees[callee] = call.ratio
|
|
|
-
|
|
|
+
|
|
|
for member in cycle.functions:
|
|
|
member[outevent] = outevent.null()
|
|
|
|
|
@@ -624,11 +624,11 @@ class Profile(Object):
|
|
|
|
|
|
if TOTAL_TIME_RATIO in call:
|
|
|
# handle exact cases first
|
|
|
- call.weight = call[TOTAL_TIME_RATIO]
|
|
|
+ call.weight = call[TOTAL_TIME_RATIO]
|
|
|
else:
|
|
|
try:
|
|
|
# make a safe estimate
|
|
|
- call.weight = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO])
|
|
|
+ call.weight = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO])
|
|
|
except UndefinedEvent:
|
|
|
pass
|
|
|
|
|
@@ -645,7 +645,7 @@ class Profile(Object):
|
|
|
call = function.calls[callee_id]
|
|
|
if callee_id not in self.functions or call.weight is not None and call.weight < edge_thres:
|
|
|
del function.calls[callee_id]
|
|
|
-
|
|
|
+
|
|
|
def dump(self):
|
|
|
for function in compat_itervalues(self.functions):
|
|
|
sys.stderr.write('Function %s:\n' % (function.name,))
|
|
@@ -677,7 +677,7 @@ class Struct:
|
|
|
if attrs is None:
|
|
|
attrs = {}
|
|
|
self.__dict__['_attrs'] = attrs
|
|
|
-
|
|
|
+
|
|
|
def __getattr__(self, name):
|
|
|
try:
|
|
|
return self._attrs[name]
|
|
@@ -692,7 +692,7 @@ class Struct:
|
|
|
|
|
|
def __repr__(self):
|
|
|
return repr(self._attrs)
|
|
|
-
|
|
|
+
|
|
|
|
|
|
class ParseError(Exception):
|
|
|
"""Raised when parsing to signal mismatches."""
|
|
@@ -700,7 +700,7 @@ class ParseError(Exception):
|
|
|
def __init__(self, msg, line):
|
|
|
Exception.__init__(self)
|
|
|
self.msg = msg
|
|
|
- # TODO: store more source line information
|
|
|
+ # : store more source line information
|
|
|
self.line = line
|
|
|
|
|
|
def __str__(self):
|
|
@@ -719,7 +719,7 @@ class Parser:
|
|
|
def parse(self):
|
|
|
raise NotImplementedError
|
|
|
|
|
|
-
|
|
|
+
|
|
|
class JsonParser(Parser):
|
|
|
"""Parser for a custom JSON representation of profile data.
|
|
|
|
|
@@ -868,21 +868,21 @@ class XmlTokenizer:
|
|
|
self.index = 0
|
|
|
self.final = False
|
|
|
self.skip_ws = skip_ws
|
|
|
-
|
|
|
+
|
|
|
self.character_pos = 0, 0
|
|
|
self.character_data = ''
|
|
|
-
|
|
|
+
|
|
|
self.parser = xml.parsers.expat.ParserCreate()
|
|
|
self.parser.StartElementHandler = self.handle_element_start
|
|
|
self.parser.EndElementHandler = self.handle_element_end
|
|
|
self.parser.CharacterDataHandler = self.handle_character_data
|
|
|
-
|
|
|
+
|
|
|
def handle_element_start(self, name, attributes):
|
|
|
self.finish_character_data()
|
|
|
line, column = self.pos()
|
|
|
token = XmlToken(XML_ELEMENT_START, name, attributes, line, column)
|
|
|
self.tokens.append(token)
|
|
|
-
|
|
|
+
|
|
|
def handle_element_end(self, name):
|
|
|
self.finish_character_data()
|
|
|
line, column = self.pos()
|
|
@@ -893,15 +893,15 @@ class XmlTokenizer:
|
|
|
if not self.character_data:
|
|
|
self.character_pos = self.pos()
|
|
|
self.character_data += data
|
|
|
-
|
|
|
+
|
|
|
def finish_character_data(self):
|
|
|
if self.character_data:
|
|
|
- if not self.skip_ws or not self.character_data.isspace():
|
|
|
+ if not self.skip_ws or not self.character_data.isspace():
|
|
|
line, column = self.character_pos
|
|
|
token = XmlToken(XML_CHARACTER_DATA, self.character_data, None, line, column)
|
|
|
self.tokens.append(token)
|
|
|
self.character_data = ''
|
|
|
-
|
|
|
+
|
|
|
def next(self):
|
|
|
size = 16*1024
|
|
|
while self.index >= len(self.tokens) and not self.final:
|
|
@@ -940,13 +940,13 @@ class XmlParser(Parser):
|
|
|
Parser.__init__(self)
|
|
|
self.tokenizer = XmlTokenizer(fp)
|
|
|
self.consume()
|
|
|
-
|
|
|
+
|
|
|
def consume(self):
|
|
|
self.token = self.tokenizer.next()
|
|
|
|
|
|
def match_element_start(self, name):
|
|
|
return self.token.type == XML_ELEMENT_START and self.token.name_or_data == name
|
|
|
-
|
|
|
+
|
|
|
def match_element_end(self, name):
|
|
|
return self.token.type == XML_ELEMENT_END and self.token.name_or_data == name
|
|
|
|
|
@@ -960,7 +960,7 @@ class XmlParser(Parser):
|
|
|
attrs = self.token.attrs
|
|
|
self.consume()
|
|
|
return attrs
|
|
|
-
|
|
|
+
|
|
|
def element_end(self, name):
|
|
|
while self.token.type == XML_CHARACTER_DATA:
|
|
|
self.consume()
|
|
@@ -1038,20 +1038,20 @@ class GprofParser(Parser):
|
|
|
)
|
|
|
|
|
|
_cg_primary_re = re.compile(
|
|
|
- r'^\[(?P<index>\d+)\]?' +
|
|
|
- r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
- r'\s+(?P<self>\d+\.\d+)' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
- r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
|
|
|
+ r'^\[(?P<index>\d+)\]?' +
|
|
|
+ r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<self>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
+ r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
|
|
|
r'\s+(?P<name>\S.*?)' +
|
|
|
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
|
|
|
r'\s\[(\d+)\]$'
|
|
|
)
|
|
|
|
|
|
_cg_parent_re = re.compile(
|
|
|
- r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
- r'\s+(?P<called>\d+)(?:/(?P<called_total>\d+))?' +
|
|
|
+ r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
+ r'\s+(?P<called>\d+)(?:/(?P<called_total>\d+))?' +
|
|
|
r'\s+(?P<name>\S.*?)' +
|
|
|
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
|
|
|
r'\s\[(?P<index>\d+)\]$'
|
|
@@ -1060,19 +1060,19 @@ class GprofParser(Parser):
|
|
|
_cg_child_re = _cg_parent_re
|
|
|
|
|
|
_cg_cycle_header_re = re.compile(
|
|
|
- r'^\[(?P<index>\d+)\]?' +
|
|
|
- r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
- r'\s+(?P<self>\d+\.\d+)' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
- r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
|
|
|
+ r'^\[(?P<index>\d+)\]?' +
|
|
|
+ r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<self>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
+ r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
|
|
|
r'\s+<cycle\s(?P<cycle>\d+)\sas\sa\swhole>' +
|
|
|
r'\s\[(\d+)\]$'
|
|
|
)
|
|
|
|
|
|
_cg_cycle_member_re = re.compile(
|
|
|
- r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
- r'\s+(?P<called>\d+)(?:\+(?P<called_self>\d+))?' +
|
|
|
+ r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
+ r'\s+(?P<called>\d+)(?:\+(?P<called_self>\d+))?' +
|
|
|
r'\s+(?P<name>\S.*?)' +
|
|
|
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
|
|
|
r'\s\[(?P<index>\d+)\]$'
|
|
@@ -1090,7 +1090,7 @@ class GprofParser(Parser):
|
|
|
line = lines.pop(0)
|
|
|
if line.startswith('['):
|
|
|
break
|
|
|
-
|
|
|
+
|
|
|
# read function parent line
|
|
|
mo = self._cg_parent_re.match(line)
|
|
|
if not mo:
|
|
@@ -1111,7 +1111,7 @@ class GprofParser(Parser):
|
|
|
|
|
|
while lines:
|
|
|
line = lines.pop(0)
|
|
|
-
|
|
|
+
|
|
|
# read function subroutine line
|
|
|
mo = self._cg_child_re.match(line)
|
|
|
if not mo:
|
|
@@ -1121,7 +1121,7 @@ class GprofParser(Parser):
|
|
|
else:
|
|
|
child = self.translate(mo)
|
|
|
children.append(child)
|
|
|
-
|
|
|
+
|
|
|
function.parents = parents
|
|
|
function.children = children
|
|
|
|
|
@@ -1146,7 +1146,7 @@ class GprofParser(Parser):
|
|
|
continue
|
|
|
call = self.translate(mo)
|
|
|
cycle.functions.append(call)
|
|
|
-
|
|
|
+
|
|
|
self.cycles[cycle.cycle] = cycle
|
|
|
|
|
|
def parse_cg_entry(self, lines):
|
|
@@ -1173,16 +1173,16 @@ class GprofParser(Parser):
|
|
|
self.parse_cg_entry(entry_lines)
|
|
|
entry_lines = []
|
|
|
else:
|
|
|
- entry_lines.append(line)
|
|
|
+ entry_lines.append(line)
|
|
|
line = self.readline()
|
|
|
-
|
|
|
+
|
|
|
def parse(self):
|
|
|
self.parse_cg()
|
|
|
self.fp.close()
|
|
|
|
|
|
profile = Profile()
|
|
|
profile[TIME] = 0.0
|
|
|
-
|
|
|
+
|
|
|
cycles = {}
|
|
|
for index in self.cycles:
|
|
|
cycles[index] = Cycle()
|
|
@@ -1197,16 +1197,16 @@ class GprofParser(Parser):
|
|
|
call = Call(entry.index)
|
|
|
call[CALLS] = entry.called_self
|
|
|
function.called += entry.called_self
|
|
|
-
|
|
|
+
|
|
|
# populate the function calls
|
|
|
for child in entry.children:
|
|
|
call = Call(child.index)
|
|
|
-
|
|
|
+
|
|
|
assert child.called is not None
|
|
|
call[CALLS] = child.called
|
|
|
|
|
|
if child.index not in self.functions:
|
|
|
- # NOTE: functions that were never called but were discovered by gprof's
|
|
|
+ # NOTE: functions that were never called but were discovered by gprof's
|
|
|
# static call graph analysis dont have a call graph entry so we need
|
|
|
# to add them here
|
|
|
missing = Function(child.index, child.name)
|
|
@@ -1222,7 +1222,7 @@ class GprofParser(Parser):
|
|
|
try:
|
|
|
cycle = cycles[entry.cycle]
|
|
|
except KeyError:
|
|
|
- sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
|
|
|
+ sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
|
|
|
cycle = Cycle()
|
|
|
cycles[entry.cycle] = cycle
|
|
|
cycle.add_function(function)
|
|
@@ -1302,18 +1302,18 @@ class AXEParser(Parser):
|
|
|
_cg_footer_re = re.compile(r'^Index\s+Function\s*$')
|
|
|
|
|
|
_cg_primary_re = re.compile(
|
|
|
- r'^\[(?P<index>\d+)\]?' +
|
|
|
- r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
- r'\s+(?P<self>\d+\.\d+)' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
+ r'^\[(?P<index>\d+)\]?' +
|
|
|
+ r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<self>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
r'\s+(?P<name>\S.*?)' +
|
|
|
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
|
|
|
r'\s+\[(\d+)\]$'
|
|
|
)
|
|
|
|
|
|
_cg_parent_re = re.compile(
|
|
|
- r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
+ r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
r'\s+(?P<name>\S.*?)' +
|
|
|
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
|
|
|
r'\s+\[(?P<index>\d+)\]$'
|
|
@@ -1322,17 +1322,17 @@ class AXEParser(Parser):
|
|
|
_cg_child_re = _cg_parent_re
|
|
|
|
|
|
_cg_cycle_header_re = re.compile(
|
|
|
- r'^\[(?P<index>\d+)\]?' +
|
|
|
- r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
- r'\s+(?P<self>\d+\.\d+)' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
+ r'^\[(?P<index>\d+)\]?' +
|
|
|
+ r'\s+(?P<percentage_time>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<self>\d+\.\d+)' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)' +
|
|
|
r'\s+<cycle\s(?P<cycle>\d+)\sas\sa\swhole>' +
|
|
|
r'\s+\[(\d+)\]$'
|
|
|
)
|
|
|
|
|
|
_cg_cycle_member_re = re.compile(
|
|
|
- r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
- r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
+ r'^\s+(?P<self>\d+\.\d+)?' +
|
|
|
+ r'\s+(?P<descendants>\d+\.\d+)?' +
|
|
|
r'\s+(?P<name>\S.*?)' +
|
|
|
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
|
|
|
r'\s+\[(?P<index>\d+)\]$'
|
|
@@ -1349,7 +1349,7 @@ class AXEParser(Parser):
|
|
|
line = lines.pop(0)
|
|
|
if line.startswith('['):
|
|
|
break
|
|
|
-
|
|
|
+
|
|
|
# read function parent line
|
|
|
mo = self._cg_parent_re.match(line)
|
|
|
if not mo:
|
|
@@ -1369,7 +1369,7 @@ class AXEParser(Parser):
|
|
|
|
|
|
while lines:
|
|
|
line = lines.pop(0)
|
|
|
-
|
|
|
+
|
|
|
# read function subroutine line
|
|
|
mo = self._cg_child_re.match(line)
|
|
|
if not mo:
|
|
@@ -1420,7 +1420,7 @@ class AXEParser(Parser):
|
|
|
continue
|
|
|
call = self.translate(mo)
|
|
|
cycle.functions.append(call)
|
|
|
-
|
|
|
+
|
|
|
cycle.parents = parents
|
|
|
self.cycles[cycle.cycle] = cycle
|
|
|
|
|
@@ -1446,7 +1446,7 @@ class AXEParser(Parser):
|
|
|
self.parse_cg_entry(entry_lines)
|
|
|
entry_lines = []
|
|
|
else:
|
|
|
- entry_lines.append(line)
|
|
|
+ entry_lines.append(line)
|
|
|
line = self.readline()
|
|
|
|
|
|
def parse(self):
|
|
@@ -1456,7 +1456,7 @@ class AXEParser(Parser):
|
|
|
|
|
|
profile = Profile()
|
|
|
profile[TIME] = 0.0
|
|
|
-
|
|
|
+
|
|
|
cycles = {}
|
|
|
for index in self.cycles:
|
|
|
cycles[index] = Cycle()
|
|
@@ -1466,7 +1466,7 @@ class AXEParser(Parser):
|
|
|
function = Function(entry.index, entry.name)
|
|
|
function[TIME] = entry.self
|
|
|
function[TOTAL_TIME_RATIO] = entry.percentage_time / 100.0
|
|
|
-
|
|
|
+
|
|
|
# populate the function calls
|
|
|
for child in entry.children:
|
|
|
call = Call(child.index)
|
|
@@ -1475,10 +1475,10 @@ class AXEParser(Parser):
|
|
|
call[TOTAL_TIME_RATIO] = function[TOTAL_TIME_RATIO]
|
|
|
|
|
|
if child.index not in self.functions:
|
|
|
- # NOTE: functions that were never called but were discovered by gprof's
|
|
|
+ # NOTE: functions that were never called but were discovered by gprof's
|
|
|
# static call graph analysis dont have a call graph entry so we need
|
|
|
# to add them here
|
|
|
- # FIXME: Is this applicable?
|
|
|
+ # : Is this applicable?
|
|
|
missing = Function(child.index, child.name)
|
|
|
function[TIME] = 0.0
|
|
|
profile.add_function(missing)
|
|
@@ -1491,7 +1491,7 @@ class AXEParser(Parser):
|
|
|
try:
|
|
|
cycle = cycles[entry.cycle]
|
|
|
except KeyError:
|
|
|
- sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
|
|
|
+ sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
|
|
|
cycle = Cycle()
|
|
|
cycles[entry.cycle] = cycle
|
|
|
cycle.add_function(function)
|
|
@@ -1519,7 +1519,7 @@ class AXEParser(Parser):
|
|
|
|
|
|
class CallgrindParser(LineParser):
|
|
|
"""Parser for valgrind's callgrind tool.
|
|
|
-
|
|
|
+
|
|
|
See also:
|
|
|
- http://valgrind.org/docs/manual/cl-format.html
|
|
|
"""
|
|
@@ -1631,7 +1631,7 @@ class CallgrindParser(LineParser):
|
|
|
self.parse_association_spec()
|
|
|
|
|
|
__subpos_re = r'(0x[0-9a-fA-F]+|\d+|\+\d+|-\d+|\*)'
|
|
|
- _cost_re = re.compile(r'^' +
|
|
|
+ _cost_re = re.compile(r'^' +
|
|
|
__subpos_re + r'( +' + __subpos_re + r')*' +
|
|
|
r'( +\d+)*' +
|
|
|
'$')
|
|
@@ -1675,12 +1675,12 @@ class CallgrindParser(LineParser):
|
|
|
events = [float(event) for event in events]
|
|
|
|
|
|
if calls is None:
|
|
|
- function[SAMPLES] += events[0]
|
|
|
+ function[SAMPLES] += events[0]
|
|
|
self.profile[SAMPLES] += events[0]
|
|
|
else:
|
|
|
callee = self.get_callee()
|
|
|
callee.called += calls
|
|
|
-
|
|
|
+
|
|
|
try:
|
|
|
call = function.calls[callee.id]
|
|
|
except KeyError:
|
|
@@ -1742,7 +1742,7 @@ class CallgrindParser(LineParser):
|
|
|
|
|
|
def parse_position_spec(self):
|
|
|
line = self.lookahead()
|
|
|
-
|
|
|
+
|
|
|
if line.startswith('jump=') or line.startswith('jcnd='):
|
|
|
self.consume()
|
|
|
return True
|
|
@@ -1801,7 +1801,7 @@ class CallgrindParser(LineParser):
|
|
|
return key, value
|
|
|
|
|
|
def make_function(self, module, filename, name):
|
|
|
- # FIXME: module and filename are not being tracked reliably
|
|
|
+ # : module and filename are not being tracked reliably
|
|
|
#id = '|'.join((module, filename, name))
|
|
|
id = name
|
|
|
try:
|
|
@@ -1817,14 +1817,14 @@ class CallgrindParser(LineParser):
|
|
|
|
|
|
def get_function(self):
|
|
|
module = self.positions.get('ob', '')
|
|
|
- filename = self.positions.get('fl', '')
|
|
|
- function = self.positions.get('fn', '')
|
|
|
+ filename = self.positions.get('fl', '')
|
|
|
+ function = self.positions.get('fn', '')
|
|
|
return self.make_function(module, filename, function)
|
|
|
|
|
|
def get_callee(self):
|
|
|
module = self.positions.get('cob', '')
|
|
|
- filename = self.positions.get('cfi', '')
|
|
|
- function = self.positions.get('cfn', '')
|
|
|
+ filename = self.positions.get('cfi', '')
|
|
|
+ function = self.positions.get('cfn', '')
|
|
|
return self.make_function(module, filename, function)
|
|
|
|
|
|
|
|
@@ -1954,7 +1954,7 @@ class PerfParser(LineParser):
|
|
|
|
|
|
class OprofileParser(LineParser):
|
|
|
"""Parser for oprofile callgraph output.
|
|
|
-
|
|
|
+
|
|
|
See also:
|
|
|
- http://oprofile.sourceforge.net/doc/opreport.html#opreport-callgraph
|
|
|
"""
|
|
@@ -1983,7 +1983,7 @@ class OprofileParser(LineParser):
|
|
|
self.update_subentries_dict(callers_total, callers)
|
|
|
function_total.samples += function.samples
|
|
|
self.update_subentries_dict(callees_total, callees)
|
|
|
-
|
|
|
+
|
|
|
def update_subentries_dict(self, totals, partials):
|
|
|
for partial in compat_itervalues(partials):
|
|
|
try:
|
|
@@ -1992,7 +1992,7 @@ class OprofileParser(LineParser):
|
|
|
totals[partial.id] = partial
|
|
|
else:
|
|
|
total.samples += partial.samples
|
|
|
-
|
|
|
+
|
|
|
def parse(self):
|
|
|
# read lookahead
|
|
|
self.readline()
|
|
@@ -2004,7 +2004,7 @@ class OprofileParser(LineParser):
|
|
|
profile = Profile()
|
|
|
|
|
|
reverse_call_samples = {}
|
|
|
-
|
|
|
+
|
|
|
# populate the profile
|
|
|
profile[SAMPLES] = 0
|
|
|
for _callers, _function, _callees in compat_itervalues(self.entries):
|
|
@@ -2027,7 +2027,7 @@ class OprofileParser(LineParser):
|
|
|
call = Call(_callee.id)
|
|
|
call[SAMPLES2] = _callee.samples
|
|
|
function.add_call(call)
|
|
|
-
|
|
|
+
|
|
|
# compute derived data
|
|
|
profile.validate()
|
|
|
profile.find_cycles()
|
|
@@ -2113,7 +2113,7 @@ class OprofileParser(LineParser):
|
|
|
def match_primary(self):
|
|
|
line = self.lookahead()
|
|
|
return not line[:1].isspace()
|
|
|
-
|
|
|
+
|
|
|
def match_secondary(self):
|
|
|
line = self.lookahead()
|
|
|
return line[:1].isspace()
|
|
@@ -2121,7 +2121,7 @@ class OprofileParser(LineParser):
|
|
|
|
|
|
class HProfParser(LineParser):
|
|
|
"""Parser for java hprof output
|
|
|
-
|
|
|
+
|
|
|
See also:
|
|
|
- http://java.sun.com/developer/technicalArticles/Programming/HPROF.html
|
|
|
"""
|
|
@@ -2282,7 +2282,7 @@ class SysprofParser(XmlParser):
|
|
|
|
|
|
def build_profile(self, objects, nodes):
|
|
|
profile = Profile()
|
|
|
-
|
|
|
+
|
|
|
profile[SAMPLES] = 0
|
|
|
for id, object in compat_iteritems(objects):
|
|
|
# Ignore fake objects (process names, modules, "Everything", "kernel", etc.)
|
|
@@ -2351,7 +2351,7 @@ class XPerfParser(Parser):
|
|
|
def parse(self):
|
|
|
import csv
|
|
|
reader = csv.reader(
|
|
|
- self.stream,
|
|
|
+ self.stream,
|
|
|
delimiter = ',',
|
|
|
quotechar = None,
|
|
|
escapechar = None,
|
|
@@ -2366,7 +2366,7 @@ class XPerfParser(Parser):
|
|
|
header = False
|
|
|
else:
|
|
|
self.parse_row(row)
|
|
|
-
|
|
|
+
|
|
|
# compute derived data
|
|
|
self.profile.validate()
|
|
|
self.profile.find_cycles()
|
|
@@ -2394,7 +2394,7 @@ class XPerfParser(Parser):
|
|
|
else:
|
|
|
break
|
|
|
fields[name] = value
|
|
|
-
|
|
|
+
|
|
|
process = fields['Process Name']
|
|
|
symbol = fields['Module'] + '!' + fields['Function']
|
|
|
weight = fields['Weight']
|
|
@@ -2465,12 +2465,12 @@ class SleepyParser(Parser):
|
|
|
self.calls = {}
|
|
|
|
|
|
self.profile = Profile()
|
|
|
-
|
|
|
+
|
|
|
_symbol_re = re.compile(
|
|
|
- r'^(?P<id>\w+)' +
|
|
|
- r'\s+"(?P<module>[^"]*)"' +
|
|
|
- r'\s+"(?P<procname>[^"]*)"' +
|
|
|
- r'\s+"(?P<sourcefile>[^"]*)"' +
|
|
|
+ r'^(?P<id>\w+)' +
|
|
|
+ r'\s+"(?P<module>[^"]*)"' +
|
|
|
+ r'\s+"(?P<procname>[^"]*)"' +
|
|
|
+ r'\s+"(?P<sourcefile>[^"]*)"' +
|
|
|
r'\s+(?P<sourceline>\d+)$'
|
|
|
)
|
|
|
|
|
@@ -2490,7 +2490,7 @@ class SleepyParser(Parser):
|
|
|
mo = self._symbol_re.match(line)
|
|
|
if mo:
|
|
|
symbol_id, module, procname, sourcefile, sourceline = mo.groups()
|
|
|
-
|
|
|
+
|
|
|
function_id = ':'.join([module, procname])
|
|
|
|
|
|
try:
|
|
@@ -2517,7 +2517,7 @@ class SleepyParser(Parser):
|
|
|
|
|
|
callee[SAMPLES] += samples
|
|
|
self.profile[SAMPLES] += samples
|
|
|
-
|
|
|
+
|
|
|
for caller in callstack[1:]:
|
|
|
try:
|
|
|
call = caller.calls[callee.id]
|
|
@@ -2650,7 +2650,7 @@ formats = {
|
|
|
|
|
|
class Theme:
|
|
|
|
|
|
- def __init__(self,
|
|
|
+ def __init__(self,
|
|
|
bgcolor = (0.0, 0.0, 1.0),
|
|
|
mincolor = (0.0, 0.0, 0.0),
|
|
|
maxcolor = (0.0, 0.0, 1.0),
|
|
@@ -2720,10 +2720,10 @@ class Theme:
|
|
|
|
|
|
def color(self, weight):
|
|
|
weight = min(max(weight, 0.0), 1.0)
|
|
|
-
|
|
|
+
|
|
|
hmin, smin, lmin = self.mincolor
|
|
|
hmax, smax, lmax = self.maxcolor
|
|
|
-
|
|
|
+
|
|
|
if self.skew < 0:
|
|
|
raise ValueError("Skew must be greater than 0")
|
|
|
elif self.skew == 1.0:
|
|
@@ -2856,7 +2856,7 @@ class DotWriter:
|
|
|
ratio = 2.0/3.0
|
|
|
height = max(int(len(name)/(1.0 - ratio) + 0.5), 1)
|
|
|
width = max(len(name)/height, 32)
|
|
|
- # TODO: break lines in symbols
|
|
|
+ # : break lines in symbols
|
|
|
name = textwrap.fill(name, width, break_long_words=False)
|
|
|
|
|
|
# Take away spaces
|
|
@@ -2908,10 +2908,10 @@ class DotWriter:
|
|
|
weight = 0.0
|
|
|
|
|
|
label = '\n'.join(labels)
|
|
|
- self.node(function.id,
|
|
|
- label = label,
|
|
|
- color = self.color(theme.node_bgcolor(weight)),
|
|
|
- fontcolor = self.color(theme.node_fgcolor(weight)),
|
|
|
+ self.node(function.id,
|
|
|
+ label = label,
|
|
|
+ color = self.color(theme.node_bgcolor(weight)),
|
|
|
+ fontcolor = self.color(theme.node_fgcolor(weight)),
|
|
|
fontsize = "%.2f" % theme.node_fontsize(weight),
|
|
|
)
|
|
|
|
|
@@ -2933,13 +2933,13 @@ class DotWriter:
|
|
|
|
|
|
label = '\n'.join(labels)
|
|
|
|
|
|
- self.edge(function.id, call.callee_id,
|
|
|
- label = label,
|
|
|
- color = self.color(theme.edge_color(weight)),
|
|
|
+ self.edge(function.id, call.callee_id,
|
|
|
+ label = label,
|
|
|
+ color = self.color(theme.edge_color(weight)),
|
|
|
fontcolor = self.color(theme.edge_color(weight)),
|
|
|
- fontsize = "%.2f" % theme.edge_fontsize(weight),
|
|
|
- penwidth = "%.2f" % theme.edge_penwidth(weight),
|
|
|
- labeldistance = "%.2f" % theme.edge_penwidth(weight),
|
|
|
+ fontsize = "%.2f" % theme.edge_fontsize(weight),
|
|
|
+ penwidth = "%.2f" % theme.edge_penwidth(weight),
|
|
|
+ labeldistance = "%.2f" % theme.edge_penwidth(weight),
|
|
|
arrowsize = "%.2f" % theme.edge_arrowsize(weight),
|
|
|
)
|
|
|
|