Index: include/llvm/ProfileData/Coverage/CoverageMapping.h =================================================================== --- include/llvm/ProfileData/Coverage/CoverageMapping.h +++ include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -18,6 +18,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/iterator.h" #include "llvm/ProfileData/InstrProf.h" @@ -428,6 +429,7 @@ /// This is the main interface to get coverage information, using a profile to /// fill out execution counts. class CoverageMapping { + StringSet<> FunctionNames; std::vector Functions; unsigned MismatchedFunctionCount; @@ -446,9 +448,19 @@ load(CoverageMappingReader &CoverageReader, IndexedInstrProfReader &ProfileReader); + static Expected> + load(ArrayRef> CoverageReaders, + IndexedInstrProfReader &ProfileReader); + /// \brief Load the coverage mapping from the given files. static Expected> load(StringRef ObjectFilename, StringRef ProfileFilename, + StringRef Arch = StringRef()) { + return load(ArrayRef(ObjectFilename), ProfileFilename, Arch); + } + + static Expected> + load(ArrayRef ObjectFilenames, StringRef ProfileFilename, StringRef Arch = StringRef()); /// \brief The number of functions that couldn't have their profiles mapped. Index: lib/ProfileData/Coverage/CoverageMapping.cpp =================================================================== --- lib/ProfileData/Coverage/CoverageMapping.cpp +++ lib/ProfileData/Coverage/CoverageMapping.cpp @@ -186,6 +186,16 @@ Error CoverageMapping::loadFunctionRecord( const CoverageMappingRecord &Record, IndexedInstrProfReader &ProfileReader) { + StringRef OrigFuncName = Record.FunctionName; + if (Record.Filenames.empty()) + OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName); + else + OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName, Record.Filenames[0]); + + // Don't load records for functions we've already seen. + if (!FunctionNames.insert(OrigFuncName).second) + return Error::success(); + CounterMappingContext Ctx(Record.Expressions); std::vector Counts; @@ -203,11 +213,6 @@ assert(!Record.MappingRegions.empty() && "Function has no regions"); - StringRef OrigFuncName = Record.FunctionName; - if (Record.Filenames.empty()) - OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName); - else - OrigFuncName = getFuncNameWithoutPrefix(OrigFuncName, Record.Filenames[0]); FunctionRecord Function(OrigFuncName, Record.Filenames); for (const auto &Region : Record.MappingRegions) { Expected ExecutionCount = Ctx.evaluate(Region.Count); @@ -238,22 +243,41 @@ return std::move(Coverage); } +Expected> CoverageMapping::load( + ArrayRef> CoverageReaders, + IndexedInstrProfReader &ProfileReader) { + auto Coverage = std::unique_ptr(new CoverageMapping()); + + for (const auto &CoverageReader : CoverageReaders) + for (const auto &Record : *CoverageReader) + if (Error E = Coverage->loadFunctionRecord(Record, ProfileReader)) + return std::move(E); + + return std::move(Coverage); +} + Expected> -CoverageMapping::load(StringRef ObjectFilename, StringRef ProfileFilename, - StringRef Arch) { - auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename); - if (std::error_code EC = CounterMappingBuff.getError()) - return errorCodeToError(EC); - auto CoverageReaderOrErr = - BinaryCoverageReader::create(CounterMappingBuff.get(), Arch); - if (Error E = CoverageReaderOrErr.takeError()) - return std::move(E); - auto CoverageReader = std::move(CoverageReaderOrErr.get()); +CoverageMapping::load(ArrayRef ObjectFilenames, + StringRef ProfileFilename, StringRef Arch) { auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename); if (Error E = ProfileReaderOrErr.takeError()) return std::move(E); auto ProfileReader = std::move(ProfileReaderOrErr.get()); - return load(*CoverageReader, *ProfileReader); + + SmallVector, 4> Readers; + SmallVector, 4> Buffers; + for (StringRef ObjectFilename : ObjectFilenames) { + auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(ObjectFilename); + if (std::error_code EC = CovMappingBufOrErr.getError()) + return errorCodeToError(EC); + auto CoverageReaderOrErr = + BinaryCoverageReader::create(CovMappingBufOrErr.get(), Arch); + if (Error E = CoverageReaderOrErr.takeError()) + return std::move(E); + Readers.push_back(std::move(CoverageReaderOrErr.get())); + Buffers.push_back(std::move(CovMappingBufOrErr.get())); + } + return load(Readers, *ProfileReader); } namespace { Index: unittests/ProfileData/CoverageMappingTest.cpp =================================================================== --- unittests/ProfileData/CoverageMappingTest.cpp +++ unittests/ProfileData/CoverageMappingTest.cpp @@ -16,6 +16,7 @@ #include "gtest/gtest.h" #include +#include using namespace llvm; using namespace coverage; @@ -108,7 +109,8 @@ : Name(std::move(Name)), Hash(Hash) {} }; -struct CoverageMappingTest : ::testing::Test { +struct CoverageMappingTest : ::testing::TestWithParam> { + bool UseMultipleReaders; StringMap Files; std::vector InputFunctions; std::vector OutputFunctions; @@ -119,7 +121,8 @@ std::unique_ptr LoadedCoverage; void SetUp() override { - ProfileWriter.setOutputSparse(false); + ProfileWriter.setOutputSparse(GetParam().first); + UseMultipleReaders = GetParam().second; } unsigned getGlobalFileIndex(StringRef Name) { @@ -205,27 +208,31 @@ ProfileReader = std::move(ReaderOrErr.get()); } + Expected> readOutputFunctions() { + if (!UseMultipleReaders) { + CoverageMappingReaderMock CovReader(OutputFunctions); + return CoverageMapping::load(CovReader, *ProfileReader); + } + + std::vector> CoverageReaders; + for (const auto &OF : OutputFunctions) { + ArrayRef Funcs(OF); + CoverageReaders.push_back(make_unique(Funcs)); + + } + return CoverageMapping::load(CoverageReaders, *ProfileReader); + } + void loadCoverageMapping(bool EmitFilenames = true) { readProfCounts(); writeAndReadCoverageRegions(EmitFilenames); - - CoverageMappingReaderMock CovReader(OutputFunctions); - auto CoverageOrErr = CoverageMapping::load(CovReader, *ProfileReader); + auto CoverageOrErr = readOutputFunctions(); ASSERT_TRUE(NoError(CoverageOrErr.takeError())); LoadedCoverage = std::move(CoverageOrErr.get()); } }; -struct MaybeSparseCoverageMappingTest - : public CoverageMappingTest, - public ::testing::WithParamInterface { - void SetUp() { - CoverageMappingTest::SetUp(); - ProfileWriter.setOutputSparse(GetParam()); - } -}; - -TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) { +TEST_P(CoverageMappingTest, basic_write_read) { startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1); addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2); @@ -250,8 +257,7 @@ } } -TEST_P(MaybeSparseCoverageMappingTest, - correct_deserialize_for_more_than_two_files) { +TEST_P(CoverageMappingTest, correct_deserialize_for_more_than_two_files) { const char *FileNames[] = {"bar", "baz", "foo"}; static const unsigned N = array_lengthof(FileNames); @@ -276,7 +282,7 @@ } } -TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_more_than_two_files) { +TEST_P(CoverageMappingTest, load_coverage_for_more_than_two_files) { InstrProfRecord Record("func", 0x1234, {0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -298,7 +304,7 @@ } } -TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_several_functions) { +TEST_P(CoverageMappingTest, load_coverage_for_several_functions) { InstrProfRecord RecordFunc1("func1", 0x1234, {10}); NoError(ProfileWriter.addRecord(std::move(RecordFunc1))); InstrProfRecord RecordFunc2("func2", 0x2345, {20}); @@ -329,7 +335,7 @@ } } -TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) { +TEST_P(CoverageMappingTest, expansion_gets_first_counter) { startFunction("func", 0x1234); addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2); // This starts earlier in "foo", so the expansion should get its counter. @@ -345,7 +351,7 @@ ASSERT_EQ(3U, Output.Regions[2].LineStart); } -TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) { +TEST_P(CoverageMappingTest, basic_coverage_iteration) { InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -368,7 +374,7 @@ ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]); } -TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) { +TEST_P(CoverageMappingTest, uncovered_function) { startFunction("func", 0x1234); addCMR(Counter::getZero(), "file1", 1, 2, 3, 4); loadCoverageMapping(); @@ -380,7 +386,7 @@ ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]); } -TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) { +TEST_P(CoverageMappingTest, uncovered_function_with_mapping) { startFunction("func", 0x1234); addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7); @@ -394,7 +400,7 @@ ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]); } -TEST_P(MaybeSparseCoverageMappingTest, combine_regions) { +TEST_P(CoverageMappingTest, combine_regions) { InstrProfRecord Record("func", 0x1234, {10, 20, 30}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -413,8 +419,7 @@ ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]); } -TEST_P(MaybeSparseCoverageMappingTest, - restore_combined_counter_after_nested_region) { +TEST_P(CoverageMappingTest, restore_combined_counter_after_nested_region) { InstrProfRecord Record("func", 0x1234, {10, 20, 40}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -435,7 +440,7 @@ // If CodeRegions and ExpansionRegions cover the same area, // only counts of CodeRegions should be used. -TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) { +TEST_P(CoverageMappingTest, dont_combine_expansions) { InstrProfRecord Record1("func", 0x1234, {10, 20}); InstrProfRecord Record2("func", 0x1234, {0, 0}); NoError(ProfileWriter.addRecord(std::move(Record1))); @@ -458,7 +463,7 @@ } // If an area is covered only by ExpansionRegions, they should be combinated. -TEST_P(MaybeSparseCoverageMappingTest, combine_expansions) { +TEST_P(CoverageMappingTest, combine_expansions) { InstrProfRecord Record("func", 0x1234, {2, 3, 7}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -480,7 +485,7 @@ EXPECT_EQ(CoverageSegment(5, 5, false), Segments[3]); } -TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) { +TEST_P(CoverageMappingTest, strip_filename_prefix) { InstrProfRecord Record("file1:func", 0x1234, {0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -495,7 +500,7 @@ ASSERT_EQ("func", Names[0]); } -TEST_P(MaybeSparseCoverageMappingTest, strip_unknown_filename_prefix) { +TEST_P(CoverageMappingTest, strip_unknown_filename_prefix) { InstrProfRecord Record(":func", 0x1234, {0}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -510,7 +515,7 @@ ASSERT_EQ("func", Names[0]); } -TEST_P(MaybeSparseCoverageMappingTest, dont_detect_false_instantiations) { +TEST_P(CoverageMappingTest, dont_detect_false_instantiations) { InstrProfRecord Record1("foo", 0x1234, {10}); InstrProfRecord Record2("bar", 0x2345, {20}); NoError(ProfileWriter.addRecord(std::move(Record1))); @@ -531,7 +536,7 @@ ASSERT_TRUE(Instantiations.empty()); } -TEST_P(MaybeSparseCoverageMappingTest, load_coverage_for_expanded_file) { +TEST_P(CoverageMappingTest, load_coverage_for_expanded_file) { InstrProfRecord Record("func", 0x1234, {10}); NoError(ProfileWriter.addRecord(std::move(Record))); @@ -548,7 +553,27 @@ EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]); } -INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseCoverageMappingTest, - ::testing::Bool()); +TEST_P(CoverageMappingTest, skip_duplicate_function_record) { + InstrProfRecord Record("func", 0x1234, {1}); + NoError(ProfileWriter.addRecord(std::move(Record))); + + startFunction("func", 0x1234); + addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); + + startFunction("func", 0x1234); + addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); + + loadCoverageMapping(); + + auto Funcs = LoadedCoverage->getCoveredFunctions(); + unsigned NumFuncs = std::distance(Funcs.begin(), Funcs.end()); + ASSERT_EQ(1U, NumFuncs); +} + +INSTANTIATE_TEST_CASE_P(ParameterizedCovMapTest, CoverageMappingTest, + ::testing::Values(std::pair({false, false}), + std::pair({false, true}), + std::pair({true, false}), + std::pair({true, true}))); } // end anonymous namespace