Skip to content

Commit

Permalink
[Coverage] Store compilation dir separately in coverage mapping
Browse files Browse the repository at this point in the history
We currently always store absolute filenames in coverage mapping.  This
is problematic for several reasons. It poses a problem for distributed
compilation as source location might vary across machines.  We are also
duplicating the path prefix potentially wasting space.

This change modifies how we store filenames in coverage mapping. Rather
than absolute paths, it stores the compilation directory and file paths
as given to the compiler, either relative or absolute. Later when
reading the coverage mapping information, we recombine relative paths
with the working directory. This approach is similar to handling
ofDW_AT_comp_dir in DWARF.

Finally, we also provide a new option, -fprofile-compilation-dir akin
to -fdebug-compilation-dir which can be used to manually override the
compilation directory which is useful in distributed compilation cases.

Differential Revision: https://reviews.llvm.org/D95753
  • Loading branch information
petrhosek committed Feb 18, 2021
1 parent 62d946e commit 5fbd1a3
Show file tree
Hide file tree
Showing 19 changed files with 148 additions and 94 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// The string to embed in debug information as the current working directory.
std::string DebugCompilationDir;

/// The string to embed in coverage mapping as the current working directory.
std::string ProfileCompilationDir;

/// The string to embed in the debug information for the compile unit, if
/// non-empty.
std::string DwarfDebugFlags;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,10 @@ def fdebug_compilation_dir_EQ : Joined<["-"], "fdebug-compilation-dir=">,
def fdebug_compilation_dir : Separate<["-"], "fdebug-compilation-dir">,
Group<f_Group>, Flags<[CC1Option, CC1AsOption, CoreOption]>,
Alias<fdebug_compilation_dir_EQ>;
def fprofile_compilation_dir_EQ : Joined<["-"], "fprofile-compilation-dir=">,
Group<f_Group>, Flags<[CC1Option, CC1AsOption, CoreOption]>,
HelpText<"The compilation directory to embed in the coverage mapping.">,
MarshallingInfoString<CodeGenOpts<"ProfileCompilationDir">>;
defm debug_info_for_profiling : BoolFOption<"debug-info-for-profiling",
CodeGenOpts<"DebugInfoForProfiling">, DefaultFalse,
PosFlag<SetTrue, [CC1Option], "Emit extra debug info to make sample profile more accurate">,
Expand Down
30 changes: 18 additions & 12 deletions clang/lib/CodeGen/CoverageMappingGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1606,9 +1606,17 @@ CoverageMappingModuleGen::CoverageMappingModuleGen(
ProfilePrefixMap = CGM.getCodeGenOpts().ProfilePrefixMap;
}

std::string CoverageMappingModuleGen::getCurrentDirname() {
if (!CGM.getCodeGenOpts().ProfileCompilationDir.empty())
return CGM.getCodeGenOpts().ProfileCompilationDir;

SmallString<256> CWD;
llvm::sys::fs::current_path(CWD);
return CWD.str().str();
}

