diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -603,7 +603,7 @@ /// Ignores non-instrumented object files unless all are not instrumented. static Expected> load(ArrayRef ObjectFilenames, StringRef ProfileFilename, - ArrayRef Arches = None); + ArrayRef Arches = None, StringRef CompilationDir = ""); /// The number of functions that couldn't have their profiles mapped. /// diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h @@ -202,13 +202,15 @@ static Expected>> create(MemoryBufferRef ObjectBuffer, StringRef Arch, - SmallVectorImpl> &ObjectFileBuffers); + SmallVectorImpl> &ObjectFileBuffers, + StringRef CompilationDir = ""); static Expected> createCoverageReaderFromBuffer(StringRef Coverage, std::string &&FuncRecords, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, - support::endianness Endian); + support::endianness Endian, + StringRef CompilationDir = ""); Error readNextRecord(CoverageMappingRecord &Record) override; }; @@ -216,14 +218,17 @@ /// Reader for the raw coverage filenames. class RawCoverageFilenamesReader : public RawCoverageReader { std::vector &Filenames; + StringRef CompilationDir; // Read an uncompressed sequence of filenames. Error readUncompressed(CovMapVersion Version, uint64_t NumFilenames); public: RawCoverageFilenamesReader(StringRef Data, - std::vector &Filenames) - : RawCoverageReader(Data), Filenames(Filenames) {} + std::vector &Filenames, + StringRef CompilationDir = "") + : RawCoverageReader(Data), Filenames(Filenames), + CompilationDir(CompilationDir) {} RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete; RawCoverageFilenamesReader & operator=(const RawCoverageFilenamesReader &) = delete; diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -319,7 +319,8 @@ Expected> CoverageMapping::load(ArrayRef ObjectFilenames, - StringRef ProfileFilename, ArrayRef Arches) { + StringRef ProfileFilename, ArrayRef Arches, + StringRef CompilationDir) { auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename); if (Error E = ProfileReaderOrErr.takeError()) return std::move(E); @@ -336,8 +337,8 @@ MemoryBufferRef CovMappingBufRef = CovMappingBufOrErr.get()->getMemBufferRef(); SmallVector, 4> Buffers; - auto CoverageReadersOrErr = - BinaryCoverageReader::create(CovMappingBufRef, Arch, Buffers); + auto CoverageReadersOrErr = BinaryCoverageReader::create( + CovMappingBufRef, Arch, Buffers, CompilationDir); if (Error E = CoverageReadersOrErr.takeError()) { E = handleMaybeNoDataFoundError(std::move(E)); if (E) diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -138,7 +138,8 @@ } StringRef UncompressedFilenames(StorageBuf.data(), StorageBuf.size()); - RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames); + RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames, + CompilationDir); return Delegate.readUncompressed(Version, NumFilenames); } @@ -168,7 +169,11 @@ if (sys::path::is_absolute(Filename)) { Filenames.push_back(Filename.str()); } else { - SmallString<256> P(CWD); + SmallString<256> P; + if (!CompilationDir.empty()) + P.assign(CompilationDir); + else + P.assign(CWD); llvm::sys::path::append(P, Filename); Filenames.push_back(static_cast(P)); } @@ -520,7 +525,7 @@ template static Expected> get(CovMapVersion Version, InstrProfSymtab &P, - std::vector &R, + std::vector &R, StringRef D, std::vector &F); }; @@ -535,6 +540,7 @@ // in \c Records. DenseMap FunctionRecords; InstrProfSymtab &ProfileNames; + StringRef CompilationDir; std::vector &Filenames; std::vector &Records; @@ -594,9 +600,9 @@ public: VersionedCovMapFuncRecordReader( InstrProfSymtab &P, - std::vector &R, + std::vector &R, StringRef D, std::vector &F) - : ProfileNames(P), Filenames(F), Records(R) {} + : ProfileNames(P), CompilationDir(D), Filenames(F), Records(R) {} ~VersionedCovMapFuncRecordReader() override = default; @@ -629,7 +635,8 @@ return make_error(coveragemap_error::malformed); size_t FilenamesBegin = Filenames.size(); StringRef FilenameRegion(CovBuf, FilenamesSize); - RawCoverageFilenamesReader Reader(FilenameRegion, Filenames); + RawCoverageFilenamesReader Reader(FilenameRegion, Filenames, + CompilationDir); if (auto Err = Reader.read(Version)) return std::move(Err); CovBuf += FilenamesSize; @@ -735,14 +742,14 @@ template Expected> CovMapFuncRecordReader::get( CovMapVersion Version, InstrProfSymtab &P, - std::vector &R, + std::vector &R, StringRef D, std::vector &F) { using namespace coverage; switch (Version) { case CovMapVersion::Version1: return std::make_unique>(P, R, F); + CovMapVersion::Version1, IntPtrT, Endian>>(P, R, D, F); case CovMapVersion::Version2: case CovMapVersion::Version3: case CovMapVersion::Version4: @@ -753,19 +760,19 @@ return std::move(E); if (Version == CovMapVersion::Version2) return std::make_unique>(P, R, F); + CovMapVersion::Version2, IntPtrT, Endian>>(P, R, D, F); else if (Version == CovMapVersion::Version3) return std::make_unique>(P, R, F); + CovMapVersion::Version3, IntPtrT, Endian>>(P, R, D, F); else if (Version == CovMapVersion::Version4) return std::make_unique>(P, R, F); + CovMapVersion::Version4, IntPtrT, Endian>>(P, R, D, F); else if (Version == CovMapVersion::Version5) return std::make_unique>(P, R, F); + CovMapVersion::Version5, IntPtrT, Endian>>(P, R, D, F); else if (Version == CovMapVersion::Version6) return std::make_unique>(P, R, F); + CovMapVersion::Version6, IntPtrT, Endian>>(P, R, D, F); } llvm_unreachable("Unsupported version"); } @@ -774,7 +781,7 @@ static Error readCoverageMappingData( InstrProfSymtab &ProfileNames, StringRef CovMap, StringRef FuncRecords, std::vector &Records, - std::vector &Filenames) { + StringRef CompilationDir, std::vector &Filenames) { using namespace coverage; // Read the records in the coverage data section. @@ -785,7 +792,7 @@ return make_error(coveragemap_error::unsupported_version); Expected> ReaderExpected = CovMapFuncRecordReader::get(Version, ProfileNames, Records, - Filenames); + CompilationDir, Filenames); if (Error E = ReaderExpected.takeError()) return E; auto Reader = std::move(ReaderExpected.get()); @@ -817,8 +824,9 @@ Expected> BinaryCoverageReader::createCoverageReaderFromBuffer( - StringRef Coverage, std::string &&FuncRecords, InstrProfSymtab &&ProfileNames, - uint8_t BytesInAddress, support::endianness Endian) { + StringRef Coverage, std::string &&FuncRecords, + InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, + support::endianness Endian, StringRef CompilationDir) { std::unique_ptr Reader( new BinaryCoverageReader(std::move(FuncRecords))); Reader->ProfileNames = std::move(ProfileNames); @@ -827,23 +835,23 @@ if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, FuncRecordsRef, - Reader->MappingRecords, Reader->Filenames)) + Reader->MappingRecords, CompilationDir, Reader->Filenames)) return std::move(E); } else if (BytesInAddress == 4 && Endian == support::endianness::big) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, FuncRecordsRef, - Reader->MappingRecords, Reader->Filenames)) + Reader->MappingRecords, CompilationDir, Reader->Filenames)) return std::move(E); } else if (BytesInAddress == 8 && Endian == support::endianness::little) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, FuncRecordsRef, - Reader->MappingRecords, Reader->Filenames)) + Reader->MappingRecords, CompilationDir, Reader->Filenames)) return std::move(E); } else if (BytesInAddress == 8 && Endian == support::endianness::big) { if (Error E = readCoverageMappingData( Reader->ProfileNames, Coverage, FuncRecordsRef, - Reader->MappingRecords, Reader->Filenames)) + Reader->MappingRecords, CompilationDir, Reader->Filenames)) return std::move(E); } else return make_error(coveragemap_error::malformed); @@ -851,7 +859,7 @@ } static Expected> -loadTestingFormat(StringRef Data) { +loadTestingFormat(StringRef Data, StringRef CompilationDir) { uint8_t BytesInAddress = 8; support::endianness Endian = support::endianness::little; @@ -911,7 +919,7 @@ } return BinaryCoverageReader::createCoverageReaderFromBuffer( CoverageMapping, CoverageRecords.str(), std::move(ProfileNames), - BytesInAddress, Endian); + BytesInAddress, Endian, CompilationDir); } /// Find all sections that match \p Name. There may be more than one if comdats @@ -941,7 +949,8 @@ } static Expected> -loadBinaryFormat(std::unique_ptr Bin, StringRef Arch) { +loadBinaryFormat(std::unique_ptr Bin, StringRef Arch, + StringRef CompilationDir = "") { std::unique_ptr OF; if (auto *Universal = dyn_cast(Bin.get())) { // If we have a universal binary, try to look up the object for the @@ -1013,7 +1022,7 @@ return BinaryCoverageReader::createCoverageReaderFromBuffer( CoverageMapping, std::move(FuncRecords), std::move(ProfileNames), - BytesInAddress, Endian); + BytesInAddress, Endian, CompilationDir); } /// Determine whether \p Arch is invalid or empty, given \p Bin. @@ -1032,12 +1041,14 @@ Expected>> BinaryCoverageReader::create( MemoryBufferRef ObjectBuffer, StringRef Arch, - SmallVectorImpl> &ObjectFileBuffers) { + SmallVectorImpl> &ObjectFileBuffers, + StringRef CompilationDir) { std::vector> Readers; if (ObjectBuffer.getBuffer().startswith(TestingFormatMagic)) { // This is a special format used for testing. - auto ReaderOrErr = loadTestingFormat(ObjectBuffer.getBuffer()); + auto ReaderOrErr = + loadTestingFormat(ObjectBuffer.getBuffer(), CompilationDir); if (!ReaderOrErr) return ReaderOrErr.takeError(); Readers.push_back(std::move(ReaderOrErr.get())); @@ -1070,7 +1081,8 @@ } return BinaryCoverageReader::create( - ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers); + ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers, + CompilationDir); } } @@ -1083,7 +1095,7 @@ return ChildBufOrErr.takeError(); auto ChildReadersOrErr = BinaryCoverageReader::create( - ChildBufOrErr.get(), Arch, ObjectFileBuffers); + ChildBufOrErr.get(), Arch, ObjectFileBuffers, CompilationDir); if (!ChildReadersOrErr) return ChildReadersOrErr.takeError(); for (auto &Reader : ChildReadersOrErr.get()) @@ -1102,7 +1114,7 @@ return std::move(Readers); } - auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch); + auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch, CompilationDir); if (!ReaderOrErr) return ReaderOrErr.takeError(); Readers.push_back(std::move(ReaderOrErr.get())); diff --git a/llvm/test/tools/llvm-cov/Inputs/compilation_dir.covmapping b/llvm/test/tools/llvm-cov/Inputs/compilation_dir.covmapping new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ CompilationDirectory( + "compilation-dir", cl::init(""), + cl::desc("Directory used as a base for relative coverage mapping paths")); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; @@ -873,6 +878,7 @@ ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ExportSummaryOnly = SummaryOnly; ViewOpts.NumThreads = NumThreads; + ViewOpts.CompilationDirectory = CompilationDirectory; return 0; }; diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -49,6 +49,7 @@ std::string ProjectTitle; std::string CreatedTimeStr; unsigned NumThreads; + std::string CompilationDirectory; /// Change the output's stream color if the colors are enabled. ColoredRawOstream colored_ostream(raw_ostream &OS, diff --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp --- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp +++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp @@ -900,7 +900,7 @@ std::pair({true, true})),); TEST(CoverageMappingTest, filename_roundtrip) { - std::vector Paths({"", "a", "b", "c", "d", "e"}); + std::vector Paths({"dir", "a", "b", "c", "d", "e"}); for (bool Compress : {false, true}) { std::string EncodedFilenames; @@ -915,8 +915,37 @@ EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion), Succeeded()); ASSERT_EQ(ReadFilenames.size(), Paths.size()); - for (unsigned I = 1; I < Paths.size(); ++I) - ASSERT_TRUE(ReadFilenames[I] == Paths[I]); + for (unsigned I = 1; I < Paths.size(); ++I) { + SmallString<256> P(Paths[0]); + llvm::sys::path::append(P, Paths[I]); + ASSERT_TRUE(ReadFilenames[I] == P); + } + } +} + +TEST(CoverageMappingTest, filename_compilation_dir) { + std::vector Paths({"dir", "a", "b", "c", "d", "e"}); + + for (bool Compress : {false, true}) { + std::string EncodedFilenames; + { + raw_string_ostream OS(EncodedFilenames); + CoverageFilenamesSectionWriter Writer(Paths); + Writer.write(OS, Compress); + } + + StringRef CompilationDir = "out"; + std::vector ReadFilenames; + RawCoverageFilenamesReader Reader(EncodedFilenames, ReadFilenames, + CompilationDir); + EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion), Succeeded()); + + ASSERT_EQ(ReadFilenames.size(), Paths.size()); + for (unsigned I = 1; I < Paths.size(); ++I) { + SmallString<256> P(CompilationDir); + llvm::sys::path::append(P, Paths[I]); + ASSERT_TRUE(ReadFilenames[I] == P); + } } }