| //===- GCOV.cpp - LLVM coverage tool --------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // GCOV implements the interface to read and write coverage files that use | 
 | // 'gcov' format. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "llvm/ProfileData/GCOV.h" | 
 | #include "llvm/ADT/STLExtras.h" | 
 | #include "llvm/ADT/SmallSet.h" | 
 | #include "llvm/Config/llvm-config.h" | 
 | #include "llvm/Demangle/Demangle.h" | 
 | #include "llvm/Support/Debug.h" | 
 | #include "llvm/Support/FileSystem.h" | 
 | #include "llvm/Support/Format.h" | 
 | #include "llvm/Support/MD5.h" | 
 | #include "llvm/Support/Path.h" | 
 | #include "llvm/Support/raw_ostream.h" | 
 | #include <algorithm> | 
 | #include <optional> | 
 | #include <system_error> | 
 |  | 
 | using namespace llvm; | 
 |  | 
 | enum : uint32_t { | 
 |   GCOV_ARC_ON_TREE = 1 << 0, | 
 |   GCOV_ARC_FALLTHROUGH = 1 << 2, | 
 |  | 
 |   GCOV_TAG_FUNCTION = 0x01000000, | 
 |   GCOV_TAG_BLOCKS = 0x01410000, | 
 |   GCOV_TAG_ARCS = 0x01430000, | 
 |   GCOV_TAG_LINES = 0x01450000, | 
 |   GCOV_TAG_COUNTER_ARCS = 0x01a10000, | 
 |   // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9. | 
 |   GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, | 
 |   GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, | 
 | }; | 
 |  | 
 | namespace { | 
 | struct Summary { | 
 |   Summary(StringRef Name) : Name(Name) {} | 
 |  | 
 |   StringRef Name; | 
 |   uint64_t lines = 0; | 
 |   uint64_t linesExec = 0; | 
 |   uint64_t branches = 0; | 
 |   uint64_t branchesExec = 0; | 
 |   uint64_t branchesTaken = 0; | 
 | }; | 
 |  | 
 | struct LineInfo { | 
 |   SmallVector<const GCOVBlock *, 1> blocks; | 
 |   uint64_t count = 0; | 
 |   bool exists = false; | 
 | }; | 
 |  | 
 | struct SourceInfo { | 
 |   StringRef filename; | 
 |   SmallString<0> displayName; | 
 |   std::vector<std::vector<const GCOVFunction *>> startLineToFunctions; | 
 |   std::vector<LineInfo> lines; | 
 |   bool ignored = false; | 
 |   SourceInfo(StringRef filename) : filename(filename) {} | 
 | }; | 
 |  | 
 | class Context { | 
 | public: | 
 |   Context(const GCOV::Options &Options) : options(Options) {} | 
 |   void print(StringRef filename, StringRef gcno, StringRef gcda, | 
 |              GCOVFile &file); | 
 |  | 
 | private: | 
 |   std::string getCoveragePath(StringRef filename, StringRef mainFilename) const; | 
 |   void printFunctionDetails(const GCOVFunction &f, raw_ostream &os) const; | 
 |   void printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, | 
 |                        raw_ostream &OS) const; | 
 |   void printSummary(const Summary &summary, raw_ostream &os) const; | 
 |  | 
 |   void collectFunction(GCOVFunction &f, Summary &summary); | 
 |   void collectSourceLine(SourceInfo &si, Summary *summary, LineInfo &line, | 
 |                          size_t lineNum) const; | 
 |   void collectSource(SourceInfo &si, Summary &summary) const; | 
 |   void annotateSource(SourceInfo &si, const GCOVFile &file, StringRef gcno, | 
 |                       StringRef gcda, raw_ostream &os) const; | 
 |   void printSourceToIntermediate(const SourceInfo &si, raw_ostream &os) const; | 
 |  | 
 |   const GCOV::Options &options; | 
 |   std::vector<SourceInfo> sources; | 
 | }; | 
 | } // namespace | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // GCOVFile implementation. | 
 |  | 
 | /// readGCNO - Read GCNO buffer. | 
 | bool GCOVFile::readGCNO(GCOVBuffer &buf) { | 
 |   if (!buf.readGCNOFormat()) | 
 |     return false; | 
 |   if (!buf.readGCOVVersion(version)) | 
 |     return false; | 
 |  | 
 |   checksum = buf.getWord(); | 
 |   if (version >= GCOV::V900 && !buf.readString(cwd)) | 
 |     return false; | 
 |   if (version >= GCOV::V800) | 
 |     buf.getWord(); // hasUnexecutedBlocks | 
 |  | 
 |   uint32_t tag, length; | 
 |   GCOVFunction *fn = nullptr; | 
 |   while ((tag = buf.getWord())) { | 
 |     if (!buf.readInt(length)) | 
 |       return false; | 
 |     uint32_t pos = buf.cursor.tell(); | 
 |     if (tag == GCOV_TAG_FUNCTION) { | 
 |       functions.push_back(std::make_unique<GCOVFunction>(*this)); | 
 |       fn = functions.back().get(); | 
 |       fn->ident = buf.getWord(); | 
 |       fn->linenoChecksum = buf.getWord(); | 
 |       if (version >= GCOV::V407) | 
 |         fn->cfgChecksum = buf.getWord(); | 
 |       buf.readString(fn->Name); | 
 |       StringRef filename; | 
 |       if (version < GCOV::V800) { | 
 |         if (!buf.readString(filename)) | 
 |           return false; | 
 |         fn->startLine = buf.getWord(); | 
 |       } else { | 
 |         fn->artificial = buf.getWord(); | 
 |         if (!buf.readString(filename)) | 
 |           return false; | 
 |         fn->startLine = buf.getWord(); | 
 |         fn->startColumn = buf.getWord(); | 
 |         fn->endLine = buf.getWord(); | 
 |         if (version >= GCOV::V900) | 
 |           fn->endColumn = buf.getWord(); | 
 |       } | 
 |       fn->srcIdx = addNormalizedPathToMap(filename); | 
 |       identToFunction[fn->ident] = fn; | 
 |     } else if (tag == GCOV_TAG_BLOCKS && fn) { | 
 |       if (version < GCOV::V800) { | 
 |         for (uint32_t i = 0; i != length; ++i) { | 
 |           buf.getWord(); // Ignored block flags | 
 |           fn->blocks.push_back(std::make_unique<GCOVBlock>(i)); | 
 |         } | 
 |       } else { | 
 |         uint32_t num = buf.getWord(); | 
 |         for (uint32_t i = 0; i != num; ++i) | 
 |           fn->blocks.push_back(std::make_unique<GCOVBlock>(i)); | 
 |       } | 
 |     } else if (tag == GCOV_TAG_ARCS && fn) { | 
 |       uint32_t srcNo = buf.getWord(); | 
 |       if (srcNo >= fn->blocks.size()) { | 
 |         errs() << "unexpected block number: " << srcNo << " (in " | 
 |                << fn->blocks.size() << ")\n"; | 
 |         return false; | 
 |       } | 
 |       GCOVBlock *src = fn->blocks[srcNo].get(); | 
 |       const uint32_t e = | 
 |           version >= GCOV::V1200 ? (length / 4 - 1) / 2 : (length - 1) / 2; | 
 |       for (uint32_t i = 0; i != e; ++i) { | 
 |         uint32_t dstNo = buf.getWord(), flags = buf.getWord(); | 
 |         GCOVBlock *dst = fn->blocks[dstNo].get(); | 
 |         auto arc = std::make_unique<GCOVArc>(*src, *dst, flags); | 
 |         src->addDstEdge(arc.get()); | 
 |         dst->addSrcEdge(arc.get()); | 
 |         if (arc->onTree()) | 
 |           fn->treeArcs.push_back(std::move(arc)); | 
 |         else | 
 |           fn->arcs.push_back(std::move(arc)); | 
 |       } | 
 |     } else if (tag == GCOV_TAG_LINES && fn) { | 
 |       uint32_t srcNo = buf.getWord(); | 
 |       if (srcNo >= fn->blocks.size()) { | 
 |         errs() << "unexpected block number: " << srcNo << " (in " | 
 |                << fn->blocks.size() << ")\n"; | 
 |         return false; | 
 |       } | 
 |       GCOVBlock &Block = *fn->blocks[srcNo]; | 
 |       for (;;) { | 
 |         uint32_t line = buf.getWord(); | 
 |         if (line) | 
 |           Block.addLine(line); | 
 |         else { | 
 |           StringRef filename; | 
 |           buf.readString(filename); | 
 |           if (filename.empty()) | 
 |             break; | 
 |           // TODO Unhandled | 
 |         } | 
 |       } | 
 |     } | 
 |     pos += version >= GCOV::V1200 ? length : 4 * length; | 
 |     if (pos < buf.cursor.tell()) | 
 |       return false; | 
 |     buf.de.skip(buf.cursor, pos - buf.cursor.tell()); | 
 |   } | 
 |  | 
 |   GCNOInitialized = true; | 
 |   return true; | 
 | } | 
 |  | 
 | /// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be | 
 | /// called after readGCNO(). | 
 | bool GCOVFile::readGCDA(GCOVBuffer &buf) { | 
 |   assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()"); | 
 |   if (!buf.readGCDAFormat()) | 
 |     return false; | 
 |   GCOV::GCOVVersion GCDAVersion; | 
 |   if (!buf.readGCOVVersion(GCDAVersion)) | 
 |     return false; | 
 |   if (version != GCDAVersion) { | 
 |     errs() << "GCOV versions do not match.\n"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t GCDAChecksum; | 
 |   if (!buf.readInt(GCDAChecksum)) | 
 |     return false; | 
 |   if (checksum != GCDAChecksum) { | 
 |     errs() << "file checksums do not match: " << checksum | 
 |            << " != " << GCDAChecksum << "\n"; | 
 |     return false; | 
 |   } | 
 |   uint32_t dummy, tag, length; | 
 |   uint32_t ident; | 
 |   GCOVFunction *fn = nullptr; | 
 |   while ((tag = buf.getWord())) { | 
 |     if (!buf.readInt(length)) | 
 |       return false; | 
 |     uint32_t pos = buf.cursor.tell(); | 
 |     if (tag == GCOV_TAG_OBJECT_SUMMARY) { | 
 |       buf.readInt(runCount); | 
 |       buf.readInt(dummy); | 
 |       // clang<11 uses a fake 4.2 format which sets length to 9. | 
 |       if (length == 9) | 
 |         buf.readInt(runCount); | 
 |     } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) { | 
 |       // clang<11 uses a fake 4.2 format which sets length to 0. | 
 |       if (length > 0) { | 
 |         buf.readInt(dummy); | 
 |         buf.readInt(dummy); | 
 |         buf.readInt(runCount); | 
 |       } | 
 |       ++programCount; | 
 |     } else if (tag == GCOV_TAG_FUNCTION) { | 
 |       if (length == 0) // Placeholder | 
 |         continue; | 
 |       // As of GCC 10, GCOV_TAG_FUNCTION_LENGTH has never been larger than 3. | 
 |       // However, clang<11 uses a fake 4.2 format which may set length larger | 
 |       // than 3. | 
 |       if (length < 2 || !buf.readInt(ident)) | 
 |         return false; | 
 |       auto It = identToFunction.find(ident); | 
 |       uint32_t linenoChecksum, cfgChecksum = 0; | 
 |       buf.readInt(linenoChecksum); | 
 |       if (version >= GCOV::V407) | 
 |         buf.readInt(cfgChecksum); | 
 |       if (It != identToFunction.end()) { | 
 |         fn = It->second; | 
 |         if (linenoChecksum != fn->linenoChecksum || | 
 |             cfgChecksum != fn->cfgChecksum) { | 
 |           errs() << fn->Name | 
 |                  << format(": checksum mismatch, (%u, %u) != (%u, %u)\n", | 
 |                            linenoChecksum, cfgChecksum, fn->linenoChecksum, | 
 |                            fn->cfgChecksum); | 
 |           return false; | 
 |         } | 
 |       } | 
 |     } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) { | 
 |       uint32_t expected = 2 * fn->arcs.size(); | 
 |       if (version >= GCOV::V1200) | 
 |         expected *= 4; | 
 |       if (length != expected) { | 
 |         errs() << fn->Name | 
 |                << format( | 
 |                       ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n", | 
 |                       length, expected); | 
 |         return false; | 
 |       } | 
 |       for (std::unique_ptr<GCOVArc> &arc : fn->arcs) { | 
 |         if (!buf.readInt64(arc->count)) | 
 |           return false; | 
 |         arc->src.count += arc->count; | 
 |       } | 
 |  | 
 |       if (fn->blocks.size() >= 2) { | 
 |         GCOVBlock &src = *fn->blocks[0]; | 
 |         GCOVBlock &sink = | 
 |             version < GCOV::V408 ? *fn->blocks.back() : *fn->blocks[1]; | 
 |         auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE); | 
 |         sink.addDstEdge(arc.get()); | 
 |         src.addSrcEdge(arc.get()); | 
 |         fn->treeArcs.push_back(std::move(arc)); | 
 |  | 
 |         for (GCOVBlock &block : fn->blocksRange()) | 
 |           fn->propagateCounts(block, nullptr); | 
 |         for (size_t i = fn->treeArcs.size() - 1; i; --i) | 
 |           fn->treeArcs[i - 1]->src.count += fn->treeArcs[i - 1]->count; | 
 |       } | 
 |     } | 
 |     pos += version >= GCOV::V1200 ? length : 4 * length; | 
 |     if (pos < buf.cursor.tell()) | 
 |       return false; | 
 |     buf.de.skip(buf.cursor, pos - buf.cursor.tell()); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void GCOVFile::print(raw_ostream &OS) const { | 
 |   for (const GCOVFunction &f : *this) | 
 |     f.print(OS); | 
 | } | 
 |  | 
 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | 
 | /// dump - Dump GCOVFile content to dbgs() for debugging purposes. | 
 | LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } | 
 | #endif | 
 |  | 
 | unsigned GCOVFile::addNormalizedPathToMap(StringRef filename) { | 
 |   // unify filename, as the same path can have different form | 
 |   SmallString<256> P(filename); | 
 |   sys::path::remove_dots(P, true); | 
 |   filename = P.str(); | 
 |  | 
 |   auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size()); | 
 |   if (r.second) | 
 |     filenames.emplace_back(filename); | 
 |  | 
 |   return r.first->second; | 
 | } | 
 |  | 
 | bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // GCOVFunction implementation. | 
 |  | 
 | StringRef GCOVFunction::getName(bool demangle) const { | 
 |   if (!demangle) | 
 |     return Name; | 
 |   if (demangled.empty()) { | 
 |     do { | 
 |       if (Name.startswith("_Z")) { | 
 |         // Name is guaranteed to be NUL-terminated. | 
 |         if (char *res = itaniumDemangle(Name.data())) { | 
 |           demangled = res; | 
 |           free(res); | 
 |           break; | 
 |         } | 
 |       } | 
 |       demangled = Name; | 
 |     } while (false); | 
 |   } | 
 |   return demangled; | 
 | } | 
 | StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; } | 
 |  | 
 | /// getEntryCount - Get the number of times the function was called by | 
 | /// retrieving the entry block's count. | 
 | uint64_t GCOVFunction::getEntryCount() const { | 
 |   return blocks.front()->getCount(); | 
 | } | 
 |  | 
 | GCOVBlock &GCOVFunction::getExitBlock() const { | 
 |   return file.getVersion() < GCOV::V408 ? *blocks.back() : *blocks[1]; | 
 | } | 
 |  | 
 | // For each basic block, the sum of incoming edge counts equals the sum of | 
 | // outgoing edge counts by Kirchoff's circuit law. If the unmeasured arcs form a | 
 | // spanning tree, the count for each unmeasured arc (GCOV_ARC_ON_TREE) can be | 
 | // uniquely identified. | 
 | uint64_t GCOVFunction::propagateCounts(const GCOVBlock &v, GCOVArc *pred) { | 
 |   // If GCOV_ARC_ON_TREE edges do form a tree, visited is not needed; otherwise | 
 |   // this prevents infinite recursion. | 
 |   if (!visited.insert(&v).second) | 
 |     return 0; | 
 |  | 
 |   uint64_t excess = 0; | 
 |   for (GCOVArc *e : v.srcs()) | 
 |     if (e != pred) | 
 |       excess += e->onTree() ? propagateCounts(e->src, e) : e->count; | 
 |   for (GCOVArc *e : v.dsts()) | 
 |     if (e != pred) | 
 |       excess -= e->onTree() ? propagateCounts(e->dst, e) : e->count; | 
 |   if (int64_t(excess) < 0) | 
 |     excess = -excess; | 
 |   if (pred) | 
 |     pred->count = excess; | 
 |   return excess; | 
 | } | 
 |  | 
 | void GCOVFunction::print(raw_ostream &OS) const { | 
 |   OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":" | 
 |      << startLine << "\n"; | 
 |   for (const auto &Block : blocks) | 
 |     Block->print(OS); | 
 | } | 
 |  | 
 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | 
 | /// dump - Dump GCOVFunction content to dbgs() for debugging purposes. | 
 | LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } | 
 | #endif | 
 |  | 
 | /// collectLineCounts - Collect line counts. This must be used after | 
 | /// reading .gcno and .gcda files. | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // GCOVBlock implementation. | 
 |  | 
 | void GCOVBlock::print(raw_ostream &OS) const { | 
 |   OS << "Block : " << number << " Counter : " << count << "\n"; | 
 |   if (!pred.empty()) { | 
 |     OS << "\tSource Edges : "; | 
 |     for (const GCOVArc *Edge : pred) | 
 |       OS << Edge->src.number << " (" << Edge->count << "), "; | 
 |     OS << "\n"; | 
 |   } | 
 |   if (!succ.empty()) { | 
 |     OS << "\tDestination Edges : "; | 
 |     for (const GCOVArc *Edge : succ) { | 
 |       if (Edge->flags & GCOV_ARC_ON_TREE) | 
 |         OS << '*'; | 
 |       OS << Edge->dst.number << " (" << Edge->count << "), "; | 
 |     } | 
 |     OS << "\n"; | 
 |   } | 
 |   if (!lines.empty()) { | 
 |     OS << "\tLines : "; | 
 |     for (uint32_t N : lines) | 
 |       OS << (N) << ","; | 
 |     OS << "\n"; | 
 |   } | 
 | } | 
 |  | 
 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | 
 | /// dump - Dump GCOVBlock content to dbgs() for debugging purposes. | 
 | LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } | 
 | #endif | 
 |  | 
 | uint64_t | 
 | GCOVBlock::augmentOneCycle(GCOVBlock *src, | 
 |                            std::vector<std::pair<GCOVBlock *, size_t>> &stack) { | 
 |   GCOVBlock *u; | 
 |   size_t i; | 
 |   stack.clear(); | 
 |   stack.emplace_back(src, 0); | 
 |   src->incoming = (GCOVArc *)1; // Mark u available for cycle detection | 
 |   for (;;) { | 
 |     std::tie(u, i) = stack.back(); | 
 |     if (i == u->succ.size()) { | 
 |       u->traversable = false; | 
 |       stack.pop_back(); | 
 |       if (stack.empty()) | 
 |         break; | 
 |       continue; | 
 |     } | 
 |     ++stack.back().second; | 
 |     GCOVArc *succ = u->succ[i]; | 
 |     // Ignore saturated arcs (cycleCount has been reduced to 0) and visited | 
 |     // blocks. Ignore self arcs to guard against bad input (.gcno has no | 
 |     // self arcs). | 
 |     if (succ->cycleCount == 0 || !succ->dst.traversable || &succ->dst == u) | 
 |       continue; | 
 |     if (succ->dst.incoming == nullptr) { | 
 |       succ->dst.incoming = succ; | 
 |       stack.emplace_back(&succ->dst, 0); | 
 |       continue; | 
 |     } | 
 |     uint64_t minCount = succ->cycleCount; | 
 |     for (GCOVBlock *v = u;;) { | 
 |       minCount = std::min(minCount, v->incoming->cycleCount); | 
 |       v = &v->incoming->src; | 
 |       if (v == &succ->dst) | 
 |         break; | 
 |     } | 
 |     succ->cycleCount -= minCount; | 
 |     for (GCOVBlock *v = u;;) { | 
 |       v->incoming->cycleCount -= minCount; | 
 |       v = &v->incoming->src; | 
 |       if (v == &succ->dst) | 
 |         break; | 
 |     } | 
 |     return minCount; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | // Get the total execution count of loops among blocks on the same line. | 
 | // Assuming a reducible flow graph, the count is the sum of back edge counts. | 
 | // Identifying loops is complex, so we simply find cycles and perform cycle | 
 | // cancelling iteratively. | 
 | uint64_t GCOVBlock::getCyclesCount(const BlockVector &blocks) { | 
 |   std::vector<std::pair<GCOVBlock *, size_t>> stack; | 
 |   uint64_t count = 0, d; | 
 |   for (;;) { | 
 |     // Make blocks on the line traversable and try finding a cycle. | 
 |     for (const auto *b : blocks) { | 
 |       const_cast<GCOVBlock *>(b)->traversable = true; | 
 |       const_cast<GCOVBlock *>(b)->incoming = nullptr; | 
 |     } | 
 |     d = 0; | 
 |     for (const auto *block : blocks) { | 
 |       auto *b = const_cast<GCOVBlock *>(block); | 
 |       if (b->traversable && (d = augmentOneCycle(b, stack)) > 0) | 
 |         break; | 
 |     } | 
 |     if (d == 0) | 
 |       break; | 
 |     count += d; | 
 |   } | 
 |   // If there is no more loop, all traversable bits should have been cleared. | 
 |   // This property is needed by subsequent calls. | 
 |   for (const auto *b : blocks) { | 
 |     assert(!b->traversable); | 
 |     (void)b; | 
 |   } | 
 |   return count; | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // FileInfo implementation. | 
 |  | 
 | // Format dividend/divisor as a percentage. Return 1 if the result is greater | 
 | // than 0% and less than 1%. | 
 | static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) { | 
 |   if (!dividend || !divisor) | 
 |     return 0; | 
 |   dividend *= 100; | 
 |   return dividend < divisor ? 1 : dividend / divisor; | 
 | } | 
 |  | 
 | // This custom division function mimics gcov's branch ouputs: | 
 | //   - Round to closest whole number | 
 | //   - Only output 0% or 100% if it's exactly that value | 
 | static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) { | 
 |   if (!Numerator) | 
 |     return 0; | 
 |   if (Numerator == Divisor) | 
 |     return 100; | 
 |  | 
 |   uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor; | 
 |   if (Res == 0) | 
 |     return 1; | 
 |   if (Res == 100) | 
 |     return 99; | 
 |   return Res; | 
 | } | 
 |  | 
 | namespace { | 
 | struct formatBranchInfo { | 
 |   formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) | 
 |       : Options(Options), Count(Count), Total(Total) {} | 
 |  | 
 |   void print(raw_ostream &OS) const { | 
 |     if (!Total) | 
 |       OS << "never executed"; | 
 |     else if (Options.BranchCount) | 
 |       OS << "taken " << Count; | 
 |     else | 
 |       OS << "taken " << branchDiv(Count, Total) << "%"; | 
 |   } | 
 |  | 
 |   const GCOV::Options &Options; | 
 |   uint64_t Count; | 
 |   uint64_t Total; | 
 | }; | 
 |  | 
 | static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { | 
 |   FBI.print(OS); | 
 |   return OS; | 
 | } | 
 |  | 
 | class LineConsumer { | 
 |   std::unique_ptr<MemoryBuffer> Buffer; | 
 |   StringRef Remaining; | 
 |  | 
 | public: | 
 |   LineConsumer() = default; | 
 |   LineConsumer(StringRef Filename) { | 
 |     // Open source files without requiring a NUL terminator. The concurrent | 
 |     // modification may nullify the NUL terminator condition. | 
 |     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = | 
 |         MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/false, | 
 |                                      /*RequiresNullTerminator=*/false); | 
 |     if (std::error_code EC = BufferOrErr.getError()) { | 
 |       errs() << Filename << ": " << EC.message() << "\n"; | 
 |       Remaining = ""; | 
 |     } else { | 
 |       Buffer = std::move(BufferOrErr.get()); | 
 |       Remaining = Buffer->getBuffer(); | 
 |     } | 
 |   } | 
 |   bool empty() { return Remaining.empty(); } | 
 |   void printNext(raw_ostream &OS, uint32_t LineNum) { | 
 |     StringRef Line; | 
 |     if (empty()) | 
 |       Line = "/*EOF*/"; | 
 |     else | 
 |       std::tie(Line, Remaining) = Remaining.split("\n"); | 
 |     OS << format("%5u:", LineNum) << Line << "\n"; | 
 |   } | 
 | }; | 
 | } // end anonymous namespace | 
 |  | 
 | /// Convert a path to a gcov filename. If PreservePaths is true, this | 
 | /// translates "/" to "#", ".." to "^", and drops ".", to match gcov. | 
 | static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { | 
 |   if (!PreservePaths) | 
 |     return sys::path::filename(Filename).str(); | 
 |  | 
 |   // This behaviour is defined by gcov in terms of text replacements, so it's | 
 |   // not likely to do anything useful on filesystems with different textual | 
 |   // conventions. | 
 |   llvm::SmallString<256> Result(""); | 
 |   StringRef::iterator I, S, E; | 
 |   for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { | 
 |     if (*I != '/') | 
 |       continue; | 
 |  | 
 |     if (I - S == 1 && *S == '.') { | 
 |       // ".", the current directory, is skipped. | 
 |     } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { | 
 |       // "..", the parent directory, is replaced with "^". | 
 |       Result.append("^#"); | 
 |     } else { | 
 |       if (S < I) | 
 |         // Leave other components intact, | 
 |         Result.append(S, I); | 
 |       // And separate with "#". | 
 |       Result.push_back('#'); | 
 |     } | 
 |     S = I + 1; | 
 |   } | 
 |  | 
 |   if (S < I) | 
 |     Result.append(S, I); | 
 |   return std::string(Result.str()); | 
 | } | 
 |  | 
 | std::string Context::getCoveragePath(StringRef filename, | 
 |                                      StringRef mainFilename) const { | 
 |   if (options.NoOutput) | 
 |     // This is probably a bug in gcov, but when -n is specified, paths aren't | 
 |     // mangled at all, and the -l and -p options are ignored. Here, we do the | 
 |     // same. | 
 |     return std::string(filename); | 
 |  | 
 |   std::string CoveragePath; | 
 |   if (options.LongFileNames && !filename.equals(mainFilename)) | 
 |     CoveragePath = | 
 |         mangleCoveragePath(mainFilename, options.PreservePaths) + "##"; | 
 |   CoveragePath += mangleCoveragePath(filename, options.PreservePaths); | 
 |   if (options.HashFilenames) { | 
 |     MD5 Hasher; | 
 |     MD5::MD5Result Result; | 
 |     Hasher.update(filename.str()); | 
 |     Hasher.final(Result); | 
 |     CoveragePath += "##" + std::string(Result.digest()); | 
 |   } | 
 |   CoveragePath += ".gcov"; | 
 |   return CoveragePath; | 
 | } | 
 |  | 
 | void Context::collectFunction(GCOVFunction &f, Summary &summary) { | 
 |   SourceInfo &si = sources[f.srcIdx]; | 
 |   if (f.startLine >= si.startLineToFunctions.size()) | 
 |     si.startLineToFunctions.resize(f.startLine + 1); | 
 |   si.startLineToFunctions[f.startLine].push_back(&f); | 
 |   SmallSet<uint32_t, 16> lines; | 
 |   SmallSet<uint32_t, 16> linesExec; | 
 |   for (const GCOVBlock &b : f.blocksRange()) { | 
 |     if (b.lines.empty()) | 
 |       continue; | 
 |     uint32_t maxLineNum = *std::max_element(b.lines.begin(), b.lines.end()); | 
 |     if (maxLineNum >= si.lines.size()) | 
 |       si.lines.resize(maxLineNum + 1); | 
 |     for (uint32_t lineNum : b.lines) { | 
 |       LineInfo &line = si.lines[lineNum]; | 
 |       if (lines.insert(lineNum).second) | 
 |         ++summary.lines; | 
 |       if (b.count && linesExec.insert(lineNum).second) | 
 |         ++summary.linesExec; | 
 |       line.exists = true; | 
 |       line.count += b.count; | 
 |       line.blocks.push_back(&b); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void Context::collectSourceLine(SourceInfo &si, Summary *summary, | 
 |                                 LineInfo &line, size_t lineNum) const { | 
 |   uint64_t count = 0; | 
 |   for (const GCOVBlock *b : line.blocks) { | 
 |     if (b->number == 0) { | 
 |       // For nonstandard control flows, arcs into the exit block may be | 
 |       // duplicately counted (fork) or not be counted (abnormal exit), and thus | 
 |       // the (exit,entry) counter may be inaccurate. Count the entry block with | 
 |       // the outgoing arcs. | 
 |       for (const GCOVArc *arc : b->succ) | 
 |         count += arc->count; | 
 |     } else { | 
 |       // Add counts from predecessors that are not on the same line. | 
 |       for (const GCOVArc *arc : b->pred) | 
 |         if (!llvm::is_contained(line.blocks, &arc->src)) | 
 |           count += arc->count; | 
 |     } | 
 |     for (GCOVArc *arc : b->succ) | 
 |       arc->cycleCount = arc->count; | 
 |   } | 
 |  | 
 |   count += GCOVBlock::getCyclesCount(line.blocks); | 
 |   line.count = count; | 
 |   if (line.exists) { | 
 |     ++summary->lines; | 
 |     if (line.count != 0) | 
 |       ++summary->linesExec; | 
 |   } | 
 |  | 
 |   if (options.BranchInfo) | 
 |     for (const GCOVBlock *b : line.blocks) { | 
 |       if (b->getLastLine() != lineNum) | 
 |         continue; | 
 |       int branches = 0, execBranches = 0, takenBranches = 0; | 
 |       for (const GCOVArc *arc : b->succ) { | 
 |         ++branches; | 
 |         if (count != 0) | 
 |           ++execBranches; | 
 |         if (arc->count != 0) | 
 |           ++takenBranches; | 
 |       } | 
 |       if (branches > 1) { | 
 |         summary->branches += branches; | 
 |         summary->branchesExec += execBranches; | 
 |         summary->branchesTaken += takenBranches; | 
 |       } | 
 |     } | 
 | } | 
 |  | 
 | void Context::collectSource(SourceInfo &si, Summary &summary) const { | 
 |   size_t lineNum = 0; | 
 |   for (LineInfo &line : si.lines) { | 
 |     collectSourceLine(si, &summary, line, lineNum); | 
 |     ++lineNum; | 
 |   } | 
 | } | 
 |  | 
 | void Context::annotateSource(SourceInfo &si, const GCOVFile &file, | 
 |                              StringRef gcno, StringRef gcda, | 
 |                              raw_ostream &os) const { | 
 |   auto source = | 
 |       options.Intermediate ? LineConsumer() : LineConsumer(si.filename); | 
 |  | 
 |   os << "        -:    0:Source:" << si.displayName << '\n'; | 
 |   os << "        -:    0:Graph:" << gcno << '\n'; | 
 |   os << "        -:    0:Data:" << gcda << '\n'; | 
 |   os << "        -:    0:Runs:" << file.runCount << '\n'; | 
 |   if (file.version < GCOV::V900) | 
 |     os << "        -:    0:Programs:" << file.programCount << '\n'; | 
 |  | 
 |   for (size_t lineNum = 1; !source.empty(); ++lineNum) { | 
 |     if (lineNum >= si.lines.size()) { | 
 |       os << "        -:"; | 
 |       source.printNext(os, lineNum); | 
 |       continue; | 
 |     } | 
 |  | 
 |     const LineInfo &line = si.lines[lineNum]; | 
 |     if (options.BranchInfo && lineNum < si.startLineToFunctions.size()) | 
 |       for (const auto *f : si.startLineToFunctions[lineNum]) | 
 |         printFunctionDetails(*f, os); | 
 |     if (!line.exists) | 
 |       os << "        -:"; | 
 |     else if (line.count == 0) | 
 |       os << "    #####:"; | 
 |     else | 
 |       os << format("%9" PRIu64 ":", line.count); | 
 |     source.printNext(os, lineNum); | 
 |  | 
 |     uint32_t blockIdx = 0, edgeIdx = 0; | 
 |     for (const GCOVBlock *b : line.blocks) { | 
 |       if (b->getLastLine() != lineNum) | 
 |         continue; | 
 |       if (options.AllBlocks) { | 
 |         if (b->getCount() == 0) | 
 |           os << "    $$$$$:"; | 
 |         else | 
 |           os << format("%9" PRIu64 ":", b->count); | 
 |         os << format("%5u-block %2u\n", lineNum, blockIdx++); | 
 |       } | 
 |       if (options.BranchInfo) { | 
 |         size_t NumEdges = b->succ.size(); | 
 |         if (NumEdges > 1) | 
 |           printBranchInfo(*b, edgeIdx, os); | 
 |         else if (options.UncondBranch && NumEdges == 1) { | 
 |           uint64_t count = b->succ[0]->count; | 
 |           os << format("unconditional %2u ", edgeIdx++) | 
 |              << formatBranchInfo(options, count, count) << '\n'; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void Context::printSourceToIntermediate(const SourceInfo &si, | 
 |                                         raw_ostream &os) const { | 
 |   os << "file:" << si.filename << '\n'; | 
 |   for (const auto &fs : si.startLineToFunctions) | 
 |     for (const GCOVFunction *f : fs) | 
 |       os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' | 
 |          << f->getName(options.Demangle) << '\n'; | 
 |   for (size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) { | 
 |     const LineInfo &line = si.lines[lineNum]; | 
 |     if (line.blocks.empty()) | 
 |       continue; | 
 |     // GCC 8 (r254259) added third third field for Ada: | 
 |     // lcount:<line>,<count>,<has_unexecuted_blocks> | 
 |     // We don't need the third field. | 
 |     os << "lcount:" << lineNum << ',' << line.count << '\n'; | 
 |  | 
 |     if (!options.BranchInfo) | 
 |       continue; | 
 |     for (const GCOVBlock *b : line.blocks) { | 
 |       if (b->succ.size() < 2 || b->getLastLine() != lineNum) | 
 |         continue; | 
 |       for (const GCOVArc *arc : b->succ) { | 
 |         const char *type = | 
 |             b->getCount() ? arc->count ? "taken" : "nottaken" : "notexec"; | 
 |         os << "branch:" << lineNum << ',' << type << '\n'; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void Context::print(StringRef filename, StringRef gcno, StringRef gcda, | 
 |                     GCOVFile &file) { | 
 |   for (StringRef filename : file.filenames) { | 
 |     sources.emplace_back(filename); | 
 |     SourceInfo &si = sources.back(); | 
 |     si.displayName = si.filename; | 
 |     if (!options.SourcePrefix.empty() && | 
 |         sys::path::replace_path_prefix(si.displayName, options.SourcePrefix, | 
 |                                        "") && | 
 |         !si.displayName.empty()) { | 
 |       // TODO replace_path_prefix may strip the prefix even if the remaining | 
 |       // part does not start with a separator. | 
 |       if (sys::path::is_separator(si.displayName[0])) | 
 |         si.displayName.erase(si.displayName.begin()); | 
 |       else | 
 |         si.displayName = si.filename; | 
 |     } | 
 |     if (options.RelativeOnly && sys::path::is_absolute(si.displayName)) | 
 |       si.ignored = true; | 
 |   } | 
 |  | 
 |   raw_ostream &os = llvm::outs(); | 
 |   for (GCOVFunction &f : make_pointee_range(file.functions)) { | 
 |     Summary summary(f.getName(options.Demangle)); | 
 |     collectFunction(f, summary); | 
 |     if (options.FuncCoverage && !options.UseStdout) { | 
 |       os << "Function '" << summary.Name << "'\n"; | 
 |       printSummary(summary, os); | 
 |       os << '\n'; | 
 |     } | 
 |   } | 
 |  | 
 |   for (SourceInfo &si : sources) { | 
 |     if (si.ignored) | 
 |       continue; | 
 |     Summary summary(si.displayName); | 
 |     collectSource(si, summary); | 
 |  | 
 |     // Print file summary unless -t is specified. | 
 |     std::string gcovName = getCoveragePath(si.filename, filename); | 
 |     if (!options.UseStdout) { | 
 |       os << "File '" << summary.Name << "'\n"; | 
 |       printSummary(summary, os); | 
 |       if (!options.NoOutput && !options.Intermediate) | 
 |         os << "Creating '" << gcovName << "'\n"; | 
 |       os << '\n'; | 
 |     } | 
 |  | 
 |     if (options.NoOutput || options.Intermediate) | 
 |       continue; | 
 |     std::optional<raw_fd_ostream> os; | 
 |     if (!options.UseStdout) { | 
 |       std::error_code ec; | 
 |       os.emplace(gcovName, ec, sys::fs::OF_TextWithCRLF); | 
 |       if (ec) { | 
 |         errs() << ec.message() << '\n'; | 
 |         continue; | 
 |       } | 
 |     } | 
 |     annotateSource(si, file, gcno, gcda, | 
 |                    options.UseStdout ? llvm::outs() : *os); | 
 |   } | 
 |  | 
 |   if (options.Intermediate && !options.NoOutput) { | 
 |     // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0 | 
 |     // (PR GCC/82702). We create just one file. | 
 |     std::string outputPath(sys::path::filename(filename)); | 
 |     std::error_code ec; | 
 |     raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_TextWithCRLF); | 
 |     if (ec) { | 
 |       errs() << ec.message() << '\n'; | 
 |       return; | 
 |     } | 
 |  | 
 |     for (const SourceInfo &si : sources) | 
 |       printSourceToIntermediate(si, os); | 
 |   } | 
 | } | 
 |  | 
 | void Context::printFunctionDetails(const GCOVFunction &f, | 
 |                                    raw_ostream &os) const { | 
 |   const uint64_t entryCount = f.getEntryCount(); | 
 |   uint32_t blocksExec = 0; | 
 |   const GCOVBlock &exitBlock = f.getExitBlock(); | 
 |   uint64_t exitCount = 0; | 
 |   for (const GCOVArc *arc : exitBlock.pred) | 
 |     exitCount += arc->count; | 
 |   for (const GCOVBlock &b : f.blocksRange()) | 
 |     if (b.number != 0 && &b != &exitBlock && b.getCount()) | 
 |       ++blocksExec; | 
 |  | 
 |   os << "function " << f.getName(options.Demangle) << " called " << entryCount | 
 |      << " returned " << formatPercentage(exitCount, entryCount) | 
 |      << "% blocks executed " | 
 |      << formatPercentage(blocksExec, f.blocks.size() - 2) << "%\n"; | 
 | } | 
 |  | 
 | /// printBranchInfo - Print conditional branch probabilities. | 
 | void Context::printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, | 
 |                               raw_ostream &os) const { | 
 |   uint64_t total = 0; | 
 |   for (const GCOVArc *arc : Block.dsts()) | 
 |     total += arc->count; | 
 |   for (const GCOVArc *arc : Block.dsts()) | 
 |     os << format("branch %2u ", edgeIdx++) | 
 |        << formatBranchInfo(options, arc->count, total) << '\n'; | 
 | } | 
 |  | 
 | void Context::printSummary(const Summary &summary, raw_ostream &os) const { | 
 |   os << format("Lines executed:%.2f%% of %" PRIu64 "\n", | 
 |                double(summary.linesExec) * 100 / summary.lines, summary.lines); | 
 |   if (options.BranchInfo) { | 
 |     if (summary.branches == 0) { | 
 |       os << "No branches\n"; | 
 |     } else { | 
 |       os << format("Branches executed:%.2f%% of %" PRIu64 "\n", | 
 |                    double(summary.branchesExec) * 100 / summary.branches, | 
 |                    summary.branches); | 
 |       os << format("Taken at least once:%.2f%% of %" PRIu64 "\n", | 
 |                    double(summary.branchesTaken) * 100 / summary.branches, | 
 |                    summary.branches); | 
 |     } | 
 |     os << "No calls\n"; | 
 |   } | 
 | } | 
 |  | 
 | void llvm::gcovOneInput(const GCOV::Options &options, StringRef filename, | 
 |                         StringRef gcno, StringRef gcda, GCOVFile &file) { | 
 |   Context fi(options); | 
 |   fi.print(filename, gcno, gcda, file); | 
 | } |