diff --git a/docs/CommandGuide/llvm-cov.rst b/docs/CommandGuide/llvm-cov.rst index 6ee05ee1a0a..478ba0fb15e 100644 --- a/docs/CommandGuide/llvm-cov.rst +++ b/docs/CommandGuide/llvm-cov.rst @@ -382,3 +382,10 @@ OPTIONS It is an error to specify an architecture that is not included in the universal binary or to use an architecture that does not match a non-universal binary. + +.. option:: -summary-only + + Export only summary information for each file in the coverage data. This mode + will not export coverage information for smaller units such as individual + functions or regions. The result will be the same as produced by :program: + `llvm-cov report` command, but presented in JSON format rather than text. diff --git a/include/llvm/Support/GCOV.h b/include/llvm/ProfileData/GCOV.h similarity index 99% rename from include/llvm/Support/GCOV.h rename to include/llvm/ProfileData/GCOV.h index 02016e7dbd6..497f80b87b2 100644 --- a/include/llvm/Support/GCOV.h +++ b/include/llvm/ProfileData/GCOV.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_GCOV_H -#define LLVM_SUPPORT_GCOV_H +#ifndef LLVM_PROFILEDATA_GCOV_H +#define LLVM_PROFILEDATA_GCOV_H #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" diff --git a/include/llvm/ProfileData/InstrProf.h b/include/llvm/ProfileData/InstrProf.h index ac3f057ec2f..b08b78cd593 100644 --- a/include/llvm/ProfileData/InstrProf.h +++ b/include/llvm/ProfileData/InstrProf.h @@ -859,7 +859,9 @@ enum ProfVersion { // In this version, profile summary data \c IndexedInstrProf::Summary is // stored after the profile header. Version4 = 4, - // The current version is 4. + // In this version, the frontend PGO stable hash algorithm defaults to V2. + Version5 = 5, + // The current version is 5. CurrentVersion = INSTR_PROF_INDEX_VERSION }; const uint64_t Version = ProfVersion::CurrentVersion; diff --git a/include/llvm/ProfileData/InstrProfData.inc b/include/llvm/ProfileData/InstrProfData.inc index 66d63a462a0..6a98dc7b9b8 100644 --- a/include/llvm/ProfileData/InstrProfData.inc +++ b/include/llvm/ProfileData/InstrProfData.inc @@ -628,7 +628,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure, /* Raw profile format version (start from 1). */ #define INSTR_PROF_RAW_VERSION 4 /* Indexed profile format version (start from 1). */ -#define INSTR_PROF_INDEX_VERSION 4 +#define INSTR_PROF_INDEX_VERSION 5 /* Coverage mapping format vresion (start from 0). */ #define INSTR_PROF_COVMAP_VERSION 2 diff --git a/include/llvm/ProfileData/SampleProf.h b/include/llvm/ProfileData/SampleProf.h index 7fc258831be..ecc2ea5d894 100644 --- a/include/llvm/ProfileData/SampleProf.h +++ b/include/llvm/ProfileData/SampleProf.h @@ -185,7 +185,9 @@ raw_ostream &operator<<(raw_ostream &OS, const SampleRecord &Sample); class FunctionSamples; using BodySampleMap = std::map<LineLocation, SampleRecord>; -using FunctionSamplesMap = StringMap<FunctionSamples>; +// NOTE: Using a StringMap here makes parsed profiles consume around 17% more +// memory, which is *very* significant for large profiles. +using FunctionSamplesMap = std::map<std::string, FunctionSamples>; using CallsiteSampleMap = std::map<LineLocation, FunctionSamplesMap>; /// Representation of the samples collected for a function. @@ -278,7 +280,7 @@ class FunctionSamples { return nullptr; auto FS = iter->second.find(CalleeName); if (FS != iter->second.end()) - return &FS->getValue(); + return &FS->second; // If we cannot find exact match of the callee name, return the FS with // the max total count. uint64_t MaxTotalSamples = 0; @@ -324,7 +326,7 @@ class FunctionSamples { const LineLocation &Loc = I.first; FunctionSamplesMap &FSMap = functionSamplesAt(Loc); for (const auto &Rec : I.second) - MergeResult(Result, FSMap[Rec.first()].merge(Rec.second, Weight)); + MergeResult(Result, FSMap[Rec.first].merge(Rec.second, Weight)); } return Result; } diff --git a/include/llvm/ProfileData/SampleProfReader.h b/include/llvm/ProfileData/SampleProfReader.h index 9c1f357cbbd..0e9ab2dc60e 100644 --- a/include/llvm/ProfileData/SampleProfReader.h +++ b/include/llvm/ProfileData/SampleProfReader.h @@ -217,10 +217,10 @@ #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/ProfileSummary.h" +#include "llvm/ProfileData/GCOV.h" #include "llvm/ProfileData/SampleProf.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorOr.h" -#include "llvm/Support/GCOV.h" #include "llvm/Support/MemoryBuffer.h" #include <algorithm> #include <cstdint> diff --git a/lib/IR/CMakeLists.txt b/lib/IR/CMakeLists.txt index eb4b9143090..17822bbbb5c 100644 --- a/lib/IR/CMakeLists.txt +++ b/lib/IR/CMakeLists.txt @@ -22,7 +22,6 @@ add_llvm_library(LLVMCore DiagnosticPrinter.cpp Dominators.cpp Function.cpp - GCOV.cpp GVMaterializer.cpp Globals.cpp IRBuilder.cpp diff --git a/lib/ProfileData/CMakeLists.txt b/lib/ProfileData/CMakeLists.txt index cd65762ae6a..3a981d8acf4 100644 --- a/lib/ProfileData/CMakeLists.txt +++ b/lib/ProfileData/CMakeLists.txt @@ -1,4 +1,5 @@ add_llvm_library(LLVMProfileData + GCOV.cpp InstrProf.cpp InstrProfReader.cpp InstrProfWriter.cpp diff --git a/lib/ProfileData/Coverage/CoverageMapping.cpp b/lib/ProfileData/Coverage/CoverageMapping.cpp index 9df37a47408..ccc8cc56eb0 100644 --- a/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -33,6 +33,7 @@ #include <cassert> #include <cstdint> #include <iterator> +#include <map> #include <memory> #include <string> #include <system_error> @@ -296,7 +297,7 @@ namespace { /// An instantiation set is a collection of functions that have the same source /// code, ie, template functions specializations. class FunctionInstantiationSetCollector { - using MapT = DenseMap<LineColPair, std::vector<const FunctionRecord *>>; + using MapT = std::map<LineColPair, std::vector<const FunctionRecord *>>; MapT InstantiatedFunctions; public: @@ -387,6 +388,11 @@ class SegmentBuilder { if (CompletedSegmentLoc == CompletedRegion->endLoc()) continue; + // Use the count from the last completed region which ends at this loc. + for (unsigned J = I + 1; J < E; ++J) + if (CompletedRegion->endLoc() == ActiveRegions[J]->endLoc()) + CompletedRegion = ActiveRegions[J]; + startSegment(*CompletedRegion, CompletedSegmentLoc, false); } diff --git a/lib/IR/GCOV.cpp b/lib/ProfileData/GCOV.cpp similarity index 99% rename from lib/IR/GCOV.cpp rename to lib/ProfileData/GCOV.cpp index d4b45522822..d6e44389f2b 100644 --- a/lib/IR/GCOV.cpp +++ b/lib/ProfileData/GCOV.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Support/GCOV.h" +#include "llvm/ProfileData/GCOV.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" diff --git a/test/tools/llvm-cov/deferred-region.cpp b/test/tools/llvm-cov/deferred-region.cpp index 38090fbb498..3bc675d66e7 100644 --- a/test/tools/llvm-cov/deferred-region.cpp +++ b/test/tools/llvm-cov/deferred-region.cpp @@ -45,7 +45,7 @@ void while_loop() { break; // CHECK: [[@LINE]]|{{ +}}1| // CHECK: [[@LINE]]|{{ +}}0| while (++x < 5) {} // CHECK: [[@LINE]]|{{ +}}0| - } // CHECK: [[@LINE]]|{{ +}}1| + } // CHECK: [[@LINE]]|{{ +}}0| if (x == 0) // CHECK: [[@LINE]]|{{ +}}1| throw Error(); // CHECK: [[@LINE]]|{{ +}}0| @@ -97,6 +97,8 @@ int main() { // MARKER-NEXT: Highlighted line 47, 14 -> 21 // MARKER-NEXT: Highlighted line 47, 21 -> 23 // MARKER-NEXT: Highlighted line 47, 23 -> 25 +// MARKER-NEXT: Highlighted line 47, 25 -> ? +// MARKER-NEXT: Highlighted line 48, 1 -> 6 // MARKER-NEXT: Highlighted line 51, 7 -> 20 // MARKER-NEXT: Marker at 53:5 = 1 // MARKER-NEXT: Highlighted line 55, 9 -> 14 diff --git a/test/tools/llvm-cov/showLineExecutionCounts.cpp b/test/tools/llvm-cov/showLineExecutionCounts.cpp index c4b76824aac..c18afedf034 100644 --- a/test/tools/llvm-cov/showLineExecutionCounts.cpp +++ b/test/tools/llvm-cov/showLineExecutionCounts.cpp @@ -39,36 +39,20 @@ int main() { // TEXT: [[@LINE]]| 161|int main( // RUN: FileCheck -input-file %t.export.json %S/Inputs/lineExecutionCounts.json // RUN: cat %t.export.json | %python -c "import json, sys; json.loads(sys.stdin.read())" // +// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata 2>/dev/null -summary-only > %t.export-summary.json +// RUN: not grep '"name":"main"' %t.export-summary.json +// // Test html output. // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -path-equivalence=/tmp,%S %s // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.filtered.dir -instr-profile %t.profdata -path-equivalence=/tmp,%S -name=main %s // RUN: FileCheck -check-prefixes=HTML,HTML-WHOLE-FILE -input-file %t.html.dir/coverage/tmp/showLineExecutionCounts.cpp.html %s // RUN: FileCheck -check-prefixes=HTML,HTML-FILTER -input-file %t.html.filtered.dir/coverage/tmp/showLineExecutionCounts.cpp.html %s // -// HTML-WHOLE-FILE: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// before -// HTML-FILTER-NOT: <td class='line-number'><a name='L[[@LINE-45]]' href='#L[[@LINE-45]]'><pre>[[@LINE-45]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// before -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre>int main() { -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> int x = 0 -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> if (x) -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre> -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre><span class='red'> }</span> -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> x = 1; -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> } -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>16.2k</pre></td><td class='code'><pre> for (int i = 0; i < 100; ++i) -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>16.1k</pre></td><td class='code'><pre> x = 1; -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>16.1k</pre></td><td class='code'><pre> } -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> x = x < 10 -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> x = x > 10 -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre> <span class='red'>x - 1</span>: -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> x + 1; -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre> return 0; -// HTML: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre>} -// HTML-WHOLE-FILE: <td class='line-number'><a name='L[[@LINE-44]]' href='#L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after -// HTML-FILTER-NOT: <td class='line-number'><a name='L[[@LINE-45]]' href='#L[[@LINE-45]]'><pre>[[@LINE-45]]</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after +// HTML-WHOLE-FILE: <td class='line-number'><a name='L4' href='#L4'><pre>4</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// before +// HTML-FILTER-NOT: <td class='line-number'><a name='L4' href='#L4'><pre>4</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// before +// HTML: <td class='line-number'><a name='L6' href='#L6'><pre>6</pre></a></td><td class='covered-line'><pre>161</pre></td><td class='code'><pre>int main() { +// HTML-WHOLE-FILE: <td class='line-number'><a name='L26' href='#L26'><pre>26</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after +// HTML-FILTER-NOT: <td class='line-number'><a name='L26' href='#L26'><pre>26</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// after // // Test index creation. // RUN: FileCheck -check-prefix=TEXT-INDEX -input-file %t.dir/index.txt %s diff --git a/test/tools/llvm-profdata/Inputs/counter-mismatch-1.proftext b/test/tools/llvm-profdata/Inputs/counter-mismatch-1.proftext new file mode 100644 index 00000000000..45d028e1ad0 --- /dev/null +++ b/test/tools/llvm-profdata/Inputs/counter-mismatch-1.proftext @@ -0,0 +1,13 @@ +foo +1024 +1 +0 + +foo +1024 +5 +0 +0 +0 +0 +0 diff --git a/test/tools/llvm-profdata/Inputs/counter-mismatch-2.proftext b/test/tools/llvm-profdata/Inputs/counter-mismatch-2.proftext new file mode 100644 index 00000000000..261bfdde595 --- /dev/null +++ b/test/tools/llvm-profdata/Inputs/counter-mismatch-2.proftext @@ -0,0 +1,5 @@ +foo +1024 +2 +0 +0 diff --git a/test/tools/llvm-profdata/Inputs/counter-mismatch-3.proftext b/test/tools/llvm-profdata/Inputs/counter-mismatch-3.proftext new file mode 100644 index 00000000000..ca70a71a923 --- /dev/null +++ b/test/tools/llvm-profdata/Inputs/counter-mismatch-3.proftext @@ -0,0 +1,6 @@ +foo +1024 +3 +0 +0 +0 diff --git a/test/tools/llvm-profdata/Inputs/counter-mismatch-4.proftext b/test/tools/llvm-profdata/Inputs/counter-mismatch-4.proftext new file mode 100644 index 00000000000..f403382e913 --- /dev/null +++ b/test/tools/llvm-profdata/Inputs/counter-mismatch-4.proftext @@ -0,0 +1,7 @@ +foo +1024 +4 +0 +0 +0 +0 diff --git a/test/tools/llvm-profdata/compat.proftext b/test/tools/llvm-profdata/compat.proftext index 17855a7f0f9..73321cc5e66 100644 --- a/test/tools/llvm-profdata/compat.proftext +++ b/test/tools/llvm-profdata/compat.proftext @@ -59,6 +59,7 @@ large_numbers # FORMATV2-NEXT: Counters: 4 # FORMATV2-NEXT: Function count: 1 # FORMATV2-NEXT: Block counts: [1000, 1000000, 499500] +# FORMATV2-NEXT: Instrumentation level: Front-end # FORMATV2-NEXT: Functions shown: 2 # FORMATV2-NEXT: Total functions: 2 # FORMATV2-NEXT: Maximum function count: 499500 diff --git a/test/tools/llvm-profdata/show-instr-level.test b/test/tools/llvm-profdata/show-instr-level.test new file mode 100644 index 00000000000..ed003265e38 --- /dev/null +++ b/test/tools/llvm-profdata/show-instr-level.test @@ -0,0 +1,7 @@ +RUN: llvm-profdata merge -o %t_clang.profdata %p/Inputs/clang_profile.proftext +RUN: llvm-profdata show %t_clang.profdata | FileCheck %s -check-prefix=FE +FE: Instrumentation level: Front-end + +RUN: llvm-profdata merge -o %t_ir.profdata %p/Inputs/IR_profile.proftext +RUN: llvm-profdata show %t_ir.profdata | FileCheck %s -check-prefix=IR +IR: Instrumentation level: IR diff --git a/test/tools/llvm-profdata/threaded-count-mismatch.test b/test/tools/llvm-profdata/threaded-count-mismatch.test new file mode 100644 index 00000000000..8e704cf85a6 --- /dev/null +++ b/test/tools/llvm-profdata/threaded-count-mismatch.test @@ -0,0 +1,10 @@ +# Test multithreaded error reporting. + +RUN: llvm-profdata merge -j 4 -o %t.profdata \ +RUN: %S/Inputs/counter-mismatch-1.proftext \ +RUN: %S/Inputs/counter-mismatch-2.proftext \ +RUN: %S/Inputs/counter-mismatch-3.proftext \ +RUN: %S/Inputs/counter-mismatch-4.proftext \ +RUN: 2>&1 | FileCheck %s + +CHECK: Function basic block count change detected (counter mismatch) diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index d2cc21b4e26..73de07c8887 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -634,6 +634,10 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { "show-instantiation-summary", cl::Optional, cl::desc("Show instantiation statistics in summary table")); + cl::opt<bool> SummaryOnly( + "summary-only", cl::Optional, + cl::desc("Export only summary information for each source file")); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; @@ -746,6 +750,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { ViewOpts.ShowRegionSummary = RegionSummary; ViewOpts.ShowInstantiationSummary = InstantiationSummary; + ViewOpts.ExportSummaryOnly = SummaryOnly; return 0; }; diff --git a/tools/llvm-cov/CoverageExporterJson.cpp b/tools/llvm-cov/CoverageExporterJson.cpp index 5b6b09f048e..7b700908968 100644 --- a/tools/llvm-cov/CoverageExporterJson.cpp +++ b/tools/llvm-cov/CoverageExporterJson.cpp @@ -177,8 +177,11 @@ class CoverageExporterJson { SourceFiles, Options); renderFiles(SourceFiles, FileReports); - emitDictKey("functions"); - renderFunctions(Coverage.getCoveredFunctions()); + // Skip functions-level information for summary-only export mode. + if (!Options.ExportSummaryOnly) { + emitDictKey("functions"); + renderFunctions(Coverage.getCoveredFunctions()); + } emitDictKey("totals"); renderSummary(Totals); @@ -254,27 +257,31 @@ class CoverageExporterJson { emitDictStart(); emitDictElement("filename", FileCoverage.getFilename()); - emitDictKey("segments"); - // Start List of Segments. - emitArrayStart(); + // Skip segments and expansions for summary-only export mode. + if (!Options.ExportSummaryOnly) { + emitDictKey("segments"); - for (const auto &Segment : FileCoverage) - renderSegment(Segment); + // Start List of Segments. + emitArrayStart(); - // End List of Segments. - emitArrayEnd(); + for (const auto &Segment : FileCoverage) + renderSegment(Segment); + + // End List of Segments. + emitArrayEnd(); - emitDictKey("expansions"); + emitDictKey("expansions"); - // Start List of Expansions. - emitArrayStart(); + // Start List of Expansions. + emitArrayStart(); - for (const auto &Expansion : FileCoverage.getExpansions()) - renderExpansion(Expansion); + for (const auto &Expansion : FileCoverage.getExpansions()) + renderExpansion(Expansion); - // End List of Expansions. - emitArrayEnd(); + // End List of Expansions. + emitArrayEnd(); + } emitDictKey("summary"); renderSummary(FileReport); diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h index a071c0aca9e..17614c4e9ba 100644 --- a/tools/llvm-cov/CoverageViewOptions.h +++ b/tools/llvm-cov/CoverageViewOptions.h @@ -32,6 +32,7 @@ struct CoverageViewOptions { bool ShowFullFilenames; bool ShowRegionSummary; bool ShowInstantiationSummary; + bool ExportSummaryOnly; OutputFormat Format; std::string ShowOutputDirectory; std::vector<std::string> DemanglerOpts; diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index 0572968b8b4..8c39dab580d 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -181,8 +181,8 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, // We need the expansions and instantiations sorted so we can go through them // while we iterate lines. - std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); - std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); + std::stable_sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); + std::stable_sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); auto NextESV = ExpansionSubViews.begin(); auto EndESV = ExpansionSubViews.end(); auto NextISV = InstantiationSubViews.begin(); diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp index 4df7f015fd1..7776f2aa9a6 100644 --- a/tools/llvm-cov/gcov.cpp +++ b/tools/llvm-cov/gcov.cpp @@ -11,11 +11,11 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ProfileData/GCOV.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/GCOV.h" #include "llvm/Support/Path.h" #include <system_error> using namespace llvm; diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index eee242107da..6049933b7de 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -37,14 +37,19 @@ using namespace llvm; enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC }; -static void exitWithError(const Twine &Message, StringRef Whence = "", - StringRef Hint = "") { - errs() << "error: "; +static void warn(StringRef Prefix, Twine Message, std::string Whence = "", + std::string Hint = "") { + errs() << Prefix; if (!Whence.empty()) errs() << Whence << ": "; errs() << Message << "\n"; if (!Hint.empty()) errs() << Hint << "\n"; +} + +static void exitWithError(Twine Message, std::string Whence = "", + std::string Hint = "") { + warn("error: ", Message, Whence, Hint); ::exit(1); } @@ -119,7 +124,7 @@ struct WriterContext { std::mutex Lock; InstrProfWriter Writer; Error Err; - StringRef ErrWhence; + std::string ErrWhence; std::mutex &ErrLock; SmallSet<instrprof_error, 4> &WriterErrorCodes; @@ -129,6 +134,22 @@ struct WriterContext { ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} }; +/// Determine whether an error is fatal for profile merging. +static bool isFatalError(instrprof_error IPE) { + switch (IPE) { + default: + return true; + case instrprof_error::success: + case instrprof_error::eof: + case instrprof_error::unknown_function: + case instrprof_error::hash_mismatch: + case instrprof_error::count_mismatch: + case instrprof_error::counter_overflow: + case instrprof_error::value_site_count_mismatch: + return false; + } +} + /// Load an input into a writer context. static void loadInput(const WeightedFile &Input, WriterContext *WC) { std::unique_lock<std::mutex> CtxGuard{WC->Lock}; @@ -137,6 +158,9 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) { if (WC->Err) return; + // Copy the filename, because llvm::ThreadPool copied the input "const + // WeightedFile &" by value, making a reference to the filename within it + // invalid outside of this packaged task. WC->ErrWhence = Input.Filename; auto ReaderOrErr = InstrProfReader::create(Input.Filename); @@ -174,12 +198,22 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) { FuncName, firstTime); }); } - if (Reader->hasError()) - WC->Err = Reader->getError(); + if (Reader->hasError()) { + if (Error E = Reader->getError()) { + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (isFatalError(IPE)) + WC->Err = make_error<InstrProfError>(IPE); + } + } } /// Merge the \p Src writer context into \p Dst. static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { + // If we've already seen a hard error, continuing with the merge would + // clobber it. + if (Dst->Err || Src->Err) + return; + bool Reported = false; Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { if (Reported) { @@ -254,10 +288,20 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, } // Handle deferred hard errors encountered during merging. - for (std::unique_ptr<WriterContext> &WC : Contexts) - if (WC->Err) + for (std::unique_ptr<WriterContext> &WC : Contexts) { + if (!WC->Err) + continue; + if (!WC->Err.isA<InstrProfError>()) exitWithError(std::move(WC->Err), WC->ErrWhence); + instrprof_error IPE = InstrProfError::take(std::move(WC->Err)); + if (isFatalError(IPE)) + exitWithError(make_error<InstrProfError>(IPE), WC->ErrWhence); + else + warn("warning: ", toString(make_error<InstrProfError>(IPE)), + WC->ErrWhence); + } + InstrProfWriter &Writer = Contexts[0]->Writer; if (OutputFormat == PF_Text) { if (Error E = Writer.writeText(Output)) @@ -625,6 +669,8 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, if (ShowCounts && TextFormat) return 0; std::unique_ptr<ProfileSummary> PS(Builder.getSummary()); + OS << "Instrumentation level: " + << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; if (ShowAllFunctions || !ShowFunction.empty()) OS << "Functions shown: " << ShownFunctions << "\n"; OS << "Total functions: " << PS->getNumFunctions() << "\n"; diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp index 74c515e5777..ded364c40d1 100644 --- a/unittests/ProfileData/CoverageMappingTest.cpp +++ b/unittests/ProfileData/CoverageMappingTest.cpp @@ -466,6 +466,34 @@ TEST_P(CoverageMappingTest, multiple_regions_end_after_parent_ends) { EXPECT_EQ(CoverageSegment(9, 9, false), Segments[7]); } +TEST_P(CoverageMappingTest, multiple_completed_segments_at_same_loc) { + ProfileWriter.addRecord({"func1", 0x1234, {0, 1, 2}}, Err); + startFunction("func1", 0x1234); + + // PR35495 + addCMR(Counter::getCounter(1), "file1", 2, 1, 18, 2); + addCMR(Counter::getCounter(0), "file1", 8, 10, 14, 6); + addCMR(Counter::getCounter(0), "file1", 8, 12, 14, 6); + addCMR(Counter::getCounter(1), "file1", 9, 1, 14, 6); + addCMR(Counter::getCounter(2), "file1", 11, 13, 11, 14); + + EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded()); + const auto FunctionRecords = LoadedCoverage->getCoveredFunctions(); + const auto &FunctionRecord = *FunctionRecords.begin(); + CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord); + std::vector<CoverageSegment> Segments(Data.begin(), Data.end()); + + ASSERT_EQ(7U, Segments.size()); + EXPECT_EQ(CoverageSegment(2, 1, 1, true), Segments[0]); + EXPECT_EQ(CoverageSegment(8, 10, 0, true), Segments[1]); + EXPECT_EQ(CoverageSegment(8, 12, 0, true), Segments[2]); + EXPECT_EQ(CoverageSegment(9, 1, 1, true), Segments[3]); + EXPECT_EQ(CoverageSegment(11, 13, 2, true), Segments[4]); + // Use count=1 (from 9:1 -> 14:6), not count=0 (from 8:12 -> 14:6). + EXPECT_EQ(CoverageSegment(11, 14, 1, false), Segments[5]); + EXPECT_EQ(CoverageSegment(18, 2, false), Segments[6]); +} + TEST_P(CoverageMappingTest, dont_emit_redundant_segments) { ProfileWriter.addRecord({"func1", 0x1234, {1, 1}}, Err); startFunction("func1", 0x1234);