diff options
author | Ted Kremenek <kremenek@apple.com> | 2009-09-18 05:37:41 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2009-09-18 05:37:41 +0000 |
commit | 6a19832d08f00ac78c0a69c4fbe38b04a55b75cc (patch) | |
tree | ae55168e28cbde260c4cc1958cce3f66c8ffb2ea /lib/Analysis/BugReporter.cpp | |
parent | f1affe6129442b230d3d09b1938e07a7b341a102 (diff) |
Introduce caching of diagnostics in BugReporter. This provides extra
pruning of diagnostics that may be emitted multiple times. This is
accomplished by adding FoldingSet profiling support to PathDiagnostic,
and then having BugReporter record what diagnostics have been issued.
This was motived to a serious bug introduced by moving the
'divide-by-zero' checking outside of GRExprEngine into a separate
'Checker' class. When analyzing code using the '-fobjc-gc' option, a
given function would be analyzed twice, but the second time various
"internal checks" would be disabled to avoid emitting multiple
diagnostics (e.g., "null dereference") for the same issue. The
problem is that such checks also effect path pruning and don't just
emit diagnostics. This resulted in an assertion failure involving a
real divide-by-zero in some analyzed code where we would get an
assertion failure in APInt because the 'DivZero' check was disabled
and didn't prune the logic that resulted in the divide-by-zero in the
analyzer.
The implemented solution is somewhat of a hack, and may not perform
extremely well. This will need to be cleaned up over time.
As a regression test, 'misc-ps.m' has been modified so that its tests
are run using -fobjc-gc to test this diagnostic pruning behavior.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82198 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/BugReporter.cpp')
-rw-r--r-- | lib/Analysis/BugReporter.cpp | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp index 38e982888e..064fff47f4 100644 --- a/lib/Analysis/BugReporter.cpp +++ b/lib/Analysis/BugReporter.cpp @@ -1732,6 +1732,50 @@ static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) { return NULL; } + +//===----------------------------------------------------------------------===// +// DiagnosticCache. This is a hack to cache analyzer diagnostics. It +// uses global state, which eventually should go elsewhere. +//===----------------------------------------------------------------------===// +namespace { +class VISIBILITY_HIDDEN DiagCacheItem : public llvm::FoldingSetNode { + llvm::FoldingSetNodeID ID; +public: + DiagCacheItem(BugReport *R, PathDiagnostic *PD) { + ID.AddString(R->getBugType().getName()); + ID.AddString(R->getBugType().getCategory()); + ID.AddString(R->getDescription()); + ID.AddInteger(R->getLocation().getRawEncoding()); + PD->Profile(ID); + } + + void Profile(llvm::FoldingSetNodeID &id) { + id = ID; + } + + llvm::FoldingSetNodeID &getID() { return ID; } +}; +} + +static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { + // FIXME: Eventually this diagnostic cache should reside in something + // like AnalysisManager instead of being a static variable. This is + // really unsafe in the long term. + typedef llvm::FoldingSet<DiagCacheItem> DiagnosticCache; + static DiagnosticCache DC; + + void *InsertPos; + DiagCacheItem *Item = new DiagCacheItem(R, PD); + + if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { + delete Item; + return true; + } + + DC.InsertNode(Item, InsertPos); + return false; +} + void BugReporter::FlushReport(BugReportEquivClass& EQ) { BugReport *R = FindReportInEquivalenceClass(EQ); @@ -1752,6 +1796,9 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { GeneratePathDiagnostic(*D.get(), EQ); + if (IsCachedDiagnostic(R, D.get())) + return; + // Get the meta data. std::pair<const char**, const char**> Meta = R->getExtraDescriptiveText(); for (const char** s = Meta.first; s != Meta.second; ++s) |