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:
commit
c660843b7a
483
contrib/llvm/tools/llvm-cov/CodeCoverage.cpp
Normal file
483
contrib/llvm/tools/llvm-cov/CodeCoverage.cpp
Normal 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);
|
||||
}
|
59
contrib/llvm/tools/llvm-cov/CoverageFilters.cpp
Normal file
59
contrib/llvm/tools/llvm-cov/CoverageFilters.cpp
Normal 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;
|
||||
}
|
127
contrib/llvm/tools/llvm-cov/CoverageFilters.h
Normal file
127
contrib/llvm/tools/llvm-cov/CoverageFilters.h
Normal 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
|
202
contrib/llvm/tools/llvm-cov/CoverageReport.cpp
Normal file
202
contrib/llvm/tools/llvm-cov/CoverageReport.cpp
Normal 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);
|
||||
}
|
40
contrib/llvm/tools/llvm-cov/CoverageReport.h
Normal file
40
contrib/llvm/tools/llvm-cov/CoverageReport.h
Normal 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
|
64
contrib/llvm/tools/llvm-cov/CoverageSummary.cpp
Normal file
64
contrib/llvm/tools/llvm-cov/CoverageSummary.cpp
Normal 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);
|
||||
}
|
45
contrib/llvm/tools/llvm-cov/CoverageSummary.h
Normal file
45
contrib/llvm/tools/llvm-cov/CoverageSummary.h
Normal 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
|
96
contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
Normal file
96
contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
Normal 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);
|
||||
}
|
133
contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h
Normal file
133
contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h
Normal 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
|
36
contrib/llvm/tools/llvm-cov/CoverageViewOptions.h
Normal file
36
contrib/llvm/tools/llvm-cov/CoverageViewOptions.h
Normal 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
|
60
contrib/llvm/tools/llvm-cov/RenderingSupport.h
Normal file
60
contrib/llvm/tools/llvm-cov/RenderingSupport.h
Normal 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
|
260
contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp
Normal file
260
contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
162
contrib/llvm/tools/llvm-cov/SourceCoverageView.h
Normal file
162
contrib/llvm/tools/llvm-cov/SourceCoverageView.h
Normal 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
|
90
contrib/llvm/tools/llvm-cov/TestingSupport.cpp
Normal file
90
contrib/llvm/tools/llvm-cov/TestingSupport.cpp
Normal 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;
|
||||
}
|
152
contrib/llvm/tools/llvm-cov/gcov.cpp
Normal file
152
contrib/llvm/tools/llvm-cov/gcov.cpp
Normal 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;
|
||||
}
|
78
contrib/llvm/tools/llvm-cov/llvm-cov.cpp
Normal file
78
contrib/llvm/tools/llvm-cov/llvm-cov.cpp
Normal 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);
|
||||
}
|
283
contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp
Normal file
283
contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp
Normal 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;
|
||||
}
|
@ -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 \
|
||||
|
26
usr.bin/clang/llvm-cov/Makefile
Normal file
26
usr.bin/clang/llvm-cov/Makefile
Normal 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"
|
165
usr.bin/clang/llvm-cov/llvm-cov.1
Normal file
165
usr.bin/clang/llvm-cov/llvm-cov.1
Normal 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.
|
||||
.
|
18
usr.bin/clang/llvm-profdata/Makefile
Normal file
18
usr.bin/clang/llvm-profdata/Makefile
Normal 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"
|
117
usr.bin/clang/llvm-profdata/llvm-profdata.1
Normal file
117
usr.bin/clang/llvm-profdata/llvm-profdata.1
Normal 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.
|
||||
.
|
Loading…
Reference in New Issue
Block a user