diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -32,6 +32,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/ThreadPool.h" @@ -80,6 +81,9 @@ /// directory, recursively collect all of the paths within the directory. void collectPaths(const std::string &Path); + /// Return the actual file path of a given source file + StringRef getSourceFilePath(StringRef SourceFile); + /// Return a memory buffer for the given source file. ErrorOr getSourceFile(StringRef SourceFile); @@ -106,6 +110,17 @@ /// Load the coverage mapping data. Return nullptr if an error occurred. std::unique_ptr load(); + // Convert remapping paths to native paths with trailing seperators. + std::string CodeCoverageTool::nativeWithTrailing(StringRef Path); + + // Initialize RemappedFilenames by a regular expression of the coverage data + // path (path-equivalence-regex). + void remapPathsFromRegex(const CoverageMapping &Coverage); + + // Initialize RemappedFilenames by a string of the coverage data path + // (path-equivalence). + void remapPathsFromString(const CoverageMapping &Coverage); + /// Create a mapping from files in the Coverage data to local copies /// (path-equivalence). void remapPathNames(const CoverageMapping &Coverage); @@ -153,6 +168,9 @@ /// remapped to, when using -path-equivalence. Optional> PathRemapping; + // True if the first value of PathRemapping is a regular expression. + bool PathRemappingRegex = false; + /// The architecture the coverage mapping data targets. std::vector CoverageArches; @@ -239,17 +257,24 @@ } } +StringRef CodeCoverageTool::getSourceFilePath(StringRef SourceFile) { + if (!RemappedFilenames.empty()) { + auto LocalFile = RemappedFilenames.find(SourceFile); + if (LocalFile != RemappedFilenames.end()) + SourceFile = LocalFile->second; + } + + return SourceFile; +} + ErrorOr CodeCoverageTool::getSourceFile(StringRef SourceFile) { // If we've remapped filenames, look up the real location for this file. std::unique_lock Guard{LoadedSourceFilesLock}; - if (!RemappedFilenames.empty()) { - auto Loc = RemappedFilenames.find(SourceFile); - if (Loc != RemappedFilenames.end()) - SourceFile = Loc->second; - } + SourceFile = getSourceFilePath(SourceFile); for (const auto &Files : LoadedSourceFiles) - if (sys::fs::equivalent(SourceFile, Files.first)) + if ((!PathRemappingRegex && sys::fs::equivalent(SourceFile, Files.first)) || + (PathRemappingRegex && SourceFile == Files.first)) return *Files.second; auto Buffer = MemoryBuffer::getFile(SourceFile); if (auto EC = Buffer.getError()) { @@ -345,12 +370,13 @@ if (FileCoverage.empty()) return nullptr; + auto RemappedSourceFile = getSourceFilePath(SourceFile); auto Branches = FileCoverage.getBranches(); auto Expansions = FileCoverage.getExpansions(); - auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), + auto View = SourceCoverageView::create(RemappedSourceFile, SourceBuffer.get(), ViewOpts, std::move(FileCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); - attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(), + attachBranchSubViews(*View, RemappedSourceFile, Branches, SourceBuffer.get(), FileCoverage); if (!ViewOpts.ShowFunctionInstantiations) return View; @@ -434,21 +460,39 @@ return Coverage; } -void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { - if (!PathRemapping) - return; +std::string CodeCoverageTool::nativeWithTrailing(StringRef Path) { + if (Path.empty()) + return ""; + SmallString<256> NativePath; + sys::path::native(Path, NativePath); + if (!sys::path::is_separator(NativePath.back())) + NativePath += sys::path::get_separator(); + return NativePath.c_str(); +}; - // 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); - sys::path::remove_dots(NativePath, true); - if (!sys::path::is_separator(NativePath.back())) - NativePath += sys::path::get_separator(); - return NativePath.c_str(); - }; +void CodeCoverageTool::remapPathsFromRegex(const CoverageMapping &Coverage) { + llvm::Regex RemapFrom = llvm::Regex(PathRemapping->first); + std::string RemapTo = nativeWithTrailing(PathRemapping->second); + + // Create a mapping from coverage data file paths to local paths. + for (StringRef Filename : Coverage.getUniqueSourceFiles()) { + SmallVector Matches; + if (RemapFrom.match(Filename.str(), &Matches)) { + SmallString<256> RemappedNativeFilename; + if (Matches.size() == 2) + sys::path::native(Matches[1].str() + + RemapFrom.sub(RemapTo, Filename.str()), + RemappedNativeFilename); + else + sys::path::native(RemapFrom.sub(RemapTo, Filename.str()), + RemappedNativeFilename); + + RemappedFilenames[Filename] = std::string(RemappedNativeFilename.str()); + } + } +} + +void CodeCoverageTool::remapPathsFromString(const CoverageMapping &Coverage) { std::string RemapFrom = nativeWithTrailing(PathRemapping->first); std::string RemapTo = nativeWithTrailing(PathRemapping->second); @@ -462,6 +506,16 @@ RemapTo + NativeFilename.substr(RemapFrom.size()).str(); } } +} + +void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { + if (!PathRemapping) + return; + + if (PathRemappingRegex) + remapPathsFromRegex(Coverage); + else + remapPathsFromString(Coverage); // Convert input files from local paths to coverage data file paths. StringMap InvRemappedFilenames; @@ -470,7 +524,7 @@ std::string(RemappedFilename.getKey()); for (std::string &Filename : SourceFiles) { - SmallString<128> NativeFilename; + SmallString<256> NativeFilename; sys::path::native(Filename, NativeFilename); auto CovFileName = InvRemappedFilenames.find(NativeFilename); if (CovFileName != InvRemappedFilenames.end()) @@ -572,7 +626,8 @@ return; } - auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); + auto OSOrErr = Printer->createViewFile(getSourceFilePath(SourceFile), + /*InToplevel=*/false); if (Error E = OSOrErr.takeError()) { error("Could not create view file!", toString(std::move(E))); return; @@ -629,6 +684,11 @@ cl::desc(", Map coverage data paths to local source file " "paths")); + cl::opt PathRemapRegex( + "path-equivalence-regex", cl::Optional, + cl::desc(", Map regular expression of coverage data paths " + "to local source file paths.")); + cl::OptionCategory FilteringCategory("Function filtering options"); cl::list NameFilters( @@ -754,6 +814,13 @@ PathRemapping = {std::string(EquivPair.first), std::string(EquivPair.second)}; + auto EquivRegexPair = StringRef(PathRemapRegex).split(','); + if (!(EquivRegexPair.first.empty() && EquivRegexPair.second.empty())) { + PathRemapping = {std::string(EquivRegexPair.first), + std::string(EquivRegexPair.second)}; + PathRemappingRegex = true; + } + // If a demangler is supplied, check if it exists and register it. if (!DemanglerOpts.empty()) { auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);