diff --git a/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp b/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp new file mode 100644 index 000000000000..7cee4d4b3f2e --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -0,0 +1,483 @@ +//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The 'CodeCoverageTool' class implements a command line tool to analyze and +// report coverage information using the profiling instrumentation and code +// coverage mapping. +// +//===----------------------------------------------------------------------===// + +#include "RenderingSupport.h" +#include "CoverageFilters.h" +#include "CoverageReport.h" +#include "CoverageSummary.h" +#include "CoverageViewOptions.h" +#include "SourceCoverageView.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/ProfileData/CoverageMappingReader.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include +#include + +using namespace llvm; +using namespace coverage; + +namespace { +/// \brief The implementation of the coverage tool. +class CodeCoverageTool { +public: + enum Command { + /// \brief The show command. + Show, + /// \brief The report command. + Report + }; + + /// \brief Print the error message to the error output stream. + void error(const Twine &Message, StringRef Whence = ""); + + /// \brief Return a memory buffer for the given source file. + ErrorOr getSourceFile(StringRef SourceFile); + + /// \brief Create source views for the expansions of the view. + void attachExpansionSubViews(SourceCoverageView &View, + ArrayRef Expansions, + CoverageMapping &Coverage); + + /// \brief Create the source view of a particular function. + std::unique_ptr + createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage); + + /// \brief Create the main source view of a particular source file. + std::unique_ptr + createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage); + + /// \brief Load the coverage mapping data. Return true if an error occured. + std::unique_ptr load(); + + int run(Command Cmd, int argc, const char **argv); + + typedef std::function CommandLineParserType; + + int show(int argc, const char **argv, + CommandLineParserType commandLineParser); + + int report(int argc, const char **argv, + CommandLineParserType commandLineParser); + + std::string ObjectFilename; + CoverageViewOptions ViewOpts; + std::string PGOFilename; + CoverageFiltersMatchAll Filters; + std::vector SourceFiles; + std::vector>> + LoadedSourceFiles; + bool CompareFilenamesOnly; + StringMap RemappedFilenames; +}; +} + +void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { + errs() << "error: "; + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; +} + +ErrorOr +CodeCoverageTool::getSourceFile(StringRef SourceFile) { + // If we've remapped filenames, look up the real location for this file. + if (!RemappedFilenames.empty()) { + auto Loc = RemappedFilenames.find(SourceFile); + if (Loc != RemappedFilenames.end()) + SourceFile = Loc->second; + } + for (const auto &Files : LoadedSourceFiles) + if (sys::fs::equivalent(SourceFile, Files.first)) + return *Files.second; + auto Buffer = MemoryBuffer::getFile(SourceFile); + if (auto EC = Buffer.getError()) { + error(EC.message(), SourceFile); + return EC; + } + LoadedSourceFiles.push_back( + std::make_pair(SourceFile, std::move(Buffer.get()))); + return *LoadedSourceFiles.back().second; +} + +void +CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View, + ArrayRef Expansions, + CoverageMapping &Coverage) { + if (!ViewOpts.ShowExpandedRegions) + return; + for (const auto &Expansion : Expansions) { + auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); + if (ExpansionCoverage.empty()) + continue; + auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); + if (!SourceBuffer) + continue; + + auto SubViewExpansions = ExpansionCoverage.getExpansions(); + auto SubView = llvm::make_unique( + SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage)); + attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + View.addExpansion(Expansion.Region, std::move(SubView)); + } +} + +std::unique_ptr +CodeCoverageTool::createFunctionView(const FunctionRecord &Function, + CoverageMapping &Coverage) { + auto FunctionCoverage = Coverage.getCoverageForFunction(Function); + if (FunctionCoverage.empty()) + return nullptr; + auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); + if (!SourceBuffer) + return nullptr; + + auto Expansions = FunctionCoverage.getExpansions(); + auto View = llvm::make_unique( + SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage)); + attachExpansionSubViews(*View, Expansions, Coverage); + + return View; +} + +std::unique_ptr +CodeCoverageTool::createSourceFileView(StringRef SourceFile, + CoverageMapping &Coverage) { + auto SourceBuffer = getSourceFile(SourceFile); + if (!SourceBuffer) + return nullptr; + auto FileCoverage = Coverage.getCoverageForFile(SourceFile); + if (FileCoverage.empty()) + return nullptr; + + auto Expansions = FileCoverage.getExpansions(); + auto View = llvm::make_unique( + SourceBuffer.get(), ViewOpts, std::move(FileCoverage)); + attachExpansionSubViews(*View, Expansions, Coverage); + + for (auto Function : Coverage.getInstantiations(SourceFile)) { + auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); + auto SubViewExpansions = SubViewCoverage.getExpansions(); + auto SubView = llvm::make_unique( + SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); + attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + + 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)); + } + } + return View; +} + +std::unique_ptr CodeCoverageTool::load() { + auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename); + if (std::error_code EC = CoverageOrErr.getError()) { + colored_ostream(errs(), raw_ostream::RED) + << "error: Failed to load coverage: " << EC.message(); + errs() << "\n"; + 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 (CompareFilenamesOnly) { + auto CoveredFiles = Coverage.get()->getUniqueSourceFiles(); + 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; + } + } + } + + return Coverage; +} + +int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::opt ObjectFilename( + cl::Positional, cl::Required, cl::location(this->ObjectFilename), + cl::desc("Covered executable or object file.")); + + cl::list InputSourceFiles( + cl::Positional, cl::desc(""), cl::ZeroOrMore); + + cl::opt PGOFilename( + "instr-profile", cl::Required, cl::location(this->PGOFilename), + cl::desc( + "File with the profile data obtained after an instrumented run")); + + cl::opt DebugDump("dump", cl::Optional, + cl::desc("Show internal debug dump")); + + cl::opt 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::OptionCategory FilteringCategory("Function filtering options"); + + cl::list NameFilters( + "name", cl::Optional, + cl::desc("Show code coverage only for functions with the given name"), + cl::ZeroOrMore, cl::cat(FilteringCategory)); + + cl::list NameRegexFilters( + "name-regex", cl::Optional, + cl::desc("Show code coverage only for functions that match the given " + "regular expression"), + cl::ZeroOrMore, cl::cat(FilteringCategory)); + + cl::opt RegionCoverageLtFilter( + "region-coverage-lt", cl::Optional, + cl::desc("Show code coverage only for functions with region coverage " + "less than the given threshold"), + cl::cat(FilteringCategory)); + + cl::opt RegionCoverageGtFilter( + "region-coverage-gt", cl::Optional, + cl::desc("Show code coverage only for functions with region coverage " + "greater than the given threshold"), + cl::cat(FilteringCategory)); + + cl::opt LineCoverageLtFilter( + "line-coverage-lt", cl::Optional, + cl::desc("Show code coverage only for functions with line coverage less " + "than the given threshold"), + cl::cat(FilteringCategory)); + + cl::opt LineCoverageGtFilter( + "line-coverage-gt", cl::Optional, + cl::desc("Show code coverage only for functions with line coverage " + "greater than the given threshold"), + cl::cat(FilteringCategory)); + + auto commandLineParser = [&, this](int argc, const char **argv) -> int { + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); + ViewOpts.Debug = DebugDump; + CompareFilenamesOnly = FilenameEquivalence; + + // Create the function filters + if (!NameFilters.empty() || !NameRegexFilters.empty()) { + auto NameFilterer = new CoverageFilters; + for (const auto &Name : NameFilters) + NameFilterer->push_back(llvm::make_unique(Name)); + for (const auto &Regex : NameRegexFilters) + NameFilterer->push_back( + llvm::make_unique(Regex)); + Filters.push_back(std::unique_ptr(NameFilterer)); + } + if (RegionCoverageLtFilter.getNumOccurrences() || + RegionCoverageGtFilter.getNumOccurrences() || + LineCoverageLtFilter.getNumOccurrences() || + LineCoverageGtFilter.getNumOccurrences()) { + auto StatFilterer = new CoverageFilters; + if (RegionCoverageLtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique( + RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); + if (RegionCoverageGtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique( + RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); + if (LineCoverageLtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique( + LineCoverageFilter::LessThan, LineCoverageLtFilter)); + if (LineCoverageGtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique( + RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); + Filters.push_back(std::unique_ptr(StatFilterer)); + } + + for (const auto &File : InputSourceFiles) { + SmallString<128> Path(File); + if (std::error_code EC = sys::fs::make_absolute(Path)) { + errs() << "error: " << File << ": " << EC.message(); + return 1; + } + SourceFiles.push_back(Path.str()); + } + return 0; + }; + + switch (Cmd) { + case Show: + return show(argc, argv, commandLineParser); + case Report: + return report(argc, argv, commandLineParser); + } + return 0; +} + +int CodeCoverageTool::show(int argc, const char **argv, + CommandLineParserType commandLineParser) { + + cl::OptionCategory ViewCategory("Viewing options"); + + cl::opt ShowLineExecutionCounts( + "show-line-counts", cl::Optional, + cl::desc("Show the execution counts for each line"), cl::init(true), + cl::cat(ViewCategory)); + + cl::opt ShowRegions( + "show-regions", cl::Optional, + cl::desc("Show the execution counts for each region"), + cl::cat(ViewCategory)); + + cl::opt ShowBestLineRegionsCounts( + "show-line-counts-or-regions", cl::Optional, + cl::desc("Show the execution counts for each line, or the execution " + "counts for each region on lines that have multiple regions"), + cl::cat(ViewCategory)); + + cl::opt ShowExpansions("show-expansions", cl::Optional, + cl::desc("Show expanded source regions"), + cl::cat(ViewCategory)); + + cl::opt ShowInstantiations("show-instantiations", cl::Optional, + cl::desc("Show function instantiations"), + cl::cat(ViewCategory)); + + cl::opt NoColors("no-colors", cl::Optional, + cl::desc("Don't show text colors"), cl::init(false), + cl::cat(ViewCategory)); + + auto Err = commandLineParser(argc, argv); + if (Err) + return Err; + + ViewOpts.Colors = !NoColors; + ViewOpts.ShowLineNumbers = true; + ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || + !ShowRegions || ShowBestLineRegionsCounts; + ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; + ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; + ViewOpts.ShowExpandedRegions = ShowExpansions; + ViewOpts.ShowFunctionInstantiations = ShowInstantiations; + + auto Coverage = load(); + if (!Coverage) + return 1; + + if (!Filters.empty()) { + // Show functions + for (const auto &Function : Coverage->getCoveredFunctions()) { + if (!Filters.matches(Function)) + continue; + + auto mainView = createFunctionView(Function, *Coverage); + if (!mainView) { + ViewOpts.colored_ostream(outs(), raw_ostream::RED) + << "warning: Could not read coverage for '" << Function.Name; + outs() << "\n"; + continue; + } + ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name + << ":"; + outs() << "\n"; + mainView->render(outs(), /*WholeFile=*/false); + outs() << "\n"; + } + return 0; + } + + // Show files + bool ShowFilenames = SourceFiles.size() != 1; + + if (SourceFiles.empty()) + // Get the source files from the function coverage mapping + for (StringRef Filename : Coverage->getUniqueSourceFiles()) + SourceFiles.push_back(Filename); + + for (const auto &SourceFile : SourceFiles) { + auto mainView = createSourceFileView(SourceFile, *Coverage); + if (!mainView) { + ViewOpts.colored_ostream(outs(), raw_ostream::RED) + << "warning: The file '" << SourceFile << "' isn't covered."; + outs() << "\n"; + continue; + } + + if (ShowFilenames) { + ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":"; + outs() << "\n"; + } + mainView->render(outs(), /*Wholefile=*/true); + if (SourceFiles.size() > 1) + outs() << "\n"; + } + + return 0; +} + +int CodeCoverageTool::report(int argc, const char **argv, + CommandLineParserType commandLineParser) { + cl::opt NoColors("no-colors", cl::Optional, + cl::desc("Don't show text colors"), cl::init(false)); + + auto Err = commandLineParser(argc, argv); + if (Err) + return Err; + + ViewOpts.Colors = !NoColors; + + auto Coverage = load(); + if (!Coverage) + return 1; + + CoverageSummary Summarizer; + Summarizer.createSummaries(*Coverage); + CoverageReport Report(ViewOpts, Summarizer); + if (SourceFiles.empty() && Filters.empty()) { + Report.renderFileReports(llvm::outs()); + return 0; + } + + Report.renderFunctionReports(llvm::outs()); + return 0; +} + +int showMain(int argc, const char *argv[]) { + CodeCoverageTool Tool; + return Tool.run(CodeCoverageTool::Show, argc, argv); +} + +int reportMain(int argc, const char *argv[]) { + CodeCoverageTool Tool; + return Tool.run(CodeCoverageTool::Report, argc, argv); +} diff --git a/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp b/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp new file mode 100644 index 000000000000..325dd7235789 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp @@ -0,0 +1,59 @@ +//===- CoverageFilters.cpp - Function coverage mapping filters ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These classes provide filtering for function coverage mapping records. +// +//===----------------------------------------------------------------------===// + +#include "CoverageFilters.h" +#include "CoverageSummaryInfo.h" +#include "llvm/Support/Regex.h" + +using namespace llvm; + +bool NameCoverageFilter::matches(const coverage::FunctionRecord &Function) { + StringRef FuncName = Function.Name; + return FuncName.find(Name) != StringRef::npos; +} + +bool +NameRegexCoverageFilter::matches(const coverage::FunctionRecord &Function) { + return llvm::Regex(Regex).match(Function.Name); +} + +bool RegionCoverageFilter::matches(const coverage::FunctionRecord &Function) { + return PassesThreshold(FunctionCoverageSummary::get(Function) + .RegionCoverage.getPercentCovered()); +} + +bool LineCoverageFilter::matches(const coverage::FunctionRecord &Function) { + return PassesThreshold( + FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered()); +} + +void CoverageFilters::push_back(std::unique_ptr Filter) { + Filters.push_back(std::move(Filter)); +} + +bool CoverageFilters::matches(const coverage::FunctionRecord &Function) { + for (const auto &Filter : Filters) { + if (Filter->matches(Function)) + return true; + } + return false; +} + +bool +CoverageFiltersMatchAll::matches(const coverage::FunctionRecord &Function) { + for (const auto &Filter : Filters) { + if (!Filter->matches(Function)) + return false; + } + return true; +} diff --git a/contrib/llvm/tools/llvm-cov/CoverageFilters.h b/contrib/llvm/tools/llvm-cov/CoverageFilters.h new file mode 100644 index 000000000000..dc5dc98807b1 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageFilters.h @@ -0,0 +1,127 @@ +//===- CoverageFilters.h - Function coverage mapping filters --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These classes provide filtering for function coverage mapping records. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEFILTERS_H +#define LLVM_COV_COVERAGEFILTERS_H + +#include "llvm/ProfileData/CoverageMapping.h" +#include +#include + +namespace llvm { + +/// \brief Matches specific functions that pass the requirement of this filter. +class CoverageFilter { +public: + virtual ~CoverageFilter() {} + + /// \brief Return true if the function passes the requirements of this filter. + virtual bool matches(const coverage::FunctionRecord &Function) { + return true; + } +}; + +/// \brief Matches functions that contain a specific string in their name. +class NameCoverageFilter : public CoverageFilter { + StringRef Name; + +public: + NameCoverageFilter(StringRef Name) : Name(Name) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief Matches functions whose name matches a certain regular expression. +class NameRegexCoverageFilter : public CoverageFilter { + StringRef Regex; + +public: + NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief Matches numbers that pass a certain threshold. +template class StatisticThresholdFilter { +public: + enum Operation { LessThan, GreaterThan }; + +protected: + Operation Op; + T Threshold; + + StatisticThresholdFilter(Operation Op, T Threshold) + : Op(Op), Threshold(Threshold) {} + + /// \brief Return true if the given number is less than + /// or greater than the certain threshold. + bool PassesThreshold(T Value) const { + switch (Op) { + case LessThan: + return Value < Threshold; + case GreaterThan: + return Value > Threshold; + } + return false; + } +}; + +/// \brief Matches functions whose region coverage percentage +/// is above/below a certain percentage. +class RegionCoverageFilter : public CoverageFilter, + public StatisticThresholdFilter { +public: + RegionCoverageFilter(Operation Op, double Threshold) + : StatisticThresholdFilter(Op, Threshold) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief Matches functions whose line coverage percentage +/// is above/below a certain percentage. +class LineCoverageFilter : public CoverageFilter, + public StatisticThresholdFilter { +public: + LineCoverageFilter(Operation Op, double Threshold) + : StatisticThresholdFilter(Op, Threshold) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief A collection of filters. +/// Matches functions that match any filters contained +/// in an instance of this class. +class CoverageFilters : public CoverageFilter { +protected: + std::vector> Filters; + +public: + /// \brief Append a filter to this collection. + void push_back(std::unique_ptr Filter); + + bool empty() const { return Filters.empty(); } + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief A collection of filters. +/// Matches functions that match all of the filters contained +/// in an instance of this class. +class CoverageFiltersMatchAll : public CoverageFilters { +public: + bool matches(const coverage::FunctionRecord &Function) override; +}; + +} // namespace llvm + +#endif // LLVM_COV_COVERAGEFILTERS_H diff --git a/contrib/llvm/tools/llvm-cov/CoverageReport.cpp b/contrib/llvm/tools/llvm-cov/CoverageReport.cpp new file mode 100644 index 000000000000..6ae6ba55eb9c --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageReport.cpp @@ -0,0 +1,202 @@ +//===- CoverageReport.cpp - Code coverage report -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering of a code coverage report. +// +//===----------------------------------------------------------------------===// + +#include "CoverageReport.h" +#include "CoverageSummary.h" +#include "RenderingSupport.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +namespace { +/// \brief Helper struct which prints trimmed and aligned columns. +struct Column { + enum TrimKind { NoTrim, LeftTrim, RightTrim }; + + enum AlignmentKind { LeftAlignment, RightAlignment }; + + StringRef Str; + unsigned Width; + TrimKind Trim; + AlignmentKind Alignment; + + Column(StringRef Str, unsigned Width) + : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {} + + Column &set(TrimKind Value) { + Trim = Value; + return *this; + } + + Column &set(AlignmentKind Value) { + Alignment = Value; + return *this; + } + + void render(raw_ostream &OS) const; +}; +raw_ostream &operator<<(raw_ostream &OS, const Column &Value) { + Value.render(OS); + return OS; +} +} + +void Column::render(raw_ostream &OS) const { + if (Str.size() <= Width) { + if (Alignment == RightAlignment) { + OS.indent(Width - Str.size()); + OS << Str; + return; + } + OS << Str; + OS.indent(Width - Str.size()); + return; + } + + switch (Trim) { + case NoTrim: + OS << Str.substr(0, Width); + break; + case LeftTrim: + OS << "..." << Str.substr(Str.size() - Width + 3); + break; + case RightTrim: + OS << Str.substr(0, Width - 3) << "..."; + break; + } +} + +static Column column(StringRef Str, unsigned Width) { + return Column(Str, Width); +} + +template +static Column column(StringRef Str, unsigned Width, const T &Value) { + return Column(Str, Width).set(Value); +} + +static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 10}; +static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8}; + +/// \brief Prints a horizontal divider which spans across the given columns. +template +static void renderDivider(T (&Columns)[N], raw_ostream &OS) { + unsigned Length = 0; + for (unsigned I = 0; I < N; ++I) + Length += Columns[I]; + for (unsigned I = 0; I < Length; ++I) + OS << '-'; +} + +/// \brief Return the color which correponds to the coverage +/// percentage of a certain metric. +template +static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) { + if (Info.isFullyCovered()) + return raw_ostream::GREEN; + return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW + : raw_ostream::RED; +} + +void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) { + OS << column(File.Name, FileReportColumns[0], Column::LeftTrim) + << format("%*u", FileReportColumns[1], (unsigned)File.RegionCoverage.NumRegions); + Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered() + ? raw_ostream::GREEN + : raw_ostream::RED) + << format("%*u", FileReportColumns[2], (unsigned)File.RegionCoverage.NotCovered); + Options.colored_ostream(OS, + determineCoveragePercentageColor(File.RegionCoverage)) + << format("%*.2f", FileReportColumns[3] - 1, + File.RegionCoverage.getPercentCovered()) << '%'; + OS << format("%*u", FileReportColumns[4], + (unsigned)File.FunctionCoverage.NumFunctions); + Options.colored_ostream( + OS, determineCoveragePercentageColor(File.FunctionCoverage)) + << format("%*.2f", FileReportColumns[5] - 1, + File.FunctionCoverage.getPercentCovered()) << '%'; + OS << "\n"; +} + +void CoverageReport::render(const FunctionCoverageSummary &Function, + raw_ostream &OS) { + OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim) + << format("%*u", FunctionReportColumns[1], + (unsigned)Function.RegionCoverage.NumRegions); + Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered() + ? raw_ostream::GREEN + : raw_ostream::RED) + << format("%*u", FunctionReportColumns[2], + (unsigned)Function.RegionCoverage.NotCovered); + Options.colored_ostream( + OS, determineCoveragePercentageColor(Function.RegionCoverage)) + << format("%*.2f", FunctionReportColumns[3] - 1, + Function.RegionCoverage.getPercentCovered()) << '%'; + OS << format("%*u", FunctionReportColumns[4], + (unsigned)Function.LineCoverage.NumLines); + Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered() + ? raw_ostream::GREEN + : raw_ostream::RED) + << format("%*u", FunctionReportColumns[5], + (unsigned)Function.LineCoverage.NotCovered); + Options.colored_ostream( + OS, determineCoveragePercentageColor(Function.LineCoverage)) + << format("%*.2f", FunctionReportColumns[6] - 1, + Function.LineCoverage.getPercentCovered()) << '%'; + OS << "\n"; +} + +void CoverageReport::renderFunctionReports(raw_ostream &OS) { + bool isFirst = true; + for (const auto &File : Summary.getFileSummaries()) { + if (isFirst) + isFirst = false; + else + OS << "\n"; + OS << "File '" << File.Name << "':\n"; + OS << column("Name", FunctionReportColumns[0]) + << column("Regions", FunctionReportColumns[1], Column::RightAlignment) + << column("Miss", FunctionReportColumns[2], Column::RightAlignment) + << column("Cover", FunctionReportColumns[3], Column::RightAlignment) + << column("Lines", FunctionReportColumns[4], Column::RightAlignment) + << column("Miss", FunctionReportColumns[5], Column::RightAlignment) + << column("Cover", FunctionReportColumns[6], Column::RightAlignment); + OS << "\n"; + renderDivider(FunctionReportColumns, OS); + OS << "\n"; + for (const auto &Function : File.FunctionSummaries) + render(Function, OS); + renderDivider(FunctionReportColumns, OS); + OS << "\n"; + render(FunctionCoverageSummary("TOTAL", /*ExecutionCount=*/0, + File.RegionCoverage, File.LineCoverage), + OS); + } +} + +void CoverageReport::renderFileReports(raw_ostream &OS) { + OS << column("Filename", FileReportColumns[0]) + << column("Regions", FileReportColumns[1], Column::RightAlignment) + << column("Miss", FileReportColumns[2], Column::RightAlignment) + << column("Cover", FileReportColumns[3], Column::RightAlignment) + << column("Functions", FileReportColumns[4], Column::RightAlignment) + << column("Executed", FileReportColumns[5], Column::RightAlignment) + << "\n"; + renderDivider(FileReportColumns, OS); + OS << "\n"; + for (const auto &File : Summary.getFileSummaries()) + render(File, OS); + renderDivider(FileReportColumns, OS); + OS << "\n"; + render(Summary.getCombinedFileSummaries(), OS); +} diff --git a/contrib/llvm/tools/llvm-cov/CoverageReport.h b/contrib/llvm/tools/llvm-cov/CoverageReport.h new file mode 100644 index 000000000000..d18611740ae6 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageReport.h @@ -0,0 +1,40 @@ +//===- CoverageReport.h - Code coverage report ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering of a code coverage report. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEREPORT_H +#define LLVM_COV_COVERAGEREPORT_H + +#include "CoverageSummary.h" +#include "CoverageViewOptions.h" + +namespace llvm { + +/// \brief Displays the code coverage report. +class CoverageReport { + const CoverageViewOptions &Options; + CoverageSummary &Summary; + + void render(const FileCoverageSummary &File, raw_ostream &OS); + void render(const FunctionCoverageSummary &Function, raw_ostream &OS); + +public: + CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary) + : Options(Options), Summary(Summary) {} + + void renderFunctionReports(raw_ostream &OS); + + void renderFileReports(raw_ostream &OS); +}; +} + +#endif // LLVM_COV_COVERAGEREPORT_H diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummary.cpp b/contrib/llvm/tools/llvm-cov/CoverageSummary.cpp new file mode 100644 index 000000000000..059c8c857e45 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageSummary.cpp @@ -0,0 +1,64 @@ +//===- CoverageSummary.cpp - Code coverage summary ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements data management and rendering for the code coverage +// summaries of all files and functions. +// +//===----------------------------------------------------------------------===// + +#include "CoverageSummary.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +unsigned CoverageSummary::getFileID(StringRef Filename) { + for (unsigned I = 0, E = Filenames.size(); I < E; ++I) { + if (sys::fs::equivalent(Filenames[I], Filename)) + return I; + } + Filenames.push_back(Filename); + return Filenames.size() - 1; +} + +void +CoverageSummary::createSummaries(const coverage::CoverageMapping &Coverage) { + for (StringRef Filename : Coverage.getUniqueSourceFiles()) { + size_t PrevSize = FunctionSummaries.size(); + for (const auto &F : Coverage.getCoveredFunctions(Filename)) + FunctionSummaries.push_back(FunctionCoverageSummary::get(F)); + size_t Count = FunctionSummaries.size() - PrevSize; + if (Count == 0) + continue; + FileSummaries.push_back(FileCoverageSummary::get( + Filename, makeArrayRef(FunctionSummaries.data() + PrevSize, Count))); + } +} + +FileCoverageSummary CoverageSummary::getCombinedFileSummaries() { + size_t NumRegions = 0, CoveredRegions = 0; + size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0; + size_t NumFunctionsExecuted = 0, NumFunctions = 0; + for (const auto &File : FileSummaries) { + NumRegions += File.RegionCoverage.NumRegions; + CoveredRegions += File.RegionCoverage.Covered; + + NumLines += File.LineCoverage.NumLines; + NonCodeLines += File.LineCoverage.NonCodeLines; + CoveredLines += File.LineCoverage.Covered; + + NumFunctionsExecuted += File.FunctionCoverage.Executed; + NumFunctions += File.FunctionCoverage.NumFunctions; + } + return FileCoverageSummary( + "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions), + LineCoverageInfo(CoveredLines, NonCodeLines, NumLines), + FunctionCoverageInfo(NumFunctionsExecuted, NumFunctions), + None); +} diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummary.h b/contrib/llvm/tools/llvm-cov/CoverageSummary.h new file mode 100644 index 000000000000..9dbebde949e5 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageSummary.h @@ -0,0 +1,45 @@ +//===- CoverageSummary.h - Code coverage summary --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements data management and rendering for the code coverage +// summaries of all files and functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGESUMMARY_H +#define LLVM_COV_COVERAGESUMMARY_H + +#include "CoverageSummaryInfo.h" +#include + +namespace llvm { + +/// \brief Manager for the function and file code coverage summaries. +class CoverageSummary { + std::vector Filenames; + std::vector FunctionSummaries; + std::vector> FunctionSummariesFileIDs; + std::vector FileSummaries; + + unsigned getFileID(StringRef Filename); + +public: + void createSummaries(const coverage::CoverageMapping &Coverage); + + ArrayRef getFileSummaries() { return FileSummaries; } + + FileCoverageSummary getCombinedFileSummaries(); + + void render(const FunctionCoverageSummary &Summary, raw_ostream &OS); + + void render(raw_ostream &OS); +}; +} + +#endif // LLVM_COV_COVERAGESUMMARY_H diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp new file mode 100644 index 000000000000..dd78ace86050 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -0,0 +1,96 @@ +//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These structures are used to represent code coverage metrics +// for functions/files. +// +//===----------------------------------------------------------------------===// + +#include "CoverageSummaryInfo.h" + +using namespace llvm; +using namespace coverage; + +FunctionCoverageSummary +FunctionCoverageSummary::get(const coverage::FunctionRecord &Function) { + // Compute the region coverage + size_t NumCodeRegions = 0, CoveredRegions = 0; + for (auto &CR : Function.CountedRegions) { + if (CR.Kind != CounterMappingRegion::CodeRegion) + continue; + ++NumCodeRegions; + if (CR.ExecutionCount != 0) + ++CoveredRegions; + } + + // Compute the line coverage + size_t NumLines = 0, CoveredLines = 0; + for (unsigned FileID = 0, E = Function.Filenames.size(); FileID < E; + ++FileID) { + // Find the line start and end of the function's source code + // in that particular file + unsigned LineStart = std::numeric_limits::max(); + unsigned LineEnd = 0; + for (auto &CR : Function.CountedRegions) { + if (CR.FileID != FileID) + continue; + LineStart = std::min(LineStart, CR.LineStart); + LineEnd = std::max(LineEnd, CR.LineEnd); + } + unsigned LineCount = LineEnd - LineStart + 1; + + // Get counters + llvm::SmallVector ExecutionCounts; + ExecutionCounts.resize(LineCount, 0); + for (auto &CR : Function.CountedRegions) { + if (CR.FileID != FileID) + continue; + // Ignore the lines that were skipped by the preprocessor. + auto ExecutionCount = CR.ExecutionCount; + if (CR.Kind == CounterMappingRegion::SkippedRegion) { + LineCount -= CR.LineEnd - CR.LineStart + 1; + ExecutionCount = 1; + } + for (unsigned I = CR.LineStart; I <= CR.LineEnd; ++I) + ExecutionCounts[I - LineStart] = ExecutionCount; + } + CoveredLines += LineCount - std::count(ExecutionCounts.begin(), + ExecutionCounts.end(), 0); + NumLines += LineCount; + } + return FunctionCoverageSummary( + Function.Name, Function.ExecutionCount, + RegionCoverageInfo(CoveredRegions, NumCodeRegions), + LineCoverageInfo(CoveredLines, 0, NumLines)); +} + +FileCoverageSummary +FileCoverageSummary::get(StringRef Name, + ArrayRef FunctionSummaries) { + size_t NumRegions = 0, CoveredRegions = 0; + size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0; + size_t NumFunctionsExecuted = 0; + for (const auto &Func : FunctionSummaries) { + CoveredRegions += Func.RegionCoverage.Covered; + NumRegions += Func.RegionCoverage.NumRegions; + + CoveredLines += Func.LineCoverage.Covered; + NonCodeLines += Func.LineCoverage.NonCodeLines; + NumLines += Func.LineCoverage.NumLines; + + if (Func.ExecutionCount != 0) + ++NumFunctionsExecuted; + } + + return FileCoverageSummary( + Name, RegionCoverageInfo(CoveredRegions, NumRegions), + LineCoverageInfo(CoveredLines, NonCodeLines, NumLines), + FunctionCoverageInfo(NumFunctionsExecuted, FunctionSummaries.size()), + FunctionSummaries); +} diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h new file mode 100644 index 000000000000..0036032ab399 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h @@ -0,0 +1,133 @@ +//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These structures are used to represent code coverage metrics +// for functions/files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGESUMMARYINFO_H +#define LLVM_COV_COVERAGESUMMARYINFO_H + +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// \brief Provides information about region coverage for a function/file. +struct RegionCoverageInfo { + /// \brief The number of regions that were executed at least once. + size_t Covered; + + /// \brief The number of regions that weren't executed. + size_t NotCovered; + + /// \brief The total number of regions in a function/file. + size_t NumRegions; + + RegionCoverageInfo(size_t Covered, size_t NumRegions) + : Covered(Covered), NotCovered(NumRegions - Covered), + NumRegions(NumRegions) {} + + bool isFullyCovered() const { return Covered == NumRegions; } + + double getPercentCovered() const { + return double(Covered) / double(NumRegions) * 100.0; + } +}; + +/// \brief Provides information about line coverage for a function/file. +struct LineCoverageInfo { + /// \brief The number of lines that were executed at least once. + size_t Covered; + + /// \brief The number of lines that weren't executed. + size_t NotCovered; + + /// \brief The number of lines that aren't code. + size_t NonCodeLines; + + /// \brief The total number of lines in a function/file. + size_t NumLines; + + LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines) + : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered), + NonCodeLines(NumNonCodeLines), NumLines(NumLines) {} + + bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); } + + double getPercentCovered() const { + return double(Covered) / double(NumLines - NonCodeLines) * 100.0; + } +}; + +/// \brief Provides information about function coverage for a file. +struct FunctionCoverageInfo { + /// \brief The number of functions that were executed. + size_t Executed; + + /// \brief The total number of functions in this file. + size_t NumFunctions; + + FunctionCoverageInfo(size_t Executed, size_t NumFunctions) + : Executed(Executed), NumFunctions(NumFunctions) {} + + bool isFullyCovered() const { return Executed == NumFunctions; } + + double getPercentCovered() const { + return double(Executed) / double(NumFunctions) * 100.0; + } +}; + +/// \brief A summary of function's code coverage. +struct FunctionCoverageSummary { + StringRef Name; + uint64_t ExecutionCount; + RegionCoverageInfo RegionCoverage; + LineCoverageInfo LineCoverage; + + FunctionCoverageSummary(StringRef Name, uint64_t ExecutionCount, + const RegionCoverageInfo &RegionCoverage, + const LineCoverageInfo &LineCoverage) + : Name(Name), ExecutionCount(ExecutionCount), + RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) { + } + + /// \brief Compute the code coverage summary for the given function coverage + /// mapping record. + static FunctionCoverageSummary + get(const coverage::FunctionRecord &Function); +}; + +/// \brief A summary of file's code coverage. +struct FileCoverageSummary { + StringRef Name; + RegionCoverageInfo RegionCoverage; + LineCoverageInfo LineCoverage; + FunctionCoverageInfo FunctionCoverage; + /// \brief The summary of every function + /// in this file. + ArrayRef FunctionSummaries; + + FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage, + const LineCoverageInfo &LineCoverage, + const FunctionCoverageInfo &FunctionCoverage, + ArrayRef FunctionSummaries) + : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage), + FunctionCoverage(FunctionCoverage), + FunctionSummaries(FunctionSummaries) {} + + /// \brief Compute the code coverage summary for a file. + static FileCoverageSummary + get(StringRef Name, ArrayRef FunctionSummaries); +}; + +} // namespace llvm + +#endif // LLVM_COV_COVERAGESUMMARYINFO_H diff --git a/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h b/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h new file mode 100644 index 000000000000..94b55fe793fc --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -0,0 +1,36 @@ +//===- CoverageViewOptions.h - Code coverage display options -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H +#define LLVM_COV_COVERAGEVIEWOPTIONS_H + +#include "RenderingSupport.h" + +namespace llvm { + +/// \brief The options for displaying the code coverage information. +struct CoverageViewOptions { + bool Debug; + bool Colors; + bool ShowLineNumbers; + bool ShowLineStats; + bool ShowRegionMarkers; + bool ShowLineStatsOrRegionMarkers; + bool ShowExpandedRegions; + bool ShowFunctionInstantiations; + + /// \brief Change the output's stream color if the colors are enabled. + ColoredRawOstream colored_ostream(raw_ostream &OS, + raw_ostream::Colors Color) const { + return llvm::colored_ostream(OS, Color, Colors); + } +}; +} + +#endif // LLVM_COV_COVERAGEVIEWOPTIONS_H diff --git a/contrib/llvm/tools/llvm-cov/RenderingSupport.h b/contrib/llvm/tools/llvm-cov/RenderingSupport.h new file mode 100644 index 000000000000..0271329997dc --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/RenderingSupport.h @@ -0,0 +1,60 @@ +//===- RenderingSupport.h - output stream rendering support functions ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_RENDERINGSUPPORT_H +#define LLVM_COV_RENDERINGSUPPORT_H + +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { + +/// \brief A helper class that resets the output stream's color if needed +/// when destroyed. +class ColoredRawOstream { + ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION; + +public: + raw_ostream &OS; + bool IsColorUsed; + + ColoredRawOstream(raw_ostream &OS, bool IsColorUsed) + : OS(OS), IsColorUsed(IsColorUsed) {} + + ColoredRawOstream(ColoredRawOstream &&Other) + : OS(Other.OS), IsColorUsed(Other.IsColorUsed) { + // Reset the other IsColorUsed so that the other object won't reset the + // color when destroyed. + Other.IsColorUsed = false; + } + + ~ColoredRawOstream() { + if (IsColorUsed) + OS.resetColor(); + } +}; + +template +inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) { + return OS.OS << std::forward(Value); +} + +/// \brief Change the color of the output stream if the `IsColorUsed` flag +/// is true. Returns an object that resets the color when destroyed. +inline ColoredRawOstream colored_ostream(raw_ostream &OS, + raw_ostream::Colors Color, + bool IsColorUsed = true, + bool Bold = false, bool BG = false) { + if (IsColorUsed) + OS.changeColor(Color, Bold, BG); + return ColoredRawOstream(OS, IsColorUsed); +} +} + +#endif // LLVM_COV_RENDERINGSUPPORT_H diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp new file mode 100644 index 000000000000..015099c7d02b --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp @@ -0,0 +1,260 @@ +//===- SourceCoverageView.cpp - Code coverage view for source code --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering for code coverage of source code. +// +//===----------------------------------------------------------------------===// + +#include "SourceCoverageView.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/LineIterator.h" + +using namespace llvm; + +void SourceCoverageView::renderLine( + raw_ostream &OS, StringRef Line, int64_t LineNumber, + const coverage::CoverageSegment *WrappedSegment, + ArrayRef Segments, + unsigned ExpansionCol) { + Optional Highlight; + SmallVector, 2> HighlightedRanges; + + // The first segment overlaps from a previous line, so we treat it specially. + if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0) + Highlight = raw_ostream::RED; + + // Output each segment of the line, possibly highlighted. + unsigned Col = 1; + for (const auto *S : Segments) { + unsigned End = std::min(S->Col, static_cast(Line.size()) + 1); + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, End - Col); + if (Options.Debug && Highlight) + HighlightedRanges.push_back(std::make_pair(Col, End)); + Col = End; + if (Col == ExpansionCol) + Highlight = raw_ostream::CYAN; + else if (S->HasCount && S->Count == 0) + Highlight = raw_ostream::RED; + else + Highlight = None; + } + + // Show the rest of the line + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, Line.size() - Col + 1); + OS << "\n"; + + if (Options.Debug) { + for (const auto &Range : HighlightedRanges) + errs() << "Highlighted line " << LineNumber << ", " << Range.first + << " -> " << Range.second << "\n"; + if (Highlight) + errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; + } +} + +void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) { + for (unsigned I = 0; I < Level; ++I) + OS << " |"; +} + +void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length, + raw_ostream &OS) { + assert(Level != 0 && "Cannot render divider at top level"); + renderIndent(OS, Level - 1); + OS.indent(2); + for (unsigned I = 0; I < Length; ++I) + OS << "-"; +} + +void +SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS, + const LineCoverageInfo &Line) { + if (!Line.isMapped()) { + OS.indent(LineCoverageColumnWidth) << '|'; + return; + } + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + BufferOS << Line.ExecutionCount; + auto Str = BufferOS.str(); + // Trim + Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth)); + // Align to the right + OS.indent(LineCoverageColumnWidth - Str.size()); + colored_ostream(OS, raw_ostream::MAGENTA, + Line.hasMultipleRegions() && Options.Colors) + << Str; + OS << '|'; +} + +void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS, + unsigned LineNo) { + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + BufferOS << LineNo; + auto Str = BufferOS.str(); + // Trim and align to the right + Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth)); + OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|'; +} + +void SourceCoverageView::renderRegionMarkers( + raw_ostream &OS, ArrayRef Segments) { + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + + unsigned PrevColumn = 1; + for (const auto *S : Segments) { + if (!S->IsRegionEntry) + continue; + // Skip to the new region + if (S->Col > PrevColumn) + OS.indent(S->Col - PrevColumn); + PrevColumn = S->Col + 1; + BufferOS << S->Count; + StringRef Str = BufferOS.str(); + // Trim the execution count + Str = Str.substr(0, std::min(Str.size(), (size_t)7)); + PrevColumn += Str.size(); + OS << '^' << Str; + Buffer.clear(); + } + OS << "\n"; + + if (Options.Debug) + for (const auto *S : Segments) + errs() << "Marker at " << S->Line << ":" << S->Col << " = " << S->Count + << (S->IsRegionEntry ? "\n" : " (pop)\n"); +} + +void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, + unsigned IndentLevel) { + // The width of the leading columns + unsigned CombinedColumnWidth = + (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) + + (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0); + // The width of the line that is used to divide between the view and the + // subviews. + unsigned DividerWidth = CombinedColumnWidth + 4; + + // We need the expansions and instantiations sorted so we can go through them + // while we iterate lines. + std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); + std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); + auto NextESV = ExpansionSubViews.begin(); + auto EndESV = ExpansionSubViews.end(); + auto NextISV = InstantiationSubViews.begin(); + auto EndISV = InstantiationSubViews.end(); + + // Get the coverage information for the file. + auto NextSegment = CoverageInfo.begin(); + auto EndSegment = CoverageInfo.end(); + + unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0; + const coverage::CoverageSegment *WrappedSegment = nullptr; + SmallVector LineSegments; + for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { + // If we aren't rendering the whole file, we need to filter out the prologue + // and epilogue. + if (!WholeFile) { + if (NextSegment == EndSegment) + break; + else if (LI.line_number() < FirstLine) + continue; + } + + // Collect the coverage information relevant to this line. + if (LineSegments.size()) + WrappedSegment = LineSegments.back(); + LineSegments.clear(); + while (NextSegment != EndSegment && NextSegment->Line == LI.line_number()) + LineSegments.push_back(&*NextSegment++); + + // Calculate a count to be for the line as a whole. + LineCoverageInfo LineCount; + if (WrappedSegment && WrappedSegment->HasCount) + LineCount.addRegionCount(WrappedSegment->Count); + for (const auto *S : LineSegments) + if (S->HasCount && S->IsRegionEntry) + LineCount.addRegionStartCount(S->Count); + + // Render the line prefix. + renderIndent(OS, IndentLevel); + if (Options.ShowLineStats) + renderLineCoverageColumn(OS, LineCount); + if (Options.ShowLineNumbers) + renderLineNumberColumn(OS, LI.line_number()); + + // If there are expansion subviews, we want to highlight the first one. + unsigned ExpansionColumn = 0; + if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && + Options.Colors) + ExpansionColumn = NextESV->getStartCol(); + + // Display the source code for the current line. + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, + ExpansionColumn); + + // Show the region markers. + if (Options.ShowRegionMarkers && (!Options.ShowLineStatsOrRegionMarkers || + LineCount.hasMultipleRegions()) && + !LineSegments.empty()) { + renderIndent(OS, IndentLevel); + OS.indent(CombinedColumnWidth); + renderRegionMarkers(OS, LineSegments); + } + + // Show the expansions and instantiations for this line. + unsigned NestedIndent = IndentLevel + 1; + bool RenderedSubView = false; + for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); + ++NextESV) { + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + if (RenderedSubView) { + // Re-render the current line and highlight the expansion range for + // this subview. + ExpansionColumn = NextESV->getStartCol(); + renderIndent(OS, IndentLevel); + OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1)); + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, + ExpansionColumn); + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + } + // Render the child subview + if (Options.Debug) + errs() << "Expansion at line " << NextESV->getLine() << ", " + << NextESV->getStartCol() << " -> " << NextESV->getEndCol() + << "\n"; + NextESV->View->render(OS, false, NestedIndent); + RenderedSubView = true; + } + for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + renderIndent(OS, NestedIndent); + OS << ' '; + Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName + << ":"; + OS << "\n"; + NextISV->View->render(OS, false, NestedIndent); + RenderedSubView = true; + } + if (RenderedSubView) { + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + } + } +} diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageView.h b/contrib/llvm/tools/llvm-cov/SourceCoverageView.h new file mode 100644 index 000000000000..d92a7486d9d3 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageView.h @@ -0,0 +1,162 @@ +//===- SourceCoverageView.h - Code coverage view for source code ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering for code coverage of source code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H +#define LLVM_COV_SOURCECOVERAGEVIEW_H + +#include "CoverageViewOptions.h" +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +namespace llvm { + +class SourceCoverageView; + +/// \brief A view that represents a macro or include expansion +struct ExpansionView { + coverage::CounterMappingRegion Region; + std::unique_ptr View; + + ExpansionView(const coverage::CounterMappingRegion &Region, + std::unique_ptr View) + : Region(Region), View(std::move(View)) {} + ExpansionView(ExpansionView &&RHS) + : Region(std::move(RHS.Region)), View(std::move(RHS.View)) {} + ExpansionView &operator=(ExpansionView &&RHS) { + Region = std::move(RHS.Region); + View = std::move(RHS.View); + return *this; + } + + unsigned getLine() const { return Region.LineStart; } + unsigned getStartCol() const { return Region.ColumnStart; } + unsigned getEndCol() const { return Region.ColumnEnd; } + + friend bool operator<(const ExpansionView &LHS, const ExpansionView &RHS) { + return LHS.Region.startLoc() < RHS.Region.startLoc(); + } +}; + +/// \brief A view that represents a function instantiation +struct InstantiationView { + StringRef FunctionName; + unsigned Line; + std::unique_ptr View; + + InstantiationView(StringRef FunctionName, unsigned Line, + std::unique_ptr View) + : FunctionName(FunctionName), Line(Line), View(std::move(View)) {} + InstantiationView(InstantiationView &&RHS) + : FunctionName(std::move(RHS.FunctionName)), Line(std::move(RHS.Line)), + View(std::move(RHS.View)) {} + InstantiationView &operator=(InstantiationView &&RHS) { + FunctionName = std::move(RHS.FunctionName); + Line = std::move(RHS.Line); + View = std::move(RHS.View); + return *this; + } + + friend bool operator<(const InstantiationView &LHS, + const InstantiationView &RHS) { + return LHS.Line < RHS.Line; + } +}; + +/// \brief A code coverage view of a specific source file. +/// It can have embedded coverage views. +class SourceCoverageView { +private: + /// \brief Coverage information for a single line. + struct LineCoverageInfo { + uint64_t ExecutionCount; + unsigned RegionCount; + bool Mapped; + + LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {} + + bool isMapped() const { return Mapped; } + + bool hasMultipleRegions() const { return RegionCount > 1; } + + void addRegionStartCount(uint64_t Count) { + Mapped = true; + ExecutionCount = Count; + ++RegionCount; + } + + void addRegionCount(uint64_t Count) { + Mapped = true; + if (!RegionCount) + ExecutionCount = Count; + } + }; + + const MemoryBuffer &File; + const CoverageViewOptions &Options; + coverage::CoverageData CoverageInfo; + std::vector ExpansionSubViews; + std::vector InstantiationSubViews; + + /// \brief Render a source line with highlighting. + void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber, + const coverage::CoverageSegment *WrappedSegment, + ArrayRef Segments, + unsigned ExpansionCol); + + void renderIndent(raw_ostream &OS, unsigned Level); + + void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS); + + /// \brief Render the line's execution count column. + void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line); + + /// \brief Render the line number column. + void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo); + + /// \brief Render all the region's execution counts on a line. + void + renderRegionMarkers(raw_ostream &OS, + ArrayRef Segments); + + static const unsigned LineCoverageColumnWidth = 7; + static const unsigned LineNumberColumnWidth = 5; + +public: + SourceCoverageView(const MemoryBuffer &File, + const CoverageViewOptions &Options, + coverage::CoverageData &&CoverageInfo) + : File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {} + + const CoverageViewOptions &getOptions() const { return Options; } + + /// \brief Add an expansion subview to this view. + void addExpansion(const coverage::CounterMappingRegion &Region, + std::unique_ptr View) { + ExpansionSubViews.emplace_back(Region, std::move(View)); + } + + /// \brief Add a function instantiation subview to this view. + void addInstantiation(StringRef FunctionName, unsigned Line, + std::unique_ptr View) { + InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); + } + + /// \brief Print the code coverage information for a specific + /// portion of a source file to the output stream. + void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0); +}; + +} // namespace llvm + +#endif // LLVM_COV_SOURCECOVERAGEVIEW_H diff --git a/contrib/llvm/tools/llvm-cov/TestingSupport.cpp b/contrib/llvm/tools/llvm-cov/TestingSupport.cpp new file mode 100644 index 000000000000..6959897482ca --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/TestingSupport.cpp @@ -0,0 +1,90 @@ +//===- TestingSupport.cpp - Convert objects files into test files --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace llvm; +using namespace object; + +int convertForTestingMain(int argc, const char *argv[]) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::opt InputSourceFile(cl::Positional, cl::Required, + cl::desc("")); + + cl::opt OutputFilename( + "o", cl::Required, + cl::desc( + "File with the profile data obtained after an instrumented run")); + + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); + + auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile); + if (auto Err = ObjErr.getError()) { + errs() << "error: " << Err.message() << "\n"; + return 1; + } + ObjectFile *OF = ObjErr.get().getBinary(); + auto BytesInAddress = OF->getBytesInAddress(); + if (BytesInAddress != 8) { + errs() << "error: 64 bit binary expected\n"; + return 1; + } + + // Look for the sections that we are interested in. + int FoundSectionCount = 0; + SectionRef ProfileNames, CoverageMapping; + for (const auto &Section : OF->sections()) { + StringRef Name; + if (Section.getName(Name)) + return 1; + if (Name == "__llvm_prf_names") { + ProfileNames = Section; + } else if (Name == "__llvm_covmap") { + CoverageMapping = Section; + } else + continue; + ++FoundSectionCount; + } + if (FoundSectionCount != 2) + return 1; + + // Get the contents of the given sections. + uint64_t ProfileNamesAddress = ProfileNames.getAddress(); + StringRef CoverageMappingData; + StringRef ProfileNamesData; + if (CoverageMapping.getContents(CoverageMappingData) || + ProfileNames.getContents(ProfileNamesData)) + return 1; + + int FD; + if (auto Err = + sys::fs::openFileForWrite(OutputFilename, FD, sys::fs::F_None)) { + errs() << "error: " << Err.message() << "\n"; + return 1; + } + + raw_fd_ostream OS(FD, true); + OS << "llvmcovmtestdata"; + encodeULEB128(ProfileNamesData.size(), OS); + encodeULEB128(ProfileNamesAddress, OS); + OS << ProfileNamesData << CoverageMappingData; + + return 0; +} diff --git a/contrib/llvm/tools/llvm-cov/gcov.cpp b/contrib/llvm/tools/llvm-cov/gcov.cpp new file mode 100644 index 000000000000..12011cea7f26 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/gcov.cpp @@ -0,0 +1,152 @@ +//===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// llvm-cov is a command line tools to analyze and report coverage information. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/GCOV.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include +using namespace llvm; + +void reportCoverage(StringRef SourceFile, StringRef ObjectDir, + const std::string &InputGCNO, const std::string &InputGCDA, + bool DumpGCOV, const GCOVOptions &Options) { + SmallString<128> CoverageFileStem(ObjectDir); + if (CoverageFileStem.empty()) { + // If no directory was specified with -o, look next to the source file. + CoverageFileStem = sys::path::parent_path(SourceFile); + sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); + } else if (sys::fs::is_directory(ObjectDir)) + // A directory name was given. Use it and the source file name. + sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); + else + // A file was given. Ignore the source file and look next to this file. + sys::path::replace_extension(CoverageFileStem, ""); + + std::string GCNO = InputGCNO.empty() + ? std::string(CoverageFileStem.str()) + ".gcno" + : InputGCNO; + std::string GCDA = InputGCDA.empty() + ? std::string(CoverageFileStem.str()) + ".gcda" + : InputGCDA; + GCOVFile GF; + + ErrorOr> GCNO_Buff = + MemoryBuffer::getFileOrSTDIN(GCNO); + if (std::error_code EC = GCNO_Buff.getError()) { + errs() << GCNO << ": " << EC.message() << "\n"; + return; + } + GCOVBuffer GCNO_GB(GCNO_Buff.get().get()); + if (!GF.readGCNO(GCNO_GB)) { + errs() << "Invalid .gcno File!\n"; + return; + } + + ErrorOr> GCDA_Buff = + MemoryBuffer::getFileOrSTDIN(GCDA); + if (std::error_code EC = GCDA_Buff.getError()) { + if (EC != errc::no_such_file_or_directory) { + errs() << GCDA << ": " << EC.message() << "\n"; + return; + } + // Clear the filename to make it clear we didn't read anything. + GCDA = "-"; + } else { + GCOVBuffer GCDA_GB(GCDA_Buff.get().get()); + if (!GF.readGCDA(GCDA_GB)) { + errs() << "Invalid .gcda File!\n"; + return; + } + } + + if (DumpGCOV) + GF.dump(); + + FileInfo FI(Options); + GF.collectLineCounts(FI); + FI.print(SourceFile, GCNO, GCDA); +} + +int gcovMain(int argc, const char *argv[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::list SourceFiles(cl::Positional, cl::OneOrMore, + cl::desc("SOURCEFILE")); + + cl::opt AllBlocks("a", cl::Grouping, cl::init(false), + cl::desc("Display all basic blocks")); + cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks)); + + cl::opt BranchProb("b", cl::Grouping, cl::init(false), + cl::desc("Display branch probabilities")); + cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb)); + + cl::opt BranchCount("c", cl::Grouping, cl::init(false), + cl::desc("Display branch counts instead " + "of percentages (requires -b)")); + cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount)); + + cl::opt LongNames("l", cl::Grouping, cl::init(false), + cl::desc("Prefix filenames with the main file")); + cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames)); + + cl::opt FuncSummary("f", cl::Grouping, cl::init(false), + cl::desc("Show coverage for each function")); + cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); + + cl::opt NoOutput("n", cl::Grouping, cl::init(false), + cl::desc("Do not output any .gcov files")); + cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); + + cl::opt ObjectDir( + "o", cl::value_desc("DIR|FILE"), cl::init(""), + cl::desc("Find objects in DIR or based on FILE's path")); + cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir)); + cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir)); + + cl::opt PreservePaths("p", cl::Grouping, cl::init(false), + cl::desc("Preserve path components")); + cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); + + cl::opt UncondBranch("u", cl::Grouping, cl::init(false), + cl::desc("Display unconditional branch info " + "(requires -b)")); + cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch)); + + cl::OptionCategory DebugCat("Internal and debugging options"); + cl::opt DumpGCOV("dump", cl::init(false), cl::cat(DebugCat), + cl::desc("Dump the gcov file to stderr")); + cl::opt InputGCNO("gcno", cl::cat(DebugCat), cl::init(""), + cl::desc("Override inferred gcno file")); + cl::opt InputGCDA("gcda", cl::cat(DebugCat), cl::init(""), + cl::desc("Override inferred gcda file")); + + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); + + GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary, + PreservePaths, UncondBranch, LongNames, NoOutput); + + for (const auto &SourceFile : SourceFiles) + reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, + Options); + return 0; +} diff --git a/contrib/llvm/tools/llvm-cov/llvm-cov.cpp b/contrib/llvm/tools/llvm-cov/llvm-cov.cpp new file mode 100644 index 000000000000..86ec26dbd188 --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/llvm-cov.cpp @@ -0,0 +1,78 @@ +//===- llvm-cov.cpp - LLVM coverage tool ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// llvm-cov is a command line tools to analyze and report coverage information. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +/// \brief The main entry point for the 'show' subcommand. +int showMain(int argc, const char *argv[]); + +/// \brief The main entry point for the 'report' subcommand. +int reportMain(int argc, const char *argv[]); + +/// \brief The main entry point for the 'convert-for-testing' subcommand. +int convertForTestingMain(int argc, const char *argv[]); + +/// \brief The main entry point for the gcov compatible coverage tool. +int gcovMain(int argc, const char *argv[]); + +/// \brief Top level help. +int helpMain(int argc, const char *argv[]) { + errs() << "OVERVIEW: LLVM code coverage tool\n\n" + << "USAGE: llvm-cov {gcov|report|show}\n"; + return 0; +} + +int main(int argc, const char **argv) { + // If argv[0] is or ends with 'gcov', always be gcov compatible + if (sys::path::stem(argv[0]).endswith_lower("gcov")) + return gcovMain(argc, argv); + + // Check if we are invoking a specific tool command. + if (argc > 1) { + typedef int (*MainFunction)(int, const char *[]); + MainFunction Func = StringSwitch(argv[1]) + .Case("convert-for-testing", convertForTestingMain) + .Case("gcov", gcovMain) + .Case("report", reportMain) + .Case("show", showMain) + .Cases("-h", "-help", "--help", helpMain) + .Default(nullptr); + + if (Func) { + std::string Invocation = std::string(argv[0]) + " " + argv[1]; + argv[1] = Invocation.c_str(); + return Func(argc - 1, argv + 1); + } + } + + // Give a warning and fall back to gcov + errs().changeColor(raw_ostream::RED); + errs() << "warning:"; + // Assume that argv[1] wasn't a command when it stats with a '-' or is a + // filename (i.e. contains a '.') + if (argc > 1 && !StringRef(argv[1]).startswith("-") && + StringRef(argv[1]).find(".") == StringRef::npos) + errs() << " Unrecognized command '" << argv[1] << "'."; + errs() << " Using the gcov compatible mode " + "(this behaviour may be dropped in the future)."; + errs().resetColor(); + errs() << "\n"; + + return gcovMain(argc, argv); +} diff --git a/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp b/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp new file mode 100644 index 000000000000..25531c776a31 --- /dev/null +++ b/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -0,0 +1,283 @@ +//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// llvm-profdata merges .profdata files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/InstrProfWriter.h" +#include "llvm/ProfileData/SampleProfReader.h" +#include "llvm/ProfileData/SampleProfWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +static void exitWithError(const Twine &Message, StringRef Whence = "") { + errs() << "error: "; + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; + ::exit(1); +} + +enum ProfileKinds { instr, sample }; + +void mergeInstrProfile(cl::list Inputs, StringRef OutputFilename) { + if (OutputFilename.compare("-") == 0) + exitWithError("Cannot write indexed profdata format to stdout."); + + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); + if (EC) + exitWithError(EC.message(), OutputFilename); + + InstrProfWriter Writer; + for (const auto &Filename : Inputs) { + auto ReaderOrErr = InstrProfReader::create(Filename); + if (std::error_code ec = ReaderOrErr.getError()) + exitWithError(ec.message(), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + for (const auto &I : *Reader) + if (std::error_code EC = + Writer.addFunctionCounts(I.Name, I.Hash, I.Counts)) + errs() << Filename << ": " << I.Name << ": " << EC.message() << "\n"; + if (Reader->hasError()) + exitWithError(Reader->getError().message(), Filename); + } + Writer.write(Output); +} + +void mergeSampleProfile(cl::list Inputs, StringRef OutputFilename, + sampleprof::SampleProfileFormat OutputFormat) { + using namespace sampleprof; + auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); + if (std::error_code EC = WriterOrErr.getError()) + exitWithError(EC.message(), OutputFilename); + + auto Writer = std::move(WriterOrErr.get()); + StringMap ProfileMap; + for (const auto &Filename : Inputs) { + auto ReaderOrErr = + SampleProfileReader::create(Filename, getGlobalContext()); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithError(EC.message(), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + if (std::error_code EC = Reader->read()) + exitWithError(EC.message(), Filename); + + StringMap &Profiles = Reader->getProfiles(); + for (StringMap::iterator I = Profiles.begin(), + E = Profiles.end(); + I != E; ++I) { + StringRef FName = I->first(); + FunctionSamples &Samples = I->second; + ProfileMap[FName].merge(Samples); + } + } + Writer->write(ProfileMap); +} + +int merge_main(int argc, const char *argv[]) { + cl::list Inputs(cl::Positional, cl::Required, cl::OneOrMore, + cl::desc("")); + + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::Required, + cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), clEnumValEnd)); + + cl::opt OutputFormat( + cl::desc("Format of output profile (only meaningful with --sample)"), + cl::init(sampleprof::SPF_Binary), + cl::values(clEnumValN(sampleprof::SPF_Binary, "binary", + "Binary encoding (default)"), + clEnumValN(sampleprof::SPF_Text, "text", "Text encoding"), + clEnumValN(sampleprof::SPF_GCC, "gcc", "GCC encoding"), + clEnumValEnd)); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + + if (ProfileKind == instr) + mergeInstrProfile(Inputs, OutputFilename); + else + mergeSampleProfile(Inputs, OutputFilename, OutputFormat); + + return 0; +} + +int showInstrProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { + auto ReaderOrErr = InstrProfReader::create(Filename); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithError(EC.message(), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + uint64_t MaxFunctionCount = 0, MaxBlockCount = 0; + size_t ShownFunctions = 0, TotalFunctions = 0; + for (const auto &Func : *Reader) { + bool Show = + ShowAllFunctions || (!ShowFunction.empty() && + Func.Name.find(ShowFunction) != Func.Name.npos); + + ++TotalFunctions; + assert(Func.Counts.size() > 0 && "function missing entry counter"); + if (Func.Counts[0] > MaxFunctionCount) + MaxFunctionCount = Func.Counts[0]; + + if (Show) { + if (!ShownFunctions) + OS << "Counters:\n"; + ++ShownFunctions; + + OS << " " << Func.Name << ":\n" + << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" + << " Counters: " << Func.Counts.size() << "\n" + << " Function count: " << Func.Counts[0] << "\n"; + } + + if (Show && ShowCounts) + OS << " Block counts: ["; + for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) { + if (Func.Counts[I] > MaxBlockCount) + MaxBlockCount = Func.Counts[I]; + if (Show && ShowCounts) + OS << (I == 1 ? "" : ", ") << Func.Counts[I]; + } + if (Show && ShowCounts) + OS << "]\n"; + } + if (Reader->hasError()) + exitWithError(Reader->getError().message(), Filename); + + if (ShowAllFunctions || !ShowFunction.empty()) + OS << "Functions shown: " << ShownFunctions << "\n"; + OS << "Total functions: " << TotalFunctions << "\n"; + OS << "Maximum function count: " << MaxFunctionCount << "\n"; + OS << "Maximum internal block count: " << MaxBlockCount << "\n"; + return 0; +} + +int showSampleProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { + using namespace sampleprof; + auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext()); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithError(EC.message(), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + Reader->read(); + if (ShowAllFunctions || ShowFunction.empty()) + Reader->dump(OS); + else + Reader->dumpFunctionProfile(ShowFunction, OS); + + return 0; +} + +int show_main(int argc, const char *argv[]) { + cl::opt Filename(cl::Positional, cl::Required, + cl::desc("")); + + cl::opt ShowCounts("counts", cl::init(false), + cl::desc("Show counter values for shown functions")); + cl::opt ShowAllFunctions("all-functions", cl::init(false), + cl::desc("Details for every function")); + cl::opt ShowFunction("function", + cl::desc("Details for matching functions")); + + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), clEnumValEnd)); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); + + if (OutputFilename.empty()) + OutputFilename = "-"; + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + if (EC) + exitWithError(EC.message(), OutputFilename); + + if (ShowAllFunctions && !ShowFunction.empty()) + errs() << "warning: -function argument ignored: showing all functions\n"; + + if (ProfileKind == instr) + return showInstrProfile(Filename, ShowCounts, ShowAllFunctions, + ShowFunction, OS); + else + return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, + ShowFunction, OS); +} + +int main(int argc, const char *argv[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + StringRef ProgName(sys::path::filename(argv[0])); + if (argc > 1) { + int (*func)(int, const char *[]) = nullptr; + + if (strcmp(argv[1], "merge") == 0) + func = merge_main; + else if (strcmp(argv[1], "show") == 0) + func = show_main; + + if (func) { + std::string Invocation(ProgName.str() + " " + argv[1]); + argv[1] = Invocation.c_str(); + return func(argc - 1, argv + 1); + } + + if (strcmp(argv[1], "-h") == 0 || + strcmp(argv[1], "-help") == 0 || + strcmp(argv[1], "--help") == 0) { + + errs() << "OVERVIEW: LLVM profile data tools\n\n" + << "USAGE: " << ProgName << " [args...]\n" + << "USAGE: " << ProgName << " -help\n\n" + << "Available commands: merge, show\n"; + return 0; + } + } + + if (argc < 2) + errs() << ProgName << ": No command specified!\n"; + else + errs() << ProgName << ": Unknown command!\n"; + + errs() << "USAGE: " << ProgName << " [args...]\n"; + return 1; +} diff --git a/usr.bin/clang/Makefile b/usr.bin/clang/Makefile index c6a3c7aead66..4d4d2ecc8abd 100644 --- a/usr.bin/clang/Makefile +++ b/usr.bin/clang/Makefile @@ -13,12 +13,14 @@ SUBDIR+=bugpoint \ llvm-as \ llvm-bcanalyzer \ llvm-diff \ + llvm-cov \ llvm-dis \ llvm-extract \ llvm-link \ llvm-mc \ llvm-nm \ llvm-objdump \ + llvm-profdata \ llvm-rtdyld \ llvm-symbolizer \ macho-dump \ diff --git a/usr.bin/clang/llvm-cov/Makefile b/usr.bin/clang/llvm-cov/Makefile new file mode 100644 index 000000000000..16834cf69b6a --- /dev/null +++ b/usr.bin/clang/llvm-cov/Makefile @@ -0,0 +1,26 @@ +# $FreeBSD$ + +.include + +PROG_CXX=llvm-cov + +SRCDIR= tools/llvm-cov +SRCS= CodeCoverage.cpp \ + CoverageFilters.cpp \ + CoverageReport.cpp \ + CoverageSummary.cpp \ + CoverageSummaryInfo.cpp \ + SourceCoverageView.cpp \ + TestingSupport.cpp \ + gcov.cpp \ + llvm-cov.cpp + +LIBDEPS=llvmprofiledata \ + llvmobject \ + llvmmcparser \ + llvmmc \ + llvmbitreader \ + llvmcore \ + llvmsupport + +.include "../clang.prog.mk" diff --git a/usr.bin/clang/llvm-cov/llvm-cov.1 b/usr.bin/clang/llvm-cov/llvm-cov.1 new file mode 100644 index 000000000000..c76b7abc1e55 --- /dev/null +++ b/usr.bin/clang/llvm-cov/llvm-cov.1 @@ -0,0 +1,165 @@ +.\" $FreeBSD$ +.\" Man page generated from reStructuredText. +. +.TH "LLVM-COV" "1" "2015-04-01" "3.6" "LLVM" +.SH NAME +llvm-cov \- emit coverage information +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBllvm\-cov\fP [options] SOURCEFILE +.SH DESCRIPTION +.sp +The \fBllvm\-cov\fP tool reads code coverage data files and displays the +coverage information for a specified source file. It is compatible with the +\fBgcov\fP tool from version 4.2 of \fBGCC\fP and may also be compatible with +some later versions of \fBgcov\fP\&. +.sp +To use llvm\-cov, you must first build an instrumented version of your +application that collects coverage data as it runs. Compile with the +\fB\-fprofile\-arcs\fP and \fB\-ftest\-coverage\fP options to add the +instrumentation. (Alternatively, you can use the \fB\-\-coverage\fP option, which +includes both of those other options.) You should compile with debugging +information (\fB\-g\fP) and without optimization (\fB\-O0\fP); otherwise, the +coverage data cannot be accurately mapped back to the source code. +.sp +At the time you compile the instrumented code, a \fB\&.gcno\fP data file will be +generated for each object file. These \fB\&.gcno\fP files contain half of the +coverage data. The other half of the data comes from \fB\&.gcda\fP files that are +generated when you run the instrumented program, with a separate \fB\&.gcda\fP +file for each object file. Each time you run the program, the execution counts +are summed into any existing \fB\&.gcda\fP files, so be sure to remove any old +files if you do not want their contents to be included. +.sp +By default, the \fB\&.gcda\fP files are written into the same directory as the +object files, but you can override that by setting the \fBGCOV_PREFIX\fP and +\fBGCOV_PREFIX_STRIP\fP environment variables. The \fBGCOV_PREFIX_STRIP\fP +variable specifies a number of directory components to be removed from the +start of the absolute path to the object file directory. After stripping those +directories, the prefix from the \fBGCOV_PREFIX\fP variable is added. These +environment variables allow you to run the instrumented program on a machine +where the original object file directories are not accessible, but you will +then need to copy the \fB\&.gcda\fP files back to the object file directories +where llvm\-cov expects to find them. +.sp +Once you have generated the coverage data files, run llvm\-cov for each main +source file where you want to examine the coverage results. This should be run +from the same directory where you previously ran the compiler. The results for +the specified source file are written to a file named by appending a \fB\&.gcov\fP +suffix. A separate output file is also created for each file included by the +main source file, also with a \fB\&.gcov\fP suffix added. +.sp +The basic content of an llvm\-cov output file is a copy of the source file with +an execution count and line number prepended to every line. The execution +count is shown as \fB\-\fP if a line does not contain any executable code. If +a line contains code but that code was never executed, the count is displayed +as \fB#####\fP\&. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-a, \-\-all\-blocks +Display all basic blocks. If there are multiple blocks for a single line of +source code, this option causes llvm\-cov to show the count for each block +instead of just one count for the entire line. +.UNINDENT +.INDENT 0.0 +.TP +.B \-b, \-\-branch\-probabilities +Display conditional branch probabilities and a summary of branch information. +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-branch\-counts +Display branch counts instead of probabilities (requires \-b). +.UNINDENT +.INDENT 0.0 +.TP +.B \-f, \-\-function\-summaries +Show a summary of coverage for each function instead of just one summary for +an entire source file. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-help +Display available options (\-\-help\-hidden for more). +.UNINDENT +.INDENT 0.0 +.TP +.B \-l, \-\-long\-file\-names +For coverage output of files included from the main source file, add the +main file name followed by \fB##\fP as a prefix to the output file names. This +can be combined with the \-\-preserve\-paths option to use complete paths for +both the main file and the included file. +.UNINDENT +.INDENT 0.0 +.TP +.B \-n, \-\-no\-output +Do not output any \fB\&.gcov\fP files. Summary information is still +displayed. +.UNINDENT +.INDENT 0.0 +.TP +.B \-o=, \-\-object\-directory=, \-\-object\-file= +Find objects in DIR or based on FILE\(aqs path. If you specify a particular +object file, the coverage data files are expected to have the same base name +with \fB\&.gcno\fP and \fB\&.gcda\fP extensions. If you specify a directory, the +files are expected in that directory with the same base name as the source +file. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-preserve\-paths +Preserve path components when naming the coverage output files. In addition +to the source file name, include the directories from the path to that +file. The directories are separate by \fB#\fP characters, with \fB\&.\fP directories +removed and \fB\&..\fP directories replaced by \fB^\fP characters. When used with +the \-\-long\-file\-names option, this applies to both the main file name and the +included file name. +.UNINDENT +.INDENT 0.0 +.TP +.B \-u, \-\-unconditional\-branches +Include unconditional branches in the output for the \-\-branch\-probabilities +option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-version +Display the version of llvm\-cov. +.UNINDENT +.SH EXIT STATUS +.sp +\fBllvm\-cov\fP returns 1 if it cannot read input files. Otherwise, it +exits with zero. +.SH AUTHOR +Maintained by The LLVM Team (http://llvm.org/). +.SH COPYRIGHT +2003-2014, LLVM Project +.\" Generated by docutils manpage writer. +. diff --git a/usr.bin/clang/llvm-profdata/Makefile b/usr.bin/clang/llvm-profdata/Makefile new file mode 100644 index 000000000000..50785210205e --- /dev/null +++ b/usr.bin/clang/llvm-profdata/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +.include + +PROG_CXX=llvm-profdata + +SRCDIR= tools/llvm-profdata +SRCS= llvm-profdata.cpp + +LIBDEPS=llvmprofiledata \ + llvmobject \ + llvmmcparser \ + llvmmc \ + llvmbitreader \ + llvmcore \ + llvmsupport + +.include "../clang.prog.mk" diff --git a/usr.bin/clang/llvm-profdata/llvm-profdata.1 b/usr.bin/clang/llvm-profdata/llvm-profdata.1 new file mode 100644 index 000000000000..4f66e633a472 --- /dev/null +++ b/usr.bin/clang/llvm-profdata/llvm-profdata.1 @@ -0,0 +1,117 @@ +.\" $FreeBSD$ +.\" Man page generated from reStructuredText. +. +.TH "LLVM-PROFDATA" "1" "2015-04-01" "3.6" "LLVM" +.SH NAME +llvm-profdata \- Profile data tool +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBllvm\-profdata\fP \fIcommand\fP [\fIargs...\fP] +.SH DESCRIPTION +.sp +The \fBllvm\-profdata\fP tool is a small utility for working with profile +data files. +.SH COMMANDS +.INDENT 0.0 +.IP \(bu 2 +\fI\%merge\fP +.IP \(bu 2 +\fI\%show\fP +.UNINDENT +.SH MERGE +.SS SYNOPSIS +.sp +\fBllvm\-profdata merge\fP [\fIoptions\fP] [\fIfilenames...\fP] +.SS DESCRIPTION +.sp +\fBllvm\-profdata merge\fP takes several profile data files +generated by PGO instrumentation and merges them together into a single +indexed profile data file. +.SS OPTIONS +.INDENT 0.0 +.TP +.B \-help +Print a summary of command line options. +.UNINDENT +.INDENT 0.0 +.TP +.B \-output=output, \-o=output +Specify the output file name. \fIOutput\fP cannot be \fB\-\fP as the resulting +indexed profile data can\(aqt be written to standard output. +.UNINDENT +.SH SHOW +.SS SYNOPSIS +.sp +\fBllvm\-profdata show\fP [\fIoptions\fP] [\fIfilename\fP] +.SS DESCRIPTION +.sp +\fBllvm\-profdata show\fP takes a profile data file and displays the +information about the profile counters for this file and +for any of the specified function(s). +.sp +If \fIfilename\fP is omitted or is \fB\-\fP, then \fBllvm\-profdata show\fP reads its +input from standard input. +.SS OPTIONS +.INDENT 0.0 +.TP +.B \-all\-functions +Print details for every function. +.UNINDENT +.INDENT 0.0 +.TP +.B \-counts +Print the counter values for the displayed functions. +.UNINDENT +.INDENT 0.0 +.TP +.B \-function=string +Print details for a function if the function\(aqs name contains the given string. +.UNINDENT +.INDENT 0.0 +.TP +.B \-help +Print a summary of command line options. +.UNINDENT +.INDENT 0.0 +.TP +.B \-output=output, \-o=output +Specify the output file name. If \fIoutput\fP is \fB\-\fP or it isn\(aqt specified, +then the output is sent to standard output. +.UNINDENT +.SH EXIT STATUS +.sp +\fBllvm\-profdata\fP returns 1 if the command is omitted or is invalid, +if it cannot read input files, or if there is a mismatch between their data. +.SH AUTHOR +Maintained by The LLVM Team (http://llvm.org/). +.SH COPYRIGHT +2003-2014, LLVM Project +.\" Generated by docutils manpage writer. +.