diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -264,6 +264,13 @@ Skip source code files with file paths that match the given regular expression. +.. option:: -ignore-symlinks + + Do not follow symbolic links to detect the same file. Following symbolic links + will generate an accurate report, but this could slow down processing time. + Thus, compare path strings only after normalization instead of following + symbolic links. + .. option:: -format= Use the specified output format. The supported formats are: "text", "html". diff --git a/llvm/test/tools/llvm-cov/sources-specified.test b/llvm/test/tools/llvm-cov/sources-specified.test --- a/llvm/test/tools/llvm-cov/sources-specified.test +++ b/llvm/test/tools/llvm-cov/sources-specified.test @@ -10,6 +10,14 @@ RUN: %S/Inputs/sources_specified/main.cc %S/Inputs/sources_specified/extra \ RUN: | FileCheck -check-prefix=SHOW %s +# It has to produce the same result even if --ignore-symlinks is given. +RUN: llvm-cov show -instr-profile %S/Inputs/sources_specified/main.profdata \ +RUN: -path-equivalence=/tmp,%S/Inputs \ +RUN: -ignore-symlinks \ +RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: %S/Inputs/sources_specified/main.cc %S/Inputs/sources_specified/extra \ +RUN: | FileCheck -check-prefix=SYMLINK %s + # Don't include all source files when provided source files are filtered out. RUN: llvm-cov show -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: -path-equivalence=/tmp,%S/Inputs \ @@ -32,6 +40,11 @@ SHOW: {{.*}}sources_specified{{.*}} SHOW: {{.*}}sources_specified{{.*}} +# Order of files may differ, check that there are 3 files and not abs.h. +SYMLINK-NOT: {{.*}}abs.h{{.*}} +SYMLINK: {{.*}}sources_specified{{.*}} +SYMLINK: {{.*}}sources_specified{{.*}} +SYMLINK: {{.*}}sources_specified{{.*}} # Test "export" command. Use a temp .json file as output is a single line. RUN: llvm-cov export -instr-profile %S/Inputs/sources_specified/main.profdata \ 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 @@ -80,6 +80,9 @@ /// directory, recursively collect all of the paths within the directory. void collectPaths(const std::string &Path); + /// Check if the two given two files are the same file. + bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2); + /// Return a memory buffer for the given source file. ErrorOr getSourceFile(StringRef SourceFile); @@ -135,6 +138,7 @@ CoverageViewOptions ViewOpts; CoverageFiltersMatchAll Filters; CoverageFilters IgnoreFilenameFilters; + bool ComparePathStringOnly = false; /// True if InputSourceFiles are provided. bool HadSourceFiles = false; @@ -239,6 +243,14 @@ } } +bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1, + StringRef FilePath2) { + if (ComparePathStringOnly) + return FilePath1 == FilePath2; + + return sys::fs::equivalent(FilePath1, FilePath2); +} + ErrorOr CodeCoverageTool::getSourceFile(StringRef SourceFile) { // If we've remapped filenames, look up the real location for this file. @@ -248,9 +260,16 @@ if (Loc != RemappedFilenames.end()) SourceFile = Loc->second; } + + SmallString<256> NormalizedPath; + sys::path::native(SourceFile, NormalizedPath); + sys::path::remove_dots(NormalizedPath, true); + SourceFile = NormalizedPath; + for (const auto &Files : LoadedSourceFiles) - if (sys::fs::equivalent(SourceFile, Files.first)) + if (isEquivalentFile(SourceFile, Files.first)) return *Files.second; + auto Buffer = MemoryBuffer::getFile(SourceFile); if (auto EC = Buffer.getError()) { error(EC.message(), SourceFile); @@ -654,6 +673,12 @@ "regular expression"), cl::ZeroOrMore, cl::cat(FilteringCategory)); + cl::opt IgnoreSymlinks( + "ignore-symlinks", cl::Optional, + cl::desc("Do not follow symlinks to check file equivalence. " + "Use only normalized path strings to find the same file."), + cl::init(false)); + cl::opt RegionCoverageLtFilter( "region-coverage-lt", cl::Optional, cl::desc("Show code coverage only for functions with region coverage " @@ -838,6 +863,7 @@ ::exit(0); } + ComparePathStringOnly = IgnoreSymlinks; ViewOpts.ShowBranchSummary = BranchSummary; ViewOpts.ShowRegionSummary = RegionSummary; ViewOpts.ShowInstantiationSummary = InstantiationSummary;