Index: llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h =================================================================== --- llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ 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. /// Index: llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h =================================================================== --- llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h +++ llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h @@ -197,13 +197,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; }; @@ -211,14 +213,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; Index: llvm/lib/ProfileData/Coverage/CoverageMapping.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ 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) Index: llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -137,7 +137,8 @@ } StringRef UncompressedFilenames(StorageBuf.data(), StorageBuf.size()); - RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames); + RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames, + CompilationDir); return Delegate.readUncompressed(Version, NumFilenames); } @@ -167,7 +168,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)); } @@ -519,7 +524,7 @@ template static Expected> get(CovMapVersion Version, InstrProfSymtab &P, - std::vector &R, + std::vector &R, StringRef D, std::vector &F); }; @@ -534,6 +539,7 @@ // in \c Records. DenseMap FunctionRecords; InstrProfSymtab &ProfileNames; + StringRef CompilationDir; std::vector &Filenames; std::vector &Records; @@ -593,9 +599,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; @@ -628,7 +634,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; @@ -734,14 +741,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: @@ -752,19 +759,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"); } @@ -773,7 +780,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. @@ -784,7 +791,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()); @@ -816,8 +823,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); @@ -826,23 +834,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); @@ -850,7 +858,7 @@ } static Expected> -loadTestingFormat(StringRef Data) { +loadTestingFormat(StringRef Data, StringRef CompilationDir) { uint8_t BytesInAddress = 8; support::endianness Endian = support::endianness::little; @@ -883,7 +891,8 @@ return make_error(coveragemap_error::malformed); CoverageMapping = CoverageMapping.substr(Pad); return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMapping, "", std::move(ProfileNames), BytesInAddress, Endian); + CoverageMapping, "", std::move(ProfileNames), BytesInAddress, Endian, + CompilationDir); } /// Find all sections that match \p Name. There may be more than one if comdats @@ -913,7 +922,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 @@ -985,7 +995,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. @@ -1004,12 +1014,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())); @@ -1042,7 +1054,8 @@ } return BinaryCoverageReader::create( - ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers); + ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers, + CompilationDir); } } @@ -1055,7 +1068,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()) @@ -1074,7 +1087,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())); Index: llvm/test/tools/llvm-cov/Inputs/compilation_dir.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/Inputs/compilation_dir.proftext @@ -0,0 +1,8 @@ +main +# Func Hash: +0 +# Num Counters: +1 +# Counter Values: +1 + Index: llvm/test/tools/llvm-cov/compilation_dir.c =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/compilation_dir.c @@ -0,0 +1,3 @@ +// RUN: llvm-profdata merge %S/Inputs/compilation_dir.proftext -o %t.profdata +// RUN: llvm-cov show %S/Inputs/compilation_dir.covmapping -instr-profile=%t.profdata -compilation-dir=%S | FileCheck %s +int main() {} // CHECK: [[@LINE]]| 1|int main() {} Index: llvm/tools/llvm-cov/CodeCoverage.cpp =================================================================== --- llvm/tools/llvm-cov/CodeCoverage.cpp +++ llvm/tools/llvm-cov/CodeCoverage.cpp @@ -434,7 +434,8 @@ warning("profile data may be out of date - object is newer", ObjectFilename); auto CoverageOrErr = - CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches); + CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches, + ViewOpts.ShowCompilationDirectory); if (Error E = CoverageOrErr.takeError()) { error("Failed to load coverage: " + toString(std::move(E)), join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); @@ -932,6 +933,10 @@ cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), cl::aliasopt(ShowOutputDirectory)); + cl::opt ShowCompilationDirectory( + "compilation-dir", cl::init(""), + cl::desc("Directory used as a base for relative coverage mapping paths")); + cl::opt TabSize( "tab-size", cl::init(2), cl::desc( @@ -961,6 +966,7 @@ ShowBranches == CoverageViewOptions::BranchOutputType::Percent; ViewOpts.ShowFunctionInstantiations = ShowInstantiations; ViewOpts.ShowOutputDirectory = ShowOutputDirectory; + ViewOpts.ShowCompilationDirectory = ShowCompilationDirectory; ViewOpts.TabSize = TabSize; ViewOpts.ProjectTitle = ProjectTitle; Index: llvm/tools/llvm-cov/CoverageViewOptions.h =================================================================== --- llvm/tools/llvm-cov/CoverageViewOptions.h +++ llvm/tools/llvm-cov/CoverageViewOptions.h @@ -44,6 +44,7 @@ OutputFormat Format; BranchOutputType ShowBranches; std::string ShowOutputDirectory; + std::string ShowCompilationDirectory; std::vector DemanglerOpts; uint32_t TabSize; std::string ProjectTitle; Index: llvm/unittests/ProfileData/CoverageMappingTest.cpp =================================================================== --- llvm/unittests/ProfileData/CoverageMappingTest.cpp +++ 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); + } } }