mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-24 04:09:45 +00:00
[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:
parent
7c52e33346
commit
20bdbbe5e9
@ -18,6 +18,7 @@
|
|||||||
#include "llvm/ADT/ArrayRef.h"
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/Hashing.h"
|
#include "llvm/ADT/Hashing.h"
|
||||||
|
#include "llvm/ADT/StringSet.h"
|
||||||
#include "llvm/ADT/Triple.h"
|
#include "llvm/ADT/Triple.h"
|
||||||
#include "llvm/ADT/iterator.h"
|
#include "llvm/ADT/iterator.h"
|
||||||
#include "llvm/ProfileData/InstrProf.h"
|
#include "llvm/ProfileData/InstrProf.h"
|
||||||
@ -428,6 +429,7 @@ public:
|
|||||||
/// This is the main interface to get coverage information, using a profile to
|
/// This is the main interface to get coverage information, using a profile to
|
||||||
/// fill out execution counts.
|
/// fill out execution counts.
|
||||||
class CoverageMapping {
|
class CoverageMapping {
|
||||||
|
StringSet<> FunctionNames;
|
||||||
std::vector<FunctionRecord> Functions;
|
std::vector<FunctionRecord> Functions;
|
||||||
unsigned MismatchedFunctionCount;
|
unsigned MismatchedFunctionCount;
|
||||||
|
|
||||||
@ -446,9 +448,19 @@ public:
|
|||||||
load(CoverageMappingReader &CoverageReader,
|
load(CoverageMappingReader &CoverageReader,
|
||||||
IndexedInstrProfReader &ProfileReader);
|
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.
|
/// \brief Load the coverage mapping from the given files.
|
||||||
static Expected<std::unique_ptr<CoverageMapping>>
|
static Expected<std::unique_ptr<CoverageMapping>>
|
||||||
load(StringRef ObjectFilename, StringRef ProfileFilename,
|
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());
|
StringRef Arch = StringRef());
|
||||||
|
|
||||||
/// \brief The number of functions that couldn't have their profiles mapped.
|
/// \brief The number of functions that couldn't have their profiles mapped.
|
||||||
|
@ -186,6 +186,16 @@ void FunctionRecordIterator::skipOtherFiles() {
|
|||||||
Error CoverageMapping::loadFunctionRecord(
|
Error CoverageMapping::loadFunctionRecord(
|
||||||
const CoverageMappingRecord &Record,
|
const CoverageMappingRecord &Record,
|
||||||
IndexedInstrProfReader &ProfileReader) {
|
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);
|
CounterMappingContext Ctx(Record.Expressions);
|
||||||
|
|
||||||
std::vector<uint64_t> Counts;
|
std::vector<uint64_t> Counts;
|
||||||
@ -203,11 +213,6 @@ Error CoverageMapping::loadFunctionRecord(
|
|||||||
|
|
||||||
assert(!Record.MappingRegions.empty() && "Function has no regions");
|
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);
|
FunctionRecord Function(OrigFuncName, Record.Filenames);
|
||||||
for (const auto &Region : Record.MappingRegions) {
|
for (const auto &Region : Record.MappingRegions) {
|
||||||
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
|
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
|
||||||
@ -238,22 +243,41 @@ CoverageMapping::load(CoverageMappingReader &CoverageReader,
|
|||||||
return std::move(Coverage);
|
return std::move(Coverage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return std::move(Coverage);
|
||||||
|
}
|
||||||
|
|
||||||
Expected<std::unique_ptr<CoverageMapping>>
|
Expected<std::unique_ptr<CoverageMapping>>
|
||||||
CoverageMapping::load(StringRef ObjectFilename, StringRef ProfileFilename,
|
CoverageMapping::load(ArrayRef<StringRef> ObjectFilenames,
|
||||||
StringRef Arch) {
|
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());
|
|
||||||
auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename);
|
auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename);
|
||||||
if (Error E = ProfileReaderOrErr.takeError())
|
if (Error E = ProfileReaderOrErr.takeError())
|
||||||
return std::move(E);
|
return std::move(E);
|
||||||
auto ProfileReader = std::move(ProfileReaderOrErr.get());
|
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 {
|
namespace {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace coverage;
|
using namespace coverage;
|
||||||
@ -118,7 +119,8 @@ struct InputFunctionCoverageData {
|
|||||||
InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete;
|
InputFunctionCoverageData &operator=(InputFunctionCoverageData &&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CoverageMappingTest : ::testing::TestWithParam<bool> {
|
struct CoverageMappingTest : ::testing::TestWithParam<std::pair<bool, bool>> {
|
||||||
|
bool UseMultipleReaders;
|
||||||
StringMap<unsigned> Files;
|
StringMap<unsigned> Files;
|
||||||
std::vector<InputFunctionCoverageData> InputFunctions;
|
std::vector<InputFunctionCoverageData> InputFunctions;
|
||||||
std::vector<OutputFunctionCoverageData> OutputFunctions;
|
std::vector<OutputFunctionCoverageData> OutputFunctions;
|
||||||
@ -129,7 +131,8 @@ struct CoverageMappingTest : ::testing::TestWithParam<bool> {
|
|||||||
std::unique_ptr<CoverageMapping> LoadedCoverage;
|
std::unique_ptr<CoverageMapping> LoadedCoverage;
|
||||||
|
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
ProfileWriter.setOutputSparse(GetParam());
|
ProfileWriter.setOutputSparse(GetParam().first);
|
||||||
|
UseMultipleReaders = GetParam().second;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned getGlobalFileIndex(StringRef Name) {
|
unsigned getGlobalFileIndex(StringRef Name) {
|
||||||
@ -215,12 +218,24 @@ struct CoverageMappingTest : ::testing::TestWithParam<bool> {
|
|||||||
ProfileReader = std::move(ReaderOrErr.get());
|
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) {
|
void loadCoverageMapping(bool EmitFilenames = true) {
|
||||||
readProfCounts();
|
readProfCounts();
|
||||||
writeAndReadCoverageRegions(EmitFilenames);
|
writeAndReadCoverageRegions(EmitFilenames);
|
||||||
|
auto CoverageOrErr = readOutputFunctions();
|
||||||
CoverageMappingReaderMock CovReader(OutputFunctions);
|
|
||||||
auto CoverageOrErr = CoverageMapping::load(CovReader, *ProfileReader);
|
|
||||||
ASSERT_TRUE(NoError(CoverageOrErr.takeError()));
|
ASSERT_TRUE(NoError(CoverageOrErr.takeError()));
|
||||||
LoadedCoverage = std::move(CoverageOrErr.get());
|
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]);
|
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,
|
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
|
} // end anonymous namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user