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 } 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 3070b57cec5SDimitry 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. 3140b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } 3150b57cec5SDimitry Andric #endif 3160b57cec5SDimitry Andric 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 330e8d8bef9SDimitry Andric bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; } 3310b57cec5SDimitry Andric 3320b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 3330b57cec5SDimitry Andric // GCOVFunction implementation. 3340b57cec5SDimitry Andric 335e8d8bef9SDimitry 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 } 3535ffd83dbSDimitry 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. 3570b57cec5SDimitry Andric uint64_t GCOVFunction::getEntryCount() const { 358e8d8bef9SDimitry Andric return blocks.front()->getCount(); 3590b57cec5SDimitry Andric } 3600b57cec5SDimitry Andric 361e8d8bef9SDimitry 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. 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 4240b57cec5SDimitry 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. 4330b57cec5SDimitry 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 4420b57cec5SDimitry 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. 4690b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } 4700b57cec5SDimitry Andric #endif 4710b57cec5SDimitry Andric 472e8d8bef9SDimitry Andric uint64_t 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. 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%. 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 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 { 5830b57cec5SDimitry Andric formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) 5840b57cec5SDimitry Andric : Options(Options), Count(Count), Total(Total) {} 5850b57cec5SDimitry Andric 5860b57cec5SDimitry 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 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; 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 } 6250b57cec5SDimitry Andric bool empty() { return Remaining.empty(); } 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. 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); 6695ffd83dbSDimitry Andric return std::string(Result.str()); 6700b57cec5SDimitry Andric } 6710b57cec5SDimitry Andric 672e8d8bef9SDimitry 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 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 722e8d8bef9SDimitry 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 771e8d8bef9SDimitry 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 779e8d8bef9SDimitry 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 836e8d8bef9SDimitry 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 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 945e8d8bef9SDimitry 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. 964e8d8bef9SDimitry 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 974e8d8bef9SDimitry 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 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