10b57cec5SDimitry Andric //===- GCOV.cpp - LLVM coverage tool --------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // GCOV implements the interface to read and write coverage files that use
100b57cec5SDimitry Andric // 'gcov' format.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/ProfileData/GCOV.h"
150b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
1681ad6265SDimitry Andric #include "llvm/ADT/SmallSet.h"
170b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
18e8d8bef9SDimitry Andric #include "llvm/Demangle/Demangle.h"
190b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
200b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
210b57cec5SDimitry Andric #include "llvm/Support/Format.h"
220b57cec5SDimitry Andric #include "llvm/Support/MD5.h"
23e8d8bef9SDimitry Andric #include "llvm/Support/Path.h"
240b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
250b57cec5SDimitry Andric #include <algorithm>
26bdd1243dSDimitry Andric #include <optional>
270b57cec5SDimitry Andric #include <system_error>
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric using namespace llvm;
300b57cec5SDimitry Andric 
315ffd83dbSDimitry Andric enum : uint32_t {
325ffd83dbSDimitry Andric   GCOV_ARC_ON_TREE = 1 << 0,
335ffd83dbSDimitry Andric   GCOV_ARC_FALLTHROUGH = 1 << 2,
345ffd83dbSDimitry Andric 
355ffd83dbSDimitry Andric   GCOV_TAG_FUNCTION = 0x01000000,
365ffd83dbSDimitry Andric   GCOV_TAG_BLOCKS = 0x01410000,
375ffd83dbSDimitry Andric   GCOV_TAG_ARCS = 0x01430000,
385ffd83dbSDimitry Andric   GCOV_TAG_LINES = 0x01450000,
395ffd83dbSDimitry Andric   GCOV_TAG_COUNTER_ARCS = 0x01a10000,
405ffd83dbSDimitry Andric   // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9.
415ffd83dbSDimitry Andric   GCOV_TAG_OBJECT_SUMMARY = 0xa1000000,
425ffd83dbSDimitry Andric   GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000,
435ffd83dbSDimitry Andric };
445ffd83dbSDimitry Andric 
45e8d8bef9SDimitry Andric namespace {
46e8d8bef9SDimitry Andric struct Summary {
Summary__anon4e48ce010211::Summary47e8d8bef9SDimitry Andric   Summary(StringRef Name) : Name(Name) {}
48e8d8bef9SDimitry Andric 
49e8d8bef9SDimitry Andric   StringRef Name;
50e8d8bef9SDimitry Andric   uint64_t lines = 0;
51e8d8bef9SDimitry Andric   uint64_t linesExec = 0;
52e8d8bef9SDimitry Andric   uint64_t branches = 0;
53e8d8bef9SDimitry Andric   uint64_t branchesExec = 0;
54e8d8bef9SDimitry Andric   uint64_t branchesTaken = 0;
55e8d8bef9SDimitry Andric };
56e8d8bef9SDimitry Andric 
57e8d8bef9SDimitry Andric struct LineInfo {
58e8d8bef9SDimitry Andric   SmallVector<const GCOVBlock *, 1> blocks;
59e8d8bef9SDimitry Andric   uint64_t count = 0;
60e8d8bef9SDimitry Andric   bool exists = false;
61e8d8bef9SDimitry Andric };
62e8d8bef9SDimitry Andric 
63e8d8bef9SDimitry Andric struct SourceInfo {
64e8d8bef9SDimitry Andric   StringRef filename;
65e8d8bef9SDimitry Andric   SmallString<0> displayName;
66e8d8bef9SDimitry Andric   std::vector<std::vector<const GCOVFunction *>> startLineToFunctions;
67e8d8bef9SDimitry Andric   std::vector<LineInfo> lines;
68e8d8bef9SDimitry Andric   bool ignored = false;
SourceInfo__anon4e48ce010211::SourceInfo69e8d8bef9SDimitry Andric   SourceInfo(StringRef filename) : filename(filename) {}
70e8d8bef9SDimitry Andric };
71e8d8bef9SDimitry Andric 
72e8d8bef9SDimitry Andric class Context {
73e8d8bef9SDimitry Andric public:
Context(const GCOV::Options & Options)74e8d8bef9SDimitry Andric   Context(const GCOV::Options &Options) : options(Options) {}
75e8d8bef9SDimitry Andric   void print(StringRef filename, StringRef gcno, StringRef gcda,
76e8d8bef9SDimitry Andric              GCOVFile &file);
77e8d8bef9SDimitry Andric 
78e8d8bef9SDimitry Andric private:
79e8d8bef9SDimitry Andric   std::string getCoveragePath(StringRef filename, StringRef mainFilename) const;
80e8d8bef9SDimitry Andric   void printFunctionDetails(const GCOVFunction &f, raw_ostream &os) const;
81e8d8bef9SDimitry Andric   void printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx,
82e8d8bef9SDimitry Andric                        raw_ostream &OS) const;
83e8d8bef9SDimitry Andric   void printSummary(const Summary &summary, raw_ostream &os) const;
84e8d8bef9SDimitry Andric 
85e8d8bef9SDimitry Andric   void collectFunction(GCOVFunction &f, Summary &summary);
86e8d8bef9SDimitry Andric   void collectSourceLine(SourceInfo &si, Summary *summary, LineInfo &line,
87e8d8bef9SDimitry Andric                          size_t lineNum) const;
88e8d8bef9SDimitry Andric   void collectSource(SourceInfo &si, Summary &summary) const;
89e8d8bef9SDimitry Andric   void annotateSource(SourceInfo &si, const GCOVFile &file, StringRef gcno,
90e8d8bef9SDimitry Andric                       StringRef gcda, raw_ostream &os) const;
91e8d8bef9SDimitry Andric   void printSourceToIntermediate(const SourceInfo &si, raw_ostream &os) const;
92e8d8bef9SDimitry Andric 
93e8d8bef9SDimitry Andric   const GCOV::Options &options;
94e8d8bef9SDimitry Andric   std::vector<SourceInfo> sources;
95e8d8bef9SDimitry Andric };
96e8d8bef9SDimitry Andric } // namespace
97e8d8bef9SDimitry Andric 
980b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
990b57cec5SDimitry Andric // GCOVFile implementation.
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric /// readGCNO - Read GCNO buffer.
readGCNO(GCOVBuffer & buf)1025ffd83dbSDimitry Andric bool GCOVFile::readGCNO(GCOVBuffer &buf) {
1035ffd83dbSDimitry Andric   if (!buf.readGCNOFormat())
1040b57cec5SDimitry Andric     return false;
105fe6060f1SDimitry Andric   if (!buf.readGCOVVersion(version))
1060b57cec5SDimitry Andric     return false;
1070b57cec5SDimitry Andric 
108fe6060f1SDimitry Andric   checksum = buf.getWord();
109fe6060f1SDimitry Andric   if (version >= GCOV::V900 && !buf.readString(cwd))
110fe6060f1SDimitry Andric     return false;
111fe6060f1SDimitry Andric   if (version >= GCOV::V800)
1125ffd83dbSDimitry Andric     buf.getWord(); // hasUnexecutedBlocks
1135ffd83dbSDimitry Andric 
1145ffd83dbSDimitry Andric   uint32_t tag, length;
115e8d8bef9SDimitry Andric   GCOVFunction *fn = nullptr;
1165ffd83dbSDimitry Andric   while ((tag = buf.getWord())) {
1175ffd83dbSDimitry Andric     if (!buf.readInt(length))
1180b57cec5SDimitry Andric       return false;
119fe6060f1SDimitry Andric     uint32_t pos = buf.cursor.tell();
1205ffd83dbSDimitry Andric     if (tag == GCOV_TAG_FUNCTION) {
121e8d8bef9SDimitry Andric       functions.push_back(std::make_unique<GCOVFunction>(*this));
122e8d8bef9SDimitry Andric       fn = functions.back().get();
1235ffd83dbSDimitry Andric       fn->ident = buf.getWord();
1245ffd83dbSDimitry Andric       fn->linenoChecksum = buf.getWord();
125fe6060f1SDimitry Andric       if (version >= GCOV::V407)
1265ffd83dbSDimitry Andric         fn->cfgChecksum = buf.getWord();
1275ffd83dbSDimitry Andric       buf.readString(fn->Name);
1285ffd83dbSDimitry Andric       StringRef filename;
129fe6060f1SDimitry Andric       if (version < GCOV::V800) {
130fe6060f1SDimitry Andric         if (!buf.readString(filename))
131fe6060f1SDimitry Andric           return false;
1325ffd83dbSDimitry Andric         fn->startLine = buf.getWord();
1335ffd83dbSDimitry Andric       } else {
1345ffd83dbSDimitry Andric         fn->artificial = buf.getWord();
135fe6060f1SDimitry Andric         if (!buf.readString(filename))
136fe6060f1SDimitry Andric           return false;
1375ffd83dbSDimitry Andric         fn->startLine = buf.getWord();
1385ffd83dbSDimitry Andric         fn->startColumn = buf.getWord();
1395ffd83dbSDimitry Andric         fn->endLine = buf.getWord();
140fe6060f1SDimitry Andric         if (version >= GCOV::V900)
1415ffd83dbSDimitry Andric           fn->endColumn = buf.getWord();
1425ffd83dbSDimitry Andric       }
14306c3fb27SDimitry Andric       fn->srcIdx = addNormalizedPathToMap(filename);
144fe6060f1SDimitry Andric       identToFunction[fn->ident] = fn;
1455ffd83dbSDimitry Andric     } else if (tag == GCOV_TAG_BLOCKS && fn) {
146fe6060f1SDimitry Andric       if (version < GCOV::V800) {
1475ffd83dbSDimitry Andric         for (uint32_t i = 0; i != length; ++i) {
1485ffd83dbSDimitry Andric           buf.getWord(); // Ignored block flags
149e8d8bef9SDimitry Andric           fn->blocks.push_back(std::make_unique<GCOVBlock>(i));
1505ffd83dbSDimitry Andric         }
1515ffd83dbSDimitry Andric       } else {
1525ffd83dbSDimitry Andric         uint32_t num = buf.getWord();
1535ffd83dbSDimitry Andric         for (uint32_t i = 0; i != num; ++i)
154e8d8bef9SDimitry Andric           fn->blocks.push_back(std::make_unique<GCOVBlock>(i));
1555ffd83dbSDimitry Andric       }
1565ffd83dbSDimitry Andric     } else if (tag == GCOV_TAG_ARCS && fn) {
1575ffd83dbSDimitry Andric       uint32_t srcNo = buf.getWord();
158e8d8bef9SDimitry Andric       if (srcNo >= fn->blocks.size()) {
1595ffd83dbSDimitry Andric         errs() << "unexpected block number: " << srcNo << " (in "
160e8d8bef9SDimitry Andric                << fn->blocks.size() << ")\n";
1615ffd83dbSDimitry Andric         return false;
1625ffd83dbSDimitry Andric       }
163e8d8bef9SDimitry Andric       GCOVBlock *src = fn->blocks[srcNo].get();
164fe6060f1SDimitry Andric       const uint32_t e =
165fe6060f1SDimitry Andric           version >= GCOV::V1200 ? (length / 4 - 1) / 2 : (length - 1) / 2;
166fe6060f1SDimitry Andric       for (uint32_t i = 0; i != e; ++i) {
1675ffd83dbSDimitry Andric         uint32_t dstNo = buf.getWord(), flags = buf.getWord();
168e8d8bef9SDimitry Andric         GCOVBlock *dst = fn->blocks[dstNo].get();
169e8d8bef9SDimitry Andric         auto arc = std::make_unique<GCOVArc>(*src, *dst, flags);
1705ffd83dbSDimitry Andric         src->addDstEdge(arc.get());
1715ffd83dbSDimitry Andric         dst->addSrcEdge(arc.get());
172e8d8bef9SDimitry Andric         if (arc->onTree())
1735ffd83dbSDimitry Andric           fn->treeArcs.push_back(std::move(arc));
1745ffd83dbSDimitry Andric         else
1755ffd83dbSDimitry Andric           fn->arcs.push_back(std::move(arc));
1765ffd83dbSDimitry Andric       }
1775ffd83dbSDimitry Andric     } else if (tag == GCOV_TAG_LINES && fn) {
1785ffd83dbSDimitry Andric       uint32_t srcNo = buf.getWord();
179e8d8bef9SDimitry Andric       if (srcNo >= fn->blocks.size()) {
1805ffd83dbSDimitry Andric         errs() << "unexpected block number: " << srcNo << " (in "
181e8d8bef9SDimitry Andric                << fn->blocks.size() << ")\n";
1825ffd83dbSDimitry Andric         return false;
1835ffd83dbSDimitry Andric       }
184e8d8bef9SDimitry Andric       GCOVBlock &Block = *fn->blocks[srcNo];
1855ffd83dbSDimitry Andric       for (;;) {
1865ffd83dbSDimitry Andric         uint32_t line = buf.getWord();
1875ffd83dbSDimitry Andric         if (line)
1885ffd83dbSDimitry Andric           Block.addLine(line);
1895ffd83dbSDimitry Andric         else {
190fe6060f1SDimitry Andric           StringRef filename;
191fe6060f1SDimitry Andric           buf.readString(filename);
1925ffd83dbSDimitry Andric           if (filename.empty())
1930b57cec5SDimitry Andric             break;
1945ffd83dbSDimitry Andric           // TODO Unhandled
1955ffd83dbSDimitry Andric         }
1965ffd83dbSDimitry Andric       }
1975ffd83dbSDimitry Andric     }
198fe6060f1SDimitry Andric     pos += version >= GCOV::V1200 ? length : 4 * length;
199fe6060f1SDimitry Andric     if (pos < buf.cursor.tell())
200fe6060f1SDimitry Andric       return false;
201fe6060f1SDimitry Andric     buf.de.skip(buf.cursor, pos - buf.cursor.tell());
2020b57cec5SDimitry Andric   }
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric   GCNOInitialized = true;
2050b57cec5SDimitry Andric   return true;
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric /// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be
2090b57cec5SDimitry Andric /// called after readGCNO().
readGCDA(GCOVBuffer & buf)2105ffd83dbSDimitry Andric bool GCOVFile::readGCDA(GCOVBuffer &buf) {
2110b57cec5SDimitry Andric   assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()");
2125ffd83dbSDimitry Andric   if (!buf.readGCDAFormat())
2130b57cec5SDimitry Andric     return false;
2140b57cec5SDimitry Andric   GCOV::GCOVVersion GCDAVersion;
2155ffd83dbSDimitry Andric   if (!buf.readGCOVVersion(GCDAVersion))
2160b57cec5SDimitry Andric     return false;
217fe6060f1SDimitry Andric   if (version != GCDAVersion) {
2180b57cec5SDimitry Andric     errs() << "GCOV versions do not match.\n";
2190b57cec5SDimitry Andric     return false;
2200b57cec5SDimitry Andric   }
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric   uint32_t GCDAChecksum;
2235ffd83dbSDimitry Andric   if (!buf.readInt(GCDAChecksum))
2240b57cec5SDimitry Andric     return false;
225fe6060f1SDimitry Andric   if (checksum != GCDAChecksum) {
226fe6060f1SDimitry Andric     errs() << "file checksums do not match: " << checksum
227fe6060f1SDimitry Andric            << " != " << GCDAChecksum << "\n";
2280b57cec5SDimitry Andric     return false;
2290b57cec5SDimitry Andric   }
2305ffd83dbSDimitry Andric   uint32_t dummy, tag, length;
2315ffd83dbSDimitry Andric   uint32_t ident;
2325ffd83dbSDimitry Andric   GCOVFunction *fn = nullptr;
2335ffd83dbSDimitry Andric   while ((tag = buf.getWord())) {
2345ffd83dbSDimitry Andric     if (!buf.readInt(length))
2350b57cec5SDimitry Andric       return false;
2365ffd83dbSDimitry Andric     uint32_t pos = buf.cursor.tell();
2375ffd83dbSDimitry Andric     if (tag == GCOV_TAG_OBJECT_SUMMARY) {
238fe6060f1SDimitry Andric       buf.readInt(runCount);
2395ffd83dbSDimitry Andric       buf.readInt(dummy);
2405ffd83dbSDimitry Andric     } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) {
2415ffd83dbSDimitry Andric       buf.readInt(dummy);
2425ffd83dbSDimitry Andric       buf.readInt(dummy);
243fe6060f1SDimitry Andric       buf.readInt(runCount);
244fe6060f1SDimitry Andric       ++programCount;
2455ffd83dbSDimitry Andric     } else if (tag == GCOV_TAG_FUNCTION) {
2465ffd83dbSDimitry Andric       if (length == 0) // Placeholder
2475ffd83dbSDimitry Andric         continue;
2485ffd83dbSDimitry Andric       if (length < 2 || !buf.readInt(ident))
2495ffd83dbSDimitry Andric         return false;
250fe6060f1SDimitry Andric       auto It = identToFunction.find(ident);
2515ffd83dbSDimitry Andric       uint32_t linenoChecksum, cfgChecksum = 0;
2525ffd83dbSDimitry Andric       buf.readInt(linenoChecksum);
253fe6060f1SDimitry Andric       if (version >= GCOV::V407)
2545ffd83dbSDimitry Andric         buf.readInt(cfgChecksum);
255fe6060f1SDimitry Andric       if (It != identToFunction.end()) {
2565ffd83dbSDimitry Andric         fn = It->second;
2575ffd83dbSDimitry Andric         if (linenoChecksum != fn->linenoChecksum ||
2585ffd83dbSDimitry Andric             cfgChecksum != fn->cfgChecksum) {
2595ffd83dbSDimitry Andric           errs() << fn->Name
2605ffd83dbSDimitry Andric                  << format(": checksum mismatch, (%u, %u) != (%u, %u)\n",
2615ffd83dbSDimitry Andric                            linenoChecksum, cfgChecksum, fn->linenoChecksum,
2625ffd83dbSDimitry Andric                            fn->cfgChecksum);
2635ffd83dbSDimitry Andric           return false;
2645ffd83dbSDimitry Andric         }
2655ffd83dbSDimitry Andric       }
2665ffd83dbSDimitry Andric     } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) {
267fe6060f1SDimitry Andric       uint32_t expected = 2 * fn->arcs.size();
268fe6060f1SDimitry Andric       if (version >= GCOV::V1200)
269fe6060f1SDimitry Andric         expected *= 4;
270fe6060f1SDimitry Andric       if (length != expected) {
2715ffd83dbSDimitry Andric         errs() << fn->Name
2725ffd83dbSDimitry Andric                << format(
2735ffd83dbSDimitry Andric                       ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n",
274fe6060f1SDimitry Andric                       length, expected);
2755ffd83dbSDimitry Andric         return false;
2765ffd83dbSDimitry Andric       }
2775ffd83dbSDimitry Andric       for (std::unique_ptr<GCOVArc> &arc : fn->arcs) {
278e8d8bef9SDimitry Andric         if (!buf.readInt64(arc->count))
2795ffd83dbSDimitry Andric           return false;
280e8d8bef9SDimitry Andric         arc->src.count += arc->count;
281e8d8bef9SDimitry Andric       }
282e8d8bef9SDimitry Andric 
283e8d8bef9SDimitry Andric       if (fn->blocks.size() >= 2) {
284e8d8bef9SDimitry Andric         GCOVBlock &src = *fn->blocks[0];
285e8d8bef9SDimitry Andric         GCOVBlock &sink =
286fe6060f1SDimitry Andric             version < GCOV::V408 ? *fn->blocks.back() : *fn->blocks[1];
287e8d8bef9SDimitry Andric         auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE);
288e8d8bef9SDimitry Andric         sink.addDstEdge(arc.get());
289e8d8bef9SDimitry Andric         src.addSrcEdge(arc.get());
290e8d8bef9SDimitry Andric         fn->treeArcs.push_back(std::move(arc));
291e8d8bef9SDimitry Andric 
292e8d8bef9SDimitry Andric         for (GCOVBlock &block : fn->blocksRange())
293e8d8bef9SDimitry Andric           fn->propagateCounts(block, nullptr);
294e8d8bef9SDimitry Andric         for (size_t i = fn->treeArcs.size() - 1; i; --i)
295e8d8bef9SDimitry Andric           fn->treeArcs[i - 1]->src.count += fn->treeArcs[i - 1]->count;
2965ffd83dbSDimitry Andric       }
2975ffd83dbSDimitry Andric     }
298fe6060f1SDimitry Andric     pos += version >= GCOV::V1200 ? length : 4 * length;
2995ffd83dbSDimitry Andric     if (pos < buf.cursor.tell())
3005ffd83dbSDimitry Andric       return false;
3015ffd83dbSDimitry Andric     buf.de.skip(buf.cursor, pos - buf.cursor.tell());
3020b57cec5SDimitry Andric   }
3030b57cec5SDimitry Andric 
3040b57cec5SDimitry Andric   return true;
3050b57cec5SDimitry Andric }
3060b57cec5SDimitry Andric 
print(raw_ostream & OS) const3070b57cec5SDimitry Andric void GCOVFile::print(raw_ostream &OS) const {
3085ffd83dbSDimitry Andric   for (const GCOVFunction &f : *this)
3095ffd83dbSDimitry Andric     f.print(OS);
3100b57cec5SDimitry Andric }
3110b57cec5SDimitry Andric 
3120b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
3130b57cec5SDimitry Andric /// dump - Dump GCOVFile content to dbgs() for debugging purposes.
dump() const3140b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); }
3150b57cec5SDimitry Andric #endif
3160b57cec5SDimitry Andric 
addNormalizedPathToMap(StringRef filename)31706c3fb27SDimitry Andric unsigned GCOVFile::addNormalizedPathToMap(StringRef filename) {
31806c3fb27SDimitry Andric   // unify filename, as the same path can have different form
31906c3fb27SDimitry Andric   SmallString<256> P(filename);
32006c3fb27SDimitry Andric   sys::path::remove_dots(P, true);
32106c3fb27SDimitry Andric   filename = P.str();
32206c3fb27SDimitry Andric 
32306c3fb27SDimitry Andric   auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size());
32406c3fb27SDimitry Andric   if (r.second)
32506c3fb27SDimitry Andric     filenames.emplace_back(filename);
32606c3fb27SDimitry Andric 
32706c3fb27SDimitry Andric   return r.first->second;
32806c3fb27SDimitry Andric }
32906c3fb27SDimitry Andric 
onTree() const330e8d8bef9SDimitry Andric bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; }
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3330b57cec5SDimitry Andric // GCOVFunction implementation.
3340b57cec5SDimitry Andric 
getName(bool demangle) const335e8d8bef9SDimitry Andric StringRef GCOVFunction::getName(bool demangle) const {
336e8d8bef9SDimitry Andric   if (!demangle)
337e8d8bef9SDimitry Andric     return Name;
338e8d8bef9SDimitry Andric   if (demangled.empty()) {
339e8d8bef9SDimitry Andric     do {
3405f757f3fSDimitry Andric       if (Name.starts_with("_Z")) {
341e8d8bef9SDimitry Andric         // Name is guaranteed to be NUL-terminated.
34206c3fb27SDimitry Andric         if (char *res = itaniumDemangle(Name.data())) {
343e8d8bef9SDimitry Andric           demangled = res;
344e8d8bef9SDimitry Andric           free(res);
345e8d8bef9SDimitry Andric           break;
346e8d8bef9SDimitry Andric         }
347e8d8bef9SDimitry Andric       }
348e8d8bef9SDimitry Andric       demangled = Name;
34904eeddc0SDimitry Andric     } while (false);
350e8d8bef9SDimitry Andric   }
351e8d8bef9SDimitry Andric   return demangled;
352e8d8bef9SDimitry Andric }
getFilename() const3535ffd83dbSDimitry Andric StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; }
3540b57cec5SDimitry Andric 
3550b57cec5SDimitry Andric /// getEntryCount - Get the number of times the function was called by
3560b57cec5SDimitry Andric /// retrieving the entry block's count.
getEntryCount() const3570b57cec5SDimitry Andric uint64_t GCOVFunction::getEntryCount() const {
358e8d8bef9SDimitry Andric   return blocks.front()->getCount();
3590b57cec5SDimitry Andric }
3600b57cec5SDimitry Andric 
getExitBlock() const361e8d8bef9SDimitry Andric GCOVBlock &GCOVFunction::getExitBlock() const {
362e8d8bef9SDimitry Andric   return file.getVersion() < GCOV::V408 ? *blocks.back() : *blocks[1];
363e8d8bef9SDimitry Andric }
364e8d8bef9SDimitry Andric 
365e8d8bef9SDimitry Andric // For each basic block, the sum of incoming edge counts equals the sum of
366e8d8bef9SDimitry Andric // outgoing edge counts by Kirchoff's circuit law. If the unmeasured arcs form a
367e8d8bef9SDimitry Andric // spanning tree, the count for each unmeasured arc (GCOV_ARC_ON_TREE) can be
3685f757f3fSDimitry Andric // uniquely identified. Use an iterative algorithm to decrease stack usage for
3695f757f3fSDimitry Andric // library users in threads. See the edge propagation algorithm in Optimally
3705f757f3fSDimitry Andric // Profiling and Tracing Programs, ACM Transactions on Programming Languages and
3715f757f3fSDimitry Andric // Systems, 1994.
propagateCounts(const GCOVBlock & v,GCOVArc * pred)3725f757f3fSDimitry Andric void GCOVFunction::propagateCounts(const GCOVBlock &v, GCOVArc *pred) {
3735f757f3fSDimitry Andric   struct Elem {
3745f757f3fSDimitry Andric     const GCOVBlock &v;
3755f757f3fSDimitry Andric     GCOVArc *pred;
3765f757f3fSDimitry Andric     bool inDst;
3775f757f3fSDimitry Andric     size_t i = 0;
378e8d8bef9SDimitry Andric     uint64_t excess = 0;
3795f757f3fSDimitry Andric   };
3805f757f3fSDimitry Andric 
3815f757f3fSDimitry Andric   SmallVector<Elem, 0> stack;
3825f757f3fSDimitry Andric   stack.push_back({v, pred, false});
3835f757f3fSDimitry Andric   for (;;) {
3845f757f3fSDimitry Andric     Elem &u = stack.back();
3855f757f3fSDimitry Andric     // If GCOV_ARC_ON_TREE edges do form a tree, visited is not needed;
3865f757f3fSDimitry Andric     // otherwise, this prevents infinite recursion for bad input.
3875f757f3fSDimitry Andric     if (u.i == 0 && !visited.insert(&u.v).second) {
3885f757f3fSDimitry Andric       stack.pop_back();
3895f757f3fSDimitry Andric       if (stack.empty())
3905f757f3fSDimitry Andric         break;
3915f757f3fSDimitry Andric       continue;
3925f757f3fSDimitry Andric     }
3935f757f3fSDimitry Andric     if (u.i < u.v.pred.size()) {
3945f757f3fSDimitry Andric       GCOVArc *e = u.v.pred[u.i++];
3955f757f3fSDimitry Andric       if (e != u.pred) {
3965f757f3fSDimitry Andric         if (e->onTree())
3975f757f3fSDimitry Andric           stack.push_back({e->src, e, /*inDst=*/false});
3985f757f3fSDimitry Andric         else
3995f757f3fSDimitry Andric           u.excess += e->count;
4005f757f3fSDimitry Andric       }
4015f757f3fSDimitry Andric     } else if (u.i < u.v.pred.size() + u.v.succ.size()) {
4025f757f3fSDimitry Andric       GCOVArc *e = u.v.succ[u.i++ - u.v.pred.size()];
4035f757f3fSDimitry Andric       if (e != u.pred) {
4045f757f3fSDimitry Andric         if (e->onTree())
4055f757f3fSDimitry Andric           stack.push_back({e->dst, e, /*inDst=*/true});
4065f757f3fSDimitry Andric         else
4075f757f3fSDimitry Andric           u.excess -= e->count;
4085f757f3fSDimitry Andric       }
4095f757f3fSDimitry Andric     } else {
4105f757f3fSDimitry Andric       uint64_t excess = u.excess;
4115f757f3fSDimitry Andric       if (static_cast<int64_t>(excess) < 0)
412e8d8bef9SDimitry Andric         excess = -excess;
4135f757f3fSDimitry Andric       if (u.pred)
4145f757f3fSDimitry Andric         u.pred->count = excess;
4155f757f3fSDimitry Andric       bool inDst = u.inDst;
4165f757f3fSDimitry Andric       stack.pop_back();
4175f757f3fSDimitry Andric       if (stack.empty())
4185f757f3fSDimitry Andric         break;
4195f757f3fSDimitry Andric       stack.back().excess += inDst ? -excess : excess;
4205f757f3fSDimitry Andric     }
4215f757f3fSDimitry Andric   }
4220b57cec5SDimitry Andric }
4230b57cec5SDimitry Andric 
print(raw_ostream & OS) const4240b57cec5SDimitry Andric void GCOVFunction::print(raw_ostream &OS) const {
4255ffd83dbSDimitry Andric   OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":"
4265ffd83dbSDimitry Andric      << startLine << "\n";
427e8d8bef9SDimitry Andric   for (const auto &Block : blocks)
4280b57cec5SDimitry Andric     Block->print(OS);
4290b57cec5SDimitry Andric }
4300b57cec5SDimitry Andric 
4310b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
4320b57cec5SDimitry Andric /// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
dump() const4330b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); }
4340b57cec5SDimitry Andric #endif
4350b57cec5SDimitry Andric 
4360b57cec5SDimitry Andric /// collectLineCounts - Collect line counts. This must be used after
4370b57cec5SDimitry Andric /// reading .gcno and .gcda files.
4380b57cec5SDimitry Andric 
4390b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4400b57cec5SDimitry Andric // GCOVBlock implementation.
4410b57cec5SDimitry Andric 
print(raw_ostream & OS) const4420b57cec5SDimitry Andric void GCOVBlock::print(raw_ostream &OS) const {
443e8d8bef9SDimitry Andric   OS << "Block : " << number << " Counter : " << count << "\n";
4445ffd83dbSDimitry Andric   if (!pred.empty()) {
4450b57cec5SDimitry Andric     OS << "\tSource Edges : ";
4465ffd83dbSDimitry Andric     for (const GCOVArc *Edge : pred)
447e8d8bef9SDimitry Andric       OS << Edge->src.number << " (" << Edge->count << "), ";
4480b57cec5SDimitry Andric     OS << "\n";
4490b57cec5SDimitry Andric   }
4505ffd83dbSDimitry Andric   if (!succ.empty()) {
4510b57cec5SDimitry Andric     OS << "\tDestination Edges : ";
452e8d8bef9SDimitry Andric     for (const GCOVArc *Edge : succ) {
453e8d8bef9SDimitry Andric       if (Edge->flags & GCOV_ARC_ON_TREE)
454e8d8bef9SDimitry Andric         OS << '*';
455e8d8bef9SDimitry Andric       OS << Edge->dst.number << " (" << Edge->count << "), ";
456e8d8bef9SDimitry Andric     }
4570b57cec5SDimitry Andric     OS << "\n";
4580b57cec5SDimitry Andric   }
459e8d8bef9SDimitry Andric   if (!lines.empty()) {
4600b57cec5SDimitry Andric     OS << "\tLines : ";
461e8d8bef9SDimitry Andric     for (uint32_t N : lines)
4620b57cec5SDimitry Andric       OS << (N) << ",";
4630b57cec5SDimitry Andric     OS << "\n";
4640b57cec5SDimitry Andric   }
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
4680b57cec5SDimitry Andric /// dump - Dump GCOVBlock content to dbgs() for debugging purposes.
dump() const4690b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); }
4700b57cec5SDimitry Andric #endif
4710b57cec5SDimitry Andric 
472e8d8bef9SDimitry Andric uint64_t
augmentOneCycle(GCOVBlock * src,std::vector<std::pair<GCOVBlock *,size_t>> & stack)473e8d8bef9SDimitry Andric GCOVBlock::augmentOneCycle(GCOVBlock *src,
474e8d8bef9SDimitry Andric                            std::vector<std::pair<GCOVBlock *, size_t>> &stack) {
475e8d8bef9SDimitry Andric   GCOVBlock *u;
476e8d8bef9SDimitry Andric   size_t i;
477e8d8bef9SDimitry Andric   stack.clear();
478e8d8bef9SDimitry Andric   stack.emplace_back(src, 0);
479e8d8bef9SDimitry Andric   src->incoming = (GCOVArc *)1; // Mark u available for cycle detection
480e8d8bef9SDimitry Andric   for (;;) {
481e8d8bef9SDimitry Andric     std::tie(u, i) = stack.back();
482e8d8bef9SDimitry Andric     if (i == u->succ.size()) {
483e8d8bef9SDimitry Andric       u->traversable = false;
484e8d8bef9SDimitry Andric       stack.pop_back();
485e8d8bef9SDimitry Andric       if (stack.empty())
486e8d8bef9SDimitry Andric         break;
4870b57cec5SDimitry Andric       continue;
4880b57cec5SDimitry Andric     }
489e8d8bef9SDimitry Andric     ++stack.back().second;
490e8d8bef9SDimitry Andric     GCOVArc *succ = u->succ[i];
491e8d8bef9SDimitry Andric     // Ignore saturated arcs (cycleCount has been reduced to 0) and visited
492e8d8bef9SDimitry Andric     // blocks. Ignore self arcs to guard against bad input (.gcno has no
493e8d8bef9SDimitry Andric     // self arcs).
494e8d8bef9SDimitry Andric     if (succ->cycleCount == 0 || !succ->dst.traversable || &succ->dst == u)
495e8d8bef9SDimitry Andric       continue;
496e8d8bef9SDimitry Andric     if (succ->dst.incoming == nullptr) {
497e8d8bef9SDimitry Andric       succ->dst.incoming = succ;
498e8d8bef9SDimitry Andric       stack.emplace_back(&succ->dst, 0);
4990b57cec5SDimitry Andric       continue;
5000b57cec5SDimitry Andric     }
501e8d8bef9SDimitry Andric     uint64_t minCount = succ->cycleCount;
502e8d8bef9SDimitry Andric     for (GCOVBlock *v = u;;) {
503e8d8bef9SDimitry Andric       minCount = std::min(minCount, v->incoming->cycleCount);
504e8d8bef9SDimitry Andric       v = &v->incoming->src;
505e8d8bef9SDimitry Andric       if (v == &succ->dst)
506e8d8bef9SDimitry Andric         break;
5070b57cec5SDimitry Andric     }
508e8d8bef9SDimitry Andric     succ->cycleCount -= minCount;
509e8d8bef9SDimitry Andric     for (GCOVBlock *v = u;;) {
510e8d8bef9SDimitry Andric       v->incoming->cycleCount -= minCount;
511e8d8bef9SDimitry Andric       v = &v->incoming->src;
512e8d8bef9SDimitry Andric       if (v == &succ->dst)
513e8d8bef9SDimitry Andric         break;
5140b57cec5SDimitry Andric     }
515e8d8bef9SDimitry Andric     return minCount;
516e8d8bef9SDimitry Andric   }
517e8d8bef9SDimitry Andric   return 0;
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric 
520e8d8bef9SDimitry Andric // Get the total execution count of loops among blocks on the same line.
521e8d8bef9SDimitry Andric // Assuming a reducible flow graph, the count is the sum of back edge counts.
522e8d8bef9SDimitry Andric // Identifying loops is complex, so we simply find cycles and perform cycle
523e8d8bef9SDimitry Andric // cancelling iteratively.
getCyclesCount(const BlockVector & blocks)524e8d8bef9SDimitry Andric uint64_t GCOVBlock::getCyclesCount(const BlockVector &blocks) {
525e8d8bef9SDimitry Andric   std::vector<std::pair<GCOVBlock *, size_t>> stack;
526e8d8bef9SDimitry Andric   uint64_t count = 0, d;
527e8d8bef9SDimitry Andric   for (;;) {
528e8d8bef9SDimitry Andric     // Make blocks on the line traversable and try finding a cycle.
529bdd1243dSDimitry Andric     for (const auto *b : blocks) {
530e8d8bef9SDimitry Andric       const_cast<GCOVBlock *>(b)->traversable = true;
531e8d8bef9SDimitry Andric       const_cast<GCOVBlock *>(b)->incoming = nullptr;
5320b57cec5SDimitry Andric     }
533e8d8bef9SDimitry Andric     d = 0;
534bdd1243dSDimitry Andric     for (const auto *block : blocks) {
535e8d8bef9SDimitry Andric       auto *b = const_cast<GCOVBlock *>(block);
536e8d8bef9SDimitry Andric       if (b->traversable && (d = augmentOneCycle(b, stack)) > 0)
537e8d8bef9SDimitry Andric         break;
5380b57cec5SDimitry Andric     }
539e8d8bef9SDimitry Andric     if (d == 0)
540e8d8bef9SDimitry Andric       break;
541e8d8bef9SDimitry Andric     count += d;
5420b57cec5SDimitry Andric   }
543e8d8bef9SDimitry Andric   // If there is no more loop, all traversable bits should have been cleared.
544e8d8bef9SDimitry Andric   // This property is needed by subsequent calls.
545bdd1243dSDimitry Andric   for (const auto *b : blocks) {
546e8d8bef9SDimitry Andric     assert(!b->traversable);
547e8d8bef9SDimitry Andric     (void)b;
5480b57cec5SDimitry Andric   }
549e8d8bef9SDimitry Andric   return count;
5500b57cec5SDimitry Andric }
5510b57cec5SDimitry Andric 
5520b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
5530b57cec5SDimitry Andric // FileInfo implementation.
5540b57cec5SDimitry Andric 
555e8d8bef9SDimitry Andric // Format dividend/divisor as a percentage. Return 1 if the result is greater
556e8d8bef9SDimitry Andric // than 0% and less than 1%.
formatPercentage(uint64_t dividend,uint64_t divisor)557e8d8bef9SDimitry Andric static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) {
558e8d8bef9SDimitry Andric   if (!dividend || !divisor)
5590b57cec5SDimitry Andric     return 0;
560e8d8bef9SDimitry Andric   dividend *= 100;
561e8d8bef9SDimitry Andric   return dividend < divisor ? 1 : dividend / divisor;
5620b57cec5SDimitry Andric }
5630b57cec5SDimitry Andric 
5640b57cec5SDimitry Andric // This custom division function mimics gcov's branch ouputs:
5650b57cec5SDimitry Andric //   - Round to closest whole number
5660b57cec5SDimitry Andric //   - Only output 0% or 100% if it's exactly that value
branchDiv(uint64_t Numerator,uint64_t Divisor)5670b57cec5SDimitry Andric static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) {
5680b57cec5SDimitry Andric   if (!Numerator)
5690b57cec5SDimitry Andric     return 0;
5700b57cec5SDimitry Andric   if (Numerator == Divisor)
5710b57cec5SDimitry Andric     return 100;
5720b57cec5SDimitry Andric 
5730b57cec5SDimitry Andric   uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
5740b57cec5SDimitry Andric   if (Res == 0)
5750b57cec5SDimitry Andric     return 1;
5760b57cec5SDimitry Andric   if (Res == 100)
5770b57cec5SDimitry Andric     return 99;
5780b57cec5SDimitry Andric   return Res;
5790b57cec5SDimitry Andric }
5800b57cec5SDimitry Andric 
5810b57cec5SDimitry Andric namespace {
5820b57cec5SDimitry Andric struct formatBranchInfo {
formatBranchInfo__anon4e48ce010311::formatBranchInfo5830b57cec5SDimitry Andric   formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total)
5840b57cec5SDimitry Andric       : Options(Options), Count(Count), Total(Total) {}
5850b57cec5SDimitry Andric 
print__anon4e48ce010311::formatBranchInfo5860b57cec5SDimitry Andric   void print(raw_ostream &OS) const {
5870b57cec5SDimitry Andric     if (!Total)
5880b57cec5SDimitry Andric       OS << "never executed";
5890b57cec5SDimitry Andric     else if (Options.BranchCount)
5900b57cec5SDimitry Andric       OS << "taken " << Count;
5910b57cec5SDimitry Andric     else
5920b57cec5SDimitry Andric       OS << "taken " << branchDiv(Count, Total) << "%";
5930b57cec5SDimitry Andric   }
5940b57cec5SDimitry Andric 
5950b57cec5SDimitry Andric   const GCOV::Options &Options;
5960b57cec5SDimitry Andric   uint64_t Count;
5970b57cec5SDimitry Andric   uint64_t Total;
5980b57cec5SDimitry Andric };
5990b57cec5SDimitry Andric 
operator <<(raw_ostream & OS,const formatBranchInfo & FBI)6000b57cec5SDimitry Andric static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
6010b57cec5SDimitry Andric   FBI.print(OS);
6020b57cec5SDimitry Andric   return OS;
6030b57cec5SDimitry Andric }
6040b57cec5SDimitry Andric 
6050b57cec5SDimitry Andric class LineConsumer {
6060b57cec5SDimitry Andric   std::unique_ptr<MemoryBuffer> Buffer;
6070b57cec5SDimitry Andric   StringRef Remaining;
6080b57cec5SDimitry Andric 
6090b57cec5SDimitry Andric public:
6105ffd83dbSDimitry Andric   LineConsumer() = default;
LineConsumer(StringRef Filename)6110b57cec5SDimitry Andric   LineConsumer(StringRef Filename) {
612e8d8bef9SDimitry Andric     // Open source files without requiring a NUL terminator. The concurrent
613e8d8bef9SDimitry Andric     // modification may nullify the NUL terminator condition.
6140b57cec5SDimitry Andric     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
615fe6060f1SDimitry Andric         MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/false,
616e8d8bef9SDimitry Andric                                      /*RequiresNullTerminator=*/false);
6170b57cec5SDimitry Andric     if (std::error_code EC = BufferOrErr.getError()) {
6180b57cec5SDimitry Andric       errs() << Filename << ": " << EC.message() << "\n";
6190b57cec5SDimitry Andric       Remaining = "";
6200b57cec5SDimitry Andric     } else {
6210b57cec5SDimitry Andric       Buffer = std::move(BufferOrErr.get());
6220b57cec5SDimitry Andric       Remaining = Buffer->getBuffer();
6230b57cec5SDimitry Andric     }
6240b57cec5SDimitry Andric   }
empty()6250b57cec5SDimitry Andric   bool empty() { return Remaining.empty(); }
printNext(raw_ostream & OS,uint32_t LineNum)6260b57cec5SDimitry Andric   void printNext(raw_ostream &OS, uint32_t LineNum) {
6270b57cec5SDimitry Andric     StringRef Line;
6280b57cec5SDimitry Andric     if (empty())
6290b57cec5SDimitry Andric       Line = "/*EOF*/";
6300b57cec5SDimitry Andric     else
6310b57cec5SDimitry Andric       std::tie(Line, Remaining) = Remaining.split("\n");
6320b57cec5SDimitry Andric     OS << format("%5u:", LineNum) << Line << "\n";
6330b57cec5SDimitry Andric   }
6340b57cec5SDimitry Andric };
6350b57cec5SDimitry Andric } // end anonymous namespace
6360b57cec5SDimitry Andric 
6370b57cec5SDimitry Andric /// Convert a path to a gcov filename. If PreservePaths is true, this
6380b57cec5SDimitry Andric /// translates "/" to "#", ".." to "^", and drops ".", to match gcov.
mangleCoveragePath(StringRef Filename,bool PreservePaths)6390b57cec5SDimitry Andric static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
6400b57cec5SDimitry Andric   if (!PreservePaths)
6410b57cec5SDimitry Andric     return sys::path::filename(Filename).str();
6420b57cec5SDimitry Andric 
6430b57cec5SDimitry Andric   // This behaviour is defined by gcov in terms of text replacements, so it's
6440b57cec5SDimitry Andric   // not likely to do anything useful on filesystems with different textual
6450b57cec5SDimitry Andric   // conventions.
6460b57cec5SDimitry Andric   llvm::SmallString<256> Result("");
6470b57cec5SDimitry Andric   StringRef::iterator I, S, E;
6480b57cec5SDimitry Andric   for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) {
6490b57cec5SDimitry Andric     if (*I != '/')
6500b57cec5SDimitry Andric       continue;
6510b57cec5SDimitry Andric 
6520b57cec5SDimitry Andric     if (I - S == 1 && *S == '.') {
6530b57cec5SDimitry Andric       // ".", the current directory, is skipped.
6540b57cec5SDimitry Andric     } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
6550b57cec5SDimitry Andric       // "..", the parent directory, is replaced with "^".
6560b57cec5SDimitry Andric       Result.append("^#");
6570b57cec5SDimitry Andric     } else {
6580b57cec5SDimitry Andric       if (S < I)
6590b57cec5SDimitry Andric         // Leave other components intact,
6600b57cec5SDimitry Andric         Result.append(S, I);
6610b57cec5SDimitry Andric       // And separate with "#".
6620b57cec5SDimitry Andric       Result.push_back('#');
6630b57cec5SDimitry Andric     }
6640b57cec5SDimitry Andric     S = I + 1;
6650b57cec5SDimitry Andric   }
6660b57cec5SDimitry Andric 
6670b57cec5SDimitry Andric   if (S < I)
6680b57cec5SDimitry Andric     Result.append(S, I);
6697a6dacacSDimitry Andric   return std::string(Result);
6700b57cec5SDimitry Andric }
6710b57cec5SDimitry Andric 
getCoveragePath(StringRef filename,StringRef mainFilename) const672e8d8bef9SDimitry Andric std::string Context::getCoveragePath(StringRef filename,
673e8d8bef9SDimitry Andric                                      StringRef mainFilename) const {
674e8d8bef9SDimitry Andric   if (options.NoOutput)
6750b57cec5SDimitry Andric     // This is probably a bug in gcov, but when -n is specified, paths aren't
6760b57cec5SDimitry Andric     // mangled at all, and the -l and -p options are ignored. Here, we do the
6770b57cec5SDimitry Andric     // same.
678e8d8bef9SDimitry Andric     return std::string(filename);
6790b57cec5SDimitry Andric 
6800b57cec5SDimitry Andric   std::string CoveragePath;
681e8d8bef9SDimitry Andric   if (options.LongFileNames && !filename.equals(mainFilename))
6820b57cec5SDimitry Andric     CoveragePath =
683e8d8bef9SDimitry Andric         mangleCoveragePath(mainFilename, options.PreservePaths) + "##";
684e8d8bef9SDimitry Andric   CoveragePath += mangleCoveragePath(filename, options.PreservePaths);
685e8d8bef9SDimitry Andric   if (options.HashFilenames) {
6860b57cec5SDimitry Andric     MD5 Hasher;
6870b57cec5SDimitry Andric     MD5::MD5Result Result;
688e8d8bef9SDimitry Andric     Hasher.update(filename.str());
6890b57cec5SDimitry Andric     Hasher.final(Result);
6905ffd83dbSDimitry Andric     CoveragePath += "##" + std::string(Result.digest());
6910b57cec5SDimitry Andric   }
6920b57cec5SDimitry Andric   CoveragePath += ".gcov";
6930b57cec5SDimitry Andric   return CoveragePath;
6940b57cec5SDimitry Andric }
6950b57cec5SDimitry Andric 
collectFunction(GCOVFunction & f,Summary & summary)696e8d8bef9SDimitry Andric void Context::collectFunction(GCOVFunction &f, Summary &summary) {
697e8d8bef9SDimitry Andric   SourceInfo &si = sources[f.srcIdx];
698e8d8bef9SDimitry Andric   if (f.startLine >= si.startLineToFunctions.size())
699e8d8bef9SDimitry Andric     si.startLineToFunctions.resize(f.startLine + 1);
700e8d8bef9SDimitry Andric   si.startLineToFunctions[f.startLine].push_back(&f);
70181ad6265SDimitry Andric   SmallSet<uint32_t, 16> lines;
70281ad6265SDimitry Andric   SmallSet<uint32_t, 16> linesExec;
703e8d8bef9SDimitry Andric   for (const GCOVBlock &b : f.blocksRange()) {
704e8d8bef9SDimitry Andric     if (b.lines.empty())
7050b57cec5SDimitry Andric       continue;
706e8d8bef9SDimitry Andric     uint32_t maxLineNum = *std::max_element(b.lines.begin(), b.lines.end());
707e8d8bef9SDimitry Andric     if (maxLineNum >= si.lines.size())
708e8d8bef9SDimitry Andric       si.lines.resize(maxLineNum + 1);
709e8d8bef9SDimitry Andric     for (uint32_t lineNum : b.lines) {
710e8d8bef9SDimitry Andric       LineInfo &line = si.lines[lineNum];
71181ad6265SDimitry Andric       if (lines.insert(lineNum).second)
712e8d8bef9SDimitry Andric         ++summary.lines;
71381ad6265SDimitry Andric       if (b.count && linesExec.insert(lineNum).second)
714e8d8bef9SDimitry Andric         ++summary.linesExec;
715e8d8bef9SDimitry Andric       line.exists = true;
716e8d8bef9SDimitry Andric       line.count += b.count;
717e8d8bef9SDimitry Andric       line.blocks.push_back(&b);
718e8d8bef9SDimitry Andric     }
719e8d8bef9SDimitry Andric   }
720e8d8bef9SDimitry Andric }
721e8d8bef9SDimitry Andric 
collectSourceLine(SourceInfo & si,Summary * summary,LineInfo & line,size_t lineNum) const722e8d8bef9SDimitry Andric void Context::collectSourceLine(SourceInfo &si, Summary *summary,
723e8d8bef9SDimitry Andric                                 LineInfo &line, size_t lineNum) const {
724e8d8bef9SDimitry Andric   uint64_t count = 0;
725e8d8bef9SDimitry Andric   for (const GCOVBlock *b : line.blocks) {
726e8d8bef9SDimitry Andric     if (b->number == 0) {
727e8d8bef9SDimitry Andric       // For nonstandard control flows, arcs into the exit block may be
728e8d8bef9SDimitry Andric       // duplicately counted (fork) or not be counted (abnormal exit), and thus
729e8d8bef9SDimitry Andric       // the (exit,entry) counter may be inaccurate. Count the entry block with
730e8d8bef9SDimitry Andric       // the outgoing arcs.
731e8d8bef9SDimitry Andric       for (const GCOVArc *arc : b->succ)
732e8d8bef9SDimitry Andric         count += arc->count;
733e8d8bef9SDimitry Andric     } else {
734e8d8bef9SDimitry Andric       // Add counts from predecessors that are not on the same line.
735e8d8bef9SDimitry Andric       for (const GCOVArc *arc : b->pred)
736e8d8bef9SDimitry Andric         if (!llvm::is_contained(line.blocks, &arc->src))
737e8d8bef9SDimitry Andric           count += arc->count;
738e8d8bef9SDimitry Andric     }
739e8d8bef9SDimitry Andric     for (GCOVArc *arc : b->succ)
740e8d8bef9SDimitry Andric       arc->cycleCount = arc->count;
741e8d8bef9SDimitry Andric   }
742e8d8bef9SDimitry Andric 
743e8d8bef9SDimitry Andric   count += GCOVBlock::getCyclesCount(line.blocks);
744e8d8bef9SDimitry Andric   line.count = count;
745e8d8bef9SDimitry Andric   if (line.exists) {
746e8d8bef9SDimitry Andric     ++summary->lines;
747e8d8bef9SDimitry Andric     if (line.count != 0)
748e8d8bef9SDimitry Andric       ++summary->linesExec;
749e8d8bef9SDimitry Andric   }
750e8d8bef9SDimitry Andric 
751e8d8bef9SDimitry Andric   if (options.BranchInfo)
752e8d8bef9SDimitry Andric     for (const GCOVBlock *b : line.blocks) {
753e8d8bef9SDimitry Andric       if (b->getLastLine() != lineNum)
754e8d8bef9SDimitry Andric         continue;
755e8d8bef9SDimitry Andric       int branches = 0, execBranches = 0, takenBranches = 0;
756e8d8bef9SDimitry Andric       for (const GCOVArc *arc : b->succ) {
757e8d8bef9SDimitry Andric         ++branches;
758e8d8bef9SDimitry Andric         if (count != 0)
759e8d8bef9SDimitry Andric           ++execBranches;
760e8d8bef9SDimitry Andric         if (arc->count != 0)
761e8d8bef9SDimitry Andric           ++takenBranches;
762e8d8bef9SDimitry Andric       }
763e8d8bef9SDimitry Andric       if (branches > 1) {
764e8d8bef9SDimitry Andric         summary->branches += branches;
765e8d8bef9SDimitry Andric         summary->branchesExec += execBranches;
766e8d8bef9SDimitry Andric         summary->branchesTaken += takenBranches;
767e8d8bef9SDimitry Andric       }
768e8d8bef9SDimitry Andric     }
769e8d8bef9SDimitry Andric }
770e8d8bef9SDimitry Andric 
collectSource(SourceInfo & si,Summary & summary) const771e8d8bef9SDimitry Andric void Context::collectSource(SourceInfo &si, Summary &summary) const {
772e8d8bef9SDimitry Andric   size_t lineNum = 0;
773e8d8bef9SDimitry Andric   for (LineInfo &line : si.lines) {
774e8d8bef9SDimitry Andric     collectSourceLine(si, &summary, line, lineNum);
775e8d8bef9SDimitry Andric     ++lineNum;
776e8d8bef9SDimitry Andric   }
777e8d8bef9SDimitry Andric }
778e8d8bef9SDimitry Andric 
annotateSource(SourceInfo & si,const GCOVFile & file,StringRef gcno,StringRef gcda,raw_ostream & os) const779e8d8bef9SDimitry Andric void Context::annotateSource(SourceInfo &si, const GCOVFile &file,
780e8d8bef9SDimitry Andric                              StringRef gcno, StringRef gcda,
781e8d8bef9SDimitry Andric                              raw_ostream &os) const {
782e8d8bef9SDimitry Andric   auto source =
783e8d8bef9SDimitry Andric       options.Intermediate ? LineConsumer() : LineConsumer(si.filename);
784e8d8bef9SDimitry Andric 
785e8d8bef9SDimitry Andric   os << "        -:    0:Source:" << si.displayName << '\n';
786e8d8bef9SDimitry Andric   os << "        -:    0:Graph:" << gcno << '\n';
787e8d8bef9SDimitry Andric   os << "        -:    0:Data:" << gcda << '\n';
788fe6060f1SDimitry Andric   os << "        -:    0:Runs:" << file.runCount << '\n';
789fe6060f1SDimitry Andric   if (file.version < GCOV::V900)
790fe6060f1SDimitry Andric     os << "        -:    0:Programs:" << file.programCount << '\n';
791e8d8bef9SDimitry Andric 
792e8d8bef9SDimitry Andric   for (size_t lineNum = 1; !source.empty(); ++lineNum) {
793e8d8bef9SDimitry Andric     if (lineNum >= si.lines.size()) {
794e8d8bef9SDimitry Andric       os << "        -:";
795e8d8bef9SDimitry Andric       source.printNext(os, lineNum);
796e8d8bef9SDimitry Andric       continue;
797e8d8bef9SDimitry Andric     }
798e8d8bef9SDimitry Andric 
799e8d8bef9SDimitry Andric     const LineInfo &line = si.lines[lineNum];
800e8d8bef9SDimitry Andric     if (options.BranchInfo && lineNum < si.startLineToFunctions.size())
801e8d8bef9SDimitry Andric       for (const auto *f : si.startLineToFunctions[lineNum])
802e8d8bef9SDimitry Andric         printFunctionDetails(*f, os);
803e8d8bef9SDimitry Andric     if (!line.exists)
804e8d8bef9SDimitry Andric       os << "        -:";
805e8d8bef9SDimitry Andric     else if (line.count == 0)
806e8d8bef9SDimitry Andric       os << "    #####:";
807e8d8bef9SDimitry Andric     else
808e8d8bef9SDimitry Andric       os << format("%9" PRIu64 ":", line.count);
809e8d8bef9SDimitry Andric     source.printNext(os, lineNum);
810e8d8bef9SDimitry Andric 
811e8d8bef9SDimitry Andric     uint32_t blockIdx = 0, edgeIdx = 0;
812e8d8bef9SDimitry Andric     for (const GCOVBlock *b : line.blocks) {
813e8d8bef9SDimitry Andric       if (b->getLastLine() != lineNum)
814e8d8bef9SDimitry Andric         continue;
815e8d8bef9SDimitry Andric       if (options.AllBlocks) {
816e8d8bef9SDimitry Andric         if (b->getCount() == 0)
817e8d8bef9SDimitry Andric           os << "    $$$$$:";
818e8d8bef9SDimitry Andric         else
819e8d8bef9SDimitry Andric           os << format("%9" PRIu64 ":", b->count);
820e8d8bef9SDimitry Andric         os << format("%5u-block %2u\n", lineNum, blockIdx++);
821e8d8bef9SDimitry Andric       }
822e8d8bef9SDimitry Andric       if (options.BranchInfo) {
823e8d8bef9SDimitry Andric         size_t NumEdges = b->succ.size();
8240b57cec5SDimitry Andric         if (NumEdges > 1)
825e8d8bef9SDimitry Andric           printBranchInfo(*b, edgeIdx, os);
826e8d8bef9SDimitry Andric         else if (options.UncondBranch && NumEdges == 1) {
827e8d8bef9SDimitry Andric           uint64_t count = b->succ[0]->count;
828e8d8bef9SDimitry Andric           os << format("unconditional %2u ", edgeIdx++)
829e8d8bef9SDimitry Andric              << formatBranchInfo(options, count, count) << '\n';
8300b57cec5SDimitry Andric         }
8310b57cec5SDimitry Andric       }
8320b57cec5SDimitry Andric     }
8330b57cec5SDimitry Andric   }
8340b57cec5SDimitry Andric }
8350b57cec5SDimitry Andric 
printSourceToIntermediate(const SourceInfo & si,raw_ostream & os) const836e8d8bef9SDimitry Andric void Context::printSourceToIntermediate(const SourceInfo &si,
837e8d8bef9SDimitry Andric                                         raw_ostream &os) const {
838e8d8bef9SDimitry Andric   os << "file:" << si.filename << '\n';
839e8d8bef9SDimitry Andric   for (const auto &fs : si.startLineToFunctions)
840e8d8bef9SDimitry Andric     for (const GCOVFunction *f : fs)
8415ffd83dbSDimitry Andric       os << "function:" << f->startLine << ',' << f->getEntryCount() << ','
842e8d8bef9SDimitry Andric          << f->getName(options.Demangle) << '\n';
843e8d8bef9SDimitry Andric   for (size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) {
844e8d8bef9SDimitry Andric     const LineInfo &line = si.lines[lineNum];
845e8d8bef9SDimitry Andric     if (line.blocks.empty())
8465ffd83dbSDimitry Andric       continue;
8475ffd83dbSDimitry Andric     // GCC 8 (r254259) added third third field for Ada:
8485ffd83dbSDimitry Andric     // lcount:<line>,<count>,<has_unexecuted_blocks>
8495ffd83dbSDimitry Andric     // We don't need the third field.
850e8d8bef9SDimitry Andric     os << "lcount:" << lineNum << ',' << line.count << '\n';
8515ffd83dbSDimitry Andric 
852e8d8bef9SDimitry Andric     if (!options.BranchInfo)
8535ffd83dbSDimitry Andric       continue;
854e8d8bef9SDimitry Andric     for (const GCOVBlock *b : line.blocks) {
855e8d8bef9SDimitry Andric       if (b->succ.size() < 2 || b->getLastLine() != lineNum)
8565ffd83dbSDimitry Andric         continue;
857e8d8bef9SDimitry Andric       for (const GCOVArc *arc : b->succ) {
858e8d8bef9SDimitry Andric         const char *type =
859e8d8bef9SDimitry Andric             b->getCount() ? arc->count ? "taken" : "nottaken" : "notexec";
860e8d8bef9SDimitry Andric         os << "branch:" << lineNum << ',' << type << '\n';
8615ffd83dbSDimitry Andric       }
8625ffd83dbSDimitry Andric     }
8635ffd83dbSDimitry Andric   }
8645ffd83dbSDimitry Andric }
8655ffd83dbSDimitry Andric 
print(StringRef filename,StringRef gcno,StringRef gcda,GCOVFile & file)866e8d8bef9SDimitry Andric void Context::print(StringRef filename, StringRef gcno, StringRef gcda,
867e8d8bef9SDimitry Andric                     GCOVFile &file) {
868e8d8bef9SDimitry Andric   for (StringRef filename : file.filenames) {
869e8d8bef9SDimitry Andric     sources.emplace_back(filename);
870e8d8bef9SDimitry Andric     SourceInfo &si = sources.back();
871e8d8bef9SDimitry Andric     si.displayName = si.filename;
872e8d8bef9SDimitry Andric     if (!options.SourcePrefix.empty() &&
873e8d8bef9SDimitry Andric         sys::path::replace_path_prefix(si.displayName, options.SourcePrefix,
874e8d8bef9SDimitry Andric                                        "") &&
875e8d8bef9SDimitry Andric         !si.displayName.empty()) {
876e8d8bef9SDimitry Andric       // TODO replace_path_prefix may strip the prefix even if the remaining
877e8d8bef9SDimitry Andric       // part does not start with a separator.
878e8d8bef9SDimitry Andric       if (sys::path::is_separator(si.displayName[0]))
879e8d8bef9SDimitry Andric         si.displayName.erase(si.displayName.begin());
8800b57cec5SDimitry Andric       else
881e8d8bef9SDimitry Andric         si.displayName = si.filename;
882e8d8bef9SDimitry Andric     }
883e8d8bef9SDimitry Andric     if (options.RelativeOnly && sys::path::is_absolute(si.displayName))
884e8d8bef9SDimitry Andric       si.ignored = true;
885e8d8bef9SDimitry Andric   }
886e8d8bef9SDimitry Andric 
887e8d8bef9SDimitry Andric   raw_ostream &os = llvm::outs();
888e8d8bef9SDimitry Andric   for (GCOVFunction &f : make_pointee_range(file.functions)) {
889e8d8bef9SDimitry Andric     Summary summary(f.getName(options.Demangle));
890e8d8bef9SDimitry Andric     collectFunction(f, summary);
891e8d8bef9SDimitry Andric     if (options.FuncCoverage && !options.UseStdout) {
892e8d8bef9SDimitry Andric       os << "Function '" << summary.Name << "'\n";
893e8d8bef9SDimitry Andric       printSummary(summary, os);
894e8d8bef9SDimitry Andric       os << '\n';
895e8d8bef9SDimitry Andric     }
896e8d8bef9SDimitry Andric   }
897e8d8bef9SDimitry Andric 
898e8d8bef9SDimitry Andric   for (SourceInfo &si : sources) {
899e8d8bef9SDimitry Andric     if (si.ignored)
900e8d8bef9SDimitry Andric       continue;
901e8d8bef9SDimitry Andric     Summary summary(si.displayName);
902e8d8bef9SDimitry Andric     collectSource(si, summary);
903e8d8bef9SDimitry Andric 
904e8d8bef9SDimitry Andric     // Print file summary unless -t is specified.
905e8d8bef9SDimitry Andric     std::string gcovName = getCoveragePath(si.filename, filename);
906e8d8bef9SDimitry Andric     if (!options.UseStdout) {
907e8d8bef9SDimitry Andric       os << "File '" << summary.Name << "'\n";
908e8d8bef9SDimitry Andric       printSummary(summary, os);
909e8d8bef9SDimitry Andric       if (!options.NoOutput && !options.Intermediate)
910e8d8bef9SDimitry Andric         os << "Creating '" << gcovName << "'\n";
911e8d8bef9SDimitry Andric       os << '\n';
912e8d8bef9SDimitry Andric     }
913e8d8bef9SDimitry Andric 
914e8d8bef9SDimitry Andric     if (options.NoOutput || options.Intermediate)
915e8d8bef9SDimitry Andric       continue;
916bdd1243dSDimitry Andric     std::optional<raw_fd_ostream> os;
917e8d8bef9SDimitry Andric     if (!options.UseStdout) {
918e8d8bef9SDimitry Andric       std::error_code ec;
919fe6060f1SDimitry Andric       os.emplace(gcovName, ec, sys::fs::OF_TextWithCRLF);
920e8d8bef9SDimitry Andric       if (ec) {
921e8d8bef9SDimitry Andric         errs() << ec.message() << '\n';
922e8d8bef9SDimitry Andric         continue;
923e8d8bef9SDimitry Andric       }
924e8d8bef9SDimitry Andric     }
925e8d8bef9SDimitry Andric     annotateSource(si, file, gcno, gcda,
926e8d8bef9SDimitry Andric                    options.UseStdout ? llvm::outs() : *os);
927e8d8bef9SDimitry Andric   }
928e8d8bef9SDimitry Andric 
929e8d8bef9SDimitry Andric   if (options.Intermediate && !options.NoOutput) {
930e8d8bef9SDimitry Andric     // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0
931e8d8bef9SDimitry Andric     // (PR GCC/82702). We create just one file.
932e8d8bef9SDimitry Andric     std::string outputPath(sys::path::filename(filename));
933e8d8bef9SDimitry Andric     std::error_code ec;
934fe6060f1SDimitry Andric     raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_TextWithCRLF);
935e8d8bef9SDimitry Andric     if (ec) {
936e8d8bef9SDimitry Andric       errs() << ec.message() << '\n';
937e8d8bef9SDimitry Andric       return;
938e8d8bef9SDimitry Andric     }
939e8d8bef9SDimitry Andric 
940e8d8bef9SDimitry Andric     for (const SourceInfo &si : sources)
941e8d8bef9SDimitry Andric       printSourceToIntermediate(si, os);
942e8d8bef9SDimitry Andric   }
943e8d8bef9SDimitry Andric }
944e8d8bef9SDimitry Andric 
printFunctionDetails(const GCOVFunction & f,raw_ostream & os) const945e8d8bef9SDimitry Andric void Context::printFunctionDetails(const GCOVFunction &f,
946e8d8bef9SDimitry Andric                                    raw_ostream &os) const {
947e8d8bef9SDimitry Andric   const uint64_t entryCount = f.getEntryCount();
948e8d8bef9SDimitry Andric   uint32_t blocksExec = 0;
949e8d8bef9SDimitry Andric   const GCOVBlock &exitBlock = f.getExitBlock();
950e8d8bef9SDimitry Andric   uint64_t exitCount = 0;
951e8d8bef9SDimitry Andric   for (const GCOVArc *arc : exitBlock.pred)
952e8d8bef9SDimitry Andric     exitCount += arc->count;
953e8d8bef9SDimitry Andric   for (const GCOVBlock &b : f.blocksRange())
954e8d8bef9SDimitry Andric     if (b.number != 0 && &b != &exitBlock && b.getCount())
955e8d8bef9SDimitry Andric       ++blocksExec;
956e8d8bef9SDimitry Andric 
957e8d8bef9SDimitry Andric   os << "function " << f.getName(options.Demangle) << " called " << entryCount
958e8d8bef9SDimitry Andric      << " returned " << formatPercentage(exitCount, entryCount)
959e8d8bef9SDimitry Andric      << "% blocks executed "
960e8d8bef9SDimitry Andric      << formatPercentage(blocksExec, f.blocks.size() - 2) << "%\n";
9610b57cec5SDimitry Andric }
9620b57cec5SDimitry Andric 
9630b57cec5SDimitry Andric /// printBranchInfo - Print conditional branch probabilities.
printBranchInfo(const GCOVBlock & Block,uint32_t & edgeIdx,raw_ostream & os) const964e8d8bef9SDimitry Andric void Context::printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx,
965e8d8bef9SDimitry Andric                               raw_ostream &os) const {
966e8d8bef9SDimitry Andric   uint64_t total = 0;
967e8d8bef9SDimitry Andric   for (const GCOVArc *arc : Block.dsts())
968e8d8bef9SDimitry Andric     total += arc->count;
969e8d8bef9SDimitry Andric   for (const GCOVArc *arc : Block.dsts())
970e8d8bef9SDimitry Andric     os << format("branch %2u ", edgeIdx++)
971e8d8bef9SDimitry Andric        << formatBranchInfo(options, arc->count, total) << '\n';
9720b57cec5SDimitry Andric }
9730b57cec5SDimitry Andric 
printSummary(const Summary & summary,raw_ostream & os) const974e8d8bef9SDimitry Andric void Context::printSummary(const Summary &summary, raw_ostream &os) const {
975e8d8bef9SDimitry Andric   os << format("Lines executed:%.2f%% of %" PRIu64 "\n",
976e8d8bef9SDimitry Andric                double(summary.linesExec) * 100 / summary.lines, summary.lines);
977e8d8bef9SDimitry Andric   if (options.BranchInfo) {
978e8d8bef9SDimitry Andric     if (summary.branches == 0) {
979e8d8bef9SDimitry Andric       os << "No branches\n";
9800b57cec5SDimitry Andric     } else {
981e8d8bef9SDimitry Andric       os << format("Branches executed:%.2f%% of %" PRIu64 "\n",
982e8d8bef9SDimitry Andric                    double(summary.branchesExec) * 100 / summary.branches,
983e8d8bef9SDimitry Andric                    summary.branches);
984e8d8bef9SDimitry Andric       os << format("Taken at least once:%.2f%% of %" PRIu64 "\n",
985e8d8bef9SDimitry Andric                    double(summary.branchesTaken) * 100 / summary.branches,
986e8d8bef9SDimitry Andric                    summary.branches);
9870b57cec5SDimitry Andric     }
988e8d8bef9SDimitry Andric     os << "No calls\n";
9890b57cec5SDimitry Andric   }
9900b57cec5SDimitry Andric }
9910b57cec5SDimitry Andric 
gcovOneInput(const GCOV::Options & options,StringRef filename,StringRef gcno,StringRef gcda,GCOVFile & file)992e8d8bef9SDimitry Andric void llvm::gcovOneInput(const GCOV::Options &options, StringRef filename,
993e8d8bef9SDimitry Andric                         StringRef gcno, StringRef gcda, GCOVFile &file) {
994e8d8bef9SDimitry Andric   Context fi(options);
995e8d8bef9SDimitry Andric   fi.print(filename, gcno, gcda, file);
9960b57cec5SDimitry Andric }
997