"==7420== by 0x4EC170F: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)", "==7420== by 0x4ECCCA4: std::ostream& std::ostream::_M_insert<double>(double) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)", "==7420== by 0x400C22: printArray(double*) (errorProducingApplication.cpp:78)", "==7420== by 0x400C9E: main (errorProducingApplication.cpp:92)", "==7420== Uninitialised value was created by a heap allocation", "==7420== at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)", "==7420== by 0x400A0B: createNewArray() (errorProducingApplication.cpp:42)", "==7420== by 0x400C55: main (errorProducingApplication.cpp:85)", ] patterns = Patterns() lineIndex = 0 assert patterns.isErrorStart(valgrindLog[lineIndex]), "Expected error start at line '" + valgrindLog[lineIndex] + "'." error = ParsedError(valgrindLog[lineIndex]) lineIndex += 1 assert patterns.isStackFrameTop.match(valgrindLog[lineIndex]), ( "Expected stack start at line '" + valgrindLog[lineIndex] + "'." ) error.setLocation(valgrindLog[lineIndex]) lineIndex += 1 for i in range(0, 8): assert patterns.isStackFrameCaller.match(valgrindLog[lineIndex]), ( "Expected error stack frame caller " + str(i) + " at line '" + valgrindLog[lineIndex] + "'." ) error.addCaller(valgrindLog[lineIndex]) lineIndex += 1
class ErrorParser(object): """Converts a list of strings, the Valgrind log, to a list of ParsedErrors.""" def __init__(self): self.patterns = Patterns() self.resetState() def parse(self, lines): # (ParsedError list, string list, process id) """Parser ParsedErrors from the given line list. All elements of 'lines' must be string-like. None elements are not allowed. @param lines - String list - The contents of a Valgrind log. @return (ParsedError list, string list, process id) tuple containing the errors found and a list of unknown valgrind lines, or None if there was an error before any errors could be read. Process id is the PID extracted from the Valgrind log file. """ setupSuccessful = self.setupState(lines) if not setupSuccessful: return (None, None, None) self.parseImplementation() id = self.id errorList = self.errors unknownErrors = self.unknownErrors self.resetState() return (errorList, unknownErrors, id) def resetState(self): # None """Prepare the parser for a new round of parsing. The parser is placed in a dormant state, waiting for the next call to parse(). """ self.lines = None self.line = None self.currentLine = None def setupState(self, lines): # Boolean """Prepare the parser for parsing from the given string list.""" if lines == None or len(lines) == 0: return False self.errors = [] # ParsedError list. self.unknownErrors = [] # String list. Lines that the parser didn't recognize. self.id = None self.lines = lines # String list. Provided by user. self.currentLine = 0 # Integer. The index of 'line' in 'lines'. self.line = self.lines[self.currentLine] # String. One of the string in 'lines'. Is always either None or a Valgrind line. return self.initFirstLine() def isParsing(self): # Boolean """Returns True if the parser is valid for continued parsing. Calling resetState will cause future calls to isParsing to return False until setupState is called. """ return self.lines != None and self.line != None and self.currentLine != None def parseImplementation(self): """Main parsing loop. Iteratively reads errors from the lines list and adds them to the errors list. @precondition self.line is a Valgrind line. """ self.readHeader() error = self.readError() while error != None: self.errors.append(error) error = self.readError() def readHeader(self): # Boolean """Jump past the Valgrind header. Resets the parser state if no Valgrind lines are found after the header. @return True if a Valgrind line was found after the header. False otherwise. @precondition self.line is a Valgrind line. @postcondition On True: self.line is a Valgrind line. On False: isParsing() returns false. """ while self.patterns.isHeader.match(self.line) != None: hadAnotherLine = self.nextValgrindLine() if not hadAnotherLine: return False return True def readError(self): # ParsedError """Read an error from the lines list. Will skip unknown Valgrind lines until an error is found. Skipped lines are added to unknownErrors. @precondition self.line is a Valgrind line. """ if not self.isParsing(): return None while not self.patterns.isErrorStart(self.line): if len(self.line) > 0: self.unknownErrors.append(self.line) hadAnotherLine = self.nextValgrindLine() if not hadAnotherLine: return None error = ParsedError(self.line) hadAnotherLine = self.nextValgrindLine() if not hadAnotherLine: return None hadStacks = self.readStacks(error) if not hadStacks: return None return error def readStacks(self, error): # There can be up to two call stacks; a mandatory one for the error and an # optional one for the source. if self.patterns.isStackFrameTop.match(self.line) == None: return False error.setLocation(self.line) while self.nextValgrindLine() and self.patterns.isStackFrameCaller.match(self.line): error.addCaller(self.line) if not self.isParsing(): return True # It is OK to run out of lines while reading callers. if not self.patterns.isSourceStart(self.line): return True # This error didn't have a source locaiton. error.setSourceType(self.line) hadAnotherLine = self.nextValgrindLine() if not hadAnotherLine: return True # This is not really a valid error, but it's close enough to be usable. if not self.patterns.isStackFrameTop.match(self.line): return True # # This is not really a valid error, but it's close enough to be usable. error.setSourceLocation(self.line) while self.nextValgrindLine() and self.patterns.isStackFrameCaller.match(self.line): error.addSourceCaller(self.line) return True def initFirstLine(self): # Boolean while not self.isValgrindLine(): hadAnotherLine = self.nextLine() if not hadAnotherLine: return False idMatch = self.patterns.readId.match(self.line) self.id = idMatch.group(1) self.stripValgrindPrefix() return True def isValgrindLine(self): # Boolean return self.line != None and self.patterns.isValgrind.match(self.line) != None def stripValgrindPrefix(self): # None match = self.patterns.stripValgrind.match(self.line) self.line = match.group(1) self.line = self.line.strip() def nextLine(self): # Boolean """Updates self.line and self.currentLine to points to the next line in self.lines. Resets the parser's internal state if the end of self.lines is reached. """ self.currentLine += 1 if (self.currentLine < len(self.lines)): self.line = self.lines[self.currentLine] assert self.line != None, "Found 'None' in lines list. This is not allowed." return True else: self.resetState() return False def nextValgrindLine(self): # Boolean """Step forward through the lines until a Valgrind line is found. Resets the parser's internal state and returns False if the end of the list is reached. """ hadAnotherLine = self.nextLine() if not hadAnotherLine: return False while not self.isValgrindLine(): hadAnotherLine = self.nextLine() if not hadAnotherLine: return False; self.stripValgrindPrefix() return True
numDependUninitialized += 1 if patterns.isInvalidRead.match(line) != None: numInvalidRead += 1 if patterns.isInvalidWrite.match(line) != None: numInvalidWrite += 1 if patterns.isMissmatchedFreeDelete.match(line) != None: numMissmatchFreeDelete += 1 if patterns.isInvalidFreeDelete.match(line) != None: numInvalidFreeDelete += 1 if patterns.isMemoryLoss.match(line) != None: numMemoryLoss += 1 if patterns.isStackAllocation.match(line) != None: numStackAllocation += 1 if patterns.isHeapAllocation.match(line) != None: numHeapAllocation += 1 if patterns.isErrorStart(line): numError += 1 if patterns.isSourceStart(line): numSource += 1 if len(sys.argv) > 1 and sys.argv[1] == "--verbose": print(str(len(lines)) + " lines in total.") print(str(numValgrind) + " lines are valgrind lines.") print(str(numHeader) + " lines are header lines.") print(str(numStackTop) + " lines are stack tops.") print(str(numStackCaller) + " lines are stack callers.") print(str(numAnyStack) + " lines are any stack frame.") print(str(numDependUninitialized) + " lines are jump or move using uninitialized.") print(str(numInvalidRead) + " lines are invalid reads.") print(str(numInvalidWrite) + " lines are invalid writes.") print(str(numMissmatchFreeDelete) + " lines are missmatched free/delte.")