From: Anna Zaks Date: Mon, 16 Jul 2012 20:21:42 +0000 (+0000) Subject: [analyzer] Make CmpRuns external-user friendly. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7acc407240152e9f7a6e9f2efb24aa4b2a71c8de;p=clang [analyzer] Make CmpRuns external-user friendly. CmpRuns can be used for static analyzer bug report comparison. However, we want to make sure external users do not rely on the way bugs are represented (plist files). Make sure that we have a user friendly/documented API for CmpRuns script. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160314 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/utils/analyzer/CmpRuns.py b/utils/analyzer/CmpRuns.py index 220045319f..6013345348 100755 --- a/utils/analyzer/CmpRuns.py +++ b/utils/analyzer/CmpRuns.py @@ -11,12 +11,63 @@ two perspectives: 2. For use by end users who want to integrate regular static analyzer testing into a buildbot like environment. + +Usage: + + # Load the results of both runs, to obtain lists of the corresponding + # AnalysisDiagnostic objects. + resultsA = loadResults(dirA, opts, deleteEmpty) + resultsB = loadResults(dirB, opts, deleteEmpty) + + # Generate a relation from diagnostics in run A to diagnostics in run B + # to obtain a list of triples (a, b, confidence). + diff = compareResults(resultsA, resultsB) + """ import os import plistlib # +class AnalysisDiagnostic: + def __init__(self, data, report, htmlReport): + self._data = data + self._loc = self._data['location'] + self._report = report + self._htmlReport = htmlReport + + def getFileName(self): + return self._report.run.getSourceName(self._report.files[self._loc['file']]) + + def getLine(self): + return self._loc['line'] + + def getColumn(self): + return self._loc['col'] + + def getCategory(self): + return self._data['category'] + + def getDescription(self): + return self._data['description'] + + def getIssueIdentifier(self) : + id = '' + if 'issue_context' in self._data : + id += self._data['issue_context'] + if 'issue_hash' in self._data : + id += str(self._data['issue_hash']) + return id + + def getReport(self): + if self._htmlReport is None: + return " " + return os.path.join(self._report.run.path, self._htmlReport) + + def getReadableName(self): + return '%s:%d:%d, %s: %s' % (self.getFileName(), self.getLine(), + self.getColumn(), self.getCategory(), + self.getDescription()) class multidict: def __init__(self, elts=()): @@ -54,34 +105,6 @@ class AnalysisReport: self.run = run self.files = files -class AnalysisDiagnostic: - def __init__(self, data, report, htmlReport): - self.data = data - self.report = report - self.htmlReport = htmlReport - - def getReadableName(self): - loc = self.data['location'] - filename = self.report.run.getSourceName(self.report.files[loc['file']]) - line = loc['line'] - column = loc['col'] - category = self.data['category'] - description = self.data['description'] - - # FIXME: Get a report number based on this key, to 'distinguish' - # reports, or something. - - return '%s:%d:%d, %s: %s' % (filename, line, column, category, - description) - - def getReportData(self): - if self.htmlReport is None: - return " " - return os.path.join(self.report.run.path, self.htmlReport) - # We could also dump the report with: - # return open(os.path.join(self.report.run.path, - # self.htmlReport), "rb").read() - class AnalysisRun: def __init__(self, path, opts): self.path = path @@ -134,13 +157,8 @@ def loadResults(path, opts, deleteEmpty=True): return run -def getIssueIdentifier(d) : - id = '' - if 'issue_context' in d.data : - id += d.data['issue_context'] - if 'issue_hash' in d.data : - id += str(d.data['issue_hash']) - return id +def cmpAnalysisDiagnostic(d) : + return d.getIssueIdentifier() def compareResults(A, B): """ @@ -160,14 +178,14 @@ def compareResults(A, B): neqB = [] eltsA = list(A.diagnostics) eltsB = list(B.diagnostics) - eltsA.sort(key = getIssueIdentifier) - eltsB.sort(key = getIssueIdentifier) + eltsA.sort(key = cmpAnalysisDiagnostic) + eltsB.sort(key = cmpAnalysisDiagnostic) while eltsA and eltsB: a = eltsA.pop() b = eltsB.pop() - if (getIssueIdentifier(a) == getIssueIdentifier(b)) : + if (a.getIssueIdentifier() == b.getIssueIdentifier()) : res.append((a, b, 0)) - elif a.data > b.data: + elif a._data > b._data: neqA.append(a) eltsB.append(b) else: @@ -189,7 +207,7 @@ def compareResults(A, B): return res -def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True): +def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True): # Load the run results. resultsA = loadResults(dirA, opts, deleteEmpty) resultsB = loadResults(dirB, opts, deleteEmpty) @@ -209,13 +227,13 @@ def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True): foundDiffs += 1 if auxLog: print >>auxLog, ("('ADDED', %r, %r)" % (b.getReadableName(), - b.getReportData())) + b.getReport())) elif b is None: print "REMOVED: %r" % a.getReadableName() foundDiffs += 1 if auxLog: print >>auxLog, ("('REMOVED', %r, %r)" % (a.getReadableName(), - a.getReportData())) + a.getReport())) elif confidence: print "CHANGED: %r to %r" % (a.getReadableName(), b.getReadableName()) @@ -224,8 +242,8 @@ def cmpScanBuildResults(dirA, dirB, opts, deleteEmpty=True): print >>auxLog, ("('CHANGED', %r, %r, %r, %r)" % (a.getReadableName(), b.getReadableName(), - a.getReportData(), - b.getReportData())) + a.getReport(), + b.getReport())) else: pass diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index 4b9d2eb60c..93a3a72676 100755 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -357,7 +357,7 @@ def runCmpResults(Dir): OLD_STDOUT = sys.stdout sys.stdout = Discarder() # Scan the results, delete empty plist files. - NumDiffs = CmpRuns.cmpScanBuildResults(RefDir, NewDir, Opts, False) + NumDiffs = CmpRuns.dumpScanBuildResultsDiff(RefDir, NewDir, Opts, False) sys.stdout = OLD_STDOUT if (NumDiffs > 0) : print "Warning: %r differences in diagnostics. See %s" % \