Skip to content

Commit

Permalink
[llvm-cov] Allow multiple remaps in --path-equivalence
Browse files Browse the repository at this point in the history
Previously the --path-equivalence parameter would allow to specify a single remap pair (coverage data path - local source file path). This patch changes this allowing to pass as many remaps as needed.

Reviewed By: keith

Differential Revision: https://reviews.llvm.org/D154223
  • Loading branch information
tcamin authored and keith committed Aug 23, 2023
1 parent 9a80ebe commit 5042e1e
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 26 deletions.
6 changes: 5 additions & 1 deletion llvm/docs/CommandGuide/llvm-cov.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,11 @@ OPTIONS

Map the paths in the coverage data to local source file paths. This allows you
to generate the coverage data on one machine, and then use llvm-cov on a
different machine where you have the same files on a different path.
different machine where you have the same files on a different path. Multiple
`-path-equivalence` arguments can be passed to specify different mappings. Each
argument consists of a source path `<from>` and its corresponding local path `<to>`.
The mappings are applied in the order they are specified. If multiple mappings can
be applied to a single path, the first mapping encountered is used.

.. option:: -coverage-watermark=<high>,<low>

Expand Down
Binary file not shown.
19 changes: 19 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/multiple-path_equivalence.proftext
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
f1
0x0
1
1

f2
0x0
1
1

f3
0x0
1
1

f4
0x0
1
1
32 changes: 32 additions & 0 deletions llvm/test/tools/llvm-cov/multiple-path_equivalence.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# multiple-path_equivalence.covmapping contains references to 4 files:
# /tmp/coverage/main.c
# /tmp/coverage/f1.c
# /tmp/coverage/a/f2.c
# /tmp/coverage/b/f3.c
# /tmp/coverage/b/c/f4.c

# Setup
// RUN: touch %/T/main.c; touch %/T/f1.c; touch %/T/f2.c; touch %/T/f3.c; touch %/T/f4.c
// RUN: llvm-profdata merge %S/Inputs/multiple-path_equivalence.proftext -o %t.profdata

# Make sure that remapping follows the specified order with the first matching entry being used first (f4 comes before f3)
// RUN: llvm-cov show %S/Inputs/multiple-path_equivalence.covmapping -instr-profile=%t.profdata -path-equivalence=/tmp/coverage/a,%/T -path-equivalence=/tmp/coverage/b/c,%/T -path-equivalence=/tmp/coverage/b,%/T -path-equivalence=/tmp/coverage,%/T 2>&1 | FileCheck %s

// CHECK-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}main.c:
// CHECK-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}f1.c:
// CHECK-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}a{{/|\\}}f2.c:
// CHECK-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}b{{/|\\}}f3.c:
// CHECK-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}b{{/|\\}}c{{/|\\}}f4.c:
// CHECK-NOT: isn't covered.

# Make sure remapping follows the specified order by proving paths in an overriding order (f4 comes after f3)
// RUN: llvm-cov show %S/Inputs/multiple-path_equivalence.covmapping -instr-profile=%t.profdata -path-equivalence=/tmp/coverage/a,%/T -path-equivalence=/tmp/coverage/b,%/T -path-equivalence=/tmp/coverage/b/c,%/T -path-equivalence=/tmp/coverage,%/T 2>&1 | FileCheck %s -check-prefix=OVERRIDING_ORDER

// OVERRIDING_ORDER-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}main.c:
// OVERRIDING_ORDER-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}f1.c:
// OVERRIDING_ORDER-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}a{{/|\\}}f2.c:
// OVERRIDING_ORDER-DAG: {{/|\\}}tmp{{/|\\}}coverage{{/|\\}}b{{/|\\}}f3.c:
// OVERRIDING_ORDER-DAG: warning: The file '{{/|\\}}tmp{{/|\\}}coverage{{/|\\}}b{{/|\\}}c{{/|\\}}f4.c' isn't covered.

// RUN: not llvm-cov show %S/Inputs/multiple-path_equivalence.covmapping -instr-profile=%t.profdata -path-equivalence=/tmp/coverage/a,%/T -path-equivalence=/tmp/coverage/b, -path-equivalence=/tmp/coverage/b/c,%/T -path-equivalence=/tmp/coverage,%/T 2>&1 | FileCheck %s -check-prefix=EMPTY_PATH
// EMPTY_PATH: must be in format 'from,to'
3 changes: 3 additions & 0 deletions llvm/test/tools/llvm-cov/path_equivalence.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ int main() {} // CHECK: [[@LINE]]| 1|int main() {}

