def runTest(self): # Do some direct matcher tests on edge cases self.assertTrue(StackFramesSymptom._match([], [StringMatch('???')])) self.assertFalse( StackFramesSymptom._match( [], [StringMatch('???'), StringMatch('a')])) # Test the diff algorithm, test array contains: # stack, signature, expected distance, proposed signature testArray = [ (['a', 'b', 'x', 'a', 'b', 'c'], ['a', 'b', '???', 'a', 'b', 'x', 'c'], 1, ['a', 'b', '???', 'a', 'b', '?', 'c']), (['b', 'x', 'a', 'b', 'c'], ['a', 'b', '???', 'a', 'b', 'x', 'c'], 2, ['?', 'b', '???', 'a', 'b', '?', 'c']), (['b', 'x', 'a', 'd', 'x'], ['a', 'b', '???', 'a', 'b', 'x', 'c'], 3, ['?', 'b', '???', 'a', '?', 'x', '?']), ] for (stack, rawSig, expectedDepth, expectedSig) in testArray: for maxDepth in (expectedDepth, 3): (actualDepth, actualSig) = StackFramesSymptom._diff( stack, [StringMatch(x) for x in rawSig], 0, 1, maxDepth) self.assertEqual(expectedDepth, actualDepth) self.assertEqual(expectedSig, [str(x) for x in actualSig])
def test_SignatureStackFramesAlgorithmsTest(): # Do some direct matcher tests on edge cases assert StackFramesSymptom._match([], [StringMatch('???')]) assert not StackFramesSymptom._match( [], [StringMatch('???'), StringMatch('a')]) # Test the diff algorithm, test array contains: # stack, signature, expected distance, proposed signature testArray = [ (['a', 'b', 'x', 'a', 'b', 'c'], ['a', 'b', '???', 'a', 'b', 'x', 'c'], 1, ['a', 'b', '???', 'a', 'b', '?', 'c']), (['b', 'x', 'a', 'b', 'c'], ['a', 'b', '???', 'a', 'b', 'x', 'c'], 2, ['?', 'b', '???', 'a', 'b', '?', 'c']), (['b', 'x', 'a', 'd', 'x'], ['a', 'b', '???', 'a', 'b', 'x', 'c'], 3, ['?', 'b', '???', 'a', '?', 'x', '?']), ] for (stack, rawSig, expectedDepth, expectedSig) in testArray: for maxDepth in (expectedDepth, 3): (actualDepth, actualSig) = StackFramesSymptom._diff( stack, [StringMatch(x) for x in rawSig], 0, 1, maxDepth) assert expectedDepth == actualDepth assert expectedSig == [str(x) for x in actualSig]
def __init__(self, obj): ''' Private constructor, called by L{Symptom.fromJSONObject}. Do not use directly. ''' Symptom.__init__(self, obj) self.output = StringMatch( JSONHelper.getObjectOrStringChecked(obj, "value", True))
def _diff(stack, signatureGuess, startIdx, depth, maxDepth): singleWildcardMatch = StringMatch("?") newSignatureGuess = [] newSignatureGuess.extend(signatureGuess) bestDepth = None bestGuess = None for idx in range(startIdx, len(newSignatureGuess)): newSignatureGuess.insert(idx, singleWildcardMatch) # Check if we have a match with our modification if StackFramesSymptom._match(stack, newSignatureGuess): return (depth, newSignatureGuess) # If we don't have a match but we're not at our current depth limit, # add one more level of depth for our search. if depth < maxDepth: (newBestDepth, newBestGuess) = StackFramesSymptom._diff( stack, newSignatureGuess, idx, depth + 1, maxDepth) if newBestDepth != None and (bestDepth == None or newBestDepth < bestDepth): bestDepth = newBestDepth bestGuess = newBestGuess newSignatureGuess.pop(idx) # Now repeat the same with replacing instead of adding # unless the match at idx is a wildcard itself if str(newSignatureGuess[idx]) == '?' or str( newSignatureGuess[idx]) == '???': continue origMatch = newSignatureGuess[idx] newSignatureGuess[idx] = singleWildcardMatch # Check if we have a match with our modification if StackFramesSymptom._match(stack, newSignatureGuess): return (depth, newSignatureGuess) # If we don't have a match but we're not at our current depth limit, # add one more level of depth for our search. if depth < maxDepth: (newBestDepth, newBestGuess) = StackFramesSymptom._diff( stack, newSignatureGuess, idx, depth + 1, maxDepth) if newBestDepth != None and (bestDepth == None or newBestDepth < bestDepth): bestDepth = newBestDepth bestGuess = newBestGuess newSignatureGuess[idx] = origMatch return (bestDepth, bestGuess)
def __init__(self, obj): ''' Private constructor, called by L{Symptom.fromJSONObject}. Do not use directly. ''' Symptom.__init__(self, obj) self.functionNames = [] rawFunctionNames = JSONHelper.getArrayChecked(obj, "functionNames", True) for fn in rawFunctionNames: self.functionNames.append(StringMatch(fn))
def __init__(self, obj): ''' Private constructor, called by L{Symptom.fromJSONObject}. Do not use directly. ''' Symptom.__init__(self, obj) self.output = StringMatch(JSONHelper.getObjectOrStringChecked(obj, "value", True)) self.src = JSONHelper.getStringChecked(obj, "src") if self.src != None: self.src = self.src.lower() if self.src != "stderr" and self.src != "stdout" and self.src != "crashdata": raise RuntimeError("Invalid source specified: %s" % self.src)
def __init__(self, obj): ''' Private constructor, called by L{Symptom.fromJSONObject}. Do not use directly. ''' Symptom.__init__(self, obj) self.registerNames = JSONHelper.getArrayChecked(obj, "registerNames") self.instructionName = JSONHelper.getObjectOrStringChecked(obj, "instructionName") if self.instructionName != None: self.instructionName = StringMatch(self.instructionName) elif self.registerNames == None or len(self.registerNames) == 0: raise RuntimeError("Must provide at least instruction name or register names")
def __init__(self, obj): ''' Private constructor, called by L{Symptom.fromJSONObject}. Do not use directly. ''' Symptom.__init__(self, obj) self.functionName = StringMatch(JSONHelper.getNumberOrStringChecked(obj, "functionName", True)) self.frameNumber = JSONHelper.getNumberOrStringChecked(obj, "frameNumber") if self.frameNumber != None: self.frameNumber = NumberMatch(self.frameNumber) else: # Default to 0 self.frameNumber = NumberMatch(0)
def _diff(stack, signatureGuess, startIdx, depth, maxDepth): singleWildcardMatch = StringMatch("?") newSignatureGuess = [] newSignatureGuess.extend(signatureGuess) bestDepth = None bestGuess = None hasVariableStackLengthQuantifier = '???' in [ str(x) for x in newSignatureGuess ] for idx in range(startIdx, len(newSignatureGuess)): newSignatureGuess.insert(idx, singleWildcardMatch) # Check if we have a match with our modification if StackFramesSymptom._match(stack, newSignatureGuess): return (depth, newSignatureGuess) # If we don't have a match but we're not at our current depth limit, # add one more level of depth for our search. if depth < maxDepth: (newBestDepth, newBestGuess) = StackFramesSymptom._diff( stack, newSignatureGuess, idx, depth + 1, maxDepth) if newBestDepth is not None and (bestDepth is None or newBestDepth < bestDepth): bestDepth = newBestDepth bestGuess = newBestGuess newSignatureGuess.pop(idx) # Now repeat the same with replacing instead of adding # unless the match at idx is a wildcard itself if str(newSignatureGuess[idx]) == '?' or str( newSignatureGuess[idx]) == '???': continue newMatch = singleWildcardMatch if not hasVariableStackLengthQuantifier and len(stack) > idx: # We can perform some optimizations here if we have a signature that does # not contain any quantifiers that can match multiple stack frames. if newSignatureGuess[idx].matches(stack[idx]): # Our frame matches, so it doesn't make sense to try and mess with it continue if not newSignatureGuess[idx].isPCRE: # If our match is not PCRE, try some heuristics to generalize the match if stack[idx] in str(newSignatureGuess[idx]): # The stack frame is a substring of the what we try to match, # use the stack frame as new matcher to ensure a match without # using a wildcard. newMatch = StringMatch(stack[idx]) origMatch = newSignatureGuess[idx] newSignatureGuess[idx] = newMatch # Check if we have a match with our modification if StackFramesSymptom._match(stack, newSignatureGuess): return (depth, newSignatureGuess) # If we don't have a match but we're not at our current depth limit, # add one more level of depth for our search. if depth < maxDepth: (newBestDepth, newBestGuess) = StackFramesSymptom._diff( stack, newSignatureGuess, idx, depth + 1, maxDepth) if newBestDepth is not None and (bestDepth is None or newBestDepth < bestDepth): bestDepth = newBestDepth bestGuess = newBestGuess newSignatureGuess[idx] = origMatch return (bestDepth, bestGuess)