Example #1
0
def checkBlameParents(repoDir, blamedRev, blamedGoodOrBad, labels, testRev, startRepo, endRepo):
    """If bisect blamed a merge, try to figure out why."""

    bisectLied = False
    missedCommonAncestor = False

    parents = sps.captureStdout(["hg", "-R", repoDir] + ["parent", '--template={node|short},',
                                                         "-r", blamedRev])[0].split(",")[:-1]

    if len(parents) == 1:
        return

    for p in parents:
        # Ensure we actually tested the parent.
        if labels.get(p) is None:
            print ""
            print ("Oops! We didn't test rev %s, a parent of the blamed revision! " +
                   "Let's do that now.") % str(p)
            if not hgCmds.isAncestor(repoDir, startRepo, p) and \
                    not hgCmds.isAncestor(repoDir, endRepo, p):
                print ('We did not test rev %s because it is not a descendant of either ' +
                       '%s or %s.') % (str(p), startRepo, endRepo)
                # Note this in case we later decide the bisect result is wrong.
                missedCommonAncestor = True
            label = testRev(p)
            labels[p] = label
            print label[0] + " (" + label[1] + ") "
            print "As expected, the parent's label is the opposite of the blamed rev's label."

        # Check that the parent's label is the opposite of the blamed merge's label.
        if labels[p][0] == "skip":
            print "Parent rev %s was marked as 'skip', so the regression window includes it." % str(p)
        elif labels[p][0] == blamedGoodOrBad:
            print "Bisect lied to us! Parent rev %s was also %s!" % (str(p), blamedGoodOrBad)
            bisectLied = True
        else:
            assert labels[p][0] == {'good': 'bad', 'bad': 'good'}[blamedGoodOrBad]

    # Explain why bisect blamed the merge.
    if bisectLied:
        if missedCommonAncestor:
            ca = hgCmds.findCommonAncestor(repoDir, parents[0], parents[1])
            print ""
            print "Bisect blamed the merge because our initial range did not include one"
            print "of the parents."
            print "The common ancestor of %s and %s is %s." % (parents[0], parents[1], ca)
            label = testRev(ca)
            print label[0] + " (" + label[1] + ") "
            print "Consider re-running autoBisect with -s %s -e %s" % (ca, blamedRev)
            print "in a configuration where earliestWorking is before the common ancestor."
        else:
            print ""
            print "Most likely, bisect's result was unhelpful because one of the"
            print "tested revisions was marked as 'good' or 'bad' for the wrong reason."
            print "I don't know which revision was incorrectly marked. Sorry."
    else:
        print ""
        print "The bug was introduced by a merge (it was not present on either parent)."
        print "I don't know which patches from each side of the merge contributed to the bug. Sorry."
