Example #1
0
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 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])
Example #3
0
 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))
Example #4
0
    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)
Example #5
0
    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")
Example #6
0
    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)
Example #7
0
class TestcaseSymptom(Symptom):
    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 matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information
        
        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against 
        
        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''
        
        # No testcase means to fail matching
        if crashInfo.testcase == None:
            return False
        
        testLines = crashInfo.testcase.splitlines()
            
        for line in testLines:
            if self.output.matches(line):
                return True
            
        return False
Example #8
0
class StackFrameSymptom(Symptom):
    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 is not None:
            self.frameNumber = NumberMatch(self.frameNumber)
        else:
            # Default to 0
            self.frameNumber = NumberMatch(0)

    def matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information

        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against

        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''

        for idx in range(len(crashInfo.backtrace)):
            # Not the most efficient way for very long stacks with a small match area
            if self.frameNumber.matches(idx):
                if self.functionName.matches(crashInfo.backtrace[idx]):
                    return True

        return False
Example #9
0
class TestcaseSymptom(Symptom):
    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 matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information

        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against

        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''

        # No testcase means to fail matching
        if crashInfo.testcase is None:
            return False

        testLines = crashInfo.testcase.splitlines()

        for line in testLines:
            if self.output.matches(line):
                return True

        return False
Example #10
0
class StackFrameSymptom(Symptom):
    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 matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information
        
        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against 
        
        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''
        
        for idx in range(len(crashInfo.backtrace)):
            # Not the most efficient way for very long stacks with a small match area
            if self.frameNumber.matches(idx):
                if self.functionName.matches(crashInfo.backtrace[idx]):
                    return True
        
        return False
Example #11
0
    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)
Example #12
0
    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))
Example #13
0
 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")
Example #14
0
 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)
Example #15
0
    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)
Example #16
0
class OutputSymptom(Symptom):
    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 is not 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 matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information

        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against

        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''
        checkedOutput = []

        if self.src is None:
            checkedOutput.extend(crashInfo.rawStdout)
            checkedOutput.extend(crashInfo.rawStderr)
            checkedOutput.extend(crashInfo.rawCrashData)
        elif (self.src == "stdout"):
            checkedOutput = crashInfo.rawStdout
        elif (self.src == "stderr"):
            checkedOutput = crashInfo.rawStderr
        else:
            checkedOutput = crashInfo.rawCrashData

        for line in reversed(checkedOutput):
            if self.output.matches(line):
                return True

        return False
Example #17
0
class OutputSymptom(Symptom):
    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 is not 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 matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information

        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against

        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''
        checkedOutput = []

        if self.src is None:
            checkedOutput.extend(crashInfo.rawStdout)
            checkedOutput.extend(crashInfo.rawStderr)
            checkedOutput.extend(crashInfo.rawCrashData)
        elif (self.src == "stdout"):
            checkedOutput = crashInfo.rawStdout
        elif (self.src == "stderr"):
            checkedOutput = crashInfo.rawStderr
        else:
            checkedOutput = crashInfo.rawCrashData

        windowsSlashWorkaround = crashInfo.configuration.os == "windows"
        for line in reversed(checkedOutput):
            if self.output.matches(line, windowsSlashWorkaround=windowsSlashWorkaround):
                return True

        return False
Example #18
0
class InstructionSymptom(Symptom):
    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 is not None:
            self.instructionName = StringMatch(self.instructionName)
        elif self.registerNames is None or len(self.registerNames) == 0:
            raise RuntimeError(
                "Must provide at least instruction name or register names")

    def matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information

        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against

        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''
        if crashInfo.crashInstruction is None:
            # No crash instruction available, do not match
            return False

        if self.registerNames is not None:
            for register in self.registerNames:
                if register not in crashInfo.crashInstruction:
                    return False

        if self.instructionName is not None:
            if not self.instructionName.matches(crashInfo.crashInstruction):
                return False

        return True
Example #19
0
class InstructionSymptom(Symptom):
    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 matches(self, crashInfo):
        '''
        Check if the symptom matches the given crash information
        
        @type crashInfo: CrashInfo
        @param crashInfo: The crash information to check against 
        
        @rtype: bool
        @return: True if the symptom matches, False otherwise
        '''
        if crashInfo.crashInstruction == None:
            # No crash instruction available, do not match
            return False
        
        if self.registerNames != None:
            for register in self.registerNames:
                if not register in crashInfo.crashInstruction:
                    return False
        
        if self.instructionName != None:
            if not self.instructionName.matches(crashInfo.crashInstruction):
                return False
        
        return True
Example #20
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)
Example #21
0
 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))