diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
commit | e6d1592492a3a379186bfb02bd0f4eda0669c0d5 (patch) | |
tree | 599ab169a01f1c86eda9adc774edaedde2f2db5b /lib/ProfileData/Coverage/CoverageMappingReader.cpp | |
parent | 1a56a5ead7a2e84bee8240f5f6b033b5f1707154 (diff) |
Notes
Diffstat (limited to 'lib/ProfileData/Coverage/CoverageMappingReader.cpp')
-rw-r--r-- | lib/ProfileData/Coverage/CoverageMappingReader.cpp | 235 |
1 files changed, 158 insertions, 77 deletions
diff --git a/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/lib/ProfileData/Coverage/CoverageMappingReader.cpp index ee48256bc2e5..e193e10f91d9 100644 --- a/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -1,9 +1,8 @@ //===- CoverageMappingReader.cpp - Code coverage mapping reader -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #include "llvm/Object/Error.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/COFF.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" @@ -59,7 +59,7 @@ Error RawCoverageReader::readULEB128(uint64_t &Result) { if (Data.empty()) return make_error<CoverageMapError>(coveragemap_error::truncated); unsigned N = 0; - Result = decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N); + Result = decodeULEB128(Data.bytes_begin(), &N); if (N > Data.size()) return make_error<CoverageMapError>(coveragemap_error::malformed); Data = Data.substr(N); @@ -348,9 +348,18 @@ Expected<bool> RawCoverageMappingDummyChecker::isDummy() { } Error InstrProfSymtab::create(SectionRef &Section) { - if (auto EC = Section.getContents(Data)) - return errorCodeToError(EC); + Expected<StringRef> DataOrErr = Section.getContents(); + if (!DataOrErr) + return DataOrErr.takeError(); + Data = *DataOrErr; Address = Section.getAddress(); + + // If this is a linked PE/COFF file, then we have to skip over the null byte + // that is allocated in the .lprfn$A section in the LLVM profiling runtime. + const ObjectFile *Obj = Section.getObject(); + if (isa<COFFObjectFile>(Obj) && !Obj->isRelocatableObject()) + Data = Data.drop_front(1); + return Error::success(); } @@ -577,35 +586,65 @@ static Error readCoverageMappingData( 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; +Expected<std::unique_ptr<BinaryCoverageReader>> +BinaryCoverageReader::createCoverageReaderFromBuffer( + StringRef Coverage, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, + support::endianness Endian) { + std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader()); + Reader->ProfileNames = std::move(ProfileNames); + if (BytesInAddress == 4 && Endian == support::endianness::little) { + if (Error E = + readCoverageMappingData<uint32_t, support::endianness::little>( + Reader->ProfileNames, Coverage, Reader->MappingRecords, + Reader->Filenames)) + return std::move(E); + } else if (BytesInAddress == 4 && Endian == support::endianness::big) { + if (Error E = readCoverageMappingData<uint32_t, support::endianness::big>( + Reader->ProfileNames, Coverage, Reader->MappingRecords, + Reader->Filenames)) + return std::move(E); + } else if (BytesInAddress == 8 && Endian == support::endianness::little) { + if (Error E = + readCoverageMappingData<uint64_t, support::endianness::little>( + Reader->ProfileNames, Coverage, Reader->MappingRecords, + Reader->Filenames)) + return std::move(E); + } else if (BytesInAddress == 8 && Endian == support::endianness::big) { + if (Error E = readCoverageMappingData<uint64_t, support::endianness::big>( + Reader->ProfileNames, Coverage, Reader->MappingRecords, + Reader->Filenames)) + return std::move(E); + } else + return make_error<CoverageMapError>(coveragemap_error::malformed); + return std::move(Reader); +} + +static Expected<std::unique_ptr<BinaryCoverageReader>> +loadTestingFormat(StringRef Data) { + uint8_t BytesInAddress = 8; + support::endianness Endian = support::endianness::little; Data = Data.substr(StringRef(TestingFormatMagic).size()); if (Data.empty()) return make_error<CoverageMapError>(coveragemap_error::truncated); unsigned N = 0; - auto ProfileNamesSize = - decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N); + uint64_t ProfileNamesSize = decodeULEB128(Data.bytes_begin(), &N); if (N > Data.size()) return make_error<CoverageMapError>(coveragemap_error::malformed); Data = Data.substr(N); if (Data.empty()) return make_error<CoverageMapError>(coveragemap_error::truncated); N = 0; - uint64_t Address = - decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N); + uint64_t Address = decodeULEB128(Data.bytes_begin(), &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); + InstrProfSymtab ProfileNames; if (Error E = ProfileNames.create(Data.substr(0, ProfileNamesSize), Address)) - return E; - CoverageMapping = Data.substr(ProfileNamesSize); + return std::move(E); + StringRef CoverageMapping = Data.substr(ProfileNamesSize); // Skip the padding bytes because coverage map data has an alignment of 8. if (CoverageMapping.empty()) return make_error<CoverageMapError>(coveragemap_error::truncated); @@ -613,29 +652,32 @@ static Error loadTestingFormat(StringRef Data, InstrProfSymtab &ProfileNames, if (CoverageMapping.size() < Pad) return make_error<CoverageMapError>(coveragemap_error::malformed); CoverageMapping = CoverageMapping.substr(Pad); - return Error::success(); + return BinaryCoverageReader::createCoverageReaderFromBuffer( + CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian); } static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) { + // On COFF, the object file section name may end in "$M". This tells the + // linker to sort these sections between "$A" and "$Z". The linker removes the + // dollar and everything after it in the final binary. Do the same to match. + bool IsCOFF = isa<COFFObjectFile>(OF); + auto stripSuffix = [IsCOFF](StringRef N) { + return IsCOFF ? N.split('$').first : N; + }; + Name = stripSuffix(Name); + StringRef FoundName; for (const auto &Section : OF.sections()) { if (auto EC = Section.getName(FoundName)) return errorCodeToError(EC); - if (FoundName == Name) + if (stripSuffix(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); - if (!BinOrErr) - return BinOrErr.takeError(); - auto Bin = std::move(BinOrErr.get()); +static Expected<std::unique_ptr<BinaryCoverageReader>> +loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch) { 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 @@ -655,9 +697,10 @@ static Error loadBinaryFormat(MemoryBufferRef ObjectBuffer, 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; + uint8_t BytesInAddress = OF->getBytesInAddress(); + support::endianness Endian = OF->isLittleEndian() + ? support::endianness::little + : support::endianness::big; // Look for the sections that we are interested in. auto ObjFormat = OF->getTripleObjectFormat(); @@ -665,63 +708,101 @@ static Error loadBinaryFormat(MemoryBufferRef ObjectBuffer, lookupSection(*OF, getInstrProfSectionName(IPSK_name, ObjFormat, /*AddSegmentInfo=*/false)); if (auto E = NamesSection.takeError()) - return E; + return std::move(E); auto CoverageSection = lookupSection(*OF, getInstrProfSectionName(IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)); if (auto E = CoverageSection.takeError()) - return E; + return std::move(E); // Get the contents of the given sections. - if (auto EC = CoverageSection->getContents(CoverageMapping)) - return errorCodeToError(EC); + auto CoverageMappingOrErr = CoverageSection->getContents(); + if (!CoverageMappingOrErr) + return CoverageMappingOrErr.takeError(); + + InstrProfSymtab ProfileNames; if (Error E = ProfileNames.create(*NamesSection)) - return E; + return std::move(E); - return Error::success(); + return BinaryCoverageReader::createCoverageReaderFromBuffer( + CoverageMappingOrErr.get(), std::move(ProfileNames), BytesInAddress, + Endian); } -Expected<std::unique_ptr<BinaryCoverageReader>> -BinaryCoverageReader::create(std::unique_ptr<MemoryBuffer> &ObjectBuffer, - StringRef Arch) { - std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader()); +Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>> +BinaryCoverageReader::create( + MemoryBufferRef ObjectBuffer, StringRef Arch, + SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers) { + std::vector<std::unique_ptr<BinaryCoverageReader>> Readers; - StringRef Coverage; - uint8_t BytesInAddress; - support::endianness Endian; - Error E = Error::success(); - consumeError(std::move(E)); - if (ObjectBuffer->getBuffer().startswith(TestingFormatMagic)) + 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); + auto ReaderOrErr = loadTestingFormat(ObjectBuffer.getBuffer()); + if (!ReaderOrErr) + return ReaderOrErr.takeError(); + Readers.push_back(std::move(ReaderOrErr.get())); + return std::move(Readers); + } - 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); + auto BinOrErr = createBinary(ObjectBuffer); + if (!BinOrErr) + return BinOrErr.takeError(); + std::unique_ptr<Binary> Bin = std::move(BinOrErr.get()); + + // MachO universal binaries which contain archives need to be treated as + // archives, not as regular binaries. + if (auto *Universal = dyn_cast<MachOUniversalBinary>(Bin.get())) { + for (auto &ObjForArch : Universal->objects()) { + // Skip slices within the universal binary which target the wrong arch. + std::string ObjArch = ObjForArch.getArchFlagName(); + if (Arch != ObjArch) + continue; + + auto ArchiveOrErr = ObjForArch.getAsArchive(); + if (!ArchiveOrErr) { + // If this is not an archive, try treating it as a regular object. + consumeError(ArchiveOrErr.takeError()); + break; + } + + return BinaryCoverageReader::create( + ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers); + } + } + + // Load coverage out of archive members. + if (auto *Ar = dyn_cast<Archive>(Bin.get())) { + Error Err = Error::success(); + for (auto &Child : Ar->children(Err)) { + Expected<MemoryBufferRef> ChildBufOrErr = Child.getMemoryBufferRef(); + if (!ChildBufOrErr) + return ChildBufOrErr.takeError(); + + auto ChildReadersOrErr = BinaryCoverageReader::create( + ChildBufOrErr.get(), Arch, ObjectFileBuffers); + if (!ChildReadersOrErr) + return ChildReadersOrErr.takeError(); + for (auto &Reader : ChildReadersOrErr.get()) + Readers.push_back(std::move(Reader)); + } + if (Err) + return std::move(Err); + + // Thin archives reference object files outside of the archive file, i.e. + // files which reside in memory not owned by the caller. Transfer ownership + // to the caller. + if (Ar->isThin()) + for (auto &Buffer : Ar->takeThinBuffers()) + ObjectFileBuffers.push_back(std::move(Buffer)); + + return std::move(Readers); + } + + auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch); + if (!ReaderOrErr) + return ReaderOrErr.takeError(); + Readers.push_back(std::move(ReaderOrErr.get())); + return std::move(Readers); } Error BinaryCoverageReader::readNextRecord(CoverageMappingRecord &Record) { |