17#include "llvm/Config/llvm-config.h"
27#include <system_error>
66 std::vector<std::vector<const GCOVFunction *>> startLineToFunctions;
67 std::vector<LineInfo> lines;
83 void printSummary(
const Summary &summary,
raw_ostream &os)
const;
86 void collectSourceLine(SourceInfo &si, Summary *summary, LineInfo &line,
87 size_t lineNum)
const;
88 void collectSource(SourceInfo &si, Summary &summary)
const;
91 void printSourceToIntermediate(
const SourceInfo &si,
raw_ostream &os)
const;
94 std::vector<SourceInfo> sources;
116 while ((tag = buf.
getWord())) {
121 functions.push_back(std::make_unique<GCOVFunction>(*
this));
146 fn->
srcIdx = r.first->second;
150 for (
uint32_t i = 0; i != length; ++i) {
152 fn->
blocks.push_back(std::make_unique<GCOVBlock>(i));
157 fn->
blocks.push_back(std::make_unique<GCOVBlock>(i));
161 if (srcNo >= fn->
blocks.size()) {
162 errs() <<
"unexpected block number: " << srcNo <<
" (in "
163 << fn->
blocks.size() <<
")\n";
172 auto arc = std::make_unique<GCOVArc>(*src, *dst, flags);
176 fn->
treeArcs.push_back(std::move(arc));
178 fn->
arcs.push_back(std::move(arc));
182 if (srcNo >= fn->
blocks.size()) {
183 errs() <<
"unexpected block number: " << srcNo <<
" (in "
184 << fn->
blocks.size() <<
")\n";
195 if (filename.
empty())
221 errs() <<
"GCOV versions do not match.\n";
226 if (!buf.
readInt(GCDAChecksum))
230 <<
" != " << GCDAChecksum <<
"\n";
236 while ((tag = buf.
getWord())) {
260 if (length < 2 || !buf.
readInt(ident))
263 uint32_t linenoChecksum, cfgChecksum = 0;
272 <<
format(
": checksum mismatch, (%u, %u) != (%u, %u)\n",
282 if (length != expected) {
285 ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n",
289 for (std::unique_ptr<GCOVArc> &arc : fn->
arcs) {
292 arc->src.count += arc->count;
295 if (fn->
blocks.size() >= 2) {
300 sink.addDstEdge(arc.get());
302 fn->
treeArcs.push_back(std::move(arc));
306 for (
size_t i = fn->
treeArcs.size() - 1; i; --i)
324#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
357 return blocks.front()->getCount();
371 if (!
visited.insert(&v).second)
381 if (int64_t(excess) < 0)
384 pred->count = excess;
395#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
407 OS <<
"Block : " <<
number <<
" Counter : " <<
count <<
"\n";
409 OS <<
"\tSource Edges : ";
411 OS << Edge->src.number <<
" (" << Edge->count <<
"), ";
415 OS <<
"\tDestination Edges : ";
419 OS << Edge->dst.number <<
" (" << Edge->count <<
"), ";
431#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
438 std::vector<std::pair<GCOVBlock *, size_t>> &stack) {
442 stack.emplace_back(src, 0);
445 std::tie(u, i) = stack.back();
446 if (i == u->succ.size()) {
447 u->traversable =
false;
453 ++stack.back().second;
458 if (
succ->cycleCount == 0 || !
succ->dst.traversable || &
succ->dst == u)
460 if (
succ->dst.incoming ==
nullptr) {
462 stack.emplace_back(&
succ->dst, 0);
467 minCount = std::min(minCount, v->incoming->cycleCount);
468 v = &v->incoming->src;
472 succ->cycleCount -= minCount;
474 v->incoming->cycleCount -= minCount;
475 v = &v->incoming->src;
489 std::vector<std::pair<GCOVBlock *, size_t>> stack;
493 for (
const auto *b : blocks) {
498 for (
const auto *
block : blocks) {
509 for (
const auto *b : blocks) {
522 if (!dividend || !divisor)
525 return dividend < divisor ? 1 : dividend / divisor;
534 if (Numerator == Divisor)
537 uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
546struct formatBranchInfo {
552 OS <<
"never executed";
554 OS <<
"taken " << Count;
570 std::unique_ptr<MemoryBuffer> Buffer;
574 LineConsumer() =
default;
581 if (std::error_code EC = BufferOrErr.
getError()) {
585 Buffer = std::move(BufferOrErr.
get());
586 Remaining = Buffer->getBuffer();
589 bool empty() {
return Remaining.
empty(); }
595 std::tie(Line, Remaining) = Remaining.
split(
"\n");
612 for (
I = S = Filename.begin(),
E = Filename.end();
I !=
E; ++
I) {
616 if (
I - S == 1 && *S ==
'.') {
618 }
else if (
I - S == 2 && *S ==
'.' && *(S + 1) ==
'.') {
626 Result.push_back(
'#');
633 return std::string(Result.str());
636std::string Context::getCoveragePath(
StringRef filename,
642 return std::string(filename);
644 std::string CoveragePath;
653 Hasher.
final(Result);
654 CoveragePath +=
"##" + std::string(
Result.digest());
656 CoveragePath +=
".gcov";
660void Context::collectFunction(
GCOVFunction &f, Summary &summary) {
661 SourceInfo &si = sources[
f.srcIdx];
662 if (
f.startLine >= si.startLineToFunctions.size())
663 si.startLineToFunctions.resize(
f.startLine + 1);
664 si.startLineToFunctions[
f.startLine].push_back(&f);
670 uint32_t maxLineNum = *std::max_element(
b.lines.begin(),
b.lines.end());
671 if (maxLineNum >= si.lines.size())
672 si.lines.resize(maxLineNum + 1);
674 LineInfo &line = si.lines[lineNum];
675 if (lines.
insert(lineNum).second)
677 if (
b.count && linesExec.
insert(lineNum).second)
680 line.count +=
b.count;
681 line.blocks.push_back(&b);
686void Context::collectSourceLine(SourceInfo &si, Summary *summary,
687 LineInfo &line,
size_t lineNum)
const {
690 if (
b->number == 0) {
712 ++summary->linesExec;
717 if (
b->getLastLine() != lineNum)
719 int branches = 0, execBranches = 0, takenBranches = 0;
720 for (
const GCOVArc *arc :
b->succ) {
729 summary->branchesExec += execBranches;
730 summary->branchesTaken += takenBranches;
735void Context::collectSource(SourceInfo &si, Summary &summary)
const {
737 for (LineInfo &line : si.lines) {
738 collectSourceLine(si, &summary, line, lineNum);
743void Context::annotateSource(SourceInfo &si,
const GCOVFile &
file,
749 os <<
" -: 0:Source:" << si.displayName <<
'\n';
750 os <<
" -: 0:Graph:" << gcno <<
'\n';
751 os <<
" -: 0:Data:" << gcda <<
'\n';
752 os <<
" -: 0:Runs:" <<
file.runCount <<
'\n';
754 os <<
" -: 0:Programs:" <<
file.programCount <<
'\n';
756 for (
size_t lineNum = 1; !source.empty(); ++lineNum) {
757 if (lineNum >= si.lines.size()) {
759 source.printNext(os, lineNum);
763 const LineInfo &line = si.lines[lineNum];
764 if (
options.BranchInfo && lineNum < si.startLineToFunctions.size())
765 for (
const auto *f : si.startLineToFunctions[lineNum])
766 printFunctionDetails(*f, os);
769 else if (line.count == 0)
772 os <<
format(
"%9" PRIu64
":", line.count);
773 source.printNext(os, lineNum);
777 if (
b->getLastLine() != lineNum)
780 if (
b->getCount() == 0)
783 os <<
format(
"%9" PRIu64
":",
b->count);
784 os <<
format(
"%5u-block %2u\n", lineNum, blockIdx++);
787 size_t NumEdges =
b->succ.size();
789 printBranchInfo(*b, edgeIdx, os);
790 else if (
options.UncondBranch && NumEdges == 1) {
792 os <<
format(
"unconditional %2u ", edgeIdx++)
793 << formatBranchInfo(options,
count,
count) <<
'\n';
800void Context::printSourceToIntermediate(
const SourceInfo &si,
802 os <<
"file:" << si.filename <<
'\n';
803 for (
const auto &fs : si.startLineToFunctions)
805 os <<
"function:" <<
f->startLine <<
',' <<
f->getEntryCount() <<
','
806 <<
f->getName(
options.Demangle) <<
'\n';
807 for (
size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) {
808 const LineInfo &line = si.lines[lineNum];
809 if (line.blocks.empty())
814 os <<
"lcount:" << lineNum <<
',' << line.count <<
'\n';
819 if (
b->succ.size() < 2 ||
b->getLastLine() != lineNum)
821 for (
const GCOVArc *arc :
b->succ) {
823 b->getCount() ? arc->
count ?
"taken" :
"nottaken" :
"notexec";
824 os <<
"branch:" << lineNum <<
',' << type <<
'\n';
833 sources.emplace_back(filename);
834 SourceInfo &si = sources.back();
835 si.displayName = si.filename;
836 if (!
options.SourcePrefix.empty() &&
839 !si.displayName.empty()) {
843 si.displayName.erase(si.displayName.begin());
845 si.displayName = si.filename;
854 collectFunction(f, summary);
856 os <<
"Function '" << summary.Name <<
"'\n";
857 printSummary(summary, os);
862 for (SourceInfo &si : sources) {
865 Summary summary(si.displayName);
866 collectSource(si, summary);
869 std::string gcovName = getCoveragePath(si.filename, filename);
871 os <<
"File '" << summary.Name <<
"'\n";
872 printSummary(summary, os);
874 os <<
"Creating '" << gcovName <<
"'\n";
880 std::optional<raw_fd_ostream> os;
885 errs() << ec.message() <<
'\n';
889 annotateSource(si,
file, gcno, gcda,
900 errs() << ec.message() <<
'\n';
904 for (
const SourceInfo &si : sources)
905 printSourceToIntermediate(si, os);
909void Context::printFunctionDetails(
const GCOVFunction &f,
911 const uint64_t entryCount =
f.getEntryCount();
913 const GCOVBlock &exitBlock =
f.getExitBlock();
916 exitCount += arc->
count;
918 if (
b.number != 0 && &b != &exitBlock &&
b.getCount())
921 os <<
"function " <<
f.getName(
options.Demangle) <<
" called " << entryCount
923 <<
"% blocks executed "
934 os <<
format(
"branch %2u ", edgeIdx++)
935 << formatBranchInfo(options, arc->
count, total) <<
'\n';
938void Context::printSummary(
const Summary &summary,
raw_ostream &os)
const {
939 os <<
format(
"Lines executed:%.2f%% of %" PRIu64
"\n",
940 double(summary.linesExec) * 100 / summary.lines, summary.lines);
942 if (summary.branches == 0) {
943 os <<
"No branches\n";
945 os <<
format(
"Branches executed:%.2f%% of %" PRIu64
"\n",
946 double(summary.branchesExec) * 100 / summary.branches,
948 os <<
format(
"Taken at least once:%.2f%% of %" PRIu64
"\n",
949 double(summary.branchesTaken) * 100 / summary.branches,
959 fi.print(filename, gcno, gcda,
file);
arc branch ARC finalize branches
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val)
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Looks at all the uses of the given value Returns the Liveness deduced from the uses of this value Adds all uses that cause the result to be MaybeLive to MaybeLiveRetUses If the result is MaybeLiveUses might be modified but its content should be ignored(since it might not be complete). DeadArgumentEliminationPass
@ GCOV_TAG_PROGRAM_SUMMARY
@ GCOV_TAG_OBJECT_SUMMARY
static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths)
Convert a path to a gcov filename.
static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor)
static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor)
static bool sink(Instruction &I, LoopInfo *LI, DominatorTree *DT, const Loop *CurLoop, ICFLoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU, OptimizationRemarkEmitter *ORE)
When an instruction is found to only be used outside of the loop, this function moves it to the exit ...
dot regions Print regions of function to dot file(with no function bodies)"
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallSet class.
unify loop Fixup each natural loop to have a single exit block
Represents either an error or a value T.
std::error_code getError() const
GCOVBlock - Collects block information.
static uint64_t getCyclesCount(const BlockVector &blocks)
SmallVector< uint32_t, 4 > lines
void addDstEdge(GCOVArc *Edge)
void addSrcEdge(GCOVArc *Edge)
void print(raw_ostream &OS) const
collectLineCounts - Collect line counts.
static uint64_t augmentOneCycle(GCOVBlock *src, std::vector< std::pair< GCOVBlock *, size_t > > &stack)
SmallVector< GCOVArc *, 2 > pred
void dump() const
dump - Dump GCOVBlock content to dbgs() for debugging purposes.
SmallVector< GCOVArc *, 2 > succ
GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific read operations.
bool readInt(uint32_t &Val)
DataExtractor::Cursor cursor
bool readInt64(uint64_t &Val)
bool readGCOVVersion(GCOV::GCOVVersion &version)
readGCOVVersion - Read GCOV version.
bool readString(StringRef &str)
bool readGCNOFormat()
readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
bool readGCDAFormat()
readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
GCOVFile - Collects coverage information for one pair of coverage file (.gcno and ....
SmallVector< std::unique_ptr< GCOVFunction >, 16 > functions
void print(raw_ostream &OS) const
GCOV::GCOVVersion version
std::vector< std::string > filenames
GCOV::GCOVVersion getVersion() const
void dump() const
dump - Dump GCOVFile content to dbgs() for debugging purposes.
std::map< uint32_t, GCOVFunction * > identToFunction
StringMap< unsigned > filenameToIdx
bool readGCNO(GCOVBuffer &Buffer)
readGCNO - Read GCNO buffer.
bool readGCDA(GCOVBuffer &Buffer)
readGCDA - Read GCDA buffer.
GCOVFunction - Collects function information.
uint64_t getEntryCount() const
getEntryCount - Get the number of times the function was called by retrieving the entry block's count...
SmallVector< std::unique_ptr< GCOVArc >, 0 > treeArcs
StringRef getName(bool demangle) const
void dump() const
dump - Dump GCOVFunction content to dbgs() for debugging purposes.
uint64_t propagateCounts(const GCOVBlock &v, GCOVArc *pred)
SmallVector< std::unique_ptr< GCOVBlock >, 0 > blocks
GCOVBlock & getExitBlock() const
StringRef getFilename() const
DenseSet< const GCOVBlock * > visited
SmallString< 0 > demangled
void print(raw_ostream &OS) const
iterator_range< BlockIterator > blocksRange() const
SmallVector< std::unique_ptr< GCOVArc >, 0 > arcs
void update(ArrayRef< uint8_t > Data)
Updates the hash for the byte stream provided.
void final(MD5Result &Result)
Finishes off the hash and puts the result in result.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
std::pair< iterator, bool > try_emplace(StringRef Key, ArgsTy &&...Args)
Emplace a new element for the specified key into the map if the key isn't already in the map.
StringRef - Represent a constant reference to a string, i.e.
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
std::string str() const
str - Get the contents as an std::string.
constexpr bool empty() const
empty - Check if the string is empty.
bool startswith(StringRef Prefix) const
bool equals(StringRef RHS) const
equals - Check for string equality, this is more efficient than compare() when the relative ordering ...
const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
A raw_ostream that writes to a file descriptor.
This class implements an extremely fast bulk output stream that can only output to a stream.
bool exists(const basic_file_status &status)
Does file exist?
@ OF_TextWithCRLF
The file should be opened in text mode and use a carriage linefeed '\r '.
StringRef filename(StringRef path, Style style=Style::native)
Get filename.
bool is_absolute(const Twine &path, Style style=Style::native)
Is path absolute?
bool replace_path_prefix(SmallVectorImpl< char > &Path, StringRef OldPrefix, StringRef NewPrefix, Style style=Style::native)
Replace matching path prefix with another path.
bool is_separator(char value, Style style=Style::native)
Check whether the given char is a path separator on the host OS.
This is an optimization pass for GlobalISel generic memory operations.
std::string demangle(const std::string &MangledName)
Attempt to demangle a string using different demangling schemes.
raw_fd_ostream & outs()
This returns a reference to a raw_fd_ostream for standard output.
iterator_range< pointee_iterator< WrappedIteratorT > > make_pointee_range(RangeT &&Range)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
void gcovOneInput(const GCOV::Options &options, StringRef filename, StringRef gcno, StringRef gcda, GCOVFile &file)
char * itaniumDemangle(std::string_view mangled_name)
Returns a non-NULL pointer to a NUL-terminated C style string that should be explicitly freed,...
A struct for passing gcov options between functions.