[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
This commit is contained in:
Vedant Kumar 2016-10-14 17:16:53 +00:00
parent 7c52e33346
commit 20bdbbe5e9
3 changed files with 94 additions and 22 deletions

View File

@ -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<FunctionRecord> Functions;
unsigned MismatchedFunctionCount;
@ -446,9 +448,19 @@ public:
load(CoverageMappingReader &CoverageReader,
IndexedInstrProfReader &ProfileReader);
static Expected<std::unique_ptr<CoverageMapping>>
load(ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
IndexedInstrProfReader &ProfileReader);
/// \brief Load the coverage mapping from the given files.
static Expected<std::unique_ptr<CoverageMapping>>
load(StringRef ObjectFilename, StringRef ProfileFilename,
StringRef Arch = StringRef()) {
return load(ArrayRef<StringRef>(ObjectFilename), ProfileFilename, Arch);
}
static Expected<std::unique_ptr<CoverageMapping>>
load(ArrayRef<StringRef> ObjectFilenames, StringRef ProfileFilename,
StringRef Arch = StringRef());
/// \brief The number of functions that couldn't have their profiles mapped.

View File

@ -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<uint64_t> 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<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
@ -238,22 +243,41 @@ CoverageMapping::load(CoverageMappingReader &CoverageReader,
return std::move(Coverage);
}
Expected<std::unique_ptr<CoverageMapping>>
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())
Expected<std::unique_ptr<CoverageMapping>> CoverageMapping::load(
ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders,
IndexedInstrProfReader &ProfileReader) {
auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());
for (const auto &CoverageReader : CoverageReaders)
for (const auto &Record : *CoverageReader)
if (Error E = Coverage->loadFunctionRecord(Record, ProfileReader))
return std::move(E);
auto CoverageReader = std::move(CoverageReaderOrErr.get());
return std::move(Coverage);
}
Expected<std::unique_ptr<CoverageMapping>>
CoverageMapping::load(ArrayRef<StringRef> 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<std::unique_ptr<CoverageMappingReader>, 4> Readers;
SmallVector<std::unique_ptr<MemoryBuffer>, 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 {

View File

@ -16,6 +16,7 @@
#include "gtest/gtest.h"
#include <ostream>
#include <utility>
using namespace llvm;
using namespace coverage;
@ -118,7 +119,8 @@ struct InputFunctionCoverageData {
InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete;
};
struct CoverageMappingTest : ::testing::TestWithParam<bool> {
struct CoverageMappingTest : ::testing::TestWithParam<std::pair<bool, bool>> {
bool UseMultipleReaders;
StringMap<unsigned> Files;
std::vector<InputFunctionCoverageData> InputFunctions;
std::vector<OutputFunctionCoverageData> OutputFunctions;
@ -129,7 +131,8 @@ struct CoverageMappingTest : ::testing::TestWithParam<bool> {
std::unique_ptr<CoverageMapping> 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<bool> {
ProfileReader = std::move(ReaderOrErr.get());
}
Expected<std::unique_ptr<CoverageMapping>> readOutputFunctions() {
if (!UseMultipleReaders) {
CoverageMappingReaderMock CovReader(OutputFunctions);
return CoverageMapping::load(CovReader, *ProfileReader);
}
std::vector<std::unique_ptr<CoverageMappingReader>> CoverageReaders;
for (const auto &OF : OutputFunctions) {
ArrayRef<OutputFunctionCoverageData> Funcs(OF);
CoverageReaders.push_back(make_unique<CoverageMappingReaderMock>(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<bool, bool>({false, false}),
std::pair<bool, bool>({false, true}),
std::pair<bool, bool>({true, false}),
std::pair<bool, bool>({true, true})));
} // end anonymous namespace