Permalink
Browse files

Now ultimately better than the last version

  • Loading branch information...
1 parent f6bd5eb commit 4a74ca97ba769b5f035f8002a2825f5d43fcd32f @choller choller committed Mar 28, 2012
Showing with 132 additions and 72 deletions.
  1. +132 −72 bugmon.py
View
204 bugmon.py
@@ -22,6 +22,7 @@
import re
import platform
import subprocess
+import traceback
from optparse import OptionParser
@@ -48,20 +49,30 @@ def parseOpts():
default=None,
help='Repository base directory, mandatory.')
- # Enable valgrind support.
parser.add_option('-v', '--verbose',
dest='verbose',
action='store_true',
default=False,
help='Be verbose. Defaults to "False"')
- # Enable valgrind support.
parser.add_option('-V', '--verify-fixed',
dest='verifyfixed',
action='store_true',
default=False,
help='Verify fix and comment. Defaults to "False"')
+ parser.add_option('-U', '--update-bug',
+ dest='updatebug',
+ action='store_true',
+ default=False,
+ help='Update the bug. Defaults to "False"')
+
+ parser.add_option('-G', '--guess-opts',
+ dest='guessopts',
+ action='store_true',
+ default=False,
+ help='Force guessing the JS shell options. Defaults to "False"')
+
(options, args) = parser.parse_args()
if len(args) < 1:
@@ -87,26 +98,35 @@ def main():
print "====== Analyzing bug " + str(bug_id) + " ======"
try:
if options.verifyfixed:
- bugmon.verifyFixedBug(bug_id)
+ bugmon.verifyFixedBug(bug_id, options.updatebug)
else:
result = bugmon.reproduceBug(bug_id)
except Exception as e:
- print "Caught exception: " + str(e)
+ if options.verbose:
+ print "Caught exception: " + str(e)
+ print traceback.format_exc()
+class BugMonitorResult:
+ # Different result states:
+ # FAILED - Unable to reproduce on original revision
+ # REPRODUCED_FIXED - Reproduced on original revision but not on tip (fixed on tip)
+ # REPRODUCED_TIP - Reproduced on both revisions
+ # REPRODUCED_SWITCHED - Reproduced on tip, but with a different crash/signal
+ statusCodes = enum('FAILED', 'REPRODUCED_FIXED', 'REPRODUCED_TIP', 'REPRODUCED_SWITCHED')
+
+ def __init__(self, branchName, origRev, tipRev, status):
+ self.branchName = branchName
+ self.origRev = origRev
+ self.tipRev = tipRev
+ self.status = status
+
class BugMonitor:
def __init__(self, apiroot, username, password, repoBase, options):
self.apiroot = apiroot
self.bz = BugzillaAgent(apiroot, username, password)
- # Different result states:
- # FAILED - Unable to reproduce on original revision
- # REPRODUCED_FIXED - Reproduced on original revision but not on tip (fixed on tip)
- # REPRODUCED_TIP - Reproduced on both revisions
- # REPRODUCED_SWITCHED - Reproduced on tip, but with a different crash/signal
- self.result = enum('FAILED', 'REPRODUCED_FIXED', 'REPRODUCED_TIP', 'REPRODUCED_SWITCHED')
-
self.repoBase = repoBase
# Here we store the tip revision per repository for caching purposes
@@ -119,25 +139,46 @@ def postComment(self, bugnum, comment):
url = urljoin(self.apiroot, 'bug/%s/comment?%s' % (bugnum, self.bz.qs()))
return Comment(text=comment).post_to(url)
- def verifyFixedBug(self, bugnum):
+ def verifyFixedBug(self, bugnum, updateBug):
# Fetch the bug
bug = self.bz.get_bug(bugnum)
if (bug.status == "RESOLVED" and bug.resolution == "FIXED"):
result = self.reproduceBug(bugnum)
- if (result == self.result.REPRODUCED_FIXED):
- print "Marking bug " + str(bugnum) + " as verified fixed..."
- # Add a comment
- self.postComment(bugnum, "JSBugMon: This bug has been automatically verified fixed.")
- # Need to refetch the bug...
- bug = self.bz.get_bug(bugnum)
- # Mark VERIFIED FIXED now
- bug.status = "VERIFIED"
- bug.put()
+ if (result.status == BugMonitorResult.statusCodes.REPRODUCED_FIXED):
+ if updateBug:
+ print "Marking bug " + str(bugnum) + " as verified fixed..."
+ # Add a comment
+ self.postComment(bugnum, "JSBugMon: This bug has been automatically verified fixed.")
+ # Need to refetch the bug...
+ bug = self.bz.get_bug(bugnum)
+ # Mark VERIFIED FIXED now
+ bug.status = "VERIFIED"
+ bug.put()
+ else:
+ print "Would mark bug " + str(bugnum) + " as verified fixed..."
return
+ def confirmOpenBug(self, bugnum, updateBug):
+ # Fetch the bug
+ bug = self.bz.get_bug(bugnum)
+
+ if (bug.status != "RESOLVED" and bug.status != "VERIFIED"):
+ result = self.reproduceBug(bugnum)
+
+ if (result.status == BugMonitorResult.statusCodes.REPRODUCED_TIP):
+ if updateBug:
+ print "Marking bug " + str(bugnum) + " as confirmed on tip..."
+ # Add a comment
+ self.postComment(bugnum, "JSBugMon: This bug has been automatically confirmed to be still valid (reproduced on revision " + rev + ").")
+ else:
+ print "Would mark bug " + str(bugnum) + " as confirmed on tip..."
+
+ return
+
+
def reproduceBug(self, bugnum):
# Fetch the bug
bug = self.bz.get_bug(bugnum)
@@ -156,13 +197,13 @@ def reproduceBug(self, bugnum):
if (rev == None):
raise Exception("Error: Failed to isolate original revision for test")
- # Isolate options for testing
- opts = self.extractOptions(text)
+ opts = None
- if (opts == None):
- print "Warning: No options found, will try to guess"
- #opts = []
- #raise Exception("Error: Failed to isolate options from comment")
+ # Isolate options for testing, not explicitly instructed to guess
+ if not self.options.guessopts:
+ opts = self.extractOptions(text)
+ if (opts == None):
+ print "Warning: No options found, will try to guess"
arch = None
if (bug.platform == "x86_64"):
@@ -181,37 +222,22 @@ def reproduceBug(self, bugnum):
repoDir = os.path.join(self.repoBase, reponame)
+ # We need at least some shell to extract the test from the bug,
+ # so we build a debug tip shell here already
updated = False
if not self.tipRev.has_key(repoDir):
# If we don't know the tip revision for this branch, update and get it
self.tipRev[repoDir] = self.hgUpdate(repoDir)
updated = True
-
- tipShell = self.getCachedShell("cache/", arch, "dbg", 0, self.tipRev[repoDir])
- if (tipShell == None):
- # If there is no cached shell, but we did not update earlier, then we need
- # to do it now to ensure we're compiling the correct version!
- # This can happen if we request another architecture for example.
- if not updated:
- self.tipRev[repoDir] = self.hgUpdate(repoDir)
- print "Compiling a new shell for tip (revision " + self.tipRev[repoDir] + ")"
- tipShell = makeShell("cache/", repoDir, arch, "dbg", 0, self.tipRev[repoDir])
-
-
- origShell = self.getCachedShell("cache/", arch, "dbg", 0, rev)
-
- if (origShell == None):
- origRev = self.hgUpdate(repoDir, rev)
- print "Compiling a new shell for orig (revision " + rev + ")"
- origShell = makeShell("cache/", repoDir, arch, "dbg", 0, rev)
+ (tipShell, tipRev) = self.getShell("cache/", arch, "dbg", 0, self.tipRev[repoDir], updated, repoDir)
# If the file already exists, then we can reuse it
-
testFile = "bug" + str(bugnum) + ".js"
if (os.path.exists(testFile)):
print "Using existing (cached) testfile " + testFile
else:
+
# We need to detect where our test is.
blocks = text.split("\n\n")
found = False
@@ -221,7 +247,7 @@ def reproduceBug(self, bugnum):
outFile = open(testFile, "w")
outFile.write(block)
outFile.close()
- (err, ret) = testBinary(origShell, testFile, [], 0)
+ (err, ret) = testBinary(tipShell, testFile, [], 0)
if (err.find("SyntaxError") < 0):
found = True
@@ -231,48 +257,64 @@ def reproduceBug(self, bugnum):
if not found:
raise Exception("Error: Failed to isolate test from comment")
- oouterr = None
- oret = None
+ (oouterr, oret) = (None, None)
+ (origShell, origRev) = (None, None)
- if (opts != None):
- (oouterr, oret) = testBinary(origShell, testFile, opts , 0)
- else:
- print "Guessing options...",
- guessopts = ['-m -n', '-m -n -a', '-m', '-j', '-j -m', '-j -m -a', '']
- for opt in guessopts:
- print " " + opt,
- opts = opt.split(' ')
- (oouterr, oret) = testBinary(origShell, testFile, opts , 0)
- if (oret < 0):
- break;
+ for compileType in ['dbg', 'opt']:
+ # Update to tip and cache result:
+ updated = False
+ if not self.tipRev.has_key(repoDir):
+ # If we don't know the tip revision for this branch, update and get it
+ self.tipRev[repoDir] = self.hgUpdate(repoDir)
+ updated = True
+
+ (tipShell, tipRev) = self.getShell("cache/", arch, compileType, 0, self.tipRev[repoDir], updated, repoDir)
+ (origShell, origRev) = self.getShell("cache/", arch, compileType, 0, rev, False, repoDir)
+
+ if (opts != None):
+ (oouterr, oret) = testBinary(origShell, testFile, opts , 0, verbose=self.options.verbose)
+ else:
+ print "Guessing options...",
+ guessopts = ['-m -n', '-m -n -a', '-m', '-j', '-j -m', '-j -m -a', '']
+ for opt in guessopts:
+ print " " + opt,
+ opts = opt.split(' ')
+ (oouterr, oret) = testBinary(origShell, testFile, opts , 0, verbose=self.options.verbose)
+ if (oret < 0):
+ break;
+
+ # If we reproduced with dbg, then we don't need to try opt
+ if (oret < 0):
+ break;
+
+ # Check if we reproduced at all (dbg or opt)
if (oret < 0):
print ""
print "Successfully reproduced bug (exit code " + str(oret) + ") on original revision " + rev + ":"
print oouterr
- if (opts != None):
- # Try running on tip now
- print "Testing bug on tip..."
- (touterr, tret) = testBinary(tipShell, testFile, opts , 0)
- else:
- print ""
+ if (opts != None):
+ # Try running on tip now
+ print "Testing bug on tip..."
+ (touterr, tret) = testBinary(tipShell, testFile, opts , 0, verbose=self.options.verbose)
+ else:
+ print ""
- if (oret < 0):
if (tret < 0):
if (tret == oret):
print "Result: Bug still reproduces"
- return self.result.REPRODUCED_TIP
+ return BugMonitorResult(reponame, rev, self.tipRev[repoDir], BugMonitorResult.statusCodes.REPRODUCED_TIP)
else:
# Unlikely but possible, switched signal
print "Result: Bug now reproduces with signal " + str(tret) + " (previously " + str(oret) + ")"
- return self.result.REPRODUCED_SWITCHED
+ return BugMonitorResult(reponame, rev, self.tipRev[repoDir], BugMonitorResult.statusCodes.REPRODUCED_SWITCHED)
else:
print "Result: Bug no longer reproduces"
- return self.result.REPRODUCED_FIXED
+ return BugMonitorResult(reponame, rev, self.tipRev[repoDir], BugMonitorResult.statusCodes.REPRODUCED_FIXED)
else:
print "Error: Failed to reproduce bug on original revision"
- return self.result.FAILED
+ return BugMonitorResult(reponame, rev, self.tipRev[repoDir], BugMonitorResult.statusCodes.FAILED)
def extractOptions(self, text):
ret = re.compile('((?: \-[a-z])+)', re.DOTALL).search(text)
@@ -285,7 +327,7 @@ def extractRevision(self, text):
tokens = text.split(' ')
for token in tokens:
if (re.match('^[a-f0-9]{12}[^a-f0-9]?', token)):
- return token[0:11]
+ return token[0:12]
return None
def hgUpdate(self, repoDir, rev=None):
@@ -312,5 +354,23 @@ def getCachedShell(self, shellCacheDir, archNum, compileType, valgrindSupport, r
return cachedShell
return None
+ def getShell(self, shellCacheDir, archNum, compileType, valgrindSupport, rev, updated, repoDir):
+ shell = self.getCachedShell(shellCacheDir, archNum, compileType, valgrindSupport, rev)
+ updRev = None
+ if (shell == None):
+ if updated:
+ updRev = rev
+ else:
+ updRev = self.hgUpdate(repoDir, rev)
+
+
+ if (rev == None):
+ print "Compiling a new shell for tip (revision " + updRev + ")"
+ else:
+ print "Compiling a new shell for revision " + updRev
+ shell = makeShell(shellCacheDir, repoDir, archNum, compileType, valgrindSupport, updRev)
+
+ return (shell, updRev)
+
if __name__ == '__main__':
main()

0 comments on commit 4a74ca9

Please sign in to comment.