""" Utilities for working with Valgrind. """ from lnt.util import logger # See: # http://valgrind.org/docs/manual/cl-format.html#cl-format.overview # for reference on the calltree data format specification. class CalltreeParseError(Exception): pass class CalltreeData(object): @staticmethod def frompath(path): with open(path) as file: return CalltreeData.fromfile(file, path) @staticmethod def fromfile(file, path): # I love valgrind, but this is really a horribly lame data format. Oh # well. it = iter(file) # Read the header. description_lines = [] command = None events = None positions = initial_positions = ['line'] for ln in it: # If there is no colon in the line, we have reached the end of the # header. if ':' not in ln: break key, value = ln.split(':', 1) if key == 'desc': description_lines.append(value.strip()) elif key == 'cmd': if command is not None: logger.warning("unexpected multiple 'cmd' keys in %r" % (path,)) command = value.strip() elif key == 'events': if events is not None: logger.warning("unexpected multiple 'events' keys in %r" % (path,)) events = value.split() elif key == 'positions': if positions is not initial_positions: logger.warning( "unexpected multiple 'positions' keys in %r" % (path,)) positions = value.split() else: logger.warning("found unknown key %r in %r" % (key, path)) # Validate that required fields were present. if events is None: raise CalltreeParseError("missing required 'events' key in header") # Construct an instance. data = CalltreeData(events, "\n".join(description_lines), command) # Read the file data. num_samples = len(positions) + len(events) current_file = None current_function = None summary_samples = None for ln in it: # Check if this is the closing summary line. if ln.startswith('summary'): key, value = ln.split(':', 1) summary_samples = list(map(int, value.split())) break # Check if this is an update to the current file or function. if ln.startswith('fl='): current_file = ln[3:-1] elif ln.startswith('fn='): current_function = ln[3:-1] else: # Otherwise, this is a data record. samples = list(map(int, ln.split())) if len(samples) != num_samples: raise CalltreeParseError( "invalid record line, unexpected sample count") data.records.append((current_file, current_function, samples)) # Validate that there are no more remaining records. for ln in it: raise CalltreeParseError("unexpected line in footer: %r" % (ln,)) # Validate that the summary line was present. if summary_samples is None: raise CalltreeParseError( "missing required 'summary' key in footer") data.summary = summary_samples return data def __init__(self, events, description=None, command=None): self.events = events self.description = description self.command = command self.records = [] self.summary = None