Add the llvm-cov and llvm-profdata tools, when WITH_CLANG_EXTRAS is

defined.  These help with processing coverage and profile data.
This commit is contained in:
dim 2015-04-03 19:43:39 +00:00
commit c660843b7a
22 changed files with 2698 additions and 0 deletions

View File

@ -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 <functional>
#include <system_error>
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<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
/// \brief Create source views for the expansions of the view.
void attachExpansionSubViews(SourceCoverageView &View,
ArrayRef<ExpansionRecord> Expansions,
CoverageMapping &Coverage);
/// \brief Create the source view of a particular function.
std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage);
/// \brief Create the main source view of a particular source file.
std::unique_ptr<SourceCoverageView>
createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage);
/// \brief Load the coverage mapping data. Return true if an error occured.
std::unique_ptr<CoverageMapping> load();
int run(Command Cmd, int argc, const char **argv);
typedef std::function<int(int, const char **)> 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<std::string> SourceFiles;
std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
LoadedSourceFiles;
bool CompareFilenamesOnly;
StringMap<std::string> RemappedFilenames;
};
}
void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
errs() << "error: ";
if (!Whence.empty())
errs() << Whence << ": ";
errs() << Message << "\n";
}
ErrorOr<const MemoryBuffer &>
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<ExpansionRecord> 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<SourceCoverageView>(
SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
View.addExpansion(Expansion.Region, std::move(SubView));
}
}
std::unique_ptr<SourceCoverageView>
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<SourceCoverageView>(
SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
attachExpansionSubViews(*View, Expansions, Coverage);
return View;
}
std::unique_ptr<SourceCoverageView>
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<SourceCoverageView>(
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<SourceCoverageView>(
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<CoverageMapping> 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<std::string, true> ObjectFilename(
cl::Positional, cl::Required, cl::location(this->ObjectFilename),
cl::desc("Covered executable or object file."));
cl::list<std::string> InputSourceFiles(
cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
cl::opt<std::string, true> PGOFilename(
"instr-profile", cl::Required, cl::location(this->PGOFilename),
cl::desc(
"File with the profile data obtained after an instrumented run"));
cl::opt<bool> DebugDump("dump", cl::Optional,
cl::desc("Show internal debug dump"));
cl::opt<bool> FilenameEquivalence(
"filename-equivalence", cl::Optional,
cl::desc("Treat source files as equivalent to paths in the coverage data "
"when the file names match, even if the full paths do not"));
cl::OptionCategory FilteringCategory("Function filtering options");
cl::list<std::string> NameFilters(
"name", cl::Optional,
cl::desc("Show code coverage only for functions with the given name"),
cl::ZeroOrMore, cl::cat(FilteringCategory));
cl::list<std::string> 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<double> 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<double> 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<double> 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<double> 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<NameCoverageFilter>(Name));
for (const auto &Regex : NameRegexFilters)
NameFilterer->push_back(
llvm::make_unique<NameRegexCoverageFilter>(Regex));
Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
}
if (RegionCoverageLtFilter.getNumOccurrences() ||
RegionCoverageGtFilter.getNumOccurrences() ||
LineCoverageLtFilter.getNumOccurrences() ||
LineCoverageGtFilter.getNumOccurrences()) {
auto StatFilterer = new CoverageFilters;
if (RegionCoverageLtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
if (RegionCoverageGtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
if (LineCoverageLtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
LineCoverageFilter::LessThan, LineCoverageLtFilter));
if (LineCoverageGtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
Filters.push_back(std::unique_ptr<CoverageFilter>(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<bool> ShowLineExecutionCounts(
"show-line-counts", cl::Optional,
cl::desc("Show the execution counts for each line"), cl::init(true),
cl::cat(ViewCategory));
cl::opt<bool> ShowRegions(
"show-regions", cl::Optional,
cl::desc("Show the execution counts for each region"),
cl::cat(ViewCategory));
cl::opt<bool> 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<bool> ShowExpansions("show-expansions", cl::Optional,
cl::desc("Show expanded source regions"),
cl::cat(ViewCategory));
cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
cl::desc("Show function instantiations"),
cl::cat(ViewCategory));
cl::opt<bool> 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<bool> 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);
}

View File

@ -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<CoverageFilter> 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;
}

View File

@ -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 <memory>
#include <vector>
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 <typename T> 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<double> {
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<double> {
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<std::unique_ptr<CoverageFilter>> Filters;
public:
/// \brief Append a filter to this collection.
void push_back(std::unique_ptr<CoverageFilter> 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

View File

@ -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 <typename T>
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 <typename T, size_t N>
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 <typename T>
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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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 <vector>
namespace llvm {
/// \brief Manager for the function and file code coverage summaries.
class CoverageSummary {
std::vector<StringRef> Filenames;
std::vector<FunctionCoverageSummary> FunctionSummaries;
std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs;
std::vector<FileCoverageSummary> FileSummaries;
unsigned getFileID(StringRef Filename);
public:
void createSummaries(const coverage::CoverageMapping &Coverage);
ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; }
FileCoverageSummary getCombinedFileSummaries();
void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);
void render(raw_ostream &OS);
};
}
#endif // LLVM_COV_COVERAGESUMMARY_H

View File

@ -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<unsigned>::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<uint64_t, 16> 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<FunctionCoverageSummary> 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);
}

View File

@ -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<FunctionCoverageSummary> FunctionSummaries;
FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage,
const LineCoverageInfo &LineCoverage,
const FunctionCoverageInfo &FunctionCoverage,
ArrayRef<FunctionCoverageSummary> 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<FunctionCoverageSummary> FunctionSummaries);
};
} // namespace llvm
#endif // LLVM_COV_COVERAGESUMMARYINFO_H