// RUN: not llvm-cov show --instr-profile=/dev/null -path-equivalence=foo /dev/null 2>&1 | FileCheck --check-prefix=INVALID %s
// INVALID: error: -path-equivalence: invalid argument 'foo', must be in format 'from,to'

// RUN: not llvm-cov show --instr-profile=/dev/null -path-equivalence=,foo /dev/null 2>&1 | FileCheck --check-prefix=EMPTY_PATH %s
// EMPTY_PATH: error: -path-equivalence: invalid argument ',foo', must be in format 'from,to'
61 changes: 36 additions & 25 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ class CodeCoverageTool {

/// The coverage data path to be remapped from, and the source path to be
/// remapped to, when using -path-equivalence.
std::optional<std::pair<std::string, std::string>> PathRemapping;
std::optional<std::vector<std::pair<std::string, std::string>>>
PathRemappings;

/// File status cache used when finding the same file.
StringMap<std::optional<sys::fs::file_status>> FileStatusCache;
Expand Down Expand Up @@ -228,7 +229,7 @@ void CodeCoverageTool::collectPaths(const std::string &Path) {
llvm::sys::fs::file_status Status;
llvm::sys::fs::status(Path, Status);
if (!llvm::sys::fs::exists(Status)) {
if (PathRemapping)
if (PathRemappings)
addCollectedPath(Path);
else
warning("Source file doesn't exist, proceeded by ignoring it.", Path);
Expand Down Expand Up @@ -474,7 +475,7 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
}

void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
if (!PathRemapping)
if (!PathRemappings)
return;

// Convert remapping paths to native paths with trailing seperators.
Expand All @@ -488,17 +489,23 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
NativePath += sys::path::get_separator();
return NativePath.c_str();
};
std::string RemapFrom = nativeWithTrailing(PathRemapping->first);
std::string RemapTo = nativeWithTrailing(PathRemapping->second);

// Create a mapping from coverage data file paths to local paths.
for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
SmallString<128> NativeFilename;
sys::path::native(Filename, NativeFilename);
sys::path::remove_dots(NativeFilename, true);
if (NativeFilename.startswith(RemapFrom)) {
RemappedFilenames[Filename] =
RemapTo + NativeFilename.substr(RemapFrom.size()).str();
for (std::pair<std::string, std::string> &PathRemapping : *PathRemappings) {
std::string RemapFrom = nativeWithTrailing(PathRemapping.first);
std::string RemapTo = nativeWithTrailing(PathRemapping.second);

// Create a mapping from coverage data file paths to local paths.
for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
if (RemappedFilenames.count(Filename) == 1)
continue;

SmallString<128> NativeFilename;
sys::path::native(Filename, NativeFilename);
sys::path::remove_dots(NativeFilename, true);
if (NativeFilename.startswith(RemapFrom)) {
RemappedFilenames[Filename] =
RemapTo + NativeFilename.substr(RemapFrom.size()).str();
}
}
}

Expand Down Expand Up @@ -674,7 +681,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
"lcov tracefile output")),
cl::init(CoverageViewOptions::OutputFormat::Text));

cl::opt<std::string> PathRemap(
cl::list<std::string> PathRemaps(
"path-equivalence", cl::Optional,
cl::desc("<from>,<to> Map coverage data paths to local source file "
"paths"));
Expand Down Expand Up @@ -812,19 +819,23 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
break;
}

// If path-equivalence was given and is a comma seperated pair then set
// PathRemapping.
if (!PathRemap.empty()) {
auto EquivPair = StringRef(PathRemap).split(',');
if (EquivPair.first.empty() || EquivPair.second.empty()) {
error("invalid argument '" + PathRemap +
"', must be in format 'from,to'",
"-path-equivalence");
return 1;
if (!PathRemaps.empty()) {
std::vector<std::pair<std::string, std::string>> Remappings;

for (const std::string &PathRemap : PathRemaps) {
auto EquivPair = StringRef(PathRemap).split(',');
if (EquivPair.first.empty() || EquivPair.second.empty()) {
error("invalid argument '" + PathRemap +
"', must be in format 'from,to'",
"-path-equivalence");
return 1;
}

Remappings.push_back(
{std::string(EquivPair.first), std::string(EquivPair.second)});
}

PathRemapping = {std::string(EquivPair.first),
std::string(EquivPair.second)};
PathRemappings = Remappings;
}

// If a demangler is supplied, check if it exists and register it.
Expand Down

0 comments on commit 5042e1e

Please sign in to comment.