Example #2
0
def checkBlameParents(repoDir, blamedRev, blamedGoodOrBad, labels, testRev, startRepo, endRepo):
    """If bisect blamed a merge, try to figure out why."""

    bisectLied = False
    missedCommonAncestor = False

    parents = sps.captureStdout(["hg", "-R", repoDir] + ["parent", '--template={node|short},',
                                                         "-r", blamedRev])[0].split(",")[:-1]

    if len(parents) == 1:
        return

    for p in parents:
        # Ensure we actually tested the parent.
        if labels.get(p) is None:
            print ""
            print ("Oops! We didn't test rev %s, a parent of the blamed revision! " +
                   "Let's do that now.") % str(p)
            if not hgCmds.isAncestor(repoDir, startRepo, p) and \
                    not hgCmds.isAncestor(repoDir, endRepo, p):
                print ('We did not test rev %s because it is not a descendant of either ' +
                       '%s or %s.') % (str(p), startRepo, endRepo)
                # Note this in case we later decide the bisect result is wrong.
                missedCommonAncestor = True
            label = testRev(p)
            labels[p] = label
            print label[0] + " (" + label[1] + ") "
            print "As expected, the parent's label is the opposite of the blamed rev's label."

        # Check that the parent's label is the opposite of the blamed merge's label.
        if labels[p][0] == "skip":
            print "Parent rev %s was marked as 'skip', so the regression window includes it." % str(p)
        elif labels[p][0] == blamedGoodOrBad:
            print "Bisect lied to us! Parent rev %s was also %s!" % (str(p), blamedGoodOrBad)
            bisectLied = True
        else:
            assert labels[p][0] == {'good': 'bad', 'bad': 'good'}[blamedGoodOrBad]

    # Explain why bisect blamed the merge.
    if bisectLied:
        if missedCommonAncestor:
            ca = hgCmds.findCommonAncestor(repoDir, parents[0], parents[1])
            print ""
            print "Bisect blamed the merge because our initial range did not include one"
            print "of the parents."
            print "The common ancestor of %s and %s is %s." % (parents[0], parents[1], ca)
            label = testRev(ca)
            print label[0] + " (" + label[1] + ") "
            print "Consider re-running autoBisect with -s %s -e %s" % (ca, blamedRev)
            print "in a configuration where earliestWorking is before the common ancestor."
        else:
            print ""
            print "Most likely, bisect's result was unhelpful because one of the"
            print "tested revisions was marked as 'good' or 'bad' for the wrong reason."
            print "I don't know which revision was incorrectly marked. Sorry."
    else:
        print ""
        print "The bug was introduced by a merge (it was not present on either parent)."
        print "I don't know which patches from each side of the merge contributed to the bug. Sorry."
