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 { 47e8d8bef9SDimitry 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; 69e8d8bef9SDimitry Andric SourceInfo(StringRef filename) : filename(filename) {} 70e8d8bef9SDimitry Andric }; 71e8d8bef9SDimitry Andric 72e8d8bef9SDimitry Andric class Context { 73e8d8bef9SDimitry Andric public: 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. 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(). 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 // clang<11 uses a fake 4.2 format which sets length to 9. 2415ffd83dbSDimitry Andric if (length == 9) 242fe6060f1SDimitry Andric buf.readInt(runCount); 2435ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) { 2445ffd83dbSDimitry Andric // clang<11 uses a fake 4.2 format which sets length to 0. 2455ffd83dbSDimitry Andric if (length > 0) { 2465ffd83dbSDimitry Andric buf.readInt(dummy); 2475ffd83dbSDimitry Andric buf.readInt(dummy); 248fe6060f1SDimitry Andric buf.readInt(runCount); 2490b57cec5SDimitry Andric } 250fe6060f1SDimitry Andric ++programCount; 2515ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_FUNCTION) { 2525ffd83dbSDimitry Andric if (length == 0) // Placeholder 2535ffd83dbSDimitry Andric continue; 2545ffd83dbSDimitry Andric // As of GCC 10, GCOV_TAG_FUNCTION_LENGTH has never been larger than 3. 2555ffd83dbSDimitry Andric // However, clang<11 uses a fake 4.2 format which may set length larger 2565ffd83dbSDimitry Andric // than 3. 2575ffd83dbSDimitry Andric if (length < 2 || !buf.readInt(ident)) 2585ffd83dbSDimitry Andric return false; 259fe6060f1SDimitry Andric auto It = identToFunction.find(ident); 2605ffd83dbSDimitry Andric uint32_t linenoChecksum, cfgChecksum = 0; 2615ffd83dbSDimitry Andric buf.readInt(linenoChecksum); 262fe6060f1SDimitry Andric if (version >= GCOV::V407) 2635ffd83dbSDimitry Andric buf.readInt(cfgChecksum); 264fe6060f1SDimitry Andric if (It != identToFunction.end()) { 2655ffd83dbSDimitry Andric fn = It->second; 2665ffd83dbSDimitry Andric if (linenoChecksum != fn->linenoChecksum || 2675ffd83dbSDimitry Andric cfgChecksum != fn->cfgChecksum) { 2685ffd83dbSDimitry Andric errs() << fn->Name 2695ffd83dbSDimitry Andric << format(": checksum mismatch, (%u, %u) != (%u, %u)\n", 2705ffd83dbSDimitry Andric linenoChecksum, cfgChecksum, fn->linenoChecksum, 2715ffd83dbSDimitry Andric fn->cfgChecksum); 2725ffd83dbSDimitry Andric return false; 2735ffd83dbSDimitry Andric } 2745ffd83dbSDimitry Andric } 2755ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) { 276fe6060f1SDimitry Andric uint32_t expected = 2 * fn->arcs.size(); 277fe6060f1SDimitry Andric if (version >= GCOV::V1200) 278fe6060f1SDimitry Andric expected *= 4; 279fe6060f1SDimitry Andric if (length != expected) { 2805ffd83dbSDimitry Andric errs() << fn->Name 2815ffd83dbSDimitry Andric << format( 2825ffd83dbSDimitry Andric ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n", 283fe6060f1SDimitry Andric length, expected); 2845ffd83dbSDimitry Andric return false; 2855ffd83dbSDimitry Andric } 2865ffd83dbSDimitry Andric for (std::unique_ptr<GCOVArc> &arc : fn->arcs) { 287e8d8bef9SDimitry Andric if (!buf.readInt64(arc->count)) 2885ffd83dbSDimitry Andric return false; 289e8d8bef9SDimitry Andric arc->src.count += arc->count; 290e8d8bef9SDimitry Andric } 291e8d8bef9SDimitry Andric 292e8d8bef9SDimitry Andric if (fn->blocks.size() >= 2) { 293e8d8bef9SDimitry Andric GCOVBlock &src = *fn->blocks[0]; 294e8d8bef9SDimitry Andric GCOVBlock &sink = 295fe6060f1SDimitry Andric version < GCOV::V408 ? *fn->blocks.back() : *fn->blocks[1]; 296e8d8bef9SDimitry Andric auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE); 297e8d8bef9SDimitry Andric sink.addDstEdge(arc.get()); 298e8d8bef9SDimitry Andric src.addSrcEdge(arc.get()); 299e8d8bef9SDimitry Andric fn->treeArcs.push_back(std::move(arc)); 300e8d8bef9SDimitry Andric 301e8d8bef9SDimitry Andric for (GCOVBlock &block : fn->blocksRange()) 302e8d8bef9SDimitry Andric fn->propagateCounts(block, nullptr); 303e8d8bef9SDimitry Andric for (size_t i = fn->treeArcs.size() - 1; i; --i) 304e8d8bef9SDimitry Andric fn->treeArcs[i - 1]->src.count += fn->treeArcs[i - 1]->count; 3055ffd83dbSDimitry Andric } 3065ffd83dbSDimitry Andric } 307fe6060f1SDimitry Andric pos += version >= GCOV::V1200 ? length : 4 * length; 3085ffd83dbSDimitry Andric if (pos < buf.cursor.tell()) 3095ffd83dbSDimitry Andric return false; 3105ffd83dbSDimitry Andric buf.de.skip(buf.cursor, pos - buf.cursor.tell()); 3110b57cec5SDimitry Andric } 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric return true; 3140b57cec5SDimitry Andric } 3150b57cec5SDimitry Andric 3160b57cec5SDimitry Andric void GCOVFile::print(raw_ostream &OS) const { 3175ffd83dbSDimitry Andric for (const GCOVFunction &f : *this) 3185ffd83dbSDimitry Andric f.print(OS); 3190b57cec5SDimitry Andric } 3200b57cec5SDimitry Andric 3210b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 3220b57cec5SDimitry Andric /// dump - Dump GCOVFile content to dbgs() for debugging purposes. 3230b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } 3240b57cec5SDimitry Andric #endif 3250b57cec5SDimitry Andric 32606c3fb27SDimitry Andric unsigned GCOVFile::addNormalizedPathToMap(StringRef filename) { 32706c3fb27SDimitry Andric // unify filename, as the same path can have different form 32806c3fb27SDimitry Andric SmallString<256> P(filename); 32906c3fb27SDimitry Andric sys::path::remove_dots(P, true); 33006c3fb27SDimitry Andric filename = P.str(); 33106c3fb27SDimitry Andric 33206c3fb27SDimitry Andric auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size()); 33306c3fb27SDimitry Andric if (r.second) 33406c3fb27SDimitry Andric filenames.emplace_back(filename); 33506c3fb27SDimitry Andric 33606c3fb27SDimitry Andric return r.first->second; 33706c3fb27SDimitry Andric } 33806c3fb27SDimitry Andric 339e8d8bef9SDimitry Andric bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; } 3400b57cec5SDimitry Andric 3410b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 3420b57cec5SDimitry Andric // GCOVFunction implementation. 3430b57cec5SDimitry Andric 344e8d8bef9SDimitry Andric StringRef GCOVFunction::getName(bool demangle) const { 345e8d8bef9SDimitry Andric if (!demangle) 346e8d8bef9SDimitry Andric return Name; 347e8d8bef9SDimitry Andric if (demangled.empty()) { 348e8d8bef9SDimitry Andric do { 349e8d8bef9SDimitry Andric if (Name.startswith("_Z")) { 350e8d8bef9SDimitry Andric // Name is guaranteed to be NUL-terminated. 35106c3fb27SDimitry Andric if (char *res = itaniumDemangle(Name.data())) { 352e8d8bef9SDimitry Andric demangled = res; 353e8d8bef9SDimitry Andric free(res); 354e8d8bef9SDimitry Andric break; 355e8d8bef9SDimitry Andric } 356e8d8bef9SDimitry Andric } 357e8d8bef9SDimitry Andric demangled = Name; 35804eeddc0SDimitry Andric } while (false); 359e8d8bef9SDimitry Andric } 360e8d8bef9SDimitry Andric return demangled; 361e8d8bef9SDimitry Andric } 3625ffd83dbSDimitry Andric StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; } 3630b57cec5SDimitry Andric 3640b57cec5SDimitry Andric /// getEntryCount - Get the number of times the function was called by 3650b57cec5SDimitry Andric /// retrieving the entry block's count. 3660b57cec5SDimitry Andric uint64_t GCOVFunction::getEntryCount() const { 367e8d8bef9SDimitry Andric return blocks.front()->getCount(); 3680b57cec5SDimitry Andric } 3690b57cec5SDimitry Andric 370e8d8bef9SDimitry Andric GCOVBlock &GCOVFunction::getExitBlock() const { 371e8d8bef9SDimitry Andric return file.getVersion() < GCOV::V408 ? *blocks.back() : *blocks[1]; 372e8d8bef9SDimitry Andric } 373e8d8bef9SDimitry Andric 374e8d8bef9SDimitry Andric // For each basic block, the sum of incoming edge counts equals the sum of 375e8d8bef9SDimitry Andric // outgoing edge counts by Kirchoff's circuit law. If the unmeasured arcs form a 376e8d8bef9SDimitry Andric // spanning tree, the count for each unmeasured arc (GCOV_ARC_ON_TREE) can be 377e8d8bef9SDimitry Andric // uniquely identified. 378e8d8bef9SDimitry Andric uint64_t GCOVFunction::propagateCounts(const GCOVBlock &v, GCOVArc *pred) { 379e8d8bef9SDimitry Andric // If GCOV_ARC_ON_TREE edges do form a tree, visited is not needed; otherwise 380e8d8bef9SDimitry Andric // this prevents infinite recursion. 381e8d8bef9SDimitry Andric if (!visited.insert(&v).second) 382e8d8bef9SDimitry Andric return 0; 383e8d8bef9SDimitry Andric 384e8d8bef9SDimitry Andric uint64_t excess = 0; 385e8d8bef9SDimitry Andric for (GCOVArc *e : v.srcs()) 386e8d8bef9SDimitry Andric if (e != pred) 387e8d8bef9SDimitry Andric excess += e->onTree() ? propagateCounts(e->src, e) : e->count; 388e8d8bef9SDimitry Andric for (GCOVArc *e : v.dsts()) 389e8d8bef9SDimitry Andric if (e != pred) 390e8d8bef9SDimitry Andric excess -= e->onTree() ? propagateCounts(e->dst, e) : e->count; 391e8d8bef9SDimitry Andric if (int64_t(excess) < 0) 392e8d8bef9SDimitry Andric excess = -excess; 393e8d8bef9SDimitry Andric if (pred) 394e8d8bef9SDimitry Andric pred->count = excess; 395e8d8bef9SDimitry Andric return excess; 3960b57cec5SDimitry Andric } 3970b57cec5SDimitry Andric 3980b57cec5SDimitry Andric void GCOVFunction::print(raw_ostream &OS) const { 3995ffd83dbSDimitry Andric OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":" 4005ffd83dbSDimitry Andric << startLine << "\n"; 401e8d8bef9SDimitry Andric for (const auto &Block : blocks) 4020b57cec5SDimitry Andric Block->print(OS); 4030b57cec5SDimitry Andric } 4040b57cec5SDimitry Andric 4050b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 4060b57cec5SDimitry Andric /// dump - Dump GCOVFunction content to dbgs() for debugging purposes. 4070b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } 4080b57cec5SDimitry Andric #endif 4090b57cec5SDimitry Andric 4100b57cec5SDimitry Andric /// collectLineCounts - Collect line counts. This must be used after 4110b57cec5SDimitry Andric /// reading .gcno and .gcda files. 4120b57cec5SDimitry Andric 4130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 4140b57cec5SDimitry Andric // GCOVBlock implementation. 4150b57cec5SDimitry Andric 4160b57cec5SDimitry Andric void GCOVBlock::print(raw_ostream &OS) const { 417e8d8bef9SDimitry Andric OS << "Block : " << number << " Counter : " << count << "\n"; 4185ffd83dbSDimitry Andric if (!pred.empty()) { 4190b57cec5SDimitry Andric OS << "\tSource Edges : "; 4205ffd83dbSDimitry Andric for (const GCOVArc *Edge : pred) 421e8d8bef9SDimitry Andric OS << Edge->src.number << " (" << Edge->count << "), "; 4220b57cec5SDimitry Andric OS << "\n"; 4230b57cec5SDimitry Andric } 4245ffd83dbSDimitry Andric if (!succ.empty()) { 4250b57cec5SDimitry Andric OS << "\tDestination Edges : "; 426e8d8bef9SDimitry Andric for (const GCOVArc *Edge : succ) { 427e8d8bef9SDimitry Andric if (Edge->flags & GCOV_ARC_ON_TREE) 428e8d8bef9SDimitry Andric OS << '*'; 429e8d8bef9SDimitry Andric OS << Edge->dst.number << " (" << Edge->count << "), "; 430e8d8bef9SDimitry Andric } 4310b57cec5SDimitry Andric OS << "\n"; 4320b57cec5SDimitry Andric } 433e8d8bef9SDimitry Andric if (!lines.empty()) { 4340b57cec5SDimitry Andric OS << "\tLines : "; 435e8d8bef9SDimitry Andric for (uint32_t N : lines) 4360b57cec5SDimitry Andric OS << (N) << ","; 4370b57cec5SDimitry Andric OS << "\n"; 4380b57cec5SDimitry Andric } 4390b57cec5SDimitry Andric } 4400b57cec5SDimitry Andric 4410b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 4420b57cec5SDimitry Andric /// dump - Dump GCOVBlock content to dbgs() for debugging purposes. 4430b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } 4440b57cec5SDimitry Andric #endif 4450b57cec5SDimitry Andric 446e8d8bef9SDimitry Andric uint64_t 447e8d8bef9SDimitry Andric GCOVBlock::augmentOneCycle(GCOVBlock *src, 448e8d8bef9SDimitry Andric std::vector<std::pair<GCOVBlock *, size_t>> &stack) { 449e8d8bef9SDimitry Andric GCOVBlock *u; 450e8d8bef9SDimitry Andric size_t i; 451e8d8bef9SDimitry Andric stack.clear(); 452e8d8bef9SDimitry Andric stack.emplace_back(src, 0); 453e8d8bef9SDimitry Andric src->incoming = (GCOVArc *)1; // Mark u available for cycle detection 454e8d8bef9SDimitry Andric for (;;) { 455e8d8bef9SDimitry Andric std::tie(u, i) = stack.back(); 456e8d8bef9SDimitry Andric if (i == u->succ.size()) { 457e8d8bef9SDimitry Andric u->traversable = false; 458e8d8bef9SDimitry Andric stack.pop_back(); 459e8d8bef9SDimitry Andric if (stack.empty()) 460e8d8bef9SDimitry Andric break; 4610b57cec5SDimitry Andric continue; 4620b57cec5SDimitry Andric } 463e8d8bef9SDimitry Andric ++stack.back().second; 464e8d8bef9SDimitry Andric GCOVArc *succ = u->succ[i]; 465e8d8bef9SDimitry Andric // Ignore saturated arcs (cycleCount has been reduced to 0) and visited 466e8d8bef9SDimitry Andric // blocks. Ignore self arcs to guard against bad input (.gcno has no 467e8d8bef9SDimitry Andric // self arcs). 468e8d8bef9SDimitry Andric if (succ->cycleCount == 0 || !succ->dst.traversable || &succ->dst == u) 469e8d8bef9SDimitry Andric continue; 470e8d8bef9SDimitry Andric if (succ->dst.incoming == nullptr) { 471e8d8bef9SDimitry Andric succ->dst.incoming = succ; 472e8d8bef9SDimitry Andric stack.emplace_back(&succ->dst, 0); 4730b57cec5SDimitry Andric continue; 4740b57cec5SDimitry Andric } 475e8d8bef9SDimitry Andric uint64_t minCount = succ->cycleCount; 476e8d8bef9SDimitry Andric for (GCOVBlock *v = u;;) { 477e8d8bef9SDimitry Andric minCount = std::min(minCount, v->incoming->cycleCount); 478e8d8bef9SDimitry Andric v = &v->incoming->src; 479e8d8bef9SDimitry Andric if (v == &succ->dst) 480e8d8bef9SDimitry Andric break; 4810b57cec5SDimitry Andric } 482e8d8bef9SDimitry Andric succ->cycleCount -= minCount; 483e8d8bef9SDimitry Andric for (GCOVBlock *v = u;;) { 484e8d8bef9SDimitry Andric v->incoming->cycleCount -= minCount; 485e8d8bef9SDimitry Andric v = &v->incoming->src; 486e8d8bef9SDimitry Andric if (v == &succ->dst) 487e8d8bef9SDimitry Andric break; 4880b57cec5SDimitry Andric } 489e8d8bef9SDimitry Andric return minCount; 490e8d8bef9SDimitry Andric } 491e8d8bef9SDimitry Andric return 0; 4920b57cec5SDimitry Andric } 4930b57cec5SDimitry Andric 494e8d8bef9SDimitry Andric // Get the total execution count of loops among blocks on the same line. 495e8d8bef9SDimitry Andric // Assuming a reducible flow graph, the count is the sum of back edge counts. 496e8d8bef9SDimitry Andric // Identifying loops is complex, so we simply find cycles and perform cycle 497e8d8bef9SDimitry Andric // cancelling iteratively. 498e8d8bef9SDimitry Andric uint64_t GCOVBlock::getCyclesCount(const BlockVector &blocks) { 499e8d8bef9SDimitry Andric std::vector<std::pair<GCOVBlock *, size_t>> stack; 500e8d8bef9SDimitry Andric uint64_t count = 0, d; 501e8d8bef9SDimitry Andric for (;;) { 502e8d8bef9SDimitry Andric // Make blocks on the line traversable and try finding a cycle. 503bdd1243dSDimitry Andric for (const auto *b : blocks) { 504e8d8bef9SDimitry Andric const_cast<GCOVBlock *>(b)->traversable = true; 505e8d8bef9SDimitry Andric const_cast<GCOVBlock *>(b)->incoming = nullptr; 5060b57cec5SDimitry Andric } 507e8d8bef9SDimitry Andric d = 0; 508bdd1243dSDimitry Andric for (const auto *block : blocks) { 509e8d8bef9SDimitry Andric auto *b = const_cast<GCOVBlock *>(block); 510e8d8bef9SDimitry Andric if (b->traversable && (d = augmentOneCycle(b, stack)) > 0) 511e8d8bef9SDimitry Andric break; 5120b57cec5SDimitry Andric } 513e8d8bef9SDimitry Andric if (d == 0) 514e8d8bef9SDimitry Andric break; 515e8d8bef9SDimitry Andric count += d; 5160b57cec5SDimitry Andric } 517e8d8bef9SDimitry Andric // If there is no more loop, all traversable bits should have been cleared. 518e8d8bef9SDimitry Andric // This property is needed by subsequent calls. 519bdd1243dSDimitry Andric for (const auto *b : blocks) { 520e8d8bef9SDimitry Andric assert(!b->traversable); 521e8d8bef9SDimitry Andric (void)b; 5220b57cec5SDimitry Andric } 523e8d8bef9SDimitry Andric return count; 5240b57cec5SDimitry Andric } 5250b57cec5SDimitry Andric 5260b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 5270b57cec5SDimitry Andric // FileInfo implementation. 5280b57cec5SDimitry Andric 529e8d8bef9SDimitry Andric // Format dividend/divisor as a percentage. Return 1 if the result is greater 530e8d8bef9SDimitry Andric // than 0% and less than 1%. 531e8d8bef9SDimitry Andric static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) { 532e8d8bef9SDimitry Andric if (!dividend || !divisor) 5330b57cec5SDimitry Andric return 0; 534e8d8bef9SDimitry Andric dividend *= 100; 535e8d8bef9SDimitry Andric return dividend < divisor ? 1 : dividend / divisor; 5360b57cec5SDimitry Andric } 5370b57cec5SDimitry Andric 5380b57cec5SDimitry Andric // This custom division function mimics gcov's branch ouputs: 5390b57cec5SDimitry Andric // - Round to closest whole number 5400b57cec5SDimitry Andric // - Only output 0% or 100% if it's exactly that value 5410b57cec5SDimitry Andric static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) { 5420b57cec5SDimitry Andric if (!Numerator) 5430b57cec5SDimitry Andric return 0; 5440b57cec5SDimitry Andric if (Numerator == Divisor) 5450b57cec5SDimitry Andric return 100; 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor; 5480b57cec5SDimitry Andric if (Res == 0) 5490b57cec5SDimitry Andric return 1; 5500b57cec5SDimitry Andric if (Res == 100) 5510b57cec5SDimitry Andric return 99; 5520b57cec5SDimitry Andric return Res; 5530b57cec5SDimitry Andric } 5540b57cec5SDimitry Andric 5550b57cec5SDimitry Andric namespace { 5560b57cec5SDimitry Andric struct formatBranchInfo { 5570b57cec5SDimitry Andric formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) 5580b57cec5SDimitry Andric : Options(Options), Count(Count), Total(Total) {} 5590b57cec5SDimitry Andric 5600b57cec5SDimitry Andric void print(raw_ostream &OS) const { 5610b57cec5SDimitry Andric if (!Total) 5620b57cec5SDimitry Andric OS << "never executed"; 5630b57cec5SDimitry Andric else if (Options.BranchCount) 5640b57cec5SDimitry Andric OS << "taken " << Count; 5650b57cec5SDimitry Andric else 5660b57cec5SDimitry Andric OS << "taken " << branchDiv(Count, Total) << "%"; 5670b57cec5SDimitry Andric } 5680b57cec5SDimitry Andric 5690b57cec5SDimitry Andric const GCOV::Options &Options; 5700b57cec5SDimitry Andric uint64_t Count; 5710b57cec5SDimitry Andric uint64_t Total; 5720b57cec5SDimitry Andric }; 5730b57cec5SDimitry Andric 5740b57cec5SDimitry Andric static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { 5750b57cec5SDimitry Andric FBI.print(OS); 5760b57cec5SDimitry Andric return OS; 5770b57cec5SDimitry Andric } 5780b57cec5SDimitry Andric 5790b57cec5SDimitry Andric class LineConsumer { 5800b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> Buffer; 5810b57cec5SDimitry Andric StringRef Remaining; 5820b57cec5SDimitry Andric 5830b57cec5SDimitry Andric public: 5845ffd83dbSDimitry Andric LineConsumer() = default; 5850b57cec5SDimitry Andric LineConsumer(StringRef Filename) { 586e8d8bef9SDimitry Andric // Open source files without requiring a NUL terminator. The concurrent 587e8d8bef9SDimitry Andric // modification may nullify the NUL terminator condition. 5880b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = 589fe6060f1SDimitry Andric MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/false, 590e8d8bef9SDimitry Andric /*RequiresNullTerminator=*/false); 5910b57cec5SDimitry Andric if (std::error_code EC = BufferOrErr.getError()) { 5920b57cec5SDimitry Andric errs() << Filename << ": " << EC.message() << "\n"; 5930b57cec5SDimitry Andric Remaining = ""; 5940b57cec5SDimitry Andric } else { 5950b57cec5SDimitry Andric Buffer = std::move(BufferOrErr.get()); 5960b57cec5SDimitry Andric Remaining = Buffer->getBuffer(); 5970b57cec5SDimitry Andric } 5980b57cec5SDimitry Andric } 5990b57cec5SDimitry Andric bool empty() { return Remaining.empty(); } 6000b57cec5SDimitry Andric void printNext(raw_ostream &OS, uint32_t LineNum) { 6010b57cec5SDimitry Andric StringRef Line; 6020b57cec5SDimitry Andric if (empty()) 6030b57cec5SDimitry Andric Line = "/*EOF*/"; 6040b57cec5SDimitry Andric else 6050b57cec5SDimitry Andric std::tie(Line, Remaining) = Remaining.split("\n"); 6060b57cec5SDimitry Andric OS << format("%5u:", LineNum) << Line << "\n"; 6070b57cec5SDimitry Andric } 6080b57cec5SDimitry Andric }; 6090b57cec5SDimitry Andric } // end anonymous namespace 6100b57cec5SDimitry Andric 6110b57cec5SDimitry Andric /// Convert a path to a gcov filename. If PreservePaths is true, this 6120b57cec5SDimitry Andric /// translates "/" to "#", ".." to "^", and drops ".", to match gcov. 6130b57cec5SDimitry Andric static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { 6140b57cec5SDimitry Andric if (!PreservePaths) 6150b57cec5SDimitry Andric return sys::path::filename(Filename).str(); 6160b57cec5SDimitry Andric 6170b57cec5SDimitry Andric // This behaviour is defined by gcov in terms of text replacements, so it's 6180b57cec5SDimitry Andric // not likely to do anything useful on filesystems with different textual 6190b57cec5SDimitry Andric // conventions. 6200b57cec5SDimitry Andric llvm::SmallString<256> Result(""); 6210b57cec5SDimitry Andric StringRef::iterator I, S, E; 6220b57cec5SDimitry Andric for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { 6230b57cec5SDimitry Andric if (*I != '/') 6240b57cec5SDimitry Andric continue; 6250b57cec5SDimitry Andric 6260b57cec5SDimitry Andric if (I - S == 1 && *S == '.') { 6270b57cec5SDimitry Andric // ".", the current directory, is skipped. 6280b57cec5SDimitry Andric } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { 6290b57cec5SDimitry Andric // "..", the parent directory, is replaced with "^". 6300b57cec5SDimitry Andric Result.append("^#"); 6310b57cec5SDimitry Andric } else { 6320b57cec5SDimitry Andric if (S < I) 6330b57cec5SDimitry Andric // Leave other components intact, 6340b57cec5SDimitry Andric Result.append(S, I); 6350b57cec5SDimitry Andric // And separate with "#". 6360b57cec5SDimitry Andric Result.push_back('#'); 6370b57cec5SDimitry Andric } 6380b57cec5SDimitry Andric S = I + 1; 6390b57cec5SDimitry Andric } 6400b57cec5SDimitry Andric 6410b57cec5SDimitry Andric if (S < I) 6420b57cec5SDimitry Andric Result.append(S, I); 6435ffd83dbSDimitry Andric return std::string(Result.str()); 6440b57cec5SDimitry Andric } 6450b57cec5SDimitry Andric 646e8d8bef9SDimitry Andric std::string Context::getCoveragePath(StringRef filename, 647e8d8bef9SDimitry Andric StringRef mainFilename) const { 648e8d8bef9SDimitry Andric if (options.NoOutput) 6490b57cec5SDimitry Andric // This is probably a bug in gcov, but when -n is specified, paths aren't 6500b57cec5SDimitry Andric // mangled at all, and the -l and -p options are ignored. Here, we do the 6510b57cec5SDimitry Andric // same. 652e8d8bef9SDimitry Andric return std::string(filename); 6530b57cec5SDimitry Andric 6540b57cec5SDimitry Andric std::string CoveragePath; 655e8d8bef9SDimitry Andric if (options.LongFileNames && !filename.equals(mainFilename)) 6560b57cec5SDimitry Andric CoveragePath = 657e8d8bef9SDimitry Andric mangleCoveragePath(mainFilename, options.PreservePaths) + "##"; 658e8d8bef9SDimitry Andric CoveragePath += mangleCoveragePath(filename, options.PreservePaths); 659e8d8bef9SDimitry Andric if (options.HashFilenames) { 6600b57cec5SDimitry Andric MD5 Hasher; 6610b57cec5SDimitry Andric MD5::MD5Result Result; 662e8d8bef9SDimitry Andric Hasher.update(filename.str()); 6630b57cec5SDimitry Andric Hasher.final(Result); 6645ffd83dbSDimitry Andric CoveragePath += "##" + std::string(Result.digest()); 6650b57cec5SDimitry Andric } 6660b57cec5SDimitry Andric CoveragePath += ".gcov"; 6670b57cec5SDimitry Andric return CoveragePath; 6680b57cec5SDimitry Andric } 6690b57cec5SDimitry Andric 670e8d8bef9SDimitry Andric void Context::collectFunction(GCOVFunction &f, Summary &summary) { 671e8d8bef9SDimitry Andric SourceInfo &si = sources[f.srcIdx]; 672e8d8bef9SDimitry Andric if (f.startLine >= si.startLineToFunctions.size()) 673e8d8bef9SDimitry Andric si.startLineToFunctions.resize(f.startLine + 1); 674e8d8bef9SDimitry Andric si.startLineToFunctions[f.startLine].push_back(&f); 67581ad6265SDimitry Andric SmallSet<uint32_t, 16> lines; 67681ad6265SDimitry Andric SmallSet<uint32_t, 16> linesExec; 677e8d8bef9SDimitry Andric for (const GCOVBlock &b : f.blocksRange()) { 678e8d8bef9SDimitry Andric if (b.lines.empty()) 6790b57cec5SDimitry Andric continue; 680e8d8bef9SDimitry Andric uint32_t maxLineNum = *std::max_element(b.lines.begin(), b.lines.end()); 681e8d8bef9SDimitry Andric if (maxLineNum >= si.lines.size()) 682e8d8bef9SDimitry Andric si.lines.resize(maxLineNum + 1); 683e8d8bef9SDimitry Andric for (uint32_t lineNum : b.lines) { 684e8d8bef9SDimitry Andric LineInfo &line = si.lines[lineNum]; 68581ad6265SDimitry Andric if (lines.insert(lineNum).second) 686e8d8bef9SDimitry Andric ++summary.lines; 68781ad6265SDimitry Andric if (b.count && linesExec.insert(lineNum).second) 688e8d8bef9SDimitry Andric ++summary.linesExec; 689e8d8bef9SDimitry Andric line.exists = true; 690e8d8bef9SDimitry Andric line.count += b.count; 691e8d8bef9SDimitry Andric line.blocks.push_back(&b); 692e8d8bef9SDimitry Andric } 693e8d8bef9SDimitry Andric } 694e8d8bef9SDimitry Andric } 695e8d8bef9SDimitry Andric 696e8d8bef9SDimitry Andric void Context::collectSourceLine(SourceInfo &si, Summary *summary, 697e8d8bef9SDimitry Andric LineInfo &line, size_t lineNum) const { 698e8d8bef9SDimitry Andric uint64_t count = 0; 699e8d8bef9SDimitry Andric for (const GCOVBlock *b : line.blocks) { 700e8d8bef9SDimitry Andric if (b->number == 0) { 701e8d8bef9SDimitry Andric // For nonstandard control flows, arcs into the exit block may be 702e8d8bef9SDimitry Andric // duplicately counted (fork) or not be counted (abnormal exit), and thus 703e8d8bef9SDimitry Andric // the (exit,entry) counter may be inaccurate. Count the entry block with 704e8d8bef9SDimitry Andric // the outgoing arcs. 705e8d8bef9SDimitry Andric for (const GCOVArc *arc : b->succ) 706e8d8bef9SDimitry Andric count += arc->count; 707e8d8bef9SDimitry Andric } else { 708e8d8bef9SDimitry Andric // Add counts from predecessors that are not on the same line. 709e8d8bef9SDimitry Andric for (const GCOVArc *arc : b->pred) 710e8d8bef9SDimitry Andric if (!llvm::is_contained(line.blocks, &arc->src)) 711e8d8bef9SDimitry Andric count += arc->count; 712e8d8bef9SDimitry Andric } 713e8d8bef9SDimitry Andric for (GCOVArc *arc : b->succ) 714e8d8bef9SDimitry Andric arc->cycleCount = arc->count; 715e8d8bef9SDimitry Andric } 716e8d8bef9SDimitry Andric 717e8d8bef9SDimitry Andric count += GCOVBlock::getCyclesCount(line.blocks); 718e8d8bef9SDimitry Andric line.count = count; 719e8d8bef9SDimitry Andric if (line.exists) { 720e8d8bef9SDimitry Andric ++summary->lines; 721e8d8bef9SDimitry Andric if (line.count != 0) 722e8d8bef9SDimitry Andric ++summary->linesExec; 723e8d8bef9SDimitry Andric } 724e8d8bef9SDimitry Andric 725e8d8bef9SDimitry Andric if (options.BranchInfo) 726e8d8bef9SDimitry Andric for (const GCOVBlock *b : line.blocks) { 727e8d8bef9SDimitry Andric if (b->getLastLine() != lineNum) 728e8d8bef9SDimitry Andric continue; 729e8d8bef9SDimitry Andric int branches = 0, execBranches = 0, takenBranches = 0; 730e8d8bef9SDimitry Andric for (const GCOVArc *arc : b->succ) { 731e8d8bef9SDimitry Andric ++branches; 732e8d8bef9SDimitry Andric if (count != 0) 733e8d8bef9SDimitry Andric ++execBranches; 734e8d8bef9SDimitry Andric if (arc->count != 0) 735e8d8bef9SDimitry Andric ++takenBranches; 736e8d8bef9SDimitry Andric } 737e8d8bef9SDimitry Andric if (branches > 1) { 738e8d8bef9SDimitry Andric summary->branches += branches; 739e8d8bef9SDimitry Andric summary->branchesExec += execBranches; 740e8d8bef9SDimitry Andric summary->branchesTaken += takenBranches; 741e8d8bef9SDimitry Andric } 742e8d8bef9SDimitry Andric } 743e8d8bef9SDimitry Andric } 744e8d8bef9SDimitry Andric 745e8d8bef9SDimitry Andric void Context::collectSource(SourceInfo &si, Summary &summary) const { 746e8d8bef9SDimitry Andric size_t lineNum = 0; 747e8d8bef9SDimitry Andric for (LineInfo &line : si.lines) { 748e8d8bef9SDimitry Andric collectSourceLine(si, &summary, line, lineNum); 749e8d8bef9SDimitry Andric ++lineNum; 750e8d8bef9SDimitry Andric } 751e8d8bef9SDimitry Andric } 752e8d8bef9SDimitry Andric 753e8d8bef9SDimitry Andric void Context::annotateSource(SourceInfo &si, const GCOVFile &file, 754e8d8bef9SDimitry Andric StringRef gcno, StringRef gcda, 755e8d8bef9SDimitry Andric raw_ostream &os) const { 756e8d8bef9SDimitry Andric auto source = 757e8d8bef9SDimitry Andric options.Intermediate ? LineConsumer() : LineConsumer(si.filename); 758e8d8bef9SDimitry Andric 759e8d8bef9SDimitry Andric os << " -: 0:Source:" << si.displayName << '\n'; 760e8d8bef9SDimitry Andric os << " -: 0:Graph:" << gcno << '\n'; 761e8d8bef9SDimitry Andric os << " -: 0:Data:" << gcda << '\n'; 762fe6060f1SDimitry Andric os << " -: 0:Runs:" << file.runCount << '\n'; 763fe6060f1SDimitry Andric if (file.version < GCOV::V900) 764fe6060f1SDimitry Andric os << " -: 0:Programs:" << file.programCount << '\n'; 765e8d8bef9SDimitry Andric 766e8d8bef9SDimitry Andric for (size_t lineNum = 1; !source.empty(); ++lineNum) { 767e8d8bef9SDimitry Andric if (lineNum >= si.lines.size()) { 768e8d8bef9SDimitry Andric os << " -:"; 769e8d8bef9SDimitry Andric source.printNext(os, lineNum); 770e8d8bef9SDimitry Andric continue; 771e8d8bef9SDimitry Andric } 772e8d8bef9SDimitry Andric 773e8d8bef9SDimitry Andric const LineInfo &line = si.lines[lineNum]; 774e8d8bef9SDimitry Andric if (options.BranchInfo && lineNum < si.startLineToFunctions.size()) 775e8d8bef9SDimitry Andric for (const auto *f : si.startLineToFunctions[lineNum]) 776e8d8bef9SDimitry Andric printFunctionDetails(*f, os); 777e8d8bef9SDimitry Andric if (!line.exists) 778e8d8bef9SDimitry Andric os << " -:"; 779e8d8bef9SDimitry Andric else if (line.count == 0) 780e8d8bef9SDimitry Andric os << " #####:"; 781e8d8bef9SDimitry Andric else 782e8d8bef9SDimitry Andric os << format("%9" PRIu64 ":", line.count); 783e8d8bef9SDimitry Andric source.printNext(os, lineNum); 784e8d8bef9SDimitry Andric 785e8d8bef9SDimitry Andric uint32_t blockIdx = 0, edgeIdx = 0; 786e8d8bef9SDimitry Andric for (const GCOVBlock *b : line.blocks) { 787e8d8bef9SDimitry Andric if (b->getLastLine() != lineNum) 788e8d8bef9SDimitry Andric continue; 789e8d8bef9SDimitry Andric if (options.AllBlocks) { 790e8d8bef9SDimitry Andric if (b->getCount() == 0) 791e8d8bef9SDimitry Andric os << " $$$$$:"; 792e8d8bef9SDimitry Andric else 793e8d8bef9SDimitry Andric os << format("%9" PRIu64 ":", b->count); 794e8d8bef9SDimitry Andric os << format("%5u-block %2u\n", lineNum, blockIdx++); 795e8d8bef9SDimitry Andric } 796e8d8bef9SDimitry Andric if (options.BranchInfo) { 797e8d8bef9SDimitry Andric size_t NumEdges = b->succ.size(); 7980b57cec5SDimitry Andric if (NumEdges > 1) 799e8d8bef9SDimitry Andric printBranchInfo(*b, edgeIdx, os); 800e8d8bef9SDimitry Andric else if (options.UncondBranch && NumEdges == 1) { 801e8d8bef9SDimitry Andric uint64_t count = b->succ[0]->count; 802e8d8bef9SDimitry Andric os << format("unconditional %2u ", edgeIdx++) 803e8d8bef9SDimitry Andric << formatBranchInfo(options, count, count) << '\n'; 8040b57cec5SDimitry Andric } 8050b57cec5SDimitry Andric } 8060b57cec5SDimitry Andric } 8070b57cec5SDimitry Andric } 8080b57cec5SDimitry Andric } 8090b57cec5SDimitry Andric 810e8d8bef9SDimitry Andric void Context::printSourceToIntermediate(const SourceInfo &si, 811e8d8bef9SDimitry Andric raw_ostream &os) const { 812e8d8bef9SDimitry Andric os << "file:" << si.filename << '\n'; 813e8d8bef9SDimitry Andric for (const auto &fs : si.startLineToFunctions) 814e8d8bef9SDimitry Andric for (const GCOVFunction *f : fs) 8155ffd83dbSDimitry Andric os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' 816e8d8bef9SDimitry Andric << f->getName(options.Demangle) << '\n'; 817e8d8bef9SDimitry Andric for (size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) { 818e8d8bef9SDimitry Andric const LineInfo &line = si.lines[lineNum]; 819e8d8bef9SDimitry Andric if (line.blocks.empty()) 8205ffd83dbSDimitry Andric continue; 8215ffd83dbSDimitry Andric // GCC 8 (r254259) added third third field for Ada: 8225ffd83dbSDimitry Andric // lcount:<line>,<count>,<has_unexecuted_blocks> 8235ffd83dbSDimitry Andric // We don't need the third field. 824e8d8bef9SDimitry Andric os << "lcount:" << lineNum << ',' << line.count << '\n'; 8255ffd83dbSDimitry Andric 826e8d8bef9SDimitry Andric if (!options.BranchInfo) 8275ffd83dbSDimitry Andric continue; 828e8d8bef9SDimitry Andric for (const GCOVBlock *b : line.blocks) { 829e8d8bef9SDimitry Andric if (b->succ.size() < 2 || b->getLastLine() != lineNum) 8305ffd83dbSDimitry Andric continue; 831e8d8bef9SDimitry Andric for (const GCOVArc *arc : b->succ) { 832e8d8bef9SDimitry Andric const char *type = 833e8d8bef9SDimitry Andric b->getCount() ? arc->count ? "taken" : "nottaken" : "notexec"; 834e8d8bef9SDimitry Andric os << "branch:" << lineNum << ',' << type << '\n'; 8355ffd83dbSDimitry Andric } 8365ffd83dbSDimitry Andric } 8375ffd83dbSDimitry Andric } 8385ffd83dbSDimitry Andric } 8395ffd83dbSDimitry Andric 840e8d8bef9SDimitry Andric void Context::print(StringRef filename, StringRef gcno, StringRef gcda, 841e8d8bef9SDimitry Andric GCOVFile &file) { 842e8d8bef9SDimitry Andric for (StringRef filename : file.filenames) { 843e8d8bef9SDimitry Andric sources.emplace_back(filename); 844e8d8bef9SDimitry Andric SourceInfo &si = sources.back(); 845e8d8bef9SDimitry Andric si.displayName = si.filename; 846e8d8bef9SDimitry Andric if (!options.SourcePrefix.empty() && 847e8d8bef9SDimitry Andric sys::path::replace_path_prefix(si.displayName, options.SourcePrefix, 848e8d8bef9SDimitry Andric "") && 849e8d8bef9SDimitry Andric !si.displayName.empty()) { 850e8d8bef9SDimitry Andric // TODO replace_path_prefix may strip the prefix even if the remaining 851e8d8bef9SDimitry Andric // part does not start with a separator. 852e8d8bef9SDimitry Andric if (sys::path::is_separator(si.displayName[0])) 853e8d8bef9SDimitry Andric si.displayName.erase(si.displayName.begin()); 8540b57cec5SDimitry Andric else 855e8d8bef9SDimitry Andric si.displayName = si.filename; 856e8d8bef9SDimitry Andric } 857e8d8bef9SDimitry Andric if (options.RelativeOnly && sys::path::is_absolute(si.displayName)) 858e8d8bef9SDimitry Andric si.ignored = true; 859e8d8bef9SDimitry Andric } 860e8d8bef9SDimitry Andric 861e8d8bef9SDimitry Andric raw_ostream &os = llvm::outs(); 862e8d8bef9SDimitry Andric for (GCOVFunction &f : make_pointee_range(file.functions)) { 863e8d8bef9SDimitry Andric Summary summary(f.getName(options.Demangle)); 864e8d8bef9SDimitry Andric collectFunction(f, summary); 865e8d8bef9SDimitry Andric if (options.FuncCoverage && !options.UseStdout) { 866e8d8bef9SDimitry Andric os << "Function '" << summary.Name << "'\n"; 867e8d8bef9SDimitry Andric printSummary(summary, os); 868e8d8bef9SDimitry Andric os << '\n'; 869e8d8bef9SDimitry Andric } 870e8d8bef9SDimitry Andric } 871e8d8bef9SDimitry Andric 872e8d8bef9SDimitry Andric for (SourceInfo &si : sources) { 873e8d8bef9SDimitry Andric if (si.ignored) 874e8d8bef9SDimitry Andric continue; 875e8d8bef9SDimitry Andric Summary summary(si.displayName); 876e8d8bef9SDimitry Andric collectSource(si, summary); 877e8d8bef9SDimitry Andric 878e8d8bef9SDimitry Andric // Print file summary unless -t is specified. 879e8d8bef9SDimitry Andric std::string gcovName = getCoveragePath(si.filename, filename); 880e8d8bef9SDimitry Andric if (!options.UseStdout) { 881e8d8bef9SDimitry Andric os << "File '" << summary.Name << "'\n"; 882e8d8bef9SDimitry Andric printSummary(summary, os); 883e8d8bef9SDimitry Andric if (!options.NoOutput && !options.Intermediate) 884e8d8bef9SDimitry Andric os << "Creating '" << gcovName << "'\n"; 885e8d8bef9SDimitry Andric os << '\n'; 886e8d8bef9SDimitry Andric } 887e8d8bef9SDimitry Andric 888e8d8bef9SDimitry Andric if (options.NoOutput || options.Intermediate) 889e8d8bef9SDimitry Andric continue; 890bdd1243dSDimitry Andric std::optional<raw_fd_ostream> os; 891e8d8bef9SDimitry Andric if (!options.UseStdout) { 892e8d8bef9SDimitry Andric std::error_code ec; 893fe6060f1SDimitry Andric os.emplace(gcovName, ec, sys::fs::OF_TextWithCRLF); 894e8d8bef9SDimitry Andric if (ec) { 895e8d8bef9SDimitry Andric errs() << ec.message() << '\n'; 896e8d8bef9SDimitry Andric continue; 897e8d8bef9SDimitry Andric } 898e8d8bef9SDimitry Andric } 899e8d8bef9SDimitry Andric annotateSource(si, file, gcno, gcda, 900e8d8bef9SDimitry Andric options.UseStdout ? llvm::outs() : *os); 901e8d8bef9SDimitry Andric } 902e8d8bef9SDimitry Andric 903e8d8bef9SDimitry Andric if (options.Intermediate && !options.NoOutput) { 904e8d8bef9SDimitry Andric // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0 905e8d8bef9SDimitry Andric // (PR GCC/82702). We create just one file. 906e8d8bef9SDimitry Andric std::string outputPath(sys::path::filename(filename)); 907e8d8bef9SDimitry Andric std::error_code ec; 908fe6060f1SDimitry Andric raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_TextWithCRLF); 909e8d8bef9SDimitry Andric if (ec) { 910e8d8bef9SDimitry Andric errs() << ec.message() << '\n'; 911e8d8bef9SDimitry Andric return; 912e8d8bef9SDimitry Andric } 913e8d8bef9SDimitry Andric 914e8d8bef9SDimitry Andric for (const SourceInfo &si : sources) 915e8d8bef9SDimitry Andric printSourceToIntermediate(si, os); 916e8d8bef9SDimitry Andric } 917e8d8bef9SDimitry Andric } 918e8d8bef9SDimitry Andric 919e8d8bef9SDimitry Andric void Context::printFunctionDetails(const GCOVFunction &f, 920e8d8bef9SDimitry Andric raw_ostream &os) const { 921e8d8bef9SDimitry Andric const uint64_t entryCount = f.getEntryCount(); 922e8d8bef9SDimitry Andric uint32_t blocksExec = 0; 923e8d8bef9SDimitry Andric const GCOVBlock &exitBlock = f.getExitBlock(); 924e8d8bef9SDimitry Andric uint64_t exitCount = 0; 925e8d8bef9SDimitry Andric for (const GCOVArc *arc : exitBlock.pred) 926e8d8bef9SDimitry Andric exitCount += arc->count; 927e8d8bef9SDimitry Andric for (const GCOVBlock &b : f.blocksRange()) 928e8d8bef9SDimitry Andric if (b.number != 0 && &b != &exitBlock && b.getCount()) 929e8d8bef9SDimitry Andric ++blocksExec; 930e8d8bef9SDimitry Andric 931e8d8bef9SDimitry Andric os << "function " << f.getName(options.Demangle) << " called " << entryCount 932e8d8bef9SDimitry Andric << " returned " << formatPercentage(exitCount, entryCount) 933e8d8bef9SDimitry Andric << "% blocks executed " 934e8d8bef9SDimitry Andric << formatPercentage(blocksExec, f.blocks.size() - 2) << "%\n"; 9350b57cec5SDimitry Andric } 9360b57cec5SDimitry Andric 9370b57cec5SDimitry Andric /// printBranchInfo - Print conditional branch probabilities. 938e8d8bef9SDimitry Andric void Context::printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, 939e8d8bef9SDimitry Andric raw_ostream &os) const { 940e8d8bef9SDimitry Andric uint64_t total = 0; 941e8d8bef9SDimitry Andric for (const GCOVArc *arc : Block.dsts()) 942e8d8bef9SDimitry Andric total += arc->count; 943e8d8bef9SDimitry Andric for (const GCOVArc *arc : Block.dsts()) 944e8d8bef9SDimitry Andric os << format("branch %2u ", edgeIdx++) 945e8d8bef9SDimitry Andric << formatBranchInfo(options, arc->count, total) << '\n'; 9460b57cec5SDimitry Andric } 9470b57cec5SDimitry Andric 948e8d8bef9SDimitry Andric void Context::printSummary(const Summary &summary, raw_ostream &os) const { 949e8d8bef9SDimitry Andric os << format("Lines executed:%.2f%% of %" PRIu64 "\n", 950e8d8bef9SDimitry Andric double(summary.linesExec) * 100 / summary.lines, summary.lines); 951e8d8bef9SDimitry Andric if (options.BranchInfo) { 952e8d8bef9SDimitry Andric if (summary.branches == 0) { 953e8d8bef9SDimitry Andric os << "No branches\n"; 9540b57cec5SDimitry Andric } else { 955e8d8bef9SDimitry Andric os << format("Branches executed:%.2f%% of %" PRIu64 "\n", 956e8d8bef9SDimitry Andric double(summary.branchesExec) * 100 / summary.branches, 957e8d8bef9SDimitry Andric summary.branches); 958e8d8bef9SDimitry Andric os << format("Taken at least once:%.2f%% of %" PRIu64 "\n", 959e8d8bef9SDimitry Andric double(summary.branchesTaken) * 100 / summary.branches, 960e8d8bef9SDimitry Andric summary.branches); 9610b57cec5SDimitry Andric } 962e8d8bef9SDimitry Andric os << "No calls\n"; 9630b57cec5SDimitry Andric } 9640b57cec5SDimitry Andric } 9650b57cec5SDimitry Andric 966e8d8bef9SDimitry Andric void llvm::gcovOneInput(const GCOV::Options &options, StringRef filename, 967e8d8bef9SDimitry Andric StringRef gcno, StringRef gcda, GCOVFile &file) { 968e8d8bef9SDimitry Andric Context fi(options); 969e8d8bef9SDimitry Andric fi.print(filename, gcno, gcda, file); 9700b57cec5SDimitry Andric } 971