From 57cb2f6ffe63bbb1b753310aaa102614c00249ec Mon Sep 17 00:00:00 2001 From: Yuhao Gu Date: Fri, 18 Aug 2023 16:31:00 +0000 Subject: 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 --- .../ProfileData/Coverage/CoverageMappingReader.cpp | 93 +++++++++++++++------- 1 file changed, 63 insertions(+), 30 deletions(-) (limited to 'llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp') 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> 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(coveragemap_error::malformed); + auto TestingVersion = + support::endian::byte_swap( + *reinterpret_cast(Data.data())); + Data = Data.substr(sizeof(uint64_t)); + + // Read the ProfileNames data. if (Data.empty()) return make_error(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(coveragemap_error::malformed); + Data = Data.substr(N); + if (CoverageMappingSize < sizeof(CovMapHeader)) + return make_error(coveragemap_error::malformed); + } else if (TestingVersion != uint64_t(TestingFormatVersion::Version1)) { + return make_error(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(coveragemap_error::malformed); Data = Data.substr(Pad); @@ -895,32 +917,38 @@ loadTestingFormat(StringRef Data, StringRef CompilationDir) { return make_error(coveragemap_error::malformed); auto const *CovHeader = reinterpret_cast( Data.substr(0, sizeof(CovMapHeader)).data()); - CovMapVersion Version = - (CovMapVersion)CovHeader->getVersion(); - StringRef CoverageMapping; - BinaryCoverageReader::FuncRecordsStorage CoverageRecords; + auto Version = + CovMapVersion(CovHeader->getVersion()); + + // 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(); + 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(coveragemap_error::truncated); - CoverageRecords = MemoryBuffer::getMemBuffer(""); + if (!Data.empty()) + return make_error(coveragemap_error::malformed); } else { - uint32_t FilenamesSize = - CovHeader->getFilenamesSize(); - uint32_t CoverageMappingSize = sizeof(CovMapHeader) + FilenamesSize; - CoverageMapping = Data.substr(0, CoverageMappingSize); - if (CoverageMapping.empty()) - return make_error(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(coveragemap_error::malformed); - CoverageRecords = MemoryBuffer::getMemBuffer(Data.substr(Pad)); - if (CoverageRecords->getBufferSize() == 0) - return make_error(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 *BinaryIDs) { std::vector> 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( + *reinterpret_cast(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); -- cgit v1.1