Example #3
0
def parseOpts():
    usage = 'Usage: %prog [options]'
    parser = OptionParser(usage)
    # http://docs.python.org/library/optparse.html#optparse.OptionParser.disable_interspersed_args
    parser.disable_interspersed_args()

    parser.set_defaults(
        resetRepoFirst=False,
        startRepo=None,
        endRepo='default',
        testInitialRevs=True,
        output='',
        watchExitCode=None,
        useInterestingnessTests=False,
        parameters='-e 42',  # http://en.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy
        compilationFailedLabel='skip',
        buildOptions="",
        useTreeherderBinaries=False,
        nameOfTreeherderBranch='mozilla-inbound',
    )

    # Specify how the shell will be built.
    # See buildOptions.py for details.
    parser.add_option('-b', '--build',
                      dest='buildOptions',
                      help='Specify js shell build options, e.g. -b "--enable-debug --32" (python buildOptions.py --help)')
    parser.add_option('-B', '--browser',
                      dest='browserOptions',
                      help='Specify browser build options, e.g. -b "-c mozconfig"')

    parser.add_option('--resetToTipFirst', dest='resetRepoFirst',
                      action='store_true',
                      help='First reset to default tip overwriting all local changes. ' +
                      'Equivalent to first executing `hg update -C default`. ' +
                      'Defaults to "%default".')

    # Specify the revisions between which to bisect.
    parser.add_option('-s', '--startRev', dest='startRepo',
                      help='Earliest changeset/build numeric ID to consider (usually a "good" cset). ' +
                      'Defaults to the earliest revision known to work at all/available.')
    parser.add_option('-e', '--endRev', dest='endRepo',
                      help='Latest changeset/build numeric ID to consider (usually a "bad" cset). ' +
                      'Defaults to the head of the main branch, "default", or latest available build.')
    parser.add_option('-k', '--skipInitialRevs', dest='testInitialRevs',
                      action='store_false',
                      help='Skip testing the -s and -e revisions and automatically trust them ' +
                      'as -g and -b.')

    # Specify the type of failure to look for.
    # (Optional -- by default, internalTestAndLabel will look for exit codes that indicate a crash or assert.)
    parser.add_option('-o', '--output', dest='output',
                      help='Stdout or stderr output to be observed. Defaults to "%default". ' +
                      'For assertions, set to "ssertion fail"')
    parser.add_option('-w', '--watchExitCode', dest='watchExitCode',
                      type='int',
                      help='Look out for a specific exit code. Only this exit code will be ' +
                      'considered "bad".')
    parser.add_option('-i', '--useInterestingnessTests',
                      dest='useInterestingnessTests',
                      action="store_true",
                      help="Interpret the final arguments as an interestingness test.")

    # Specify parameters for the js shell.
    parser.add_option('-p', '--parameters', dest='parameters',
                      help='Specify parameters for the js shell, e.g. -p "-a --ion-eager testcase.js".')

    # Specify how to treat revisions that fail to compile.
    # (You might want to add these to kbew.knownBrokenRanges in knownBrokenEarliestWorking.py.)
    parser.add_option('-l', '--compilationFailedLabel', dest='compilationFailedLabel',
                      help='Specify how to treat revisions that fail to compile. ' +
                      '(bad, good, or skip) Defaults to "%default"')

    parser.add_option('-T', '--useTreeherderBinaries',
                      dest='useTreeherderBinaries',
                      action="store_true",
                      help='Use treeherder binaries for quick bisection, assuming a fast ' +
                      'internet connection. Defaults to "%default"')
    parser.add_option('-N', '--nameOfTreeherderBranch',
                      dest='nameOfTreeherderBranch',
                      help='Name of the branch to download. Defaults to "%default"')

    (options, args) = parser.parse_args()
    if options.browserOptions:
        assert not options.buildOptions
        options.browserOptions = buildBrowser.parseOptions(options.browserOptions.split())
        options.skipRevs = ' + '.join(kbew.knownBrokenRangesBrowser(options.browserOptions))
    else:
        options.buildOptions = buildOptions.parseShellOptions(options.buildOptions)
        options.skipRevs = ' + '.join(kbew.knownBrokenRanges(options.buildOptions))

    options.paramList = [sps.normExpUserPath(x) for x in options.parameters.split(' ') if x]
    # First check that the testcase is present.
    if '-e 42' not in options.parameters and not os.path.isfile(options.paramList[-1]):
        print '\nList of parameters to be passed to the shell is: ' + ' '.join(options.paramList)
        print
        raise Exception('Testcase at ' + options.paramList[-1] + ' is not present.')

    assert options.compilationFailedLabel in ('bad', 'good', 'skip')

    extraFlags = []

    if options.useInterestingnessTests:
        if len(args) < 1:
            print 'args are: ' + args
            parser.error('Not enough arguments.')
        if not options.browserOptions:
            for a in args:
                if a.startswith("--flags="):
                    extraFlags = a[8:].split(' ')
        options.testAndLabel = externalTestAndLabel(options, args)
    else:
        assert not options.browserOptions  # autoBisect doesn't have a built-in way to run the browser
        if len(args) >= 1:
            parser.error('Too many arguments.')
        options.testAndLabel = internalTestAndLabel(options)

    if options.browserOptions:
        earliestKnownQuery = kbew.earliestKnownWorkingRevForBrowser(options.browserOptions)
    else:
        earliestKnownQuery = kbew.earliestKnownWorkingRev(options.buildOptions, options.paramList + extraFlags, options.skipRevs)

    earliestKnown = hgCmds.getRepoHashAndId(options.buildOptions.repoDir, repoRev=earliestKnownQuery)[0]

    if options.startRepo is None:
        if options.useTreeherderBinaries:
            options.startRepo = 'default'
        else:
            options.startRepo = earliestKnown
    elif not (options.useTreeherderBinaries or hgCmds.isAncestor(options.buildOptions.repoDir, earliestKnown, options.startRepo)):
        raise Exception('startRepo is not a descendant of kbew.earliestKnownWorkingRev for this configuration')

    if not options.useTreeherderBinaries and not hgCmds.isAncestor(options.buildOptions.repoDir, earliestKnown, options.endRepo):
        raise Exception('endRepo is not a descendant of kbew.earliestKnownWorkingRev for this configuration')

    if options.parameters == '-e 42':
        print "Note: since no parameters were specified, we're just ensuring the shell does not crash on startup/shutdown."

    if options.nameOfTreeherderBranch != 'mozilla-inbound' and not options.useTreeherderBinaries:
        raise Exception('Setting the name of branches only works for treeherder shell bisection.')

    return options