#!/usr/bin/python # -*- mode:python -*- # ABRT Bugzilla Duplication Checker # Downloads all backtraces reported by ABRT from Bugzilla, # and search for duplicates using the newest ABRT duplication # checker. # # Some bugs in Bugzilla were reported by older ABRT # versions, which had poor duplication detection. # # Please do not run this script unless it's neccessary to do so. # It forces Bugzilla to send data related to thousands of bug reports. from bugzilla import RHBugzilla from optparse import OptionParser import sys import os.path import subprocess parser = OptionParser(version="%prog 1.0") parser.add_option("-u", "--user", dest="user", help="Bugzilla user name (REQUIRED)", metavar="USERNAME") parser.add_option("-p", "--password", dest="password", help="Bugzilla password (REQUIRED)", metavar="PASSWORD") parser.add_option("-b", "--bugzilla", dest="bugzilla", help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL") parser.add_option("-v", "--verbose", dest="verbose", help="Detailed output") (options, args) = parser.parse_args() if not options.user or len(options.user) == 0: parser.error("User name is required.\nTry {0} --help".format(sys.argv[0])) if not options.password or len(options.password) == 0: parser.error("Password is required.\nTry {0} --help".format(sys.argv[0])) if not options.bugzilla or len(options.bugzilla) == 0: options.bugzilla = "https://bugzilla.redhat.com/xmlrpc.cgi" bz = RHBugzilla() bz.connect(options.bugzilla) bz.login(options.user, options.password) buginfos = bz.query({'status_whiteboard_type':'allwordssubstr','status_whiteboard':'abrt_hash'}) print "{0} bugs found.".format(len(buginfos)) database = {} for buginfo in buginfos: if not buginfo.bug_status in ["NEW", "ASSIGNED", "MODIFIED", "VERIFIED"]: if options.verbose: print "Bug {0} has status {1}, skipping.".format(buginfo.bug_id, buginfo.bug_status) continue # Skip bugs with already downloaded backtraces. filename = "{0}.bt".format(buginfo.bug_id) if os.path.isfile(filename): if options.verbose: print "Skipping {0} (already exists).".format(filename) else: # Get backtrace from bug and store it as a file. bug = bz.getbug(buginfo.bug_id) for attachment in bug.attachments: if attachment['filename'] == 'backtrace': data = bz.openattachment(attachment['id']) f = open(filename, 'w') f.write(data.read()) f.close() if options.verbose: print "Attachment {0} downloaded.".format(filename) command = ["/usr/bin/abrt-backtrace"] command.append(filename) command.append("--single-thread") command.append("--frame-depth=5") command.append("--remove-exit-handlers") command.append("--remove-noncrash-frames") helper = subprocess.Popen(command, stdout=subprocess.PIPE) out, err = helper.communicate() helper.wait() if helper.returncode != 0: print "Problems parsing {0}".format(filename) continue # Empty backtrace is provided by Python apps. if len(out) == 0: continue if out in database: database[out].append(buginfo.bug_id) if options.verbose: print "Duplicate found: {0}".format(database[out]) print "Backtrace: {0}".format(out) else: database[out] = [ buginfo.bug_id ] bz.logout() print "SUMMARY" print "==========================================================================" # The number of duplicates. dupcount = 0 for key, value in database.items(): if len(value) > 1: dupcount += len(value) - 1 print "Total number of duplicate bugs detected: {0}".format(dupcount) print "------------------------------" for key, value in database.items(): if len(value) > 1: print "Duplicates: {0}".format(value) print "Backtrace: {0}".format(key)