std::string CoverageMappingModuleGen::normalizeFilename(StringRef Filename) {
llvm::SmallString<256> Path(Filename);
llvm::sys::fs::make_absolute(Path);
llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
for (const auto &Entry : ProfilePrefixMap) {
if (llvm::sys::path::replace_path_prefix(Path, Entry.first, Entry.second))
Expand Down Expand Up @@ -1689,18 +1697,17 @@ void CoverageMappingModuleGen::addFunctionMappingRecord(
// also processed by the CoverageMappingWriter which performs
// additional minimization operations such as reducing the number of
// expressions.
llvm::SmallVector<std::string, 16> FilenameStrs;
std::vector<StringRef> Filenames;
std::vector<CounterExpression> Expressions;
std::vector<CounterMappingRegion> Regions;
llvm::SmallVector<std::string, 16> FilenameStrs;
llvm::SmallVector<StringRef, 16> FilenameRefs;
FilenameStrs.resize(FileEntries.size());
FilenameRefs.resize(FileEntries.size());
FilenameStrs.resize(FileEntries.size() + 1);
FilenameStrs[0] = normalizeFilename(getCurrentDirname());
for (const auto &Entry : FileEntries) {
auto I = Entry.second;
FilenameStrs[I] = normalizeFilename(Entry.first->getName());
FilenameRefs[I] = FilenameStrs[I];
}
ArrayRef<std::string> FilenameRefs = llvm::makeArrayRef(FilenameStrs);
RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames,
Expressions, Regions);
if (Reader.read())
Expand All @@ -1717,19 +1724,18 @@ void CoverageMappingModuleGen::emit() {

// Create the filenames and merge them with coverage mappings
llvm::SmallVector<std::string, 16> FilenameStrs;
llvm::SmallVector<StringRef, 16> FilenameRefs;
FilenameStrs.resize(FileEntries.size());
FilenameRefs.resize(FileEntries.size());
FilenameStrs.resize(FileEntries.size() + 1);
// The first filename is the current working directory.
FilenameStrs[0] = getCurrentDirname();
for (const auto &Entry : FileEntries) {
auto I = Entry.second;
FilenameStrs[I] = normalizeFilename(Entry.first->getName());
FilenameRefs[I] = FilenameStrs[I];
}

std::string Filenames;
{
llvm::raw_string_ostream OS(Filenames);
CoverageFilenamesSectionWriter(FilenameRefs).write(OS);
CoverageFilenamesSectionWriter(FilenameStrs).write(OS);
}
auto *FilenamesVal =
llvm::ConstantDataArray::getString(Ctx, Filenames, false);
Expand Down Expand Up @@ -1787,7 +1793,7 @@ unsigned CoverageMappingModuleGen::getFileID(const FileEntry *File) {
auto It = FileEntries.find(File);
if (It != FileEntries.end())
return It->second;
unsigned FileID = FileEntries.size();
unsigned FileID = FileEntries.size() + 1;
FileEntries.insert(std::make_pair(File, FileID));
return FileID;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CoverageMappingGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class CoverageMappingModuleGen {
std::vector<FunctionInfo> FunctionRecords;
std::map<std::string, std::string> ProfilePrefixMap;

std::string getCurrentDirname();
std::string normalizeFilename(StringRef Filename);

/// Emit a function record.
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,13 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C,
CmdArgs.push_back("-fcoverage-mapping");
}

if (Arg *A = Args.getLastArg(options::OPT_fprofile_compilation_dir_EQ)) {
A->render(Args, CmdArgs);
} else if (llvm::ErrorOr<std::string> CWD =
D.getVFS().getCurrentWorkingDirectory()) {
Args.MakeArgString("-fprofile-compilation-dir=" + *CWD);
}

if (Args.hasArg(options::OPT_fprofile_exclude_files_EQ)) {
auto *Arg = Args.getLastArg(options::OPT_fprofile_exclude_files_EQ);
if (!Args.hasArg(options::OPT_coverage))
Expand Down
7 changes: 7 additions & 0 deletions clang/test/CodeGen/profile-compilation-dir.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: mkdir -p %t.dir && cd %t.dir
// RUN: cp %s rel.c
// RUN: %clang_cc1 -fprofile-instrument=clang -fprofile-compilation-dir=/nonsense -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false rel.c -o - | FileCheck -check-prefix=CHECK-NONSENSE %s

// CHECK-NONSENSE: nonsense

void f() {}
12 changes: 8 additions & 4 deletions clang/test/CoverageMapping/abspath.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp %S/Inputs/../abspath.cpp -o - | FileCheck -check-prefix=RMDOTS %s

// RMDOTS: @__llvm_coverage_mapping = {{.*}}"\01
// RMDOTS: @__llvm_coverage_mapping = {{.*}}"\02
// RMDOTS-NOT: Inputs
// RMDOTS: "

// RUN: mkdir -p %t/test && cd %t/test
// RUN: echo "void f1() {}" > f1.c
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp ../test/f1.c -o - | FileCheck -check-prefix=RELPATH %s
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp %t/test/f1.c -o - | FileCheck -check-prefix=ABSPATH %s

// RELPATH: @__llvm_coverage_mapping = {{.*}}"\01
// RELPATH: {{[/\\].*(/|\\\\)test(/|\\\\)f1}}.c
// RELPATH: @__llvm_coverage_mapping = {{.*}}"\02
// RELPATH: {{..(/|\\\\)test(/|\\\\)f1}}.c
// RELPATH: "

// ABSPATH: @__llvm_coverage_mapping = {{.*}}"\02
// ABSPATH: {{[/\\].*(/|\\\\)test(/|\\\\)f1}}.c
// ABSPATH: "

void f1() {}
12 changes: 8 additions & 4 deletions clang/test/Profile/profile-prefix-map.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
// RUN: echo "void f1() {}" > %t/root/nested/profile-prefix-map.c
// RUN: cd %t/root

// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name profile-prefix-map.c nested/profile-prefix-map.c -o - | FileCheck --check-prefix=ABSOLUTE %s
// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name profile-prefix-map.c %t/root/nested/profile-prefix-map.c -o - | FileCheck --check-prefix=ABSOLUTE %s
//
// ABSOLUTE: @__llvm_coverage_mapping = {{.*"\\01.*root.*nested.*profile-prefix-map\.c}}
// ABSOLUTE: @__llvm_coverage_mapping = {{.*"\\02.*root.*nested.*profile-prefix-map\.c}}

// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name profile-prefix-map.c nested/profile-prefix-map.c -fprofile-prefix-map=%/t/root=. -o - | FileCheck --check-prefix=PROFILE-PREFIX-MAP %s --implicit-check-not=root
// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name profile-prefix-map.c ../root/nested/profile-prefix-map.c -o - | FileCheck --check-prefix=RELATIVE %s
//
// PROFILE-PREFIX-MAP: @__llvm_coverage_mapping = {{.*"\\01[^/]*}}.{{/|\\+}}nested{{.*profile-prefix-map\.c}}
// RELATIVE: @__llvm_coverage_mapping = {{.*"\\02.*}}..{{/|\\+}}root{{/|\\+}}nested{{.*profile-prefix-map\.c}}

// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name profile-prefix-map.c %t/root/nested/profile-prefix-map.c -fprofile-prefix-map=%/t/root=. -o - | FileCheck --check-prefix=PROFILE-PREFIX-MAP %s
// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -mllvm -enable-name-compression=false -main-file-name profile-prefix-map.c ../root/nested/profile-prefix-map.c -fprofile-prefix-map=../root=. -o - | FileCheck --check-prefix=PROFILE-PREFIX-MAP %s
// PROFILE-PREFIX-MAP: @__llvm_coverage_mapping = {{.*"\\02.*}}.{{/|\\+}}nested{{.*profile-prefix-map\.c}}
2 changes: 1 addition & 1 deletion compiler-rt/include/profile/InstrProfData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 7
/* Coverage mapping format version (start from 0). */
#define INSTR_PROF_COVMAP_VERSION 4
#define INSTR_PROF_COVMAP_VERSION 5

/* Profile version is always of type uint64_t. Reserve the upper 8 bits in the
* version for other variants of profile. We set the lowest bit of the upper 8
Expand Down
11 changes: 10 additions & 1 deletion llvm/docs/CoverageMappingFormat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,16 @@ too deeply).
[32 x i8] c"..." ; Encoded data (dissected later)
}, section "__llvm_covmap", align 8
The current version of the format is version 5. There is one difference from version 4:
The current version of the format is version 6.

There is one difference between versions 6 and 5:

* The first entry in the filename list is the compilation directory. When the
filename is relative, the compilation directory is combined with the relative
path to get an absolute path. This can reduce size by omitting the duplicate
prefix in filenames.

There is one difference between versions 5 and 4:

* The notion of branch region has been introduced along with a corresponding
region kind. Branch regions encode two counters, one to track how many
Expand Down
5 changes: 4 additions & 1 deletion llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,10 @@ enum CovMapVersion {
Version4 = 3,
// Branch regions referring to two counters are added
Version5 = 4,
// The current version is Version5.
// Compilation directory is stored separately and combined with relative
// filenames to produce an absolute file path.
Version6 = 5,
// The current version is Version6.
CurrentVersion = INSTR_PROF_COVMAP_VERSION
};

Expand Down
22 changes: 8 additions & 14 deletions llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,14 @@ class RawCoverageMappingDummyChecker : public RawCoverageReader {

/// Reader for the raw coverage mapping data.
class RawCoverageMappingReader : public RawCoverageReader {
ArrayRef<StringRef> TranslationUnitFilenames;
ArrayRef<std::string> &TranslationUnitFilenames;
std::vector<StringRef> &Filenames;
std::vector<CounterExpression> &Expressions;
std::vector<CounterMappingRegion> &MappingRegions;

public:
RawCoverageMappingReader(StringRef MappingData,
ArrayRef<StringRef> TranslationUnitFilenames,
ArrayRef<std::string> &TranslationUnitFilenames,
std::vector<StringRef> &Filenames,
std::vector<CounterExpression> &Expressions,
std::vector<CounterMappingRegion> &MappingRegions)
Expand Down Expand Up @@ -174,10 +174,8 @@ class BinaryCoverageReader : public CoverageMappingReader {
FilenamesBegin(FilenamesBegin), FilenamesSize(FilenamesSize) {}
};

using DecompressedData = std::vector<std::unique_ptr<SmallVector<char, 0>>>;

private:
std::vector<StringRef> Filenames;
std::vector<std::string> Filenames;
std::vector<ProfileMappingRecord> MappingRecords;
InstrProfSymtab ProfileNames;
size_t CurrentRecord = 0;
Expand All @@ -190,10 +188,6 @@ class BinaryCoverageReader : public CoverageMappingReader {
// D69471, which can split up function records into multiple sections on ELF.
std::string FuncRecords;

// Used to tie the lifetimes of decompressed strings to the lifetime of this
// BinaryCoverageReader instance.
DecompressedData Decompressed;

BinaryCoverageReader(std::string &&FuncRecords)
: FuncRecords(std::move(FuncRecords)) {}

Expand All @@ -216,20 +210,20 @@ class BinaryCoverageReader : public CoverageMappingReader {

/// Reader for the raw coverage filenames.
class RawCoverageFilenamesReader : public RawCoverageReader {
std::vector<StringRef> &Filenames;
std::vector<std::string> &Filenames;

// Read an uncompressed sequence of filenames.
Error readUncompressed(uint64_t NumFilenames);
Error readUncompressed(CovMapVersion Version, uint64_t NumFilenames);

public:
RawCoverageFilenamesReader(StringRef Data, std::vector<StringRef> &Filenames)
RawCoverageFilenamesReader(StringRef Data,
std::vector<std::string> &Filenames)
: RawCoverageReader(Data), Filenames(Filenames) {}
RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete;
RawCoverageFilenamesReader &
operator=(const RawCoverageFilenamesReader &) = delete;

Error read(CovMapVersion Version,
BinaryCoverageReader::DecompressedData &Decompressed);
Error read(CovMapVersion Version);
};

} // end namespace coverage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ namespace coverage {
/// Writer of the filenames section for the instrumentation
/// based code coverage.
class CoverageFilenamesSectionWriter {
ArrayRef<StringRef> Filenames;
ArrayRef<std::string> Filenames;

public:
CoverageFilenamesSectionWriter(ArrayRef<StringRef> Filenames);
CoverageFilenamesSectionWriter(ArrayRef<std::string> Filenames);

/// Write encoded filenames to the given output stream. If \p Compress is
/// true, attempt to compress the filenames.
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/ProfileData/InstrProfData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 7
/* Coverage mapping format version (start from 0). */
#define INSTR_PROF_COVMAP_VERSION 4
#define INSTR_PROF_COVMAP_VERSION 5

/* Profile version is always of type uint64_t. Reserve the upper 8 bits in the
* version for other variants of profile. We set the lowest bit of the upper 8
Expand Down
Loading

0 comments on commit 5fbd1a3

Please sign in to comment.