xazax.hun removed rL LLVM as the repository for this revision.
xazax.hun updated this revision to Diff 75923.
https://reviews.llvm.org/D25985
Files:
include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
test/Analysis/analyzer-config.c
test/Analysis/analyzer-config.cpp
test/Analysis/record-coverage.cpp
test/Analysis/record-coverage.cpp.expected
Index: test/Analysis/record-coverage.cpp.expected
===================================================================
--- /dev/null
+++ test/Analysis/record-coverage.cpp.expected
@@ -0,0 +1,9 @@
+// CHECK: -:4:int main() {
+// CHECK-NEXT: -:5: int i = 2;
+// CHECK-NEXT: 1:6: ++i;
+// CHECK-NEXT: 1:7: if (i != 0) {
+// CHECK-NEXT: 1:8: ++i;
+// CHECK-NEXT: 1:9: } else {
+// CHECK-NEXT: -:10: --i;
+// CHECK-NEXT: -:11: }
+// CHECK-NEXT: -:12:}
Index: test/Analysis/record-coverage.cpp
===================================================================
--- /dev/null
+++ test/Analysis/record-coverage.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config record-coverage=%T %s
+// RUN: FileCheck -input-file %T/%s.gcov %s.expected
+
+int main() {
+ int i = 2;
+ ++i;
+ if (i != 0) {
+ ++i;
+ } else {
+ --i;
+ }
+}
Index: test/Analysis/analyzer-config.cpp
===================================================================
--- test/Analysis/analyzer-config.cpp
+++ test/Analysis/analyzer-config.cpp
@@ -35,7 +35,8 @@
// CHECK-NEXT: max-times-inline-large = 32
// CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14
// CHECK-NEXT: mode = deep
+// CHECK-NEXT: record-coverage =
// CHECK-NEXT: region-store-small-struct-limit = 2
// CHECK-NEXT: widen-loops = false
// CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 20
+// CHECK-NEXT: num-entries = 21
Index: test/Analysis/analyzer-config.c
===================================================================
--- test/Analysis/analyzer-config.c
+++ test/Analysis/analyzer-config.c
@@ -24,8 +24,9 @@
// CHECK-NEXT: max-times-inline-large = 32
// CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14
// CHECK-NEXT: mode = deep
+// CHECK-NEXT: record-coverage =
// CHECK-NEXT: region-store-small-struct-limit = 2
// CHECK-NEXT: widen-loops = false
// CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 15
+// CHECK-NEXT: num-entries = 16
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -28,8 +28,13 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/raw_os_ostream.h"
+#include <fstream>
#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
@@ -251,6 +256,45 @@
return State;
}
+// Mapping from file to line indexed hit count vector.
+static llvm::DenseMap<const FileEntry *,std::vector<int>> CoverageInfo;
+
+static void dumpCoverageInfo(llvm::SmallVectorImpl<char> &Path,
+ SourceManager &SM) {
+ for (auto &Entry : CoverageInfo) {
+ SmallString<128> FilePath;
+ const FileEntry *FE = Entry.getFirst();
+ llvm::sys::path::append(FilePath, Path, FE->getName() + ".gcov");
+ SmallString<128> DirPath = FilePath;
+ llvm::sys::path::remove_filename(DirPath);
+ llvm::sys::fs::create_directories(DirPath);
+ bool Invalid = false;
+ llvm::MemoryBuffer *Buf = SM.getMemoryBufferForFile(FE, &Invalid);
+ if (Invalid)
+ continue;
+ std::ofstream OutFile(FilePath.c_str());
+ if (!OutFile) {
+ llvm::errs() << FilePath << " not found!\n";
+ continue;
+ }
+ llvm::raw_os_ostream Out(OutFile);
+ Out << "-:0:Source:" << FE->getName() << '\n';
+ Out << "-:0:Runs:1\n";
+ Out << "-:0:Programs:1\n";
+ for (llvm::line_iterator LI(*Buf, false); !LI.is_at_eof(); ++LI) {
+ int Count = Entry.getSecond()[LI.line_number()-1];
+ if (Count != 0) {
+ Out << Count;
+ } else if (Count < 0) {
+ Out << "#####";
+ } else {
+ Out << '-';
+ }
+ Out << ':' << LI.line_number() << ':' << *LI << '\n';
+ }
+ }
+}
+
//===----------------------------------------------------------------------===//
// Top-level transfer function logic (Dispatcher).
//===----------------------------------------------------------------------===//
@@ -282,6 +326,12 @@
}
void ExprEngine::processEndWorklist(bool hasWorkRemaining) {
+ if (!AMgr.options.coverageExportDir().empty()) {
+ SmallString<128> Path = AMgr.options.coverageExportDir();
+ SourceManager &SM = getContext().getSourceManager();
+ SM.getFileManager().makeAbsolutePath(Path);
+ dumpCoverageInfo(Path, SM);
+ }
getCheckerManager().runCheckersForEndAnalysis(G, BR, *this);
}
@@ -1408,12 +1458,72 @@
return true;
}
+// Add the line range of the CFGBlock to a file entry indexed map.
+static void processCoverageInfo(const CFGBlock &Block, SourceManager &SM) {
+ llvm::SmallVector<unsigned, 32> LinesInBlock;
+ const FileEntry *FE = nullptr;
+ for (unsigned I = 0; I < Block.size(); ++I) {
+ const Stmt *S = nullptr;
+ switch (Block[I].getKind()) {
+ case CFGElement::Statement:
+ S = Block[I].castAs<CFGStmt>().getStmt();
+ break;
+ case CFGElement::Initializer:
+ S = Block[I].castAs<CFGInitializer>().getInitializer()->getInit();
+ if (!S)
+ continue;
+ break;
+ case CFGElement::NewAllocator:
+ S = Block[I].castAs<CFGNewAllocator>().getAllocatorExpr();
+ break;
+ default:
+ continue;
+ }
+ assert(S);
+ SourceLocation SpellingStartLoc = SM.getSpellingLoc(S->getLocStart());
+ if (SM.isInSystemHeader(SpellingStartLoc))
+ return;
+ FileID FID = SM.getFileID(SpellingStartLoc);
+ if (FE) {
+ if (FE != SM.getFileEntryForID(FID))
+ continue;
+ } else {
+ FE = SM.getFileEntryForID(FID);
+ if (CoverageInfo.find(FE) == CoverageInfo.end()) {
+ unsigned Lines = SM.getSpellingLineNumber(SM.getLocForEndOfFile(FID));
+ CoverageInfo.insert(std::make_pair(FE, std::vector<int>(Lines, 0)));
+ }
+ }
+ bool Invalid = false;
+ unsigned LineBegin = SM.getSpellingLineNumber(S->getLocStart(), &Invalid);
+ if (Invalid)
+ continue;
+ unsigned LineEnd = SM.getSpellingLineNumber(S->getLocEnd(), &Invalid);
+ if (Invalid)
+ continue;
+ for (unsigned Line = LineBegin; Line <= LineEnd; ++Line) {
+ LinesInBlock.push_back(Line);
+ }
+ }
+ if (!FE)
+ return;
+ std::sort(LinesInBlock.begin(), LinesInBlock.end());
+ LinesInBlock.erase(std::unique(LinesInBlock.begin(), LinesInBlock.end()),
+ LinesInBlock.end());
+ for (unsigned Line : LinesInBlock) {
+ CoverageInfo[FE][Line]++;
+ }
+}
+
/// Block entrance. (Update counters).
void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
NodeBuilderWithSinks &nodeBuilder,
ExplodedNode *Pred) {
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
+ if (!AMgr.options.coverageExportDir().empty())
+ processCoverageInfo(*L.getDst(), getContext().getSourceManager());
+
// If this block is terminated by a loop and it has already been visited the
// maximum number of times, widen the loop.
unsigned int BlockCount = nodeBuilder.getContext().blockCount();
Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -351,3 +351,10 @@
getBooleanOption("notes-as-events", /*Default=*/false);
return DisplayNotesAsEvents.getValue();
}
+
+StringRef AnalyzerOptions::coverageExportDir() {
+ if (!CoverageExportDir.hasValue())
+ CoverageExportDir = getOptionAsString("record-coverage", /*Default=*/"");
+ return CoverageExportDir.getValue();
+}
+
Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -269,6 +269,9 @@
/// \sa shouldDisplayNotesAsEvents
Optional<bool> DisplayNotesAsEvents;
+ /// \sa shouldRecordCoverage
+ Optional<StringRef> CoverageExportDir;
+
/// A helper function that retrieves option for a given full-qualified
/// checker name.
/// Options for checkers can be specified via 'analyzer-config' command-line
@@ -545,6 +548,10 @@
/// to false when unset.
bool shouldDisplayNotesAsEvents();
+ /// Determines where the coverage info should be dumped to. The coverage
+ /// information is recorded on the basic block level granularity.
+ StringRef coverageExportDir();
+
public:
AnalyzerOptions() :
AnalysisStoreOpt(RegionStoreModel),
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits