From 20bdbbe5e9f5888094e894a30c6cd79fc585c833 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Fri, 14 Oct 2016 17:16:53 +0000 Subject: [PATCH] [Coverage] Support loading multiple binaries into a CoverageMapping Add support for loading multiple coverage readers into a single CoverageMapping instance. This should make it easier to prepare a unified coverage report for multiple binaries. Differential Revision: https://reviews.llvm.org/D25535 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@284251 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../ProfileData/Coverage/CoverageMapping.h | 12 ++++ lib/ProfileData/Coverage/CoverageMapping.cpp | 56 +++++++++++++------ unittests/ProfileData/CoverageMappingTest.cpp | 48 ++++++++++++++-- 3 files changed, 94 insertions(+), 22 deletions(-) diff --git a/include/llvm/ProfileData/Coverage/CoverageMapping.h b/include/llvm/ProfileData/Coverage/CoverageMapping.h index 02914cea7d4..0ec5ff67c0f 100644 --- a/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/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 @@ public: /// 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 @@ public: 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. diff --git a/lib/ProfileData/Coverage/CoverageMapping.cpp b/lib/ProfileData/Coverage/CoverageMapping.cpp index 0fa104f89e9..2d800a3592b 100644 --- a/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -186,6 +186,16 @@ void FunctionRecordIterator::skipOtherFiles() { 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 @@ Error CoverageMapping::loadFunctionRecord( 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 @@ CoverageMapping::load(CoverageMappingReader &CoverageReader, 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 { diff --git a/unittests/ProfileData/CoverageMappingTest.cpp b/unittests/ProfileData/CoverageMappingTest.cpp index 43a7d490be8..e6d7aa30dc4 100644 --- a/unittests/ProfileData/CoverageMappingTest.cpp +++ b/unittests/ProfileData/CoverageMappingTest.cpp @@ -16,6 +16,7 @@ #include "gtest/gtest.h" #include +#include using namespace llvm; using namespace coverage; @@ -118,7 +119,8 @@ struct InputFunctionCoverageData { InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete; }; -struct CoverageMappingTest : ::testing::TestWithParam { +struct CoverageMappingTest : ::testing::TestWithParam> { + bool UseMultipleReaders; StringMap Files; std::vector InputFunctions; std::vector OutputFunctions; @@ -129,7 +131,8 @@ struct CoverageMappingTest : ::testing::TestWithParam { std::unique_ptr LoadedCoverage; void SetUp() override { - ProfileWriter.setOutputSparse(GetParam()); + ProfileWriter.setOutputSparse(GetParam().first); + UseMultipleReaders = GetParam().second; } unsigned getGlobalFileIndex(StringRef Name) { @@ -215,12 +218,24 @@ struct CoverageMappingTest : ::testing::TestWithParam { 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()); } @@ -547,7 +562,28 @@ TEST_P(CoverageMappingTest, load_coverage_for_expanded_file) { EXPECT_EQ(CoverageSegment(1, 10, false), Segments[1]); } +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); +} + +// FIXME: Use ::testing::Combine() when llvm updates its copy of googletest. INSTANTIATE_TEST_CASE_P(ParameterizedCovMapTest, CoverageMappingTest, - ::testing::Bool()); + ::testing::Values(std::pair({false, false}), + std::pair({false, true}), + std::pair({true, false}), + std::pair({true, true}))); } // end anonymous namespace