llvm-project/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp

740 lines
28 KiB
C++
Raw Normal View History

//===- CoverageMappingReader.cpp - Code coverage mapping reader -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains support for reading coverage mapping data for
// instrumentation based coverage.
//
//===----------------------------------------------------------------------===//
#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
using namespace llvm;
using namespace coverage;
using namespace object;
#define DEBUG_TYPE "coverage-mapping"
void CoverageMappingIterator::increment() {
// Check if all the records were read or if an error occurred while reading
// the next record.
if (auto E = Reader->readNextRecord(Record)) {
handleAllErrors(std::move(E), [&](const CoverageMapError &CME) {
if (CME.get() == coveragemap_error::eof)
*this = CoverageMappingIterator();
else
llvm_unreachable("Unexpected error in coverage mapping iterator");
});
}
}
Error RawCoverageReader::readULEB128(uint64_t &Result) {
if (Data.size() < 1)
return make_error<CoverageMapError>(coveragemap_error::truncated);
unsigned N = 0;
Result = decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
if (N > Data.size())
return make_error<CoverageMapError>(coveragemap_error::malformed);
Data = Data.substr(N);
return Error::success();
}
Error RawCoverageReader::readIntMax(uint64_t &Result, uint64_t MaxPlus1) {
if (auto Err = readULEB128(Result))
return Err;
if (Result >= MaxPlus1)
return make_error<CoverageMapError>(coveragemap_error::malformed);
return Error::success();
}
Error RawCoverageReader::readSize(uint64_t &Result) {
if (auto Err = readULEB128(Result))
return Err;
// Sanity check the number.
if (Result > Data.size())
return make_error<CoverageMapError>(coveragemap_error::malformed);
return Error::success();
}
Error RawCoverageReader::readString(StringRef &Result) {
uint64_t Length;
if (auto Err = readSize(Length))
return Err;
Result = Data.substr(0, Length);
Data = Data.substr(Length);
return Error::success();
}
Error RawCoverageFilenamesReader::read() {
uint64_t NumFilenames;
if (auto Err = readSize(NumFilenames))
return Err;
for (size_t I = 0; I < NumFilenames; ++I) {
StringRef Filename;
if (auto Err = readString(Filename))
return Err;
Filenames.push_back(Filename);
}
return Error::success();
}
Error RawCoverageMappingReader::decodeCounter(unsigned Value, Counter &C) {
auto Tag = Value & Counter::EncodingTagMask;
switch (Tag) {
case Counter::Zero:
C = Counter::getZero();
return Error::success();
case Counter::CounterValueReference:
C = Counter::getCounter(Value >> Counter::EncodingTagBits);
return Error::success();
default:
break;
}
Tag -= Counter::Expression;
switch (Tag) {
case CounterExpression::Subtract:
case CounterExpression::Add: {
auto ID = Value >> Counter::EncodingTagBits;
if (ID >= Expressions.size())
return make_error<CoverageMapError>(coveragemap_error::malformed);
Expressions[ID].Kind = CounterExpression::ExprKind(Tag);
C = Counter::getExpression(ID);
break;
}
default:
return make_error<CoverageMapError>(coveragemap_error::malformed);
}
return Error::success();
}
Error RawCoverageMappingReader::readCounter(Counter &C) {
uint64_t EncodedCounter;
if (auto Err =
readIntMax(EncodedCounter, std::numeric_limits<unsigned>::max()))
return Err;
if (auto Err = decodeCounter(EncodedCounter, C))
return Err;
return Error::success();
}
static const unsigned EncodingExpansionRegionBit = 1
<< Counter::EncodingTagBits;
/// \brief Read the sub-array of regions for the given inferred file id.
/// \param NumFileIDs the number of file ids that are defined for this
/// function.
Error RawCoverageMappingReader::readMappingRegionsSubArray(
std::vector<CounterMappingRegion> &MappingRegions, unsigned InferredFileID,
size_t NumFileIDs) {
uint64_t NumRegions;
if (auto Err = readSize(NumRegions))
return Err;
unsigned LineStart = 0;
for (size_t I = 0; I < NumRegions; ++I) {
Counter C;
CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion;
// Read the combined counter + region kind.
uint64_t EncodedCounterAndRegion;
if (auto Err = readIntMax(EncodedCounterAndRegion,
std::numeric_limits<unsigned>::max()))
return Err;
unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask;
uint64_t ExpandedFileID = 0;
if (Tag != Counter::Zero) {
if (auto Err = decodeCounter(EncodedCounterAndRegion, C))
return Err;
} else {
// Is it an expansion region?
if (EncodedCounterAndRegion & EncodingExpansionRegionBit) {
Kind = CounterMappingRegion::ExpansionRegion;
ExpandedFileID = EncodedCounterAndRegion >>
Counter::EncodingCounterTagAndExpansionRegionTagBits;
if (ExpandedFileID >= NumFileIDs)
return make_error<CoverageMapError>(coveragemap_error::malformed);
} else {
switch (EncodedCounterAndRegion >>
Counter::EncodingCounterTagAndExpansionRegionTagBits) {
case CounterMappingRegion::CodeRegion:
// Don't do anything when we have a code region with a zero counter.
break;
case CounterMappingRegion::SkippedRegion:
Kind = CounterMappingRegion::SkippedRegion;
break;
default:
return make_error<CoverageMapError>(coveragemap_error::malformed);
}
}
}
// Read the source range.
uint64_t LineStartDelta, ColumnStart, NumLines, ColumnEnd;
if (auto Err =
readIntMax(LineStartDelta, std::numeric_limits<unsigned>::max()))
return Err;
if (auto Err = readULEB128(ColumnStart))
return Err;
if (ColumnStart > std::numeric_limits<unsigned>::max())
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (auto Err = readIntMax(NumLines, std::numeric_limits<unsigned>::max()))
return Err;
if (auto Err = readIntMax(ColumnEnd, std::numeric_limits<unsigned>::max()))
return Err;
LineStart += LineStartDelta;
// Adjust the column locations for the empty regions that are supposed to
// cover whole lines. Those regions should be encoded with the
// column range (1 -> std::numeric_limits<unsigned>::max()), but because
// the encoded std::numeric_limits<unsigned>::max() is several bytes long,
// we set the column range to (0 -> 0) to ensure that the column start and
// column end take up one byte each.
// The std::numeric_limits<unsigned>::max() is used to represent a column
// position at the end of the line without knowing the length of that line.
if (ColumnStart == 0 && ColumnEnd == 0) {
ColumnStart = 1;
ColumnEnd = std::numeric_limits<unsigned>::max();
}
DEBUG({
dbgs() << "Counter in file " << InferredFileID << " " << LineStart << ":"
<< ColumnStart << " -> " << (LineStart + NumLines) << ":"
<< ColumnEnd << ", ";
if (Kind == CounterMappingRegion::ExpansionRegion)
dbgs() << "Expands to file " << ExpandedFileID;
else
CounterMappingContext(Expressions).dump(C, dbgs());
dbgs() << "\n";
});
MappingRegions.push_back(CounterMappingRegion(
C, InferredFileID, ExpandedFileID, LineStart, ColumnStart,
LineStart + NumLines, ColumnEnd, Kind));
}
return Error::success();
}
Error RawCoverageMappingReader::read() {
// Read the virtual file mapping.
SmallVector<unsigned, 8> VirtualFileMapping;
uint64_t NumFileMappings;
if (auto Err = readSize(NumFileMappings))
return Err;
for (size_t I = 0; I < NumFileMappings; ++I) {
uint64_t FilenameIndex;
if (auto Err = readIntMax(FilenameIndex, TranslationUnitFilenames.size()))
return Err;
VirtualFileMapping.push_back(FilenameIndex);
}
// Construct the files using unique filenames and virtual file mapping.
for (auto I : VirtualFileMapping) {
Filenames.push_back(TranslationUnitFilenames[I]);
}
// Read the expressions.
uint64_t NumExpressions;
if (auto Err = readSize(NumExpressions))
return Err;
// Create an array of dummy expressions that get the proper counters
// when the expressions are read, and the proper kinds when the counters
// are decoded.
Expressions.resize(
NumExpressions,
CounterExpression(CounterExpression::Subtract, Counter(), Counter()));
for (size_t I = 0; I < NumExpressions; ++I) {
if (auto Err = readCounter(Expressions[I].LHS))
return Err;
if (auto Err = readCounter(Expressions[I].RHS))
return Err;
}
// Read the mapping regions sub-arrays.
for (unsigned InferredFileID = 0, S = VirtualFileMapping.size();
InferredFileID < S; ++InferredFileID) {
if (auto Err = readMappingRegionsSubArray(MappingRegions, InferredFileID,
VirtualFileMapping.size()))
return Err;
}
// Set the counters for the expansion regions.
// i.e. Counter of expansion region = counter of the first region
// from the expanded file.
// Perform multiple passes to correctly propagate the counters through
// all the nested expansion regions.
SmallVector<CounterMappingRegion *, 8> FileIDExpansionRegionMapping;
FileIDExpansionRegionMapping.resize(VirtualFileMapping.size(), nullptr);
for (unsigned Pass = 1, S = VirtualFileMapping.size(); Pass < S; ++Pass) {
for (auto &R : MappingRegions) {
if (R.Kind != CounterMappingRegion::ExpansionRegion)
continue;
assert(!FileIDExpansionRegionMapping[R.ExpandedFileID]);
FileIDExpansionRegionMapping[R.ExpandedFileID] = &R;
}
for (auto &R : MappingRegions) {
if (FileIDExpansionRegionMapping[R.FileID]) {
FileIDExpansionRegionMapping[R.FileID]->Count = R.Count;
FileIDExpansionRegionMapping[R.FileID] = nullptr;
}
}
}
return Error::success();
}
Expected<bool> RawCoverageMappingDummyChecker::isDummy() {
// A dummy coverage mapping data consists of just one region with zero count.
uint64_t NumFileMappings;
if (Error Err = readSize(NumFileMappings))
return std::move(Err);
if (NumFileMappings != 1)
return false;
// We don't expect any specific value for the filename index, just skip it.
uint64_t FilenameIndex;
if (Error Err =
readIntMax(FilenameIndex, std::numeric_limits<unsigned>::max()))
return std::move(Err);
uint64_t NumExpressions;
if (Error Err = readSize(NumExpressions))
return std::move(Err);
if (NumExpressions != 0)
return false;
uint64_t NumRegions;
if (Error Err = readSize(NumRegions))
return std::move(Err);
if (NumRegions != 1)
return false;
uint64_t EncodedCounterAndRegion;
if (Error Err = readIntMax(EncodedCounterAndRegion,
std::numeric_limits<unsigned>::max()))
return std::move(Err);
unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask;
return Tag == Counter::Zero;
}
Error InstrProfSymtab::create(SectionRef &Section) {
if (auto EC = Section.getContents(Data))
return errorCodeToError(EC);
Address = Section.getAddress();
return Error::success();
}
StringRef InstrProfSymtab::getFuncName(uint64_t Pointer, size_t Size) {
if (Pointer < Address)
return StringRef();
auto Offset = Pointer - Address;
if (Offset + Size > Data.size())
return StringRef();
return Data.substr(Pointer - Address, Size);
}
// Check if the mapping data is a dummy, i.e. is emitted for an unused function.
static Expected<bool> isCoverageMappingDummy(uint64_t Hash, StringRef Mapping) {
// The hash value of dummy mapping records is always zero.
if (Hash)
return false;
return RawCoverageMappingDummyChecker(Mapping).isDummy();
}
namespace {
struct CovMapFuncRecordReader {
virtual ~CovMapFuncRecordReader() = default;
// The interface to read coverage mapping function records for a module.
//
// \p Buf points to the buffer containing the \c CovHeader of the coverage
// mapping data associated with the module.
//
// Returns a pointer to the next \c CovHeader if it exists, or a pointer
// greater than \p End if not.
virtual Expected<const char *> readFunctionRecords(const char *Buf,
const char *End) = 0;
template <class IntPtrT, support::endianness Endian>
static Expected<std::unique_ptr<CovMapFuncRecordReader>>
get(CovMapVersion Version, InstrProfSymtab &P,
std::vector<BinaryCoverageReader::ProfileMappingRecord> &R,
std::vector<StringRef> &F);
};
// A class for reading coverage mapping function records for a module.
template <CovMapVersion Version, class IntPtrT, support::endianness Endian>
class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
typedef typename CovMapTraits<
Version, IntPtrT>::CovMapFuncRecordType FuncRecordType;
typedef typename CovMapTraits<Version, IntPtrT>::NameRefType NameRefType;
// Maps function's name references to the indexes of their records
// in \c Records.
DenseMap<NameRefType, size_t> FunctionRecords;
InstrProfSymtab &ProfileNames;
std::vector<StringRef> &Filenames;
std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records;
// Add the record to the collection if we don't already have a record that
// points to the same function name. This is useful to ignore the redundant
// records for the functions with ODR linkage.
// In addition, prefer records with real coverage mapping data to dummy
// records, which were emitted for inline functions which were seen but
// not used in the corresponding translation unit.
Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR,
StringRef Mapping, size_t FilenamesBegin) {
uint64_t FuncHash = CFR->template getFuncHash<Endian>();
NameRefType NameRef = CFR->template getFuncNameRef<Endian>();
auto InsertResult =
FunctionRecords.insert(std::make_pair(NameRef, Records.size()));
if (InsertResult.second) {
StringRef FuncName;
if (Error Err = CFR->template getFuncName<Endian>(ProfileNames, FuncName))
return Err;
Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin,
Filenames.size() - FilenamesBegin);
return Error::success();
}
// Update the existing record if it's a dummy and the new record is real.
size_t OldRecordIndex = InsertResult.first->second;
BinaryCoverageReader::ProfileMappingRecord &OldRecord =
Records[OldRecordIndex];
Expected<bool> OldIsDummyExpected = isCoverageMappingDummy(
OldRecord.FunctionHash, OldRecord.CoverageMapping);
if (Error Err = OldIsDummyExpected.takeError())
return Err;
if (!*OldIsDummyExpected)
return Error::success();
Expected<bool> NewIsDummyExpected =
isCoverageMappingDummy(FuncHash, Mapping);
if (Error Err = NewIsDummyExpected.takeError())
return Err;
if (*NewIsDummyExpected)
return Error::success();
OldRecord.FunctionHash = FuncHash;
OldRecord.CoverageMapping = Mapping;
OldRecord.FilenamesBegin = FilenamesBegin;
OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin;
return Error::success();
}
public:
VersionedCovMapFuncRecordReader(
InstrProfSymtab &P,
std::vector<BinaryCoverageReader::ProfileMappingRecord> &R,
std::vector<StringRef> &F)
: ProfileNames(P), Filenames(F), Records(R) {}
~VersionedCovMapFuncRecordReader() override = default;
Expected<const char *> readFunctionRecords(const char *Buf,
const char *End) override {
using namespace support;
if (Buf + sizeof(CovMapHeader) > End)
return make_error<CoverageMapError>(coveragemap_error::malformed);
auto CovHeader = reinterpret_cast<const CovMapHeader *>(Buf);
uint32_t NRecords = CovHeader->getNRecords<Endian>();
uint32_t FilenamesSize = CovHeader->getFilenamesSize<Endian>();
uint32_t CoverageSize = CovHeader->getCoverageSize<Endian>();
assert((CovMapVersion)CovHeader->getVersion<Endian>() == Version);
Buf = reinterpret_cast<const char *>(CovHeader + 1);
// Skip past the function records, saving the start and end for later.
const char *FunBuf = Buf;
Buf += NRecords * sizeof(FuncRecordType);
const char *FunEnd = Buf;
// Get the filenames.
if (Buf + FilenamesSize > End)
return make_error<CoverageMapError>(coveragemap_error::malformed);
size_t FilenamesBegin = Filenames.size();
RawCoverageFilenamesReader Reader(StringRef(Buf, FilenamesSize), Filenames);
if (auto Err = Reader.read())
return std::move(Err);
Buf += FilenamesSize;
// We'll read the coverage mapping records in the loop below.
const char *CovBuf = Buf;
Buf += CoverageSize;
const char *CovEnd = Buf;
if (Buf > End)
return make_error<CoverageMapError>(coveragemap_error::malformed);
// Each coverage map has an alignment of 8, so we need to adjust alignment
// before reading the next map.
Buf += alignmentAdjustment(Buf, 8);
auto CFR = reinterpret_cast<const FuncRecordType *>(FunBuf);
while ((const char *)CFR < FunEnd) {
// Read the function information
uint32_t DataSize = CFR->template getDataSize<Endian>();
// Now use that to read the coverage data.
if (CovBuf + DataSize > CovEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed);
auto Mapping = StringRef(CovBuf, DataSize);
CovBuf += DataSize;
if (Error Err =
insertFunctionRecordIfNeeded(CFR, Mapping, FilenamesBegin))
return std::move(Err);
CFR++;
}
return Buf;
}
};
} // end anonymous namespace
template <class IntPtrT, support::endianness Endian>
Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
CovMapVersion Version, InstrProfSymtab &P,
std::vector<BinaryCoverageReader::ProfileMappingRecord> &R,
std::vector<StringRef> &F) {
using namespace coverage;
switch (Version) {
case CovMapVersion::Version1:
return llvm::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F);
case CovMapVersion::Version2:
// Decompress the name data.
if (Error E = P.create(P.getNameData()))
return std::move(E);
return llvm::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F);
}
llvm_unreachable("Unsupported version");
}
template <typename T, support::endianness Endian>
static Error readCoverageMappingData(
InstrProfSymtab &ProfileNames, StringRef Data,
std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records,
std::vector<StringRef> &Filenames) {
using namespace coverage;
// Read the records in the coverage data section.
auto CovHeader =
reinterpret_cast<const CovMapHeader *>(Data.data());
CovMapVersion Version = (CovMapVersion)CovHeader->getVersion<Endian>();
if (Version > CovMapVersion::CurrentVersion)
return make_error<CoverageMapError>(coveragemap_error::unsupported_version);
Expected<std::unique_ptr<CovMapFuncRecordReader>> ReaderExpected =
CovMapFuncRecordReader::get<T, Endian>(Version, ProfileNames, Records,
Filenames);
if (Error E = ReaderExpected.takeError())
return E;
auto Reader = std::move(ReaderExpected.get());
for (const char *Buf = Data.data(), *End = Buf + Data.size(); Buf < End;) {
auto NextHeaderOrErr = Reader->readFunctionRecords(Buf, End);
if (auto E = NextHeaderOrErr.takeError())
return E;
Buf = NextHeaderOrErr.get();
}
return Error::success();
}
static const char *TestingFormatMagic = "llvmcovmtestdata";
static Error loadTestingFormat(StringRef Data, InstrProfSymtab &ProfileNames,
StringRef &CoverageMapping,
uint8_t &BytesInAddress,
support::endianness &Endian) {
BytesInAddress = 8;
Endian = support::endianness::little;
Data = Data.substr(StringRef(TestingFormatMagic).size());
if (Data.size() < 1)
return make_error<CoverageMapError>(coveragemap_error::truncated);
unsigned N = 0;
auto ProfileNamesSize =
decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
if (N > Data.size())
return make_error<CoverageMapError>(coveragemap_error::malformed);
Data = Data.substr(N);
if (Data.size() < 1)
return make_error<CoverageMapError>(coveragemap_error::truncated);
N = 0;
uint64_t Address =
decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
if (N > Data.size())
return make_error<CoverageMapError>(coveragemap_error::malformed);
Data = Data.substr(N);
if (Data.size() < ProfileNamesSize)
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Error E = ProfileNames.create(Data.substr(0, ProfileNamesSize), Address))
return E;
CoverageMapping = Data.substr(ProfileNamesSize);
// Skip the padding bytes because coverage map data has an alignment of 8.
if (CoverageMapping.size() < 1)
return make_error<CoverageMapError>(coveragemap_error::truncated);
size_t Pad = alignmentAdjustment(CoverageMapping.data(), 8);
if (CoverageMapping.size() < Pad)
return make_error<CoverageMapError>(coveragemap_error::malformed);
CoverageMapping = CoverageMapping.substr(Pad);
return Error::success();
}
static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) {
StringRef FoundName;
for (const auto &Section : OF.sections()) {
if (auto EC = Section.getName(FoundName))
return errorCodeToError(EC);
if (FoundName == Name)
return Section;
}
return make_error<CoverageMapError>(coveragemap_error::no_data_found);
}
static Error loadBinaryFormat(MemoryBufferRef ObjectBuffer,
InstrProfSymtab &ProfileNames,
StringRef &CoverageMapping,
uint8_t &BytesInAddress,
support::endianness &Endian, StringRef Arch) {
auto BinOrErr = createBinary(ObjectBuffer);
Thread Expected<...> up from createMachOObjectFile() to allow llvm-objdump to produce a real error message Produce the first specific error message for a malformed Mach-O file describing the problem instead of the generic message for object_error::parse_failed of "Invalid data was encountered while parsing the file”.  Many more good error messages will follow after this first one. This is built on Lang Hames’ great work of adding the ’Error' class for structured error handling and threading Error through MachOObjectFile construction. And making createMachOObjectFile return Expected<...> . So to to get the error to the llvm-obdump tool, I changed the stack of these methods to also return Expected<...> : object::ObjectFile::createObjectFile() object::SymbolicFile::createSymbolicFile() object::createBinary() Then finally in ParseInputMachO() in MachODump.cpp the error can be reported and the specific error message can be printed in llvm-objdump and can be seen in the existing test case for the existing malformed binary but with the updated error message. Converting these interfaces to Expected<> from ErrorOr<> does involve touching a number of places. To contain the changes for now use of errorToErrorCode() and errorOrToExpected() are used where the callers are yet to be converted. Also there some were bugs in the existing code that did not deal with the old ErrorOr<> return values. So now with Expected<> since they must be checked and the error handled, I added a TODO and a comment: “// TODO: Actually report errors helpfully” and a call something like consumeError(ObjOrErr.takeError()) so the buggy code will not crash since needed to deal with the Error. Note there is one fix also needed to lld/COFF/InputFiles.cpp that goes along with this that I will commit right after this. So expect lld not to built after this commit and before the next one. llvm-svn: 265606
2016-04-07 06:14:09 +08:00
if (!BinOrErr)
return BinOrErr.takeError();
auto Bin = std::move(BinOrErr.get());
std::unique_ptr<ObjectFile> OF;
if (auto *Universal = dyn_cast<MachOUniversalBinary>(Bin.get())) {
// If we have a universal binary, try to look up the object for the
// appropriate architecture.
auto ObjectFileOrErr = Universal->getObjectForArch(Arch);
if (!ObjectFileOrErr)
return ObjectFileOrErr.takeError();
OF = std::move(ObjectFileOrErr.get());
} else if (isa<ObjectFile>(Bin.get())) {
// For any other object file, upcast and take ownership.
OF.reset(cast<ObjectFile>(Bin.release()));
// If we've asked for a particular arch, make sure they match.
if (!Arch.empty() && OF->getArch() != Triple(Arch).getArch())
return errorCodeToError(object_error::arch_not_found);
} else
// We can only handle object files.
return make_error<CoverageMapError>(coveragemap_error::malformed);
// The coverage uses native pointer sizes for the object it's written in.
BytesInAddress = OF->getBytesInAddress();
Endian = OF->isLittleEndian() ? support::endianness::little
: support::endianness::big;
// Look for the sections that we are interested in.
auto ObjFormat = OF->getTripleObjectFormat();
auto NamesSection =
lookupSection(*OF, getInstrProfSectionName(IPSK_name, ObjFormat,
/*AddSegmentInfo=*/false));
if (auto E = NamesSection.takeError())
return E;
auto CoverageSection =
lookupSection(*OF, getInstrProfSectionName(IPSK_covmap, ObjFormat,
/*AddSegmentInfo=*/false));
if (auto E = CoverageSection.takeError())
return E;
// Get the contents of the given sections.
if (auto EC = CoverageSection->getContents(CoverageMapping))
return errorCodeToError(EC);
if (Error E = ProfileNames.create(*NamesSection))
return E;
return Error::success();
}
Expected<std::unique_ptr<BinaryCoverageReader>>
BinaryCoverageReader::create(std::unique_ptr<MemoryBuffer> &ObjectBuffer,
StringRef Arch) {
std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader());
StringRef Coverage;
uint8_t BytesInAddress;
support::endianness Endian;
Error E = Error::success();
consumeError(std::move(E));
if (ObjectBuffer->getBuffer().startswith(TestingFormatMagic))
// This is a special format used for testing.
E = loadTestingFormat(ObjectBuffer->getBuffer(), Reader->ProfileNames,
Coverage, BytesInAddress, Endian);
else
E = loadBinaryFormat(ObjectBuffer->getMemBufferRef(), Reader->ProfileNames,
Coverage, BytesInAddress, Endian, Arch);
if (E)
return std::move(E);
if (BytesInAddress == 4 && Endian == support::endianness::little)
E = readCoverageMappingData<uint32_t, support::endianness::little>(
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames);
else if (BytesInAddress == 4 && Endian == support::endianness::big)
E = readCoverageMappingData<uint32_t, support::endianness::big>(
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames);
else if (BytesInAddress == 8 && Endian == support::endianness::little)
E = readCoverageMappingData<uint64_t, support::endianness::little>(
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames);
else if (BytesInAddress == 8 && Endian == support::endianness::big)
E = readCoverageMappingData<uint64_t, support::endianness::big>(
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames);
else
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (E)
return std::move(E);
return std::move(Reader);
}
Error BinaryCoverageReader::readNextRecord(CoverageMappingRecord &Record) {
if (CurrentRecord >= MappingRecords.size())
return make_error<CoverageMapError>(coveragemap_error::eof);
FunctionsFilenames.clear();
Expressions.clear();
MappingRegions.clear();
auto &R = MappingRecords[CurrentRecord];
RawCoverageMappingReader Reader(
R.CoverageMapping,
makeArrayRef(Filenames).slice(R.FilenamesBegin, R.FilenamesSize),
FunctionsFilenames, Expressions, MappingRegions);
if (auto Err = Reader.read())
return Err;
Record.FunctionName = R.FunctionName;
Record.FunctionHash = R.FunctionHash;
Record.Filenames = FunctionsFilenames;
Record.Expressions = Expressions;
Record.MappingRegions = MappingRegions;
++CurrentRecord;
return Error::success();
}