diff options
author | Yuhao Gu <yhgu2000@outlook.com> | 2023-08-18 16:31:00 +0000 |
---|---|---|
committer | Gulfem Savrun Yeniceri <gulfem@google.com> | 2023-08-18 16:43:45 +0000 |
commit | 57cb2f6ffe63bbb1b753310aaa102614c00249ec (patch) | |
tree | 0eb23aea4ef3361cae5f538859a92da327d02e8c /llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp | |
parent | f631a10ac364ba39a396cb823ca1ca5eff4ad8b3 (diff) | |
download | llvm-57cb2f6ffe63bbb1b753310aaa102614c00249ec.zip llvm-57cb2f6ffe63bbb1b753310aaa102614c00249ec.tar.gz llvm-57cb2f6ffe63bbb1b753310aaa102614c00249ec.tar.bz2 |
Reland "[llvm-cov] Support multi-source object files for convert-for-testing"
`llvm-cov convert-for-testing` only functions properly when the input binary contains a single source file. When the binary has multiple source files, a `Malformed coverage data` error will occur when the generated .covmapping is read back. This is because the testing format lacks support for indicating the size of its file records, and current implementation just assumes there's only one record in it. This patch fixes this problem by introducing a new testing format version.
Changes to the code:
- Add a new format version. The version number is stored in the the last 8 bytes of the orignial magic number field to be backward-compatible.
- Output a LEB128 number before the file records section to indicate its size in the new version.
- Change the format parsing code correspondingly.
- Update the document to formalize the testing format.
- Additionally, fix the bug when converting COFF binaries.
Reviewed By: phosek, gulfem
Differential Revision: https://reviews.llvm.org/D156611
Diffstat (limited to 'llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp')
-rw-r--r-- | llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp | 93 |
1 files changed, 63 insertions, 30 deletions
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index 0573732..bee9c8d 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -822,8 +822,6 @@ static Error readCoverageMappingData( return Error::success(); } -static const char *TestingFormatMagic = "llvmcovmtestdata"; - Expected<std::unique_ptr<BinaryCoverageReader>> BinaryCoverageReader::createCoverageReaderFromBuffer( StringRef Coverage, FuncRecordsStorage &&FuncRecords, @@ -865,7 +863,16 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) { uint8_t BytesInAddress = 8; support::endianness Endian = support::endianness::little; - Data = Data.substr(StringRef(TestingFormatMagic).size()); + // Read the magic and version. + Data = Data.substr(sizeof(TestingFormatMagic)); + if (Data.size() < sizeof(uint64_t)) + return make_error<CoverageMapError>(coveragemap_error::malformed); + auto TestingVersion = + support::endian::byte_swap<uint64_t, support::endianness::little>( + *reinterpret_cast<const uint64_t *>(Data.data())); + Data = Data.substr(sizeof(uint64_t)); + + // Read the ProfileNames data. if (Data.empty()) return make_error<CoverageMapError>(coveragemap_error::truncated); unsigned N = 0; @@ -886,8 +893,23 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) { if (Error E = ProfileNames.create(Data.substr(0, ProfileNamesSize), Address)) return std::move(E); Data = Data.substr(ProfileNamesSize); + + // In Version2, the size of CoverageMapping is stored directly. + uint64_t CoverageMappingSize; + if (TestingVersion == uint64_t(TestingFormatVersion::Version2)) { + N = 0; + CoverageMappingSize = decodeULEB128(Data.bytes_begin(), &N); + if (N > Data.size()) + return make_error<CoverageMapError>(coveragemap_error::malformed); + Data = Data.substr(N); + if (CoverageMappingSize < sizeof(CovMapHeader)) + return make_error<CoverageMapError>(coveragemap_error::malformed); + } else if (TestingVersion != uint64_t(TestingFormatVersion::Version1)) { + return make_error<CoverageMapError>(coveragemap_error::unsupported_version); + } + // Skip the padding bytes because coverage map data has an alignment of 8. - size_t Pad = offsetToAlignedAddr(Data.data(), Align(8)); + auto Pad = offsetToAlignedAddr(Data.data(), Align(8)); if (Data.size() < Pad) return make_error<CoverageMapError>(coveragemap_error::malformed); Data = Data.substr(Pad); @@ -895,32 +917,38 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) { return make_error<CoverageMapError>(coveragemap_error::malformed); auto const *CovHeader = reinterpret_cast<const CovMapHeader *>( Data.substr(0, sizeof(CovMapHeader)).data()); - CovMapVersion Version = - (CovMapVersion)CovHeader->getVersion<support::endianness::little>(); - StringRef CoverageMapping; - BinaryCoverageReader::FuncRecordsStorage CoverageRecords; + auto Version = + CovMapVersion(CovHeader->getVersion<support::endianness::little>()); + + // In Version1, the size of CoverageMapping is calculated. + if (TestingVersion == uint64_t(TestingFormatVersion::Version1)) { + if (Version < CovMapVersion::Version4) { + CoverageMappingSize = Data.size(); + } else { + auto FilenamesSize = + CovHeader->getFilenamesSize<support::endianness::little>(); + CoverageMappingSize = sizeof(CovMapHeader) + FilenamesSize; + } + } + + auto CoverageMapping = Data.substr(0, CoverageMappingSize); + Data = Data.substr(CoverageMappingSize); + + // Read the CoverageRecords data. if (Version < CovMapVersion::Version4) { - CoverageMapping = Data; - if (CoverageMapping.empty()) - return make_error<CoverageMapError>(coveragemap_error::truncated); - CoverageRecords = MemoryBuffer::getMemBuffer(""); + if (!Data.empty()) + return make_error<CoverageMapError>(coveragemap_error::malformed); } else { - uint32_t FilenamesSize = - CovHeader->getFilenamesSize<support::endianness::little>(); - uint32_t CoverageMappingSize = sizeof(CovMapHeader) + FilenamesSize; - CoverageMapping = Data.substr(0, CoverageMappingSize); - if (CoverageMapping.empty()) - return make_error<CoverageMapError>(coveragemap_error::truncated); - Data = Data.substr(CoverageMappingSize); // Skip the padding bytes because coverage records data has an alignment // of 8. Pad = offsetToAlignedAddr(Data.data(), Align(8)); if (Data.size() < Pad) return make_error<CoverageMapError>(coveragemap_error::malformed); - CoverageRecords = MemoryBuffer::getMemBuffer(Data.substr(Pad)); - if (CoverageRecords->getBufferSize() == 0) - return make_error<CoverageMapError>(coveragemap_error::truncated); + Data = Data.substr(Pad); } + BinaryCoverageReader::FuncRecordsStorage CoverageRecords = + MemoryBuffer::getMemBuffer(Data); + return BinaryCoverageReader::createCoverageReaderFromBuffer( CoverageMapping, std::move(CoverageRecords), std::move(ProfileNames), BytesInAddress, Endian, CompilationDir); @@ -1081,14 +1109,19 @@ BinaryCoverageReader::create( StringRef CompilationDir, SmallVectorImpl<object::BuildIDRef> *BinaryIDs) { std::vector<std::unique_ptr<BinaryCoverageReader>> Readers; - if (ObjectBuffer.getBuffer().startswith(TestingFormatMagic)) { - // This is a special format used for testing. - auto ReaderOrErr = - loadTestingFormat(ObjectBuffer.getBuffer(), CompilationDir); - if (!ReaderOrErr) - return ReaderOrErr.takeError(); - Readers.push_back(std::move(ReaderOrErr.get())); - return std::move(Readers); + if (ObjectBuffer.getBuffer().size() > sizeof(TestingFormatMagic)) { + uint64_t Magic = + support::endian::byte_swap<uint64_t, support::endianness::little>( + *reinterpret_cast<const uint64_t *>(ObjectBuffer.getBufferStart())); + if (Magic == TestingFormatMagic) { + // This is a special format used for testing. + auto ReaderOrErr = + loadTestingFormat(ObjectBuffer.getBuffer(), CompilationDir); + if (!ReaderOrErr) + return ReaderOrErr.takeError(); + Readers.push_back(std::move(ReaderOrErr.get())); + return std::move(Readers); + } } auto BinOrErr = createBinary(ObjectBuffer); |