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));
143 fn->
srcIdx = addNormalizedPathToMap(filename);
147 for (
uint32_t i = 0; i != length; ++i) {
149 fn->
blocks.push_back(std::make_unique<GCOVBlock>(i));
154 fn->
blocks.push_back(std::make_unique<GCOVBlock>(i));
158 if (srcNo >= fn->
blocks.size()) {
159 errs() <<
"unexpected block number: " << srcNo <<
" (in "
160 << fn->
blocks.size() <<
")\n";
169 auto arc = std::make_unique<GCOVArc>(*src, *dst, flags);
173 fn->
treeArcs.push_back(std::move(arc));
175 fn->
arcs.push_back(std::move(arc));
179 if (srcNo >= fn->
blocks.size()) {
180 errs() <<
"unexpected block number: " << srcNo <<
" (in "
181 << fn->
blocks.size() <<
")\n";
192 if (filename.
empty())
218 errs() <<
"GCOV versions do not match.\n";
223 if (!buf.
readInt(GCDAChecksum))
227 <<
" != " << GCDAChecksum <<
"\n";
233 while ((tag = buf.
getWord())) {
248 if (length < 2 || !buf.
readInt(ident))
251 uint32_t linenoChecksum, cfgChecksum = 0;
260 <<
format(
": checksum mismatch, (%u, %u) != (%u, %u)\n",
270 if (length != expected) {
273 ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n",
277 for (std::unique_ptr<GCOVArc> &arc : fn->
arcs) {
280 arc->src.count += arc->count;
283 if (fn->
blocks.size() >= 2) {
288 sink.addDstEdge(arc.get());
290 fn->
treeArcs.push_back(std::move(arc));
294 for (
size_t i = fn->
treeArcs.size() - 1; i; --i)
312#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
317unsigned GCOVFile::addNormalizedPathToMap(
StringRef filename) {
327 return r.first->second;
358 return blocks.front()->getCount();
372 if (!
visited.insert(&v).second)
382 if (int64_t(excess) < 0)
385 pred->count = excess;
396#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
408 OS <<
"Block : " <<
number <<
" Counter : " <<
count <<
"\n";
410 OS <<
"\tSource Edges : ";
412 OS << Edge->src.number <<
" (" << Edge->count <<
"), ";
416 OS <<
"\tDestination Edges : ";
420 OS << Edge->dst.number <<
" (" << Edge->count <<
"), ";
432#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
439 std::vector<std::pair<GCOVBlock *, size_t>> &stack) {
443 stack.emplace_back(src, 0);
446 std::tie(u, i) = stack.back();
447 if (i == u->succ.size()) {
448 u->traversable =
false;
454 ++stack.back().second;
459 if (
succ->cycleCount == 0 || !
succ->dst.traversable || &
succ->dst == u)
461 if (
succ->dst.incoming ==
nullptr) {
463 stack.emplace_back(&
succ->dst, 0);
468 minCount = std::min(minCount, v->incoming->cycleCount);
469 v = &v->incoming->src;
473 succ->cycleCount -= minCount;
475 v->incoming->cycleCount -= minCount;
476 v = &v->incoming->src;
490 std::vector<std::pair<GCOVBlock *, size_t>> stack;
494 for (
const auto *b :
blocks) {
510 for (
const auto *b :
blocks) {
523 if (!dividend || !divisor)
526 return dividend < divisor ? 1 : dividend / divisor;
535 if (Numerator == Divisor)
538 uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
547struct formatBranchInfo {
553 OS <<
"never executed";
555 OS <<
"taken " << Count;
571 std::unique_ptr<MemoryBuffer> Buffer;
575 LineConsumer() =
default;
582 if (std::error_code EC = BufferOrErr.
getError()) {
586 Buffer = std::move(BufferOrErr.
get());
587 Remaining = Buffer->getBuffer();
590 bool empty() {
return Remaining.
empty(); }
596 std::tie(Line, Remaining) = Remaining.
split(
"\n");
613 for (
I = S = Filename.begin(),
E = Filename.end();
I !=
E; ++
I) {
617 if (
I - S == 1 && *S ==
'.') {
619 }
else if (
I - S == 2 && *S ==
'.' && *(S + 1) ==
'.') {
627 Result.push_back(
'#');
634 return std::string(Result.str());
637std::string Context::getCoveragePath(
StringRef filename,
643 return std::string(filename);
645 std::string CoveragePath;
654 Hasher.
final(Result);
655 CoveragePath +=
"##" + std::string(
Result.digest());
657 CoveragePath +=
".gcov";
661void Context::collectFunction(
GCOVFunction &f, Summary &summary) {
662 SourceInfo &si = sources[
f.srcIdx];
663 if (
f.startLine >= si.startLineToFunctions.size())
664 si.startLineToFunctions.resize(
f.startLine + 1);
665 si.startLineToFunctions[
f.startLine].push_back(&f);
671 uint32_t maxLineNum = *std::max_element(
b.lines.begin(),
b.lines.end());
672 if (maxLineNum >= si.lines.size())
673 si.lines.resize(maxLineNum + 1);
675 LineInfo &line = si.lines[lineNum];
676 if (lines.
insert(lineNum).second)
678 if (
b.count && linesExec.
insert(lineNum).second)
681 line.count +=
b.count;
682 line.blocks.push_back(&b);
687void Context::collectSourceLine(SourceInfo &si, Summary *summary,
688 LineInfo &line,
size_t lineNum)
const {
691 if (
b->number == 0) {
713 ++summary->linesExec;
718 if (
b->getLastLine() != lineNum)
720 int branches = 0, execBranches = 0, takenBranches = 0;
721 for (
const GCOVArc *arc :
b->succ) {
730 summary->branchesExec += execBranches;
731 summary->branchesTaken += takenBranches;
736void Context::collectSource(SourceInfo &si, Summary &summary)
const {
738 for (LineInfo &line : si.lines) {
739 collectSourceLine(si, &summary, line, lineNum);
744void Context::annotateSource(SourceInfo &si,
const GCOVFile &
file,
750 os <<
" -: 0:Source:" << si.displayName <<
'\n';
751 os <<
" -: 0:Graph:" << gcno <<
'\n';
752 os <<
" -: 0:Data:" << gcda <<
'\n';
753 os <<
" -: 0:Runs:" <<
file.runCount <<
'\n';
755 os <<
" -: 0:Programs:" <<
file.programCount <<
'\n';
757 for (
size_t lineNum = 1; !source.empty(); ++lineNum) {
758 if (lineNum >= si.lines.size()) {
760 source.printNext(os, lineNum);
764 const LineInfo &line = si.lines[lineNum];
765 if (
options.BranchInfo && lineNum < si.startLineToFunctions.size())
766 for (
const auto *f : si.startLineToFunctions[lineNum])
767 printFunctionDetails(*f, os);
770 else if (line.count == 0)
773 os <<
format(
"%9" PRIu64
":", line.count);
774 source.printNext(os, lineNum);
778 if (
b->getLastLine() != lineNum)
781 if (
b->getCount() == 0)
784 os <<
format(
"%9" PRIu64
":",
b->count);
785 os <<
format(
"%5u-block %2u\n", lineNum, blockIdx++);
788 size_t NumEdges =
b->succ.size();
790 printBranchInfo(*b, edgeIdx, os);
791 else if (
options.UncondBranch && NumEdges == 1) {
793 os <<
format(
"unconditional %2u ", edgeIdx++)
794 << formatBranchInfo(options,
count,
count) <<
'\n';
801void Context::printSourceToIntermediate(
const SourceInfo &si,
803 os <<
"file:" << si.filename <<
'\n';
804 for (
const auto &fs : si.startLineToFunctions)
806 os <<
"function:" <<
f->startLine <<
',' <<
f->getEntryCount() <<
','
807 <<
f->getName(
options.Demangle) <<
'\n';
808 for (
size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) {
809 const LineInfo &line = si.lines[lineNum];
810 if (line.blocks.empty())
815 os <<
"lcount:" << lineNum <<
',' << line.count <<
'\n';
820 if (
b->succ.size() < 2 ||
b->getLastLine() != lineNum)
822 for (
const GCOVArc *arc :
b->succ) {
824 b->getCount() ? arc->
count ?
"taken" :
"nottaken" :
"notexec";
825 os <<
"branch:" << lineNum <<
',' << type <<
'\n';
834 sources.emplace_back(filename);
835 SourceInfo &si = sources.back();
836 si.displayName = si.filename;
837 if (!
options.SourcePrefix.empty() &&
840 !si.displayName.empty()) {
844 si.displayName.erase(si.displayName.begin());
846 si.displayName = si.filename;
855 collectFunction(f, summary);
857 os <<
"Function '" << summary.Name <<
"'\n";
858 printSummary(summary, os);
863 for (SourceInfo &si : sources) {
866 Summary summary(si.displayName);
867 collectSource(si, summary);
870 std::string gcovName = getCoveragePath(si.filename, filename);
872 os <<
"File '" << summary.Name <<
"'\n";
873 printSummary(summary, os);
875 os <<
"Creating '" << gcovName <<
"'\n";
881 std::optional<raw_fd_ostream> os;
886 errs() << ec.message() <<
'\n';
890 annotateSource(si,
file, gcno, gcda,
901 errs() << ec.message() <<
'\n';
905 for (
const SourceInfo &si : sources)
906 printSourceToIntermediate(si, os);
910void Context::printFunctionDetails(
const GCOVFunction &f,
912 const uint64_t entryCount =
f.getEntryCount();
914 const GCOVBlock &exitBlock =
f.getExitBlock();
917 exitCount += arc->
count;
919 if (
b.number != 0 && &b != &exitBlock &&
b.getCount())
922 os <<
"function " <<
f.getName(
options.Demangle) <<
" called " << entryCount
924 <<
"% blocks executed "
935 os <<
format(
"branch %2u ", edgeIdx++)
936 << formatBranchInfo(options, arc->
count, total) <<
'\n';
939void Context::printSummary(
const Summary &summary,
raw_ostream &os)
const {
940 os <<
format(
"Lines executed:%.2f%% of %" PRIu64
"\n",
941 double(summary.linesExec) * 100 / summary.lines, summary.lines);
943 if (summary.branches == 0) {
944 os <<
"No branches\n";
946 os <<
format(
"Branches executed:%.2f%% of %" PRIu64
"\n",
947 double(summary.branchesExec) * 100 / summary.branches,
949 os <<
format(
"Taken at least once:%.2f%% of %" PRIu64
"\n",
950 double(summary.branchesTaken) * 100 / summary.branches,
960 fi.print(filename, gcno, gcda,
file);
arc branch ARC finalize branches
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val)
bbsections Prepares for basic block by splitting functions into clusters of basic blocks
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 '.
bool remove_dots(SmallVectorImpl< char > &path, bool remove_dot_dot=false, Style style=Style::native)
In-place remove any '.
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.
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,...
std::string demangle(std::string_view MangledName)
Attempt to demangle a string using different demangling schemes.
A struct for passing gcov options between functions.