Index: test/tools/llvm-cov/Inputs/path_equivalence.proftext =================================================================== --- test/tools/llvm-cov/Inputs/path_equivalence.proftext +++ test/tools/llvm-cov/Inputs/path_equivalence.proftext @@ -0,0 +1,8 @@ +main +# Func Hash: +0 +# Num Counters: +1 +# Counter Values: +1 + Index: test/tools/llvm-cov/path_equivalence.c =================================================================== --- test/tools/llvm-cov/path_equivalence.c +++ test/tools/llvm-cov/path_equivalence.c @@ -0,0 +1,7 @@ +// RUN: llvm-profdata merge %S/Inputs/path_equivalence.proftext -o %t.profdata +// RUN: llvm-cov show %S/Inputs/path_equivalence.covmapping -instr-profile=%t.profdata -path-equivalence=/tmp,%S | FileCheck %s + +int main() {} // CHECK: [[@LINE]]| 1|int main() {} + +// RUN: llvm-cov show %S/Inputs/path_equivalence.covmapping -instr-profile=%t.profdata -path-equivalence=/tmp,%S --filename-equivalence %s | FileCheck --check-prefix IGNORED_REMAP %s +// IGNORED_REMAP: warning: path-equivalence was ignored as source files were given Index: tools/llvm-cov/CodeCoverage.cpp =================================================================== --- tools/llvm-cov/CodeCoverage.cpp +++ tools/llvm-cov/CodeCoverage.cpp @@ -132,6 +132,10 @@ /// coverage mapping data to input source files. StringMap RemappedFilenames; + /// The coverage data path to be remapped from, and the source path to be + /// remapped to, when using -path-equivalence. + std::pair PathRemapping; + /// The architecture the coverage mapping data targets. std::string CoverageArch; @@ -518,6 +522,11 @@ cl::desc("Treat source files as equivalent to paths in the coverage data " "when the file names match, even if the full paths do not")); + cl::opt PathRemap( + "path-equivalence", cl::Optional, + cl::desc(", Map coverage data paths to local source file " + "paths")); + cl::OptionCategory FilteringCategory("Function filtering options"); cl::list NameFilters( @@ -590,6 +599,22 @@ break; } + // If path-equivalence was given, normalise the paths for comparison. + PathRemapping = StringRef(PathRemap).split(','); + if (!(PathRemapping.first.empty() || PathRemapping.second.empty())) { + // Convert to native paths with trailing seperators + auto nativeWithTrailing = [](StringRef path) -> std::string { + SmallString<128> NativePath; + sys::path::native(path, NativePath); + if (!sys::path::is_separator(NativePath.back())) + NativePath += sys::path::get_separator(); + return NativePath.c_str(); + }; + PathRemapping.first = nativeWithTrailing(PathRemapping.first); + PathRemapping.second = nativeWithTrailing(PathRemapping.second); + } else + PathRemapping = std::make_pair("", ""); + // If a demangler is supplied, check if it exists and register it. if (DemanglerOpts.size()) { auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); @@ -784,11 +809,27 @@ (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); - if (SourceFiles.empty()) + if (SourceFiles.empty()) { // Get the source files from the function coverage mapping. - for (StringRef Filename : Coverage->getUniqueSourceFiles()) + for (StringRef Filename : Coverage->getUniqueSourceFiles()) { SourceFiles.push_back(Filename); + // Remap by leading path + if (!PathRemapping.first.empty()) { + SmallString<128> NativeFilename; + sys::path::native(Filename, NativeFilename); + if (NativeFilename.startswith(PathRemapping.first)) { + RemappedFilenames[Filename] = + PathRemapping.second + + NativeFilename.substr(PathRemapping.first.size()).str(); + } + } + } + } else if (!PathRemapping.first.empty()) { + ViewOpts.colored_ostream(errs(), raw_ostream::RED) + << "path-equivalence was ignored as source files were given"; + } + // Create an index out of the source files. if (ViewOpts.hasOutputDirectory()) { if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) {