diff options
| author | 2017-01-24 08:32:59 +0000 | |
|---|---|---|
| committer | 2017-01-24 08:32:59 +0000 | |
| commit | 53d771aafdbe5b919f264f53cba3788e2c4cffd2 (patch) | |
| tree | 7eca39498be0ff1e3a6daf583cd9ca5886bb2636 /gnu/llvm/tools/llvm-cov/CodeCoverage.cpp | |
| parent | In preparation of compiling our kernels with -ffreestanding, explicitly map (diff) | |
| download | wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.tar.xz wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.zip | |
Import LLVM 4.0.0 rc1 including clang and lld to help the current
development effort on OpenBSD/arm64.
Diffstat (limited to 'gnu/llvm/tools/llvm-cov/CodeCoverage.cpp')
| -rw-r--r-- | gnu/llvm/tools/llvm-cov/CodeCoverage.cpp | 379 |
1 files changed, 261 insertions, 118 deletions
diff --git a/gnu/llvm/tools/llvm-cov/CodeCoverage.cpp b/gnu/llvm/tools/llvm-cov/CodeCoverage.cpp index 0a4d1a67d61..0a9807ab003 100644 --- a/gnu/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/gnu/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/ToolOutputFile.h" #include <functional> @@ -38,6 +39,9 @@ using namespace llvm; using namespace coverage; +void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping, + raw_ostream &OS); + namespace { /// \brief The implementation of the coverage tool. class CodeCoverageTool { @@ -46,24 +50,28 @@ public: /// \brief The show command. Show, /// \brief The report command. - Report + Report, + /// \brief The export command. + Export }; + int run(Command Cmd, int argc, const char **argv); + +private: /// \brief Print the error message to the error output stream. void error(const Twine &Message, StringRef Whence = ""); - /// \brief Record (but do not print) an error message in a thread-safe way. - void deferError(const Twine &Message, StringRef Whence = ""); - - /// \brief Record (but do not print) a warning message in a thread-safe way. - void deferWarning(const Twine &Message, StringRef Whence = ""); - - /// \brief Print (and then clear) all deferred error and warning messages. - void consumeDeferredMessages(); + /// \brief Print the warning message to the error output stream. + void warning(const Twine &Message, StringRef Whence = ""); - /// \brief Append a reference to a private copy of \p Path into SourceFiles. + /// \brief Convert \p Path into an absolute path and append it to the list + /// of collected paths. void addCollectedPath(const std::string &Path); + /// \brief If \p Path is a regular file, collect the path. If it's a + /// directory, recursively collect all of the paths within the directory. + void collectPaths(const std::string &Path); + /// \brief Return a memory buffer for the given source file. ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); @@ -81,16 +89,21 @@ public: std::unique_ptr<SourceCoverageView> createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); - /// \brief Load the coverage mapping data. Return nullptr if an error occured. + /// \brief Load the coverage mapping data. Return nullptr if an error occurred. std::unique_ptr<CoverageMapping> load(); + /// \brief Remove input source files which aren't mapped by \p Coverage. + void removeUnmappedInputs(const CoverageMapping &Coverage); + /// \brief If a demangler is available, demangle all symbol names. void demangleSymbols(const CoverageMapping &Coverage); /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. StringRef getSymbolForHumans(StringRef Sym) const; - int run(Command Cmd, int argc, const char **argv); + /// \brief Write out a source file view to the filesystem. + void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, + CoveragePrinter *Printer, bool ShowFilenames); typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; @@ -100,25 +113,34 @@ public: int report(int argc, const char **argv, CommandLineParserType commandLineParser); - std::string ObjectFilename; + int export_(int argc, const char **argv, + CommandLineParserType commandLineParser); + + std::vector<StringRef> ObjectFilenames; CoverageViewOptions ViewOpts; - std::string PGOFilename; CoverageFiltersMatchAll Filters; - std::vector<StringRef> SourceFiles; + + /// The path to the indexed profile. + std::string PGOFilename; + + /// 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. StringMap<std::string> RemappedFilenames; + + /// The architecture the coverage mapping data targets. std::string CoverageArch; -private: /// A cache for demangled symbol names. StringMap<std::string> DemangledNames; - /// File paths (absolute, or otherwise) to input source files. - std::vector<std::string> CollectedPaths; - /// Errors and warnings which have not been printed. - std::mutex DeferredMessagesLock; - std::vector<std::string> DeferredMessages; + std::mutex ErrsLock; /// A container for input source file buffers. std::mutex LoadedSourceFilesLock; @@ -138,29 +160,57 @@ static std::string getErrorString(const Twine &Message, StringRef Whence, } void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { - errs() << getErrorString(Message, Whence, false); + std::unique_lock<std::mutex> Guard{ErrsLock}; + ViewOpts.colored_ostream(errs(), raw_ostream::RED) + << getErrorString(Message, Whence, false); } -void CodeCoverageTool::deferError(const Twine &Message, StringRef Whence) { - std::unique_lock<std::mutex> Guard{DeferredMessagesLock}; - DeferredMessages.emplace_back(getErrorString(Message, Whence, false)); +void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { + std::unique_lock<std::mutex> Guard{ErrsLock}; + ViewOpts.colored_ostream(errs(), raw_ostream::RED) + << getErrorString(Message, Whence, true); } -void CodeCoverageTool::deferWarning(const Twine &Message, StringRef Whence) { - std::unique_lock<std::mutex> Guard{DeferredMessagesLock}; - DeferredMessages.emplace_back(getErrorString(Message, Whence, true)); +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()); + } } -void CodeCoverageTool::consumeDeferredMessages() { - std::unique_lock<std::mutex> Guard{DeferredMessagesLock}; - for (const std::string &Message : DeferredMessages) - ViewOpts.colored_ostream(errs(), raw_ostream::RED) << Message; - DeferredMessages.clear(); -} +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) + addCollectedPath(Path); + else + error("Missing source file", Path); + return; + } -void CodeCoverageTool::addCollectedPath(const std::string &Path) { - CollectedPaths.push_back(Path); - SourceFiles.emplace_back(CollectedPaths.back()); + if (llvm::sys::fs::is_regular_file(Status)) { + addCollectedPath(Path); + return; + } + + if (llvm::sys::fs::is_directory(Status)) { + std::error_code EC; + for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; + F != E && !EC; F.increment(EC)) { + if (llvm::sys::fs::is_regular_file(F->path())) + addCollectedPath(F->path()); + } + if (EC) + warning(EC.message(), Path); + } } ErrorOr<const MemoryBuffer &> @@ -177,7 +227,7 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) { return *Files.second; auto Buffer = MemoryBuffer::getFile(SourceFile); if (auto EC = Buffer.getError()) { - deferError(EC.message(), SourceFile); + error(EC.message(), SourceFile); return EC; } LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get())); @@ -241,21 +291,24 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile, attachExpansionSubViews(*View, Expansions, Coverage); for (const auto *Function : Coverage.getInstantiations(SourceFile)) { - auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); - auto SubViewExpansions = SubViewCoverage.getExpansions(); - auto SubView = SourceCoverageView::create( - getSymbolForHumans(Function->Name), SourceBuffer.get(), ViewOpts, - std::move(SubViewCoverage)); - attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + std::unique_ptr<SourceCoverageView> SubView{nullptr}; + + StringRef Funcname = getSymbolForHumans(Function->Name); - if (SubView) { - unsigned FileID = Function->CountedRegions.front().FileID; - unsigned Line = 0; - for (const auto &CR : Function->CountedRegions) - if (CR.FileID == FileID) - Line = std::max(CR.LineEnd, Line); - View->addInstantiation(Function->Name, Line, std::move(SubView)); + if (Function->ExecutionCount > 0) { + auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); + auto SubViewExpansions = SubViewCoverage.getExpansions(); + SubView = SourceCoverageView::create( + Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); + attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); } + + unsigned FileID = Function->CountedRegions.front().FileID; + unsigned Line = 0; + for (const auto &CR : Function->CountedRegions) + if (CR.FileID == FileID) + Line = std::max(CR.LineEnd, Line); + View->addInstantiation(Funcname, Line, std::move(SubView)); } return View; } @@ -272,39 +325,59 @@ static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { } std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { - if (modifiedTimeGT(ObjectFilename, PGOFilename)) - errs() << "warning: profile data may be out of date - object is newer\n"; - auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename, - CoverageArch); + for (StringRef ObjectFilename : ObjectFilenames) + if (modifiedTimeGT(ObjectFilename, PGOFilename)) + warning("profile data may be out of date - object is newer", + ObjectFilename); + auto CoverageOrErr = + CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArch); if (Error E = CoverageOrErr.takeError()) { - colored_ostream(errs(), raw_ostream::RED) - << "error: Failed to load coverage: " << toString(std::move(E)) << "\n"; + error("Failed to load coverage: " + toString(std::move(E)), + join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); return nullptr; } auto Coverage = std::move(CoverageOrErr.get()); unsigned Mismatched = Coverage->getMismatchedCount(); - if (Mismatched) { - colored_ostream(errs(), raw_ostream::RED) - << "warning: " << Mismatched << " functions have mismatched data. "; - errs() << "\n"; - } + if (Mismatched) + warning(utostr(Mismatched) + " functions have mismatched data"); - if (CompareFilenamesOnly) { - auto CoveredFiles = Coverage.get()->getUniqueSourceFiles(); + if (!SourceFiles.empty()) + removeUnmappedInputs(*Coverage); + + demangleSymbols(*Coverage); + + return Coverage; +} + +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) + 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); }); } - demangleSymbols(*Coverage); - - return Coverage; + SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); } void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { @@ -390,14 +463,43 @@ StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const { return DemangledName->getValue(); } +void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, + CoverageMapping *Coverage, + CoveragePrinter *Printer, + bool ShowFilenames) { + auto View = createSourceFileView(SourceFile, *Coverage); + if (!View) { + warning("The file '" + SourceFile + "' isn't covered."); + return; + } + + auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); + if (Error E = OSOrErr.takeError()) { + error("Could not create view file!", toString(std::move(E))); + return; + } + auto OS = std::move(OSOrErr.get()); + + View->print(*OS.get(), /*Wholefile=*/true, + /*ShowSourceName=*/ShowFilenames); + Printer->closeViewFile(std::move(OS)); +} + int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { - cl::opt<std::string, true> ObjectFilename( - cl::Positional, cl::Required, cl::location(this->ObjectFilename), - cl::desc("Covered executable or object file.")); + cl::opt<std::string> CovFilename( + cl::Positional, cl::desc("Covered executable or object file.")); + + cl::list<std::string> CovFilenames( + "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore, + cl::CommaSeparated); cl::list<std::string> InputSourceFiles( cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); + cl::opt<bool> DebugDumpCollectedPaths( + "dump-collected-paths", cl::Optional, cl::Hidden, + cl::desc("Show the collected paths to source files")); + cl::opt<std::string, true> PGOFilename( "instr-profile", cl::Required, cl::location(this->PGOFilename), cl::desc( @@ -414,8 +516,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", "Text output"), clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", - "HTML output"), - clEnumValEnd), + "HTML output")), cl::init(CoverageViewOptions::OutputFormat::Text)); cl::opt<bool> FilenameEquivalence( @@ -472,6 +573,15 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { ViewOpts.Debug = DebugDump; CompareFilenamesOnly = FilenameEquivalence; + if (!CovFilename.empty()) + ObjectFilenames.emplace_back(CovFilename); + for (const std::string &Filename : CovFilenames) + ObjectFilenames.emplace_back(Filename); + if (ObjectFilenames.empty()) { + errs() << "No filenames specified!\n"; + ::exit(1); + } + ViewOpts.Format = Format; switch (ViewOpts.Format) { case CoverageViewOptions::OutputFormat::Text: @@ -481,7 +591,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { break; case CoverageViewOptions::OutputFormat::HTML: if (UseColor == cl::BOU_FALSE) - error("Color output cannot be disabled when generating html."); + errs() << "Color output cannot be disabled when generating html.\n"; ViewOpts.Colors = true; break; } @@ -530,20 +640,20 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { if (!Arch.empty() && Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { - errs() << "error: Unknown architecture: " << Arch << "\n"; + error("Unknown architecture: " + Arch); return 1; } CoverageArch = Arch; - for (const auto &File : InputSourceFiles) { - SmallString<128> Path(File); - if (!CompareFilenamesOnly) - if (std::error_code EC = sys::fs::make_absolute(Path)) { - errs() << "error: " << File << ": " << EC.message(); - return 1; - } - addCollectedPath(Path.str()); + for (const std::string &File : InputSourceFiles) + collectPaths(File); + + if (DebugDumpCollectedPaths) { + for (const std::string &SF : SourceFiles) + outs() << SF << '\n'; + ::exit(0); } + return 0; }; @@ -552,6 +662,8 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { return show(argc, argv, commandLineParser); case Report: return report(argc, argv, commandLineParser); + case Export: + return export_(argc, argv, commandLineParser); } return 0; } @@ -591,6 +703,15 @@ int CodeCoverageTool::show(int argc, const char **argv, cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), cl::aliasopt(ShowOutputDirectory)); + cl::opt<uint32_t> TabSize( + "tab-size", cl::init(2), + cl::desc( + "Set tab expansion size for html coverage reports (default = 2)")); + + cl::opt<std::string> ProjectTitle( + "project-title", cl::Optional, + cl::desc("Set project title for the coverage report")); + auto Err = commandLineParser(argc, argv); if (Err) return Err; @@ -603,6 +724,8 @@ int CodeCoverageTool::show(int argc, const char **argv, ViewOpts.ShowExpandedRegions = ShowExpansions; ViewOpts.ShowFunctionInstantiations = ShowInstantiations; ViewOpts.ShowOutputDirectory = ShowOutputDirectory; + ViewOpts.TabSize = TabSize; + ViewOpts.ProjectTitle = ProjectTitle; if (ViewOpts.hasOutputDirectory()) { if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { @@ -611,6 +734,19 @@ int CodeCoverageTool::show(int argc, const char **argv, } } + sys::fs::file_status Status; + if (sys::fs::status(PGOFilename, Status)) { + error("profdata file error: can not get the file status. \n"); + return 1; + } + + auto ModifiedTime = Status.getLastModificationTime(); + std::string ModifiedTimeStr = to_string(ModifiedTime); + size_t found = ModifiedTimeStr.rfind(':'); + ViewOpts.CreatedTimeStr = (found != std::string::npos) + ? "Created: " + ModifiedTimeStr.substr(0, found) + : "Created: " + ModifiedTimeStr; + auto Coverage = load(); if (!Coverage) return 1; @@ -632,9 +768,7 @@ int CodeCoverageTool::show(int argc, const char **argv, auto mainView = createFunctionView(Function, *Coverage); if (!mainView) { - ViewOpts.colored_ostream(errs(), raw_ostream::RED) - << "warning: Could not read coverage for '" << Function.Name << "'." - << "\n"; + warning("Could not read coverage for '" + Function.Name + "'."); continue; } @@ -646,7 +780,9 @@ int CodeCoverageTool::show(int argc, const char **argv, } // Show files - bool ShowFilenames = SourceFiles.size() != 1; + bool ShowFilenames = + (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || + (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); if (SourceFiles.empty()) // Get the source files from the function coverage mapping. @@ -655,43 +791,27 @@ int CodeCoverageTool::show(int argc, const char **argv, // Create an index out of the source files. if (ViewOpts.hasOutputDirectory()) { - if (Error E = Printer->createIndexFile(SourceFiles)) { + if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) { error("Could not create index file!", toString(std::move(E))); return 1; } } - // In -output-dir mode, it's safe to use multiple threads to print files. - unsigned ThreadCount = 1; - if (ViewOpts.hasOutputDirectory()) - ThreadCount = std::thread::hardware_concurrency(); - ThreadPool Pool(ThreadCount); - - for (StringRef SourceFile : SourceFiles) { - Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] { - auto View = createSourceFileView(SourceFile, *Coverage); - if (!View) { - deferWarning("The file '" + SourceFile.str() + "' isn't covered."); - return; - } - - auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); - if (Error E = OSOrErr.takeError()) { - deferError("Could not create view file!", toString(std::move(E))); - return; - } - auto OS = std::move(OSOrErr.get()); - - View->print(*OS.get(), /*Wholefile=*/true, - /*ShowSourceName=*/ShowFilenames); - Printer->closeViewFile(std::move(OS)); - }); + // FIXME: Sink the hardware_concurrency() == 1 check into ThreadPool. + if (!ViewOpts.hasOutputDirectory() || + std::thread::hardware_concurrency() == 1) { + for (const std::string &SourceFile : SourceFiles) + writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), + ShowFilenames); + } else { + // In -output-dir mode, it's safe to use multiple threads to print files. + ThreadPool Pool; + for (const std::string &SourceFile : SourceFiles) + Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, + Coverage.get(), Printer.get(), ShowFilenames); + Pool.wait(); } - Pool.wait(); - - consumeDeferredMessages(); - return 0; } @@ -708,7 +828,7 @@ int CodeCoverageTool::report(int argc, const char **argv, if (!Coverage) return 1; - CoverageReport Report(ViewOpts, std::move(Coverage)); + CoverageReport Report(ViewOpts, *Coverage.get()); if (SourceFiles.empty()) Report.renderFileReports(llvm::outs()); else @@ -716,6 +836,24 @@ int CodeCoverageTool::report(int argc, const char **argv, return 0; } +int CodeCoverageTool::export_(int argc, const char **argv, + CommandLineParserType commandLineParser) { + + auto Err = commandLineParser(argc, argv); + if (Err) + return Err; + + auto Coverage = load(); + if (!Coverage) { + error("Could not load coverage information"); + return 1; + } + + exportCoverageDataToJson(*Coverage.get(), outs()); + + return 0; +} + int showMain(int argc, const char *argv[]) { CodeCoverageTool Tool; return Tool.run(CodeCoverageTool::Show, argc, argv); @@ -725,3 +863,8 @@ int reportMain(int argc, const char *argv[]) { CodeCoverageTool Tool; return Tool.run(CodeCoverageTool::Report, argc, argv); } + +int exportMain(int argc, const char *argv[]) { + CodeCoverageTool Tool; + return Tool.run(CodeCoverageTool::Export, argc, argv); +} |
