Go to the documentation of this file.
16 #include "llvm/Config/llvm-config.h"
25 #include <system_error>
26 #include <unordered_map>
50 uint64_t linesExec = 0;
52 uint64_t branchesExec = 0;
53 uint64_t branchesTaken = 0;
65 std::vector<std::vector<const GCOVFunction *>> startLineToFunctions;
66 std::vector<LineInfo> lines;
82 void printSummary(
const Summary &summary,
raw_ostream &os)
const;
85 void collectSourceLine(SourceInfo &si, Summary *summary, LineInfo &line,
86 size_t lineNum)
const;
87 void collectSource(SourceInfo &si, Summary &summary)
const;
90 void printSourceToIntermediate(
const SourceInfo &si,
raw_ostream &os)
const;
93 std::vector<SourceInfo> sources;
115 while ((tag = buf.
getWord())) {
119 functions.push_back(std::make_unique<GCOVFunction>(*
this));
142 fn->
srcIdx = r.first->second;
148 fn->
blocks.push_back(std::make_unique<GCOVBlock>(
i));
153 fn->
blocks.push_back(std::make_unique<GCOVBlock>(
i));
157 if (srcNo >= fn->
blocks.size()) {
158 errs() <<
"unexpected block number: " << srcNo <<
" (in "
159 << fn->
blocks.size() <<
")\n";
166 auto arc = std::make_unique<GCOVArc>(*src, *dst, flags);
176 if (srcNo >= fn->
blocks.size()) {
177 errs() <<
"unexpected block number: " << srcNo <<
" (in "
178 << fn->
blocks.size() <<
")\n";
210 errs() <<
"GCOV versions do not match.\n";
215 if (!buf.
readInt(GCDAChecksum))
219 <<
" != " << GCDAChecksum <<
".\n";
225 while ((tag = buf.
getWord())) {
249 if (length < 2 || !buf.
readInt(ident))
252 uint32_t linenoChecksum, cfgChecksum = 0;
261 <<
format(
": checksum mismatch, (%u, %u) != (%u, %u)\n",
268 if (length != 2 * fn->
arcs.size()) {
271 ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n",
272 length,
unsigned(2 * fn->
arcs.size()));
275 for (std::unique_ptr<GCOVArc> &
arc : fn->
arcs) {
278 arc->src.count +=
arc->count;
281 if (fn->
blocks.size() >= 2) {
310 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
345 return blocks.front()->getCount();
359 if (!
visited.insert(&v).second)
369 if (int64_t(excess) < 0)
372 pred->count = excess;
379 for (
const auto &Block :
blocks)
383 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
395 OS <<
"Block : " <<
number <<
" Counter : " <<
count <<
"\n";
397 OS <<
"\tSource Edges : ";
399 OS << Edge->src.number <<
" (" << Edge->count <<
"), ";
403 OS <<
"\tDestination Edges : ";
407 OS << Edge->dst.number <<
" (" << Edge->count <<
"), ";
411 if (!
lines.empty()) {
419 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
430 stack.emplace_back(src, 0);
433 std::tie(u,
i) =
stack.back();
434 if (
i == u->
succ.size()) {
441 ++
stack.back().second;
446 if (
succ->cycleCount == 0 || !
succ->dst.traversable || &
succ->dst == u)
448 if (
succ->dst.incoming ==
nullptr) {
453 uint64_t minCount =
succ->cycleCount;
455 minCount =
std::min(minCount, v->incoming->cycleCount);
456 v = &v->incoming->src;
460 succ->cycleCount -= minCount;
462 v->incoming->cycleCount -= minCount;
463 v = &v->incoming->src;
477 std::vector<std::pair<GCOVBlock *, size_t>>
stack;
481 for (
auto b : blocks) {
486 for (
auto block : blocks) {
497 for (
auto b : blocks) {
510 if (!dividend || !divisor)
513 return dividend < divisor ? 1 : dividend / divisor;
522 if (Numerator == Divisor)
525 uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
534 struct formatBranchInfo {
535 formatBranchInfo(
const GCOV::Options &Options, uint64_t Count, uint64_t Total)
536 : Options(Options), Count(Count), Total(Total) {}
540 OS <<
"never executed";
541 else if (Options.BranchCount)
542 OS <<
"taken " << Count;
544 OS <<
"taken " <<
branchDiv(Count, Total) <<
"%";
558 std::unique_ptr<MemoryBuffer> Buffer;
562 LineConsumer() =
default;
569 if (std::error_code EC = BufferOrErr.
getError()) {
570 errs() << Filename <<
": " <<
EC.message() <<
"\n";
574 Remaining = Buffer->getBuffer();
583 std::tie(Line, Remaining) = Remaining.
split(
"\n");
584 OS <<
format(
"%5u:", LineNum) << Line <<
"\n";
604 if (
I -
S == 1 && *
S ==
'.') {
606 }
else if (
I -
S == 2 && *
S ==
'.' && *(
S + 1) ==
'.') {
614 Result.push_back(
'#');
621 return std::string(Result.str());
626 if (options.NoOutput)
632 std::string CoveragePath;
637 if (options.HashFilenames) {
641 Hasher.
final(Result);
642 CoveragePath +=
"##" + std::string(
Result.digest());
644 CoveragePath +=
".gcov";
648 void Context::collectFunction(
GCOVFunction &
f, Summary &summary) {
649 SourceInfo &si = sources[
f.srcIdx];
650 if (
f.startLine >= si.startLineToFunctions.size())
651 si.startLineToFunctions.resize(
f.startLine + 1);
652 si.startLineToFunctions[
f.startLine].push_back(&
f);
656 uint32_t maxLineNum = *std::max_element(
b.lines.begin(),
b.lines.end());
657 if (maxLineNum >= si.lines.size())
658 si.lines.resize(maxLineNum + 1);
660 LineInfo &line = si.lines[lineNum];
663 if (line.count == 0 &&
b.count)
666 line.count +=
b.count;
667 line.blocks.push_back(&
b);
672 void Context::collectSourceLine(SourceInfo &si, Summary *summary,
673 LineInfo &line,
size_t lineNum)
const {
676 if (
b->number == 0) {
690 arc->cycleCount =
arc->count;
698 ++summary->linesExec;
701 if (options.BranchInfo)
703 if (
b->getLastLine() != lineNum)
705 int branches = 0, execBranches = 0, takenBranches = 0;
715 summary->branchesExec += execBranches;
716 summary->branchesTaken += takenBranches;
721 void Context::collectSource(SourceInfo &si, Summary &summary)
const {
723 for (LineInfo &line : si.lines) {
724 collectSourceLine(si, &summary, line, lineNum);
729 void Context::annotateSource(SourceInfo &si,
const GCOVFile &
file,
733 options.Intermediate ? LineConsumer() : LineConsumer(si.
filename);
735 os <<
" -: 0:Source:" << si.displayName <<
'\n';
736 os <<
" -: 0:Graph:" << gcno <<
'\n';
737 os <<
" -: 0:Data:" << gcda <<
'\n';
738 os <<
" -: 0:Runs:" <<
file.RunCount <<
'\n';
740 os <<
" -: 0:Programs:" <<
file.ProgramCount <<
'\n';
742 for (
size_t lineNum = 1; !source.empty(); ++lineNum) {
743 if (lineNum >= si.lines.size()) {
745 source.printNext(os, lineNum);
749 const LineInfo &line = si.lines[lineNum];
750 if (options.BranchInfo && lineNum < si.startLineToFunctions.size())
751 for (
const auto *
f : si.startLineToFunctions[lineNum])
752 printFunctionDetails(*
f, os);
755 else if (line.count == 0)
758 os <<
format(
"%9" PRIu64
":", line.count);
759 source.printNext(os, lineNum);
763 if (
b->getLastLine() != lineNum)
765 if (options.AllBlocks) {
766 if (
b->getCount() == 0)
769 os <<
format(
"%9" PRIu64
":",
b->count);
770 os <<
format(
"%5u-block %2u\n", lineNum, blockIdx++);
772 if (options.BranchInfo) {
773 size_t NumEdges =
b->succ.size();
775 printBranchInfo(*
b, edgeIdx, os);
776 else if (options.UncondBranch && NumEdges == 1) {
777 uint64_t
count =
b->succ[0]->count;
778 os <<
format(
"unconditional %2u ", edgeIdx++)
779 << formatBranchInfo(options,
count,
count) <<
'\n';
786 void Context::printSourceToIntermediate(
const SourceInfo &si,
788 os <<
"file:" << si.filename <<
'\n';
789 for (
const auto &fs : si.startLineToFunctions)
791 os <<
"function:" <<
f->startLine <<
',' <<
f->getEntryCount() <<
','
792 <<
f->getName(options.Demangle) <<
'\n';
793 for (
size_t lineNum = 1,
size = si.lines.size(); lineNum <
size; ++lineNum) {
794 const LineInfo &line = si.lines[lineNum];
795 if (line.blocks.empty())
800 os <<
"lcount:" << lineNum <<
',' << line.count <<
'\n';
802 if (!options.BranchInfo)
805 if (
b->succ.size() < 2 ||
b->getLastLine() != lineNum)
809 b->getCount() ?
arc->count ?
"taken" :
"nottaken" :
"notexec";
810 os <<
"branch:" << lineNum <<
',' <<
type <<
'\n';
820 SourceInfo &si = sources.back();
821 si.displayName = si.filename;
822 if (!options.SourcePrefix.empty() &&
825 !si.displayName.empty()) {
829 si.displayName.erase(si.displayName.begin());
831 si.displayName = si.filename;
839 Summary summary(
f.getName(options.Demangle));
840 collectFunction(
f, summary);
841 if (options.FuncCoverage && !options.UseStdout) {
842 os <<
"Function '" << summary.Name <<
"'\n";
843 printSummary(summary, os);
848 for (SourceInfo &si : sources) {
851 Summary summary(si.displayName);
852 collectSource(si, summary);
855 std::string gcovName = getCoveragePath(si.filename,
filename);
856 if (!options.UseStdout) {
857 os <<
"File '" << summary.Name <<
"'\n";
858 printSummary(summary, os);
859 if (!options.NoOutput && !options.Intermediate)
860 os <<
"Creating '" << gcovName <<
"'\n";
864 if (options.NoOutput || options.Intermediate)
867 if (!options.UseStdout) {
871 errs() << ec.message() <<
'\n';
875 annotateSource(si,
file, gcno, gcda,
879 if (options.Intermediate && !options.NoOutput) {
886 errs() << ec.message() <<
'\n';
890 for (
const SourceInfo &si : sources)
891 printSourceToIntermediate(si, os);
897 const uint64_t entryCount =
f.getEntryCount();
899 const GCOVBlock &exitBlock =
f.getExitBlock();
900 uint64_t exitCount = 0;
902 exitCount +=
arc->count;
904 if (
b.number != 0 && &
b != &exitBlock &&
b.getCount())
907 os <<
"function " <<
f.getName(options.Demangle) <<
" called " << entryCount
909 <<
"% blocks executed "
920 os <<
format(
"branch %2u ", edgeIdx++)
921 << formatBranchInfo(options,
arc->count, total) <<
'\n';
924 void Context::printSummary(
const Summary &summary,
raw_ostream &os)
const {
925 os <<
format(
"Lines executed:%.2f%% of %" PRIu64
"\n",
926 double(summary.linesExec) * 100 / summary.lines, summary.lines);
927 if (options.BranchInfo) {
928 if (summary.branches == 0) {
929 os <<
"No branches\n";
931 os <<
format(
"Branches executed:%.2f%% of %" PRIu64
"\n",
932 double(summary.branchesExec) * 100 / summary.branches,
934 os <<
format(
"Taken at least once:%.2f%% of %" PRIu64
"\n",
935 double(summary.branchesTaken) * 100 / summary.branches,
SmallVector< std::unique_ptr< GCOVFunction >, 16 > functions
we get the following basic block
LLVM_NODISCARD bool startswith(StringRef Prefix) const
Check if this string starts with the given Prefix.
DataExtractor::Cursor cursor
iterator_range< EdgeIterator > srcs() const
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
This class represents lattice values for constants.
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.
LLVM_NODISCARD bool empty() const
empty - Check if the string is empty.
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.
*ViewGraph Emit a dot run run gv on the postscript file
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)
std::map< uint32_t, GCOVFunction * > IdentToFunction
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, int64_t FileSize=-1, bool RequiresNullTerminator=true)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
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.
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")
LLVM_NODISCARD std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
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
SmallVector< GCOVArc *, 2 > succ
std::error_code getError() const
static uint64_t getCyclesCount(const BlockVector &blocks)
StringRef getFilename() const
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)
static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor)
LLVM_NODISCARD std::string str() const
str - Get the contents as an std::string.
std::string demangle(const std::string &MangledName)
Attempt to demangle a string using different demangling schemes.
LLVM_NODISCARD 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
bool readGCOVVersion(GCOV::GCOVVersion &Version)
readGCOVVersion - Read GCOV version.
GCOVBlock & getExitBlock() const
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...
@ OF_Text
The file should be opened in text mode on platforms that make this distinction.
void emplace(ArgTypes &&... Args)
Create a new object by constructing it in place with the given arguments.
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)
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.
@ GCOV_TAG_OBJECT_SUMMARY
void print(raw_ostream &OS) const
constexpr bool empty(const T &RangeOrContainer)
Test whether RangeOrContainer is empty. Similar to C++17 std::empty.
A struct for passing gcov options between functions.
void print(raw_ostream &OS) const
collectLineCounts - Collect line counts.
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 readString(StringRef &Str)
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
const LLVM_NODISCARD char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
@ GCOV_TAG_PROGRAM_SUMMARY
char * itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status)
SmallVector< std::unique_ptr< GCOVBlock >, 0 > blocks
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::GCOVVersion Version
arc branch ARC finalize branches
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.
uint64_t propagateCounts(const GCOVBlock &v, GCOVArc *pred)
bool readGCNO(GCOVBuffer &Buffer)
readGCNO - Read GCNO buffer.