Go to the documentation of this file.
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;
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);
182 if (srcNo >= fn->
blocks.size()) {
183 errs() <<
"unexpected block number: " << srcNo <<
" (in "
184 << fn->
blocks.size() <<
")\n";
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());
324 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
359 return blocks.front()->getCount();
373 if (!
visited.insert(&v).second)
383 if (int64_t(excess) < 0)
386 pred->count = excess;
393 for (
const auto &Block :
blocks)
397 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
409 OS <<
"Block : " <<
number <<
" Counter : " <<
count <<
"\n";
411 OS <<
"\tSource Edges : ";
413 OS << Edge->src.number <<
" (" << Edge->count <<
"), ";
417 OS <<
"\tDestination Edges : ";
421 OS << Edge->dst.number <<
" (" << Edge->count <<
"), ";
425 if (!
lines.empty()) {
433 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
440 std::vector<std::pair<GCOVBlock *, size_t>> &
stack) {
444 stack.emplace_back(src, 0);
447 std::tie(u,
i) =
stack.back();
448 if (
i == u->
succ.size()) {
455 ++
stack.back().second;
460 if (
succ->cycleCount == 0 || !
succ->dst.traversable || &
succ->dst == u)
462 if (
succ->dst.incoming ==
nullptr) {
469 minCount =
std::min(minCount, v->incoming->cycleCount);
470 v = &v->incoming->src;
474 succ->cycleCount -= minCount;
476 v->incoming->cycleCount -= minCount;
477 v = &v->incoming->src;
491 std::vector<std::pair<GCOVBlock *, size_t>>
stack;
495 for (
const auto *
b : blocks) {
500 for (
const auto *
block : blocks) {
511 for (
const auto *
b : blocks) {
524 if (!dividend || !divisor)
527 return dividend < divisor ? 1 : dividend / divisor;
536 if (Numerator == Divisor)
539 uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
548 struct formatBranchInfo {
554 OS <<
"never executed";
556 OS <<
"taken " << Count;
558 OS <<
"taken " <<
branchDiv(Count, Total) <<
"%";
572 std::unique_ptr<MemoryBuffer> Buffer;
576 LineConsumer() =
default;
583 if (std::error_code EC = BufferOrErr.
getError()) {
588 Remaining = Buffer->getBuffer();
591 bool empty() {
return Remaining.
empty(); }
597 std::tie(Line, Remaining) = Remaining.
split(
"\n");
598 OS <<
format(
"%5u:", LineNum) <<
Line <<
"\n";
614 for (
I =
S = Filename.begin(),
E = Filename.end();
I !=
E; ++
I) {
618 if (
I -
S == 1 && *
S ==
'.') {
620 }
else if (
I -
S == 2 && *
S ==
'.' && *(
S + 1) ==
'.') {
628 Result.push_back(
'#');
635 return std::string(Result.str());
646 std::string CoveragePath;
655 Hasher.
final(Result);
656 CoveragePath +=
"##" + std::string(
Result.digest());
658 CoveragePath +=
".gcov";
662 void Context::collectFunction(
GCOVFunction &
f, Summary &summary) {
663 SourceInfo &si = sources[
f.srcIdx];
664 if (
f.startLine >= si.startLineToFunctions.size())
665 si.startLineToFunctions.resize(
f.startLine + 1);
666 si.startLineToFunctions[
f.startLine].push_back(&
f);
672 uint32_t maxLineNum = *std::max_element(
b.lines.begin(),
b.lines.end());
673 if (maxLineNum >= si.lines.size())
674 si.lines.resize(maxLineNum + 1);
676 LineInfo &line = si.lines[lineNum];
677 if (lines.
insert(lineNum).second)
679 if (
b.count && linesExec.
insert(lineNum).second)
682 line.count +=
b.count;
683 line.blocks.push_back(&
b);
688 void Context::collectSourceLine(SourceInfo &si, Summary *summary,
689 LineInfo &line,
size_t lineNum)
const {
692 if (
b->number == 0) {
714 ++summary->linesExec;
719 if (
b->getLastLine() != lineNum)
721 int branches = 0, execBranches = 0, takenBranches = 0;
722 for (
const GCOVArc *arc :
b->succ) {
731 summary->branchesExec += execBranches;
732 summary->branchesTaken += takenBranches;
737 void Context::collectSource(SourceInfo &si, Summary &summary)
const {
739 for (LineInfo &line : si.lines) {
740 collectSourceLine(si, &summary, line, lineNum);
745 void Context::annotateSource(SourceInfo &si,
const GCOVFile &
file,
751 os <<
" -: 0:Source:" << si.displayName <<
'\n';
752 os <<
" -: 0:Graph:" << gcno <<
'\n';
753 os <<
" -: 0:Data:" << gcda <<
'\n';
754 os <<
" -: 0:Runs:" <<
file.runCount <<
'\n';
756 os <<
" -: 0:Programs:" <<
file.programCount <<
'\n';
758 for (
size_t lineNum = 1; !source.empty(); ++lineNum) {
759 if (lineNum >= si.lines.size()) {
761 source.printNext(os, lineNum);
765 const LineInfo &line = si.lines[lineNum];
766 if (
options.BranchInfo && lineNum < si.startLineToFunctions.size())
767 for (
const auto *
f : si.startLineToFunctions[lineNum])
768 printFunctionDetails(*
f, os);
771 else if (line.count == 0)
774 os <<
format(
"%9" PRIu64
":", line.count);
775 source.printNext(os, lineNum);
779 if (
b->getLastLine() != lineNum)
782 if (
b->getCount() == 0)
785 os <<
format(
"%9" PRIu64
":",
b->count);
786 os <<
format(
"%5u-block %2u\n", lineNum, blockIdx++);
789 size_t NumEdges =
b->succ.size();
791 printBranchInfo(*
b, edgeIdx, os);
792 else if (
options.UncondBranch && NumEdges == 1) {
794 os <<
format(
"unconditional %2u ", edgeIdx++)
802 void Context::printSourceToIntermediate(
const SourceInfo &si,
804 os <<
"file:" << si.filename <<
'\n';
805 for (
const auto &fs : si.startLineToFunctions)
807 os <<
"function:" <<
f->startLine <<
',' <<
f->getEntryCount() <<
','
808 <<
f->getName(
options.Demangle) <<
'\n';
809 for (
size_t lineNum = 1,
size = si.lines.size(); lineNum <
size; ++lineNum) {
810 const LineInfo &line = si.lines[lineNum];
811 if (line.blocks.empty())
816 os <<
"lcount:" << lineNum <<
',' << line.count <<
'\n';
821 if (
b->succ.size() < 2 ||
b->getLastLine() != lineNum)
823 for (
const GCOVArc *arc :
b->succ) {
825 b->getCount() ? arc->
count ?
"taken" :
"nottaken" :
"notexec";
826 os <<
"branch:" << lineNum <<
',' <<
type <<
'\n';
836 SourceInfo &si = sources.back();
837 si.displayName = si.filename;
838 if (!
options.SourcePrefix.empty() &&
841 !si.displayName.empty()) {
845 si.displayName.erase(si.displayName.begin());
847 si.displayName = si.filename;
856 collectFunction(
f, summary);
858 os <<
"Function '" << summary.Name <<
"'\n";
859 printSummary(summary, os);
864 for (SourceInfo &si : sources) {
867 Summary summary(si.displayName);
868 collectSource(si, summary);
871 std::string gcovName = getCoveragePath(si.filename,
filename);
873 os <<
"File '" << summary.Name <<
"'\n";
874 printSummary(summary, os);
876 os <<
"Creating '" << gcovName <<
"'\n";
882 std::optional<raw_fd_ostream> os;
887 errs() << ec.message() <<
'\n';
891 annotateSource(si,
file, gcno, gcda,
902 errs() << ec.message() <<
'\n';
906 for (
const SourceInfo &si : sources)
907 printSourceToIntermediate(si, os);
913 const uint64_t entryCount =
f.getEntryCount();
915 const GCOVBlock &exitBlock =
f.getExitBlock();
918 exitCount += arc->
count;
920 if (
b.number != 0 && &
b != &exitBlock &&
b.getCount())
923 os <<
"function " <<
f.getName(
options.Demangle) <<
" called " << entryCount
925 <<
"% blocks executed "
936 os <<
format(
"branch %2u ", edgeIdx++)
937 << formatBranchInfo(
options, arc->
count, total) <<
'\n';
940 void Context::printSummary(
const Summary &summary,
raw_ostream &os)
const {
941 os <<
format(
"Lines executed:%.2f%% of %" PRIu64
"\n",
942 double(summary.linesExec) * 100 / summary.lines, summary.lines);
944 if (summary.branches == 0) {
945 os <<
"No branches\n";
947 os <<
format(
"Branches executed:%.2f%% of %" PRIu64
"\n",
948 double(summary.branchesExec) * 100 / summary.branches,
950 os <<
format(
"Taken at least once:%.2f%% of %" PRIu64
"\n",
951 double(summary.branchesTaken) * 100 / summary.branches,
SmallVector< std::unique_ptr< GCOVFunction >, 16 > functions
we get the following basic block
DataExtractor::Cursor cursor
iterator_range< EdgeIterator > srcs() const
bool readGCOVVersion(GCOV::GCOVVersion &version)
readGCOVVersion - Read GCOV version.
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
This is an optimization pass for GlobalISel generic memory operations.
uint64_t getEntryCount() const
getEntryCount - Get the number of times the function was called by retrieving the entry block's count...
bool replace_path_prefix(SmallVectorImpl< char > &Path, StringRef OldPrefix, StringRef NewPrefix, Style style=Style::native)
Replace matching path prefix with another path.
SmallVector< GCOVArc *, 2 > pred
void update(ArrayRef< uint8_t > Data)
Updates the hash for the byte stream provided.
SmallString< 0 > demangled
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val)
GCOVBlock - Collects block information.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool is_absolute(const Twine &path, Style style=Style::native)
Is path absolute?
DenseSet< const GCOVBlock * > visited
static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor)
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
std::map< uint32_t, GCOVFunction * > identToFunction
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
@ GCOV_TAG_PROGRAM_SUMMARY
StringRef getName(bool demangle) const
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
StringMap< unsigned > filenameToIdx
void dump() const
dump - Dump GCOVFile content to dbgs() for debugging purposes.
void addDstEdge(GCOVArc *Edge)
void final(MD5Result &Result)
Finishes off the hash and puts the result in result.
bool startswith(StringRef Prefix) const
raw_fd_ostream & outs()
This returns a reference to a raw_fd_ostream for standard output.
iterator_range< BlockIterator > blocksRange() const
Itanium Name Demangler i e convert the string _Z1fv into f()". You can also use the CRTP base ManglingParser to perform some simple analysis on the mangled name
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
bool readGCDAFormat()
readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
the resulting code requires compare and branches when and if the revised code is with conditional branches instead of More there is a byte word extend before each where there should be only and the condition codes are not remembered when the same two values are compared twice More LSR enhancements i8 and i32 load store addressing modes are identical int b
SmallVector< uint32_t, 4 > lines
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 "-".
SmallVector< GCOVArc *, 2 > succ
std::error_code getError() const
static uint64_t getCyclesCount(const BlockVector &blocks)
StringRef getFilename() const
const char LLVMTargetMachineRef LLVMPassBuilderOptionsRef Options
This class implements an extremely fast bulk output stream that can only output to a stream.
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor)
std::string demangle(const std::string &MangledName)
Attempt to demangle a string using different demangling schemes.
@ OF_TextWithCRLF
The file should be opened in text mode and use a carriage linefeed '\r '.
bool equals(StringRef RHS) const
equals - Check for string equality, this is more efficient than compare() when the relative ordering ...
static uint64_t augmentOneCycle(GCOVBlock *src, std::vector< std::pair< GCOVBlock *, size_t >> &stack)
GCOV::GCOVVersion getVersion() const
constexpr bool empty() const
empty - Check if the string is empty.
GCOVBlock & getExitBlock() const
dot regions Print regions of function to dot file(with no function bodies)"
AMD64 Optimization Manual has some nice information about optimizing integer multiplication by a constant How much of it applies to Intel s X86 implementation There are definite trade offs to xmm0 cvttss2siq rdx jb L3 subss xmm0 rax cvttss2siq rdx xorq rdx rax ret instead of xmm1 cvttss2siq rcx movaps xmm2 subss xmm2 cvttss2siq rax rdx xorq rax ucomiss xmm0 cmovb rax ret Seems like the jb branch has high likelihood of being taken It would have saved a few instructions It s not possible to reference and DH registers in an instruction requiring REX prefix divb and mulb both produce results in AH If isel emits a CopyFromReg which gets turned into a movb and that can be allocated a r8b r15b To get around isel emits a CopyFromReg from AX and then right shift it down by and truncate it It s not pretty but it works We need some register allocation magic to make the hack go which would often require a callee saved register Callees usually need to keep this value live for most of their body so it doesn t add a significant burden on them We currently implement this in however this is suboptimal because it means that it would be quite awkward to implement the optimization for callers A better implementation would be to relax the LLVM IR rules for sret arguments to allow a function with an sret argument to have a non void return type
bool exists(const basic_file_status &status)
Does file exist?
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...
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
bool is_contained(R &&Range, const E &Element)
Wrapper function around std::find to detect if an element exists in a container.
SmallVector< std::unique_ptr< GCOVArc >, 0 > arcs
iterator_range< pointee_iterator< WrappedIteratorT > > make_pointee_range(RangeT &&Range)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
bool readInt(uint32_t &Val)
void gcovOneInput(const GCOV::Options &options, StringRef filename, StringRef gcno, StringRef gcda, GCOVFile &file)
bool readInt64(uint64_t &Val)
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr)
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
gvn sink
When an instruction is found to only be used outside of the loop, this function moves it to the exit ...
Expected< ExpressionValue > min(const ExpressionValue &Lhs, const ExpressionValue &Rhs)
StringRef - Represent a constant reference to a string, i.e.
add sub stmia L5 ldr r0 bl L_printf $stub Instead of a and a wouldn t it be better to do three moves *Return an aggregate type is even return S
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths)
Convert a path to a gcov filename.
A raw_ostream that writes to a file descriptor.
void dump() const
dump - Dump GCOVFunction content to dbgs() for debugging purposes.
void print(raw_ostream &OS) const
A struct for passing gcov options between functions.
GCOV::GCOVVersion version
The object format emitted by the WebAssembly backed is documented see the home and packaging for producing WebAssembly applications that can run in browsers and other environments wasi sdk provides a more minimal C C SDK based on llvm and a libc based on for producing WebAssemmbly applictions that use the WASI ABI Rust provides WebAssembly support integrated into Cargo There are two main options
void print(raw_ostream &OS) const
collectLineCounts - Collect line counts.
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
void print(raw_ostream &OS) const
bool readGCNOFormat()
readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
iterator_range< EdgeIterator > dsts() const
std::vector< std::string > filenames
bool readGCDA(GCOVBuffer &Buffer)
readGCDA - Read GCDA buffer.
bool is_separator(char value, Style style=Style::native)
Check whether the given char is a path separator on the host OS.
StringRef filename(StringRef path, Style style=Style::native)
Get filename.
GCOVFile - Collects coverage information for one pair of coverage file (.gcno and ....
std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
void addSrcEdge(GCOVArc *Edge)
Represents either an error or a value T.
void dump() const
dump - Dump GCOVBlock content to dbgs() for debugging purposes.
SmallVector< std::unique_ptr< GCOVArc >, 0 > treeArcs
S is passed via registers r2 But gcc stores them to the stack
std::string str() const
str - Get the contents as an std::string.
bool readString(StringRef &str)
char * itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status)
SmallVector< std::unique_ptr< GCOVBlock >, 0 > blocks
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.
GCOVFunction - Collects function information.
GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific read operations.
the resulting code requires compare and branches when and if the revised code is with conditional branches instead of More there is a byte word extend before each where there should be only and the condition codes are not remembered when the same two values are compared twice More LSR enhancements i8 and i32 load store addressing modes are identical int int int d
@ GCOV_TAG_OBJECT_SUMMARY
arc branch ARC finalize branches
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
uint64_t propagateCounts(const GCOVBlock &v, GCOVArc *pred)
bool readGCNO(GCOVBuffer &Buffer)
readGCNO - Read GCNO buffer.