View File

@ -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

View File

@ -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 <utility>
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 <typename T>
inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
return OS.OS << std::forward<T>(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

View File

@ -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<const coverage::CoverageSegment *> Segments,
unsigned ExpansionCol) {
Optional<raw_ostream::Colors> Highlight;
SmallVector<std::pair<unsigned, unsigned>, 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<unsigned>(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<const coverage::CoverageSegment *> 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<const coverage::CoverageSegment *, 8> 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";
}
}
}

View File

@ -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 <vector>
namespace llvm {
class SourceCoverageView;
/// \brief A view that represents a macro or include expansion
struct ExpansionView {
coverage::CounterMappingRegion Region;
std::unique_ptr<SourceCoverageView> View;
ExpansionView(const coverage::CounterMappingRegion &Region,
std::unique_ptr<SourceCoverageView> 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<SourceCoverageView> View;
InstantiationView(StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> 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<ExpansionView> ExpansionSubViews;
std::vector<InstantiationView> InstantiationSubViews;
/// \brief Render a source line with highlighting.
void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
const coverage::CoverageSegment *WrappedSegment,
ArrayRef<const coverage::CoverageSegment *> 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<const coverage::CoverageSegment *> 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<SourceCoverageView> 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<SourceCoverageView> 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

View File

@ -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 <functional>
#include <system_error>
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<std::string> InputSourceFile(cl::Positional, cl::Required,
cl::desc("<Source file>"));
cl::opt<std::string> 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;
}

View File

@ -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 <system_error>
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<std::unique_ptr<MemoryBuffer>> 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<std::unique_ptr<MemoryBuffer>> 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<std::string> SourceFiles(cl::Positional, cl::OneOrMore,
cl::desc("SOURCEFILE"));
cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false),
cl::desc("Display all basic blocks"));
cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks));
cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false),
cl::desc("Display branch probabilities"));
cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb));
cl::opt<bool> 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<bool> 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<bool> FuncSummary("f", cl::Grouping, cl::init(false),
cl::desc("Show coverage for each function"));
cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary));
cl::opt<bool> 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<std::string> 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<bool> PreservePaths("p", cl::Grouping, cl::init(false),
cl::desc("Preserve path components"));
cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths));
cl::opt<bool> 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<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat),
cl::desc("Dump the gcov file to stderr"));
cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""),
cl::desc("Override inferred gcno file"));
cl::opt<std::string> 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;
}

View File

@ -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 <string>
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<MainFunction>(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);
}

View File

@ -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<std::string> 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<std::string> 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<FunctionSamples> 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<FunctionSamples> &Profiles = Reader->getProfiles();
for (StringMap<FunctionSamples>::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<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore,
cl::desc("<filenames...>"));
cl::opt<std::string> 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<ProfileKinds> ProfileKind(
cl::desc("Profile kind:"), cl::init(instr),
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
clEnumVal(sample, "Sample profile"), clEnumValEnd));
cl::opt<sampleprof::SampleProfileFormat> 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<std::string> Filename(cl::Positional, cl::Required,
cl::desc("<profdata-file>"));
cl::opt<bool> ShowCounts("counts", cl::init(false),
cl::desc("Show counter values for shown functions"));
cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
cl::desc("Details for every function"));
cl::opt<std::string> ShowFunction("function",
cl::desc("Details for matching functions"));
cl::opt<std::string> 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<ProfileKinds> 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 << " <command> [args...]\n"
<< "USAGE: " << ProgName << " <command> -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 << " <merge|show> [args...]\n";
return 1;
}

View File

@ -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 \

View File

@ -0,0 +1,26 @@
# $FreeBSD$
.include <bsd.own.mk>
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"

View File

@ -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=<DIR|FILE>, \-\-object\-directory=<DIR>, \-\-object\-file=<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.
.

View File

@ -0,0 +1,18 @@
# $FreeBSD$
.include <bsd.own.mk>
PROG_CXX=llvm-profdata
SRCDIR= tools/llvm-profdata
SRCS= llvm-profdata.cpp
LIBDEPS=llvmprofiledata \
llvmobject \
llvmmcparser \
llvmmc \
llvmbitreader \
llvmcore \
llvmsupport
.include "../clang.prog.mk"

View File

@ -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.
.