[llvm-cov] Add an option which maps the location of source directories on another machine to your local copies
Summary:
This patch adds the -path-equivalence option (example: llvm-cov show -path-equivalence=/origin/path,/local/path) which maps the source code path from one machine to another when using `llvm-cov show`. This is similar to the -filename-equivalence option, but doesn't require you to specify all the source files on the command line.
This allows you to generate the coverage data on one machine (e.g. in a CI system), and then use llvm-cov on another machine where you have the same code base on a different path.
Reviewers: vsk
Reviewed By: vsk
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D36391
llvm-svn: 310827
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index 8afae22..e5878df 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -94,6 +94,10 @@
/// \brief Load the coverage mapping data. Return nullptr if an error occurred.
std::unique_ptr<CoverageMapping> load();
+ /// \brief Create a mapping from files in the Coverage data to local copies
+ /// (path-equivalence).
+ void remapPathNames(const CoverageMapping &Coverage);
+
/// \brief Remove input source files which aren't mapped by \p Coverage.
void removeUnmappedInputs(const CoverageMapping &Coverage);
@@ -125,13 +129,14 @@
/// A list of input source files.
std::vector<std::string> SourceFiles;
- /// Whether or not we're in -filename-equivalence mode.
- bool CompareFilenamesOnly;
-
- /// In -filename-equivalence mode, this maps absolute paths from the
- /// coverage mapping data to input source files.
+ /// In -path-equivalence mode, this maps the absolute paths from the coverage
+ /// mapping data to the input source files.
StringMap<std::string> RemappedFilenames;
+ /// The coverage data path to be remapped from, and the source path to be
+ /// remapped to, when using -path-equivalence.
+ Optional<std::pair<std::string, std::string>> PathRemapping;
+
/// The architecture the coverage mapping data targets.
std::vector<StringRef> CoverageArches;
@@ -171,24 +176,20 @@
}
void CodeCoverageTool::addCollectedPath(const std::string &Path) {
- if (CompareFilenamesOnly) {
- SourceFiles.emplace_back(Path);
- } else {
- SmallString<128> EffectivePath(Path);
- if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
- error(EC.message(), Path);
- return;
- }
- sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true);
- SourceFiles.emplace_back(EffectivePath.str());
+ SmallString<128> EffectivePath(Path);
+ if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
+ error(EC.message(), Path);
+ return;
}
+ sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true);
+ SourceFiles.emplace_back(EffectivePath.str());
}
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 (CompareFilenamesOnly)
+ if (PathRemapping)
addCollectedPath(Path);
else
error("Missing source file", Path);
@@ -348,6 +349,8 @@
if (Mismatched)
warning(utostr(Mismatched) + " functions have mismatched data");
+ remapPathNames(*Coverage);
+
if (!SourceFiles.empty())
removeUnmappedInputs(*Coverage);
@@ -356,33 +359,58 @@
return Coverage;
}
+void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
+ if (!PathRemapping)
+ return;
+
+ // Convert remapping paths to native paths with trailing seperators.
+ auto nativeWithTrailing = [](StringRef Path) -> std::string {
+ if (Path.empty())
+ return "";
+ SmallString<128> NativePath;
+ sys::path::native(Path, NativePath);
+ if (!sys::path::is_separator(NativePath.back()))
+ 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);
+ if (NativeFilename.startswith(RemapFrom)) {
+ RemappedFilenames[Filename] =
+ RemapTo + NativeFilename.substr(RemapFrom.size()).str();
+ }
+ }
+
+ // Convert input files from local paths to coverage data file paths.
+ StringMap<std::string> InvRemappedFilenames;
+ for (const auto &RemappedFilename : RemappedFilenames)
+ InvRemappedFilenames[RemappedFilename.getValue()] = RemappedFilename.getKey();
+
+ for (std::string &Filename : SourceFiles) {
+ SmallString<128> NativeFilename;
+ sys::path::native(Filename, NativeFilename);
+ auto CovFileName = InvRemappedFilenames.find(NativeFilename);
+ if (CovFileName != InvRemappedFilenames.end())
+ Filename = CovFileName->second;
+ }
+}
+
void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
auto UncoveredFilesIt = SourceFiles.end();
- if (!CompareFilenamesOnly) {
- // The user may have specified source files which aren't in the coverage
- // mapping. Filter these files away.
- UncoveredFilesIt = std::remove_if(
- SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) {
- return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(),
- SF);
- });
- } else {
- for (auto &SF : SourceFiles) {
- StringRef SFBase = sys::path::filename(SF);
- for (const auto &CF : CoveredFiles) {
- if (SFBase == sys::path::filename(CF)) {
- RemappedFilenames[CF] = SF;
- SF = CF;
- break;
- }
- }
- }
- UncoveredFilesIt = std::remove_if(
- SourceFiles.begin(), SourceFiles.end(),
- [&](const std::string &SF) { return !RemappedFilenames.count(SF); });
- }
+ // The user may have specified source files which aren't in the coverage
+ // mapping. Filter these files away.
+ UncoveredFilesIt = std::remove_if(
+ SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) {
+ return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(),
+ SF);
+ });
SourceFiles.erase(UncoveredFilesIt, SourceFiles.end());
}
@@ -521,10 +549,10 @@
"HTML output")),
cl::init(CoverageViewOptions::OutputFormat::Text));
- cl::opt<bool> FilenameEquivalence(
- "filename-equivalence", cl::Optional,
- 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<std::string> PathRemap(
+ "path-equivalence", cl::Optional,
+ cl::desc("<from>,<to> Map coverage data paths to local source file "
+ "paths"));
cl::OptionCategory FilteringCategory("Function filtering options");
@@ -573,7 +601,6 @@
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
ViewOpts.Debug = DebugDump;
- CompareFilenamesOnly = FilenameEquivalence;
if (!CovFilename.empty())
ObjectFilenames.emplace_back(CovFilename);
@@ -598,6 +625,12 @@
break;
}
+ // If path-equivalence was given and is a comma seperated pair then set
+ // PathRemapping.
+ auto EquivPair = StringRef(PathRemap).split(',');
+ if (!(EquivPair.first.empty() && EquivPair.second.empty()))
+ PathRemapping = EquivPair;
+
// If a demangler is supplied, check if it exists and register it.
if (DemanglerOpts.size()) {
auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);