mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-25 21:00:00 +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/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.
|
||||
|
@ -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(
|
||||
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>>
|
||||
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<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 {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user