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" 160b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h" 170b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 180b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 190b57cec5SDimitry Andric #include "llvm/Support/Format.h" 200b57cec5SDimitry Andric #include "llvm/Support/Path.h" 210b57cec5SDimitry Andric #include "llvm/Support/MD5.h" 220b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 230b57cec5SDimitry Andric #include <algorithm> 240b57cec5SDimitry Andric #include <system_error> 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric using namespace llvm; 270b57cec5SDimitry Andric 285ffd83dbSDimitry Andric enum : uint32_t { 295ffd83dbSDimitry Andric GCOV_ARC_ON_TREE = 1 << 0, 305ffd83dbSDimitry Andric GCOV_ARC_FALLTHROUGH = 1 << 2, 315ffd83dbSDimitry Andric 325ffd83dbSDimitry Andric GCOV_TAG_FUNCTION = 0x01000000, 335ffd83dbSDimitry Andric GCOV_TAG_BLOCKS = 0x01410000, 345ffd83dbSDimitry Andric GCOV_TAG_ARCS = 0x01430000, 355ffd83dbSDimitry Andric GCOV_TAG_LINES = 0x01450000, 365ffd83dbSDimitry Andric GCOV_TAG_COUNTER_ARCS = 0x01a10000, 375ffd83dbSDimitry Andric // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9. 385ffd83dbSDimitry Andric GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, 395ffd83dbSDimitry Andric GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, 405ffd83dbSDimitry Andric }; 415ffd83dbSDimitry Andric 420b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 430b57cec5SDimitry Andric // GCOVFile implementation. 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric /// readGCNO - Read GCNO buffer. 465ffd83dbSDimitry Andric bool GCOVFile::readGCNO(GCOVBuffer &buf) { 475ffd83dbSDimitry Andric if (!buf.readGCNOFormat()) 480b57cec5SDimitry Andric return false; 495ffd83dbSDimitry Andric if (!buf.readGCOVVersion(Version)) 500b57cec5SDimitry Andric return false; 510b57cec5SDimitry Andric 525ffd83dbSDimitry Andric Checksum = buf.getWord(); 535ffd83dbSDimitry Andric if (Version >= GCOV::V900) 545ffd83dbSDimitry Andric cwd = buf.getString(); 555ffd83dbSDimitry Andric if (Version >= GCOV::V800) 565ffd83dbSDimitry Andric buf.getWord(); // hasUnexecutedBlocks 575ffd83dbSDimitry Andric 585ffd83dbSDimitry Andric uint32_t tag, length; 595ffd83dbSDimitry Andric GCOVFunction *fn; 605ffd83dbSDimitry Andric while ((tag = buf.getWord())) { 615ffd83dbSDimitry Andric if (!buf.readInt(length)) 620b57cec5SDimitry Andric return false; 635ffd83dbSDimitry Andric if (tag == GCOV_TAG_FUNCTION) { 645ffd83dbSDimitry Andric Functions.push_back(std::make_unique<GCOVFunction>(*this)); 655ffd83dbSDimitry Andric fn = Functions.back().get(); 665ffd83dbSDimitry Andric fn->ident = buf.getWord(); 675ffd83dbSDimitry Andric fn->linenoChecksum = buf.getWord(); 685ffd83dbSDimitry Andric if (Version >= GCOV::V407) 695ffd83dbSDimitry Andric fn->cfgChecksum = buf.getWord(); 705ffd83dbSDimitry Andric buf.readString(fn->Name); 715ffd83dbSDimitry Andric StringRef filename; 725ffd83dbSDimitry Andric if (Version < GCOV::V800) { 735ffd83dbSDimitry Andric filename = buf.getString(); 745ffd83dbSDimitry Andric fn->startLine = buf.getWord(); 755ffd83dbSDimitry Andric } else { 765ffd83dbSDimitry Andric fn->artificial = buf.getWord(); 775ffd83dbSDimitry Andric filename = buf.getString(); 785ffd83dbSDimitry Andric fn->startLine = buf.getWord(); 795ffd83dbSDimitry Andric fn->startColumn = buf.getWord(); 805ffd83dbSDimitry Andric fn->endLine = buf.getWord(); 815ffd83dbSDimitry Andric if (Version >= GCOV::V900) 825ffd83dbSDimitry Andric fn->endColumn = buf.getWord(); 835ffd83dbSDimitry Andric } 845ffd83dbSDimitry Andric auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size()); 855ffd83dbSDimitry Andric if (r.second) 865ffd83dbSDimitry Andric filenames.emplace_back(filename); 875ffd83dbSDimitry Andric fn->srcIdx = r.first->second; 885ffd83dbSDimitry Andric IdentToFunction[fn->ident] = fn; 895ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_BLOCKS && fn) { 905ffd83dbSDimitry Andric if (Version < GCOV::V800) { 915ffd83dbSDimitry Andric for (uint32_t i = 0; i != length; ++i) { 925ffd83dbSDimitry Andric buf.getWord(); // Ignored block flags 935ffd83dbSDimitry Andric fn->Blocks.push_back(std::make_unique<GCOVBlock>(*fn, i)); 945ffd83dbSDimitry Andric } 955ffd83dbSDimitry Andric } else { 965ffd83dbSDimitry Andric uint32_t num = buf.getWord(); 975ffd83dbSDimitry Andric for (uint32_t i = 0; i != num; ++i) 985ffd83dbSDimitry Andric fn->Blocks.push_back(std::make_unique<GCOVBlock>(*fn, i)); 995ffd83dbSDimitry Andric } 1005ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_ARCS && fn) { 1015ffd83dbSDimitry Andric uint32_t srcNo = buf.getWord(); 1025ffd83dbSDimitry Andric if (srcNo >= fn->Blocks.size()) { 1035ffd83dbSDimitry Andric errs() << "unexpected block number: " << srcNo << " (in " 1045ffd83dbSDimitry Andric << fn->Blocks.size() << ")\n"; 1055ffd83dbSDimitry Andric return false; 1065ffd83dbSDimitry Andric } 1075ffd83dbSDimitry Andric GCOVBlock *src = fn->Blocks[srcNo].get(); 1085ffd83dbSDimitry Andric for (uint32_t i = 0, e = (length - 1) / 2; i != e; ++i) { 1095ffd83dbSDimitry Andric uint32_t dstNo = buf.getWord(), flags = buf.getWord(); 1105ffd83dbSDimitry Andric GCOVBlock *dst = fn->Blocks[dstNo].get(); 1115ffd83dbSDimitry Andric auto arc = 1125ffd83dbSDimitry Andric std::make_unique<GCOVArc>(*src, *dst, flags & GCOV_ARC_FALLTHROUGH); 1135ffd83dbSDimitry Andric src->addDstEdge(arc.get()); 1145ffd83dbSDimitry Andric dst->addSrcEdge(arc.get()); 1155ffd83dbSDimitry Andric if (flags & GCOV_ARC_ON_TREE) 1165ffd83dbSDimitry Andric fn->treeArcs.push_back(std::move(arc)); 1175ffd83dbSDimitry Andric else 1185ffd83dbSDimitry Andric fn->arcs.push_back(std::move(arc)); 1195ffd83dbSDimitry Andric } 1205ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_LINES && fn) { 1215ffd83dbSDimitry Andric uint32_t srcNo = buf.getWord(); 1225ffd83dbSDimitry Andric if (srcNo >= fn->Blocks.size()) { 1235ffd83dbSDimitry Andric errs() << "unexpected block number: " << srcNo << " (in " 1245ffd83dbSDimitry Andric << fn->Blocks.size() << ")\n"; 1255ffd83dbSDimitry Andric return false; 1265ffd83dbSDimitry Andric } 1275ffd83dbSDimitry Andric GCOVBlock &Block = *fn->Blocks[srcNo]; 1285ffd83dbSDimitry Andric for (;;) { 1295ffd83dbSDimitry Andric uint32_t line = buf.getWord(); 1305ffd83dbSDimitry Andric if (line) 1315ffd83dbSDimitry Andric Block.addLine(line); 1325ffd83dbSDimitry Andric else { 1335ffd83dbSDimitry Andric StringRef filename = buf.getString(); 1345ffd83dbSDimitry Andric if (filename.empty()) 1350b57cec5SDimitry Andric break; 1365ffd83dbSDimitry Andric // TODO Unhandled 1375ffd83dbSDimitry Andric } 1385ffd83dbSDimitry Andric } 1395ffd83dbSDimitry Andric } 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric GCNOInitialized = true; 1430b57cec5SDimitry Andric return true; 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric /// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be 1470b57cec5SDimitry Andric /// called after readGCNO(). 1485ffd83dbSDimitry Andric bool GCOVFile::readGCDA(GCOVBuffer &buf) { 1490b57cec5SDimitry Andric assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()"); 1505ffd83dbSDimitry Andric if (!buf.readGCDAFormat()) 1510b57cec5SDimitry Andric return false; 1520b57cec5SDimitry Andric GCOV::GCOVVersion GCDAVersion; 1535ffd83dbSDimitry Andric if (!buf.readGCOVVersion(GCDAVersion)) 1540b57cec5SDimitry Andric return false; 1550b57cec5SDimitry Andric if (Version != GCDAVersion) { 1560b57cec5SDimitry Andric errs() << "GCOV versions do not match.\n"; 1570b57cec5SDimitry Andric return false; 1580b57cec5SDimitry Andric } 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric uint32_t GCDAChecksum; 1615ffd83dbSDimitry Andric if (!buf.readInt(GCDAChecksum)) 1620b57cec5SDimitry Andric return false; 1630b57cec5SDimitry Andric if (Checksum != GCDAChecksum) { 1640b57cec5SDimitry Andric errs() << "File checksums do not match: " << Checksum 1650b57cec5SDimitry Andric << " != " << GCDAChecksum << ".\n"; 1660b57cec5SDimitry Andric return false; 1670b57cec5SDimitry Andric } 1685ffd83dbSDimitry Andric uint32_t dummy, tag, length; 1695ffd83dbSDimitry Andric uint32_t ident; 1705ffd83dbSDimitry Andric GCOVFunction *fn = nullptr; 1715ffd83dbSDimitry Andric while ((tag = buf.getWord())) { 1725ffd83dbSDimitry Andric if (!buf.readInt(length)) 1730b57cec5SDimitry Andric return false; 1745ffd83dbSDimitry Andric uint32_t pos = buf.cursor.tell(); 1755ffd83dbSDimitry Andric if (tag == GCOV_TAG_OBJECT_SUMMARY) { 1765ffd83dbSDimitry Andric buf.readInt(RunCount); 1775ffd83dbSDimitry Andric buf.readInt(dummy); 1785ffd83dbSDimitry Andric // clang<11 uses a fake 4.2 format which sets length to 9. 1795ffd83dbSDimitry Andric if (length == 9) 1805ffd83dbSDimitry Andric buf.readInt(RunCount); 1815ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) { 1825ffd83dbSDimitry Andric // clang<11 uses a fake 4.2 format which sets length to 0. 1835ffd83dbSDimitry Andric if (length > 0) { 1845ffd83dbSDimitry Andric buf.readInt(dummy); 1855ffd83dbSDimitry Andric buf.readInt(dummy); 1865ffd83dbSDimitry Andric buf.readInt(RunCount); 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric ++ProgramCount; 1895ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_FUNCTION) { 1905ffd83dbSDimitry Andric if (length == 0) // Placeholder 1915ffd83dbSDimitry Andric continue; 1925ffd83dbSDimitry Andric // As of GCC 10, GCOV_TAG_FUNCTION_LENGTH has never been larger than 3. 1935ffd83dbSDimitry Andric // However, clang<11 uses a fake 4.2 format which may set length larger 1945ffd83dbSDimitry Andric // than 3. 1955ffd83dbSDimitry Andric if (length < 2 || !buf.readInt(ident)) 1965ffd83dbSDimitry Andric return false; 1975ffd83dbSDimitry Andric auto It = IdentToFunction.find(ident); 1985ffd83dbSDimitry Andric uint32_t linenoChecksum, cfgChecksum = 0; 1995ffd83dbSDimitry Andric buf.readInt(linenoChecksum); 2005ffd83dbSDimitry Andric if (Version >= GCOV::V407) 2015ffd83dbSDimitry Andric buf.readInt(cfgChecksum); 2025ffd83dbSDimitry Andric if (It != IdentToFunction.end()) { 2035ffd83dbSDimitry Andric fn = It->second; 2045ffd83dbSDimitry Andric if (linenoChecksum != fn->linenoChecksum || 2055ffd83dbSDimitry Andric cfgChecksum != fn->cfgChecksum) { 2065ffd83dbSDimitry Andric errs() << fn->Name 2075ffd83dbSDimitry Andric << format(": checksum mismatch, (%u, %u) != (%u, %u)\n", 2085ffd83dbSDimitry Andric linenoChecksum, cfgChecksum, fn->linenoChecksum, 2095ffd83dbSDimitry Andric fn->cfgChecksum); 2105ffd83dbSDimitry Andric return false; 2115ffd83dbSDimitry Andric } 2125ffd83dbSDimitry Andric } 2135ffd83dbSDimitry Andric } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) { 2145ffd83dbSDimitry Andric if (length != 2 * fn->arcs.size()) { 2155ffd83dbSDimitry Andric errs() << fn->Name 2165ffd83dbSDimitry Andric << format( 2175ffd83dbSDimitry Andric ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n", 2185ffd83dbSDimitry Andric length, unsigned(2 * fn->arcs.size())); 2195ffd83dbSDimitry Andric return false; 2205ffd83dbSDimitry Andric } 2215ffd83dbSDimitry Andric for (std::unique_ptr<GCOVArc> &arc : fn->arcs) { 2225ffd83dbSDimitry Andric if (!buf.readInt64(arc->Count)) 2235ffd83dbSDimitry Andric return false; 2245ffd83dbSDimitry Andric // FIXME Fix counters 2255ffd83dbSDimitry Andric arc->src.Counter += arc->Count; 2265ffd83dbSDimitry Andric if (arc->dst.succ.empty()) 2275ffd83dbSDimitry Andric arc->dst.Counter += arc->Count; 2285ffd83dbSDimitry Andric } 2295ffd83dbSDimitry Andric } 2305ffd83dbSDimitry Andric pos += 4 * length; 2315ffd83dbSDimitry Andric if (pos < buf.cursor.tell()) 2325ffd83dbSDimitry Andric return false; 2335ffd83dbSDimitry Andric buf.de.skip(buf.cursor, pos - buf.cursor.tell()); 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric return true; 2370b57cec5SDimitry Andric } 2380b57cec5SDimitry Andric 2390b57cec5SDimitry Andric void GCOVFile::print(raw_ostream &OS) const { 2405ffd83dbSDimitry Andric for (const GCOVFunction &f : *this) 2415ffd83dbSDimitry Andric f.print(OS); 2420b57cec5SDimitry Andric } 2430b57cec5SDimitry Andric 2440b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 2450b57cec5SDimitry Andric /// dump - Dump GCOVFile content to dbgs() for debugging purposes. 2460b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } 2470b57cec5SDimitry Andric #endif 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric /// collectLineCounts - Collect line counts. This must be used after 2500b57cec5SDimitry Andric /// reading .gcno and .gcda files. 2515ffd83dbSDimitry Andric void GCOVFile::collectLineCounts(FileInfo &fi) { 2525ffd83dbSDimitry Andric assert(fi.sources.empty()); 2535ffd83dbSDimitry Andric for (StringRef filename : filenames) 2545ffd83dbSDimitry Andric fi.sources.emplace_back(filename); 2555ffd83dbSDimitry Andric for (GCOVFunction &f : *this) { 2565ffd83dbSDimitry Andric f.collectLineCounts(fi); 2575ffd83dbSDimitry Andric fi.sources[f.srcIdx].functions.push_back(&f); 2585ffd83dbSDimitry Andric } 2595ffd83dbSDimitry Andric fi.setRunCount(RunCount); 2605ffd83dbSDimitry Andric fi.setProgramCount(ProgramCount); 2610b57cec5SDimitry Andric } 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 2640b57cec5SDimitry Andric // GCOVFunction implementation. 2650b57cec5SDimitry Andric 2665ffd83dbSDimitry Andric StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; } 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric /// getEntryCount - Get the number of times the function was called by 2690b57cec5SDimitry Andric /// retrieving the entry block's count. 2700b57cec5SDimitry Andric uint64_t GCOVFunction::getEntryCount() const { 2710b57cec5SDimitry Andric return Blocks.front()->getCount(); 2720b57cec5SDimitry Andric } 2730b57cec5SDimitry Andric 2740b57cec5SDimitry Andric /// getExitCount - Get the number of times the function returned by retrieving 2750b57cec5SDimitry Andric /// the exit block's count. 2760b57cec5SDimitry Andric uint64_t GCOVFunction::getExitCount() const { 2770b57cec5SDimitry Andric return Blocks.back()->getCount(); 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric void GCOVFunction::print(raw_ostream &OS) const { 2815ffd83dbSDimitry Andric OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":" 2825ffd83dbSDimitry Andric << startLine << "\n"; 2830b57cec5SDimitry Andric for (const auto &Block : Blocks) 2840b57cec5SDimitry Andric Block->print(OS); 2850b57cec5SDimitry Andric } 2860b57cec5SDimitry Andric 2870b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 2880b57cec5SDimitry Andric /// dump - Dump GCOVFunction content to dbgs() for debugging purposes. 2890b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } 2900b57cec5SDimitry Andric #endif 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric /// collectLineCounts - Collect line counts. This must be used after 2930b57cec5SDimitry Andric /// reading .gcno and .gcda files. 2940b57cec5SDimitry Andric void GCOVFunction::collectLineCounts(FileInfo &FI) { 2950b57cec5SDimitry Andric // If the line number is zero, this is a function that doesn't actually appear 2960b57cec5SDimitry Andric // in the source file, so there isn't anything we can do with it. 2975ffd83dbSDimitry Andric if (startLine == 0) 2980b57cec5SDimitry Andric return; 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric for (const auto &Block : Blocks) 3010b57cec5SDimitry Andric Block->collectLineCounts(FI); 3025ffd83dbSDimitry Andric FI.addFunctionLine(getFilename(), startLine, this); 3030b57cec5SDimitry Andric } 3040b57cec5SDimitry Andric 3050b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 3060b57cec5SDimitry Andric // GCOVBlock implementation. 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric /// collectLineCounts - Collect line counts. This must be used after 3090b57cec5SDimitry Andric /// reading .gcno and .gcda files. 3100b57cec5SDimitry Andric void GCOVBlock::collectLineCounts(FileInfo &FI) { 3110b57cec5SDimitry Andric for (uint32_t N : Lines) 3120b57cec5SDimitry Andric FI.addBlockLine(Parent.getFilename(), N, this); 3130b57cec5SDimitry Andric } 3140b57cec5SDimitry Andric 3150b57cec5SDimitry Andric void GCOVBlock::print(raw_ostream &OS) const { 3160b57cec5SDimitry Andric OS << "Block : " << Number << " Counter : " << Counter << "\n"; 3175ffd83dbSDimitry Andric if (!pred.empty()) { 3180b57cec5SDimitry Andric OS << "\tSource Edges : "; 3195ffd83dbSDimitry Andric for (const GCOVArc *Edge : pred) 3205ffd83dbSDimitry Andric OS << Edge->src.Number << " (" << Edge->Count << "), "; 3210b57cec5SDimitry Andric OS << "\n"; 3220b57cec5SDimitry Andric } 3235ffd83dbSDimitry Andric if (!succ.empty()) { 3240b57cec5SDimitry Andric OS << "\tDestination Edges : "; 3255ffd83dbSDimitry Andric for (const GCOVArc *Edge : succ) 3265ffd83dbSDimitry Andric OS << Edge->dst.Number << " (" << Edge->Count << "), "; 3270b57cec5SDimitry Andric OS << "\n"; 3280b57cec5SDimitry Andric } 3290b57cec5SDimitry Andric if (!Lines.empty()) { 3300b57cec5SDimitry Andric OS << "\tLines : "; 3310b57cec5SDimitry Andric for (uint32_t N : Lines) 3320b57cec5SDimitry Andric OS << (N) << ","; 3330b57cec5SDimitry Andric OS << "\n"; 3340b57cec5SDimitry Andric } 3350b57cec5SDimitry Andric } 3360b57cec5SDimitry Andric 3370b57cec5SDimitry Andric #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 3380b57cec5SDimitry Andric /// dump - Dump GCOVBlock content to dbgs() for debugging purposes. 3390b57cec5SDimitry Andric LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } 3400b57cec5SDimitry Andric #endif 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 3430b57cec5SDimitry Andric // Cycles detection 3440b57cec5SDimitry Andric // 345480093f4SDimitry Andric // The algorithm in GCC is based on the algorithm by Hawick & James: 3460b57cec5SDimitry Andric // "Enumerating Circuits and Loops in Graphs with Self-Arcs and Multiple-Arcs" 3470b57cec5SDimitry Andric // http://complexity.massey.ac.nz/cstn/013/cstn-013.pdf. 3480b57cec5SDimitry Andric 3490b57cec5SDimitry Andric /// Get the count for the detected cycle. 3500b57cec5SDimitry Andric uint64_t GCOVBlock::getCycleCount(const Edges &Path) { 3510b57cec5SDimitry Andric uint64_t CycleCount = std::numeric_limits<uint64_t>::max(); 3520b57cec5SDimitry Andric for (auto E : Path) { 3530b57cec5SDimitry Andric CycleCount = std::min(E->CyclesCount, CycleCount); 3540b57cec5SDimitry Andric } 3550b57cec5SDimitry Andric for (auto E : Path) { 3560b57cec5SDimitry Andric E->CyclesCount -= CycleCount; 3570b57cec5SDimitry Andric } 3580b57cec5SDimitry Andric return CycleCount; 3590b57cec5SDimitry Andric } 3600b57cec5SDimitry Andric 3610b57cec5SDimitry Andric /// Unblock a vertex previously marked as blocked. 3620b57cec5SDimitry Andric void GCOVBlock::unblock(const GCOVBlock *U, BlockVector &Blocked, 3630b57cec5SDimitry Andric BlockVectorLists &BlockLists) { 3640b57cec5SDimitry Andric auto it = find(Blocked, U); 3650b57cec5SDimitry Andric if (it == Blocked.end()) { 3660b57cec5SDimitry Andric return; 3670b57cec5SDimitry Andric } 3680b57cec5SDimitry Andric 3690b57cec5SDimitry Andric const size_t index = it - Blocked.begin(); 3700b57cec5SDimitry Andric Blocked.erase(it); 3710b57cec5SDimitry Andric 3720b57cec5SDimitry Andric const BlockVector ToUnblock(BlockLists[index]); 3730b57cec5SDimitry Andric BlockLists.erase(BlockLists.begin() + index); 3740b57cec5SDimitry Andric for (auto GB : ToUnblock) { 3750b57cec5SDimitry Andric GCOVBlock::unblock(GB, Blocked, BlockLists); 3760b57cec5SDimitry Andric } 3770b57cec5SDimitry Andric } 3780b57cec5SDimitry Andric 3790b57cec5SDimitry Andric bool GCOVBlock::lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, 3800b57cec5SDimitry Andric Edges &Path, BlockVector &Blocked, 3810b57cec5SDimitry Andric BlockVectorLists &BlockLists, 3820b57cec5SDimitry Andric const BlockVector &Blocks, uint64_t &Count) { 3830b57cec5SDimitry Andric Blocked.push_back(V); 3840b57cec5SDimitry Andric BlockLists.emplace_back(BlockVector()); 3850b57cec5SDimitry Andric bool FoundCircuit = false; 3860b57cec5SDimitry Andric 3870b57cec5SDimitry Andric for (auto E : V->dsts()) { 3885ffd83dbSDimitry Andric const GCOVBlock *W = &E->dst; 3890b57cec5SDimitry Andric if (W < Start || find(Blocks, W) == Blocks.end()) { 3900b57cec5SDimitry Andric continue; 3910b57cec5SDimitry Andric } 3920b57cec5SDimitry Andric 3930b57cec5SDimitry Andric Path.push_back(E); 3940b57cec5SDimitry Andric 3950b57cec5SDimitry Andric if (W == Start) { 3960b57cec5SDimitry Andric // We've a cycle. 3970b57cec5SDimitry Andric Count += GCOVBlock::getCycleCount(Path); 3980b57cec5SDimitry Andric FoundCircuit = true; 3990b57cec5SDimitry Andric } else if (find(Blocked, W) == Blocked.end() && // W is not blocked. 4000b57cec5SDimitry Andric GCOVBlock::lookForCircuit(W, Start, Path, Blocked, BlockLists, 4010b57cec5SDimitry Andric Blocks, Count)) { 4020b57cec5SDimitry Andric FoundCircuit = true; 4030b57cec5SDimitry Andric } 4040b57cec5SDimitry Andric 4050b57cec5SDimitry Andric Path.pop_back(); 4060b57cec5SDimitry Andric } 4070b57cec5SDimitry Andric 4080b57cec5SDimitry Andric if (FoundCircuit) { 4090b57cec5SDimitry Andric GCOVBlock::unblock(V, Blocked, BlockLists); 4100b57cec5SDimitry Andric } else { 4110b57cec5SDimitry Andric for (auto E : V->dsts()) { 4125ffd83dbSDimitry Andric const GCOVBlock *W = &E->dst; 4130b57cec5SDimitry Andric if (W < Start || find(Blocks, W) == Blocks.end()) { 4140b57cec5SDimitry Andric continue; 4150b57cec5SDimitry Andric } 4160b57cec5SDimitry Andric const size_t index = find(Blocked, W) - Blocked.begin(); 4170b57cec5SDimitry Andric BlockVector &List = BlockLists[index]; 4180b57cec5SDimitry Andric if (find(List, V) == List.end()) { 4190b57cec5SDimitry Andric List.push_back(V); 4200b57cec5SDimitry Andric } 4210b57cec5SDimitry Andric } 4220b57cec5SDimitry Andric } 4230b57cec5SDimitry Andric 4240b57cec5SDimitry Andric return FoundCircuit; 4250b57cec5SDimitry Andric } 4260b57cec5SDimitry Andric 4270b57cec5SDimitry Andric /// Get the count for the list of blocks which lie on the same line. 4280b57cec5SDimitry Andric void GCOVBlock::getCyclesCount(const BlockVector &Blocks, uint64_t &Count) { 4290b57cec5SDimitry Andric for (auto Block : Blocks) { 4300b57cec5SDimitry Andric Edges Path; 4310b57cec5SDimitry Andric BlockVector Blocked; 4320b57cec5SDimitry Andric BlockVectorLists BlockLists; 4330b57cec5SDimitry Andric 4340b57cec5SDimitry Andric GCOVBlock::lookForCircuit(Block, Block, Path, Blocked, BlockLists, Blocks, 4350b57cec5SDimitry Andric Count); 4360b57cec5SDimitry Andric } 4370b57cec5SDimitry Andric } 4380b57cec5SDimitry Andric 4390b57cec5SDimitry Andric /// Get the count for the list of blocks which lie on the same line. 4400b57cec5SDimitry Andric uint64_t GCOVBlock::getLineCount(const BlockVector &Blocks) { 4410b57cec5SDimitry Andric uint64_t Count = 0; 4420b57cec5SDimitry Andric 4430b57cec5SDimitry Andric for (auto Block : Blocks) { 4440b57cec5SDimitry Andric if (Block->getNumSrcEdges() == 0) { 4450b57cec5SDimitry Andric // The block has no predecessors and a non-null counter 4460b57cec5SDimitry Andric // (can be the case with entry block in functions). 4470b57cec5SDimitry Andric Count += Block->getCount(); 4480b57cec5SDimitry Andric } else { 4490b57cec5SDimitry Andric // Add counts from predecessors that are not on the same line. 4500b57cec5SDimitry Andric for (auto E : Block->srcs()) { 4515ffd83dbSDimitry Andric const GCOVBlock *W = &E->src; 4520b57cec5SDimitry Andric if (find(Blocks, W) == Blocks.end()) { 4530b57cec5SDimitry Andric Count += E->Count; 4540b57cec5SDimitry Andric } 4550b57cec5SDimitry Andric } 4560b57cec5SDimitry Andric } 4570b57cec5SDimitry Andric for (auto E : Block->dsts()) { 4580b57cec5SDimitry Andric E->CyclesCount = E->Count; 4590b57cec5SDimitry Andric } 4600b57cec5SDimitry Andric } 4610b57cec5SDimitry Andric 4620b57cec5SDimitry Andric GCOVBlock::getCyclesCount(Blocks, Count); 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric return Count; 4650b57cec5SDimitry Andric } 4660b57cec5SDimitry Andric 4670b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 4680b57cec5SDimitry Andric // FileInfo implementation. 4690b57cec5SDimitry Andric 4700b57cec5SDimitry Andric // Safe integer division, returns 0 if numerator is 0. 4710b57cec5SDimitry Andric static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) { 4720b57cec5SDimitry Andric if (!Numerator) 4730b57cec5SDimitry Andric return 0; 4740b57cec5SDimitry Andric return Numerator / Divisor; 4750b57cec5SDimitry Andric } 4760b57cec5SDimitry Andric 4770b57cec5SDimitry Andric // This custom division function mimics gcov's branch ouputs: 4780b57cec5SDimitry Andric // - Round to closest whole number 4790b57cec5SDimitry Andric // - Only output 0% or 100% if it's exactly that value 4800b57cec5SDimitry Andric static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) { 4810b57cec5SDimitry Andric if (!Numerator) 4820b57cec5SDimitry Andric return 0; 4830b57cec5SDimitry Andric if (Numerator == Divisor) 4840b57cec5SDimitry Andric return 100; 4850b57cec5SDimitry Andric 4860b57cec5SDimitry Andric uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor; 4870b57cec5SDimitry Andric if (Res == 0) 4880b57cec5SDimitry Andric return 1; 4890b57cec5SDimitry Andric if (Res == 100) 4900b57cec5SDimitry Andric return 99; 4910b57cec5SDimitry Andric return Res; 4920b57cec5SDimitry Andric } 4930b57cec5SDimitry Andric 4940b57cec5SDimitry Andric namespace { 4950b57cec5SDimitry Andric struct formatBranchInfo { 4960b57cec5SDimitry Andric formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) 4970b57cec5SDimitry Andric : Options(Options), Count(Count), Total(Total) {} 4980b57cec5SDimitry Andric 4990b57cec5SDimitry Andric void print(raw_ostream &OS) const { 5000b57cec5SDimitry Andric if (!Total) 5010b57cec5SDimitry Andric OS << "never executed"; 5020b57cec5SDimitry Andric else if (Options.BranchCount) 5030b57cec5SDimitry Andric OS << "taken " << Count; 5040b57cec5SDimitry Andric else 5050b57cec5SDimitry Andric OS << "taken " << branchDiv(Count, Total) << "%"; 5060b57cec5SDimitry Andric } 5070b57cec5SDimitry Andric 5080b57cec5SDimitry Andric const GCOV::Options &Options; 5090b57cec5SDimitry Andric uint64_t Count; 5100b57cec5SDimitry Andric uint64_t Total; 5110b57cec5SDimitry Andric }; 5120b57cec5SDimitry Andric 5130b57cec5SDimitry Andric static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { 5140b57cec5SDimitry Andric FBI.print(OS); 5150b57cec5SDimitry Andric return OS; 5160b57cec5SDimitry Andric } 5170b57cec5SDimitry Andric 5180b57cec5SDimitry Andric class LineConsumer { 5190b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> Buffer; 5200b57cec5SDimitry Andric StringRef Remaining; 5210b57cec5SDimitry Andric 5220b57cec5SDimitry Andric public: 5235ffd83dbSDimitry Andric LineConsumer() = default; 5240b57cec5SDimitry Andric LineConsumer(StringRef Filename) { 5250b57cec5SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = 5260b57cec5SDimitry Andric MemoryBuffer::getFileOrSTDIN(Filename); 5270b57cec5SDimitry Andric if (std::error_code EC = BufferOrErr.getError()) { 5280b57cec5SDimitry Andric errs() << Filename << ": " << EC.message() << "\n"; 5290b57cec5SDimitry Andric Remaining = ""; 5300b57cec5SDimitry Andric } else { 5310b57cec5SDimitry Andric Buffer = std::move(BufferOrErr.get()); 5320b57cec5SDimitry Andric Remaining = Buffer->getBuffer(); 5330b57cec5SDimitry Andric } 5340b57cec5SDimitry Andric } 5350b57cec5SDimitry Andric bool empty() { return Remaining.empty(); } 5360b57cec5SDimitry Andric void printNext(raw_ostream &OS, uint32_t LineNum) { 5370b57cec5SDimitry Andric StringRef Line; 5380b57cec5SDimitry Andric if (empty()) 5390b57cec5SDimitry Andric Line = "/*EOF*/"; 5400b57cec5SDimitry Andric else 5410b57cec5SDimitry Andric std::tie(Line, Remaining) = Remaining.split("\n"); 5420b57cec5SDimitry Andric OS << format("%5u:", LineNum) << Line << "\n"; 5430b57cec5SDimitry Andric } 5440b57cec5SDimitry Andric }; 5450b57cec5SDimitry Andric } // end anonymous namespace 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric /// Convert a path to a gcov filename. If PreservePaths is true, this 5480b57cec5SDimitry Andric /// translates "/" to "#", ".." to "^", and drops ".", to match gcov. 5490b57cec5SDimitry Andric static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { 5500b57cec5SDimitry Andric if (!PreservePaths) 5510b57cec5SDimitry Andric return sys::path::filename(Filename).str(); 5520b57cec5SDimitry Andric 5530b57cec5SDimitry Andric // This behaviour is defined by gcov in terms of text replacements, so it's 5540b57cec5SDimitry Andric // not likely to do anything useful on filesystems with different textual 5550b57cec5SDimitry Andric // conventions. 5560b57cec5SDimitry Andric llvm::SmallString<256> Result(""); 5570b57cec5SDimitry Andric StringRef::iterator I, S, E; 5580b57cec5SDimitry Andric for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { 5590b57cec5SDimitry Andric if (*I != '/') 5600b57cec5SDimitry Andric continue; 5610b57cec5SDimitry Andric 5620b57cec5SDimitry Andric if (I - S == 1 && *S == '.') { 5630b57cec5SDimitry Andric // ".", the current directory, is skipped. 5640b57cec5SDimitry Andric } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { 5650b57cec5SDimitry Andric // "..", the parent directory, is replaced with "^". 5660b57cec5SDimitry Andric Result.append("^#"); 5670b57cec5SDimitry Andric } else { 5680b57cec5SDimitry Andric if (S < I) 5690b57cec5SDimitry Andric // Leave other components intact, 5700b57cec5SDimitry Andric Result.append(S, I); 5710b57cec5SDimitry Andric // And separate with "#". 5720b57cec5SDimitry Andric Result.push_back('#'); 5730b57cec5SDimitry Andric } 5740b57cec5SDimitry Andric S = I + 1; 5750b57cec5SDimitry Andric } 5760b57cec5SDimitry Andric 5770b57cec5SDimitry Andric if (S < I) 5780b57cec5SDimitry Andric Result.append(S, I); 5795ffd83dbSDimitry Andric return std::string(Result.str()); 5800b57cec5SDimitry Andric } 5810b57cec5SDimitry Andric 5820b57cec5SDimitry Andric std::string FileInfo::getCoveragePath(StringRef Filename, 5830b57cec5SDimitry Andric StringRef MainFilename) { 5840b57cec5SDimitry Andric if (Options.NoOutput) 5850b57cec5SDimitry Andric // This is probably a bug in gcov, but when -n is specified, paths aren't 5860b57cec5SDimitry Andric // mangled at all, and the -l and -p options are ignored. Here, we do the 5870b57cec5SDimitry Andric // same. 5885ffd83dbSDimitry Andric return std::string(Filename); 5890b57cec5SDimitry Andric 5900b57cec5SDimitry Andric std::string CoveragePath; 5910b57cec5SDimitry Andric if (Options.LongFileNames && !Filename.equals(MainFilename)) 5920b57cec5SDimitry Andric CoveragePath = 5930b57cec5SDimitry Andric mangleCoveragePath(MainFilename, Options.PreservePaths) + "##"; 5940b57cec5SDimitry Andric CoveragePath += mangleCoveragePath(Filename, Options.PreservePaths); 5950b57cec5SDimitry Andric if (Options.HashFilenames) { 5960b57cec5SDimitry Andric MD5 Hasher; 5970b57cec5SDimitry Andric MD5::MD5Result Result; 5980b57cec5SDimitry Andric Hasher.update(Filename.str()); 5990b57cec5SDimitry Andric Hasher.final(Result); 6005ffd83dbSDimitry Andric CoveragePath += "##" + std::string(Result.digest()); 6010b57cec5SDimitry Andric } 6020b57cec5SDimitry Andric CoveragePath += ".gcov"; 6030b57cec5SDimitry Andric return CoveragePath; 6040b57cec5SDimitry Andric } 6050b57cec5SDimitry Andric 6060b57cec5SDimitry Andric std::unique_ptr<raw_ostream> 6070b57cec5SDimitry Andric FileInfo::openCoveragePath(StringRef CoveragePath) { 6080b57cec5SDimitry Andric std::error_code EC; 6090b57cec5SDimitry Andric auto OS = 6108bcb0991SDimitry Andric std::make_unique<raw_fd_ostream>(CoveragePath, EC, sys::fs::OF_Text); 6110b57cec5SDimitry Andric if (EC) { 6120b57cec5SDimitry Andric errs() << EC.message() << "\n"; 6138bcb0991SDimitry Andric return std::make_unique<raw_null_ostream>(); 6140b57cec5SDimitry Andric } 6150b57cec5SDimitry Andric return std::move(OS); 6160b57cec5SDimitry Andric } 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric /// print - Print source files with collected line count information. 6190b57cec5SDimitry Andric void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename, 6205ffd83dbSDimitry Andric StringRef GCNOFile, StringRef GCDAFile, GCOVFile &file) { 6210b57cec5SDimitry Andric SmallVector<StringRef, 4> Filenames; 6220b57cec5SDimitry Andric for (const auto &LI : LineInfo) 6230b57cec5SDimitry Andric Filenames.push_back(LI.first()); 6240b57cec5SDimitry Andric llvm::sort(Filenames); 6250b57cec5SDimitry Andric 6260b57cec5SDimitry Andric for (StringRef Filename : Filenames) { 6275ffd83dbSDimitry Andric auto AllLines = 6285ffd83dbSDimitry Andric Options.Intermediate ? LineConsumer() : LineConsumer(Filename); 6290b57cec5SDimitry Andric std::string CoveragePath = getCoveragePath(Filename, MainFilename); 6305ffd83dbSDimitry Andric std::unique_ptr<raw_ostream> CovStream; 6315ffd83dbSDimitry Andric if (Options.NoOutput || Options.Intermediate) 6325ffd83dbSDimitry Andric CovStream = std::make_unique<raw_null_ostream>(); 6335ffd83dbSDimitry Andric else if (!Options.UseStdout) 6345ffd83dbSDimitry Andric CovStream = openCoveragePath(CoveragePath); 6355ffd83dbSDimitry Andric raw_ostream &CovOS = 6365ffd83dbSDimitry Andric !Options.NoOutput && Options.UseStdout ? llvm::outs() : *CovStream; 6370b57cec5SDimitry Andric 6380b57cec5SDimitry Andric CovOS << " -: 0:Source:" << Filename << "\n"; 6390b57cec5SDimitry Andric CovOS << " -: 0:Graph:" << GCNOFile << "\n"; 6400b57cec5SDimitry Andric CovOS << " -: 0:Data:" << GCDAFile << "\n"; 6410b57cec5SDimitry Andric CovOS << " -: 0:Runs:" << RunCount << "\n"; 6425ffd83dbSDimitry Andric if (file.getVersion() < GCOV::V900) 6430b57cec5SDimitry Andric CovOS << " -: 0:Programs:" << ProgramCount << "\n"; 6440b57cec5SDimitry Andric 6450b57cec5SDimitry Andric const LineData &Line = LineInfo[Filename]; 6460b57cec5SDimitry Andric GCOVCoverage FileCoverage(Filename); 6470b57cec5SDimitry Andric for (uint32_t LineIndex = 0; LineIndex < Line.LastLine || !AllLines.empty(); 6480b57cec5SDimitry Andric ++LineIndex) { 6490b57cec5SDimitry Andric if (Options.BranchInfo) { 6500b57cec5SDimitry Andric FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex); 6510b57cec5SDimitry Andric if (FuncsIt != Line.Functions.end()) 6520b57cec5SDimitry Andric printFunctionSummary(CovOS, FuncsIt->second); 6530b57cec5SDimitry Andric } 6540b57cec5SDimitry Andric 6550b57cec5SDimitry Andric BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex); 6560b57cec5SDimitry Andric if (BlocksIt == Line.Blocks.end()) { 6570b57cec5SDimitry Andric // No basic blocks are on this line. Not an executable line of code. 6580b57cec5SDimitry Andric CovOS << " -:"; 6590b57cec5SDimitry Andric AllLines.printNext(CovOS, LineIndex + 1); 6600b57cec5SDimitry Andric } else { 6610b57cec5SDimitry Andric const BlockVector &Blocks = BlocksIt->second; 6620b57cec5SDimitry Andric 6630b57cec5SDimitry Andric // Add up the block counts to form line counts. 6640b57cec5SDimitry Andric DenseMap<const GCOVFunction *, bool> LineExecs; 6650b57cec5SDimitry Andric for (const GCOVBlock *Block : Blocks) { 6660b57cec5SDimitry Andric if (Options.FuncCoverage) { 6670b57cec5SDimitry Andric // This is a slightly convoluted way to most accurately gather line 6680b57cec5SDimitry Andric // statistics for functions. Basically what is happening is that we 6690b57cec5SDimitry Andric // don't want to count a single line with multiple blocks more than 6700b57cec5SDimitry Andric // once. However, we also don't simply want to give the total line 6710b57cec5SDimitry Andric // count to every function that starts on the line. Thus, what is 6720b57cec5SDimitry Andric // happening here are two things: 6730b57cec5SDimitry Andric // 1) Ensure that the number of logical lines is only incremented 6740b57cec5SDimitry Andric // once per function. 6750b57cec5SDimitry Andric // 2) If there are multiple blocks on the same line, ensure that the 6760b57cec5SDimitry Andric // number of lines executed is incremented as long as at least 6770b57cec5SDimitry Andric // one of the blocks are executed. 6780b57cec5SDimitry Andric const GCOVFunction *Function = &Block->getParent(); 6790b57cec5SDimitry Andric if (FuncCoverages.find(Function) == FuncCoverages.end()) { 6800b57cec5SDimitry Andric std::pair<const GCOVFunction *, GCOVCoverage> KeyValue( 6810b57cec5SDimitry Andric Function, GCOVCoverage(Function->getName())); 6820b57cec5SDimitry Andric FuncCoverages.insert(KeyValue); 6830b57cec5SDimitry Andric } 6840b57cec5SDimitry Andric GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second; 6850b57cec5SDimitry Andric 6860b57cec5SDimitry Andric if (LineExecs.find(Function) == LineExecs.end()) { 6870b57cec5SDimitry Andric if (Block->getCount()) { 6880b57cec5SDimitry Andric ++FuncCoverage.LinesExec; 6890b57cec5SDimitry Andric LineExecs[Function] = true; 6900b57cec5SDimitry Andric } else { 6910b57cec5SDimitry Andric LineExecs[Function] = false; 6920b57cec5SDimitry Andric } 6930b57cec5SDimitry Andric ++FuncCoverage.LogicalLines; 6940b57cec5SDimitry Andric } else if (!LineExecs[Function] && Block->getCount()) { 6950b57cec5SDimitry Andric ++FuncCoverage.LinesExec; 6960b57cec5SDimitry Andric LineExecs[Function] = true; 6970b57cec5SDimitry Andric } 6980b57cec5SDimitry Andric } 6990b57cec5SDimitry Andric } 7000b57cec5SDimitry Andric 7010b57cec5SDimitry Andric const uint64_t LineCount = GCOVBlock::getLineCount(Blocks); 7020b57cec5SDimitry Andric if (LineCount == 0) 7030b57cec5SDimitry Andric CovOS << " #####:"; 7040b57cec5SDimitry Andric else { 7050b57cec5SDimitry Andric CovOS << format("%9" PRIu64 ":", LineCount); 7060b57cec5SDimitry Andric ++FileCoverage.LinesExec; 7070b57cec5SDimitry Andric } 7080b57cec5SDimitry Andric ++FileCoverage.LogicalLines; 7090b57cec5SDimitry Andric 7100b57cec5SDimitry Andric AllLines.printNext(CovOS, LineIndex + 1); 7110b57cec5SDimitry Andric 7120b57cec5SDimitry Andric uint32_t BlockNo = 0; 7130b57cec5SDimitry Andric uint32_t EdgeNo = 0; 7140b57cec5SDimitry Andric for (const GCOVBlock *Block : Blocks) { 7150b57cec5SDimitry Andric // Only print block and branch information at the end of the block. 7160b57cec5SDimitry Andric if (Block->getLastLine() != LineIndex + 1) 7170b57cec5SDimitry Andric continue; 7180b57cec5SDimitry Andric if (Options.AllBlocks) 7190b57cec5SDimitry Andric printBlockInfo(CovOS, *Block, LineIndex, BlockNo); 7200b57cec5SDimitry Andric if (Options.BranchInfo) { 7210b57cec5SDimitry Andric size_t NumEdges = Block->getNumDstEdges(); 7220b57cec5SDimitry Andric if (NumEdges > 1) 7230b57cec5SDimitry Andric printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo); 7240b57cec5SDimitry Andric else if (Options.UncondBranch && NumEdges == 1) 7255ffd83dbSDimitry Andric printUncondBranchInfo(CovOS, EdgeNo, Block->succ[0]->Count); 7260b57cec5SDimitry Andric } 7270b57cec5SDimitry Andric } 7280b57cec5SDimitry Andric } 7290b57cec5SDimitry Andric } 7305ffd83dbSDimitry Andric SourceInfo &source = sources[file.filenameToIdx.find(Filename)->second]; 7315ffd83dbSDimitry Andric source.name = CoveragePath; 7325ffd83dbSDimitry Andric source.coverage = FileCoverage; 7330b57cec5SDimitry Andric } 7340b57cec5SDimitry Andric 7355ffd83dbSDimitry Andric if (Options.Intermediate && !Options.NoOutput) { 7365ffd83dbSDimitry Andric // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0 7375ffd83dbSDimitry Andric // (PR GCC/82702). We create just one file. 7385ffd83dbSDimitry Andric std::string outputPath(sys::path::filename(MainFilename)); 7395ffd83dbSDimitry Andric std::error_code ec; 7405ffd83dbSDimitry Andric raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_Text); 7415ffd83dbSDimitry Andric if (ec) { 7425ffd83dbSDimitry Andric errs() << ec.message() << "\n"; 7435ffd83dbSDimitry Andric return; 7445ffd83dbSDimitry Andric } 7455ffd83dbSDimitry Andric 7465ffd83dbSDimitry Andric for (const SourceInfo &source : sources) { 7475ffd83dbSDimitry Andric os << "file:" << source.filename << '\n'; 7485ffd83dbSDimitry Andric for (const GCOVFunction *f : source.functions) 7495ffd83dbSDimitry Andric os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' 7505ffd83dbSDimitry Andric << f->Name << '\n'; 7515ffd83dbSDimitry Andric const LineData &line = LineInfo[source.filename]; 7525ffd83dbSDimitry Andric for (uint32_t lineNum = 0; lineNum != line.LastLine; ++lineNum) { 7535ffd83dbSDimitry Andric BlockLines::const_iterator BlocksIt = line.Blocks.find(lineNum); 7545ffd83dbSDimitry Andric if (BlocksIt == line.Blocks.end()) 7555ffd83dbSDimitry Andric continue; 7565ffd83dbSDimitry Andric const BlockVector &blocks = BlocksIt->second; 7575ffd83dbSDimitry Andric // GCC 8 (r254259) added third third field for Ada: 7585ffd83dbSDimitry Andric // lcount:<line>,<count>,<has_unexecuted_blocks> 7595ffd83dbSDimitry Andric // We don't need the third field. 7605ffd83dbSDimitry Andric os << "lcount:" << (lineNum + 1) << ',' 7615ffd83dbSDimitry Andric << GCOVBlock::getLineCount(blocks) << '\n'; 7625ffd83dbSDimitry Andric 7635ffd83dbSDimitry Andric if (!Options.BranchInfo) 7645ffd83dbSDimitry Andric continue; 7655ffd83dbSDimitry Andric for (const GCOVBlock *block : blocks) { 7665ffd83dbSDimitry Andric if (block->getLastLine() != lineNum + 1 || 7675ffd83dbSDimitry Andric block->getNumDstEdges() < 2) 7685ffd83dbSDimitry Andric continue; 7695ffd83dbSDimitry Andric for (const GCOVArc *arc : block->dsts()) { 7705ffd83dbSDimitry Andric const char *type = block->getCount() 7715ffd83dbSDimitry Andric ? arc->Count ? "taken" : "nottaken" 7725ffd83dbSDimitry Andric : "notexec"; 7735ffd83dbSDimitry Andric os << "branch:" << (lineNum + 1) << ',' << type << '\n'; 7745ffd83dbSDimitry Andric } 7755ffd83dbSDimitry Andric } 7765ffd83dbSDimitry Andric } 7775ffd83dbSDimitry Andric } 7785ffd83dbSDimitry Andric } 7795ffd83dbSDimitry Andric 7805ffd83dbSDimitry Andric if (!Options.UseStdout) { 7810b57cec5SDimitry Andric // FIXME: There is no way to detect calls given current instrumentation. 7820b57cec5SDimitry Andric if (Options.FuncCoverage) 7830b57cec5SDimitry Andric printFuncCoverage(InfoOS); 7840b57cec5SDimitry Andric printFileCoverage(InfoOS); 7850b57cec5SDimitry Andric } 7865ffd83dbSDimitry Andric } 7870b57cec5SDimitry Andric 7880b57cec5SDimitry Andric /// printFunctionSummary - Print function and block summary. 7890b57cec5SDimitry Andric void FileInfo::printFunctionSummary(raw_ostream &OS, 7900b57cec5SDimitry Andric const FunctionVector &Funcs) const { 7910b57cec5SDimitry Andric for (const GCOVFunction *Func : Funcs) { 7920b57cec5SDimitry Andric uint64_t EntryCount = Func->getEntryCount(); 7930b57cec5SDimitry Andric uint32_t BlocksExec = 0; 7940b57cec5SDimitry Andric for (const GCOVBlock &Block : Func->blocks()) 7950b57cec5SDimitry Andric if (Block.getNumDstEdges() && Block.getCount()) 7960b57cec5SDimitry Andric ++BlocksExec; 7970b57cec5SDimitry Andric 7980b57cec5SDimitry Andric OS << "function " << Func->getName() << " called " << EntryCount 7990b57cec5SDimitry Andric << " returned " << safeDiv(Func->getExitCount() * 100, EntryCount) 8000b57cec5SDimitry Andric << "% blocks executed " 8010b57cec5SDimitry Andric << safeDiv(BlocksExec * 100, Func->getNumBlocks() - 1) << "%\n"; 8020b57cec5SDimitry Andric } 8030b57cec5SDimitry Andric } 8040b57cec5SDimitry Andric 8050b57cec5SDimitry Andric /// printBlockInfo - Output counts for each block. 8060b57cec5SDimitry Andric void FileInfo::printBlockInfo(raw_ostream &OS, const GCOVBlock &Block, 8070b57cec5SDimitry Andric uint32_t LineIndex, uint32_t &BlockNo) const { 8080b57cec5SDimitry Andric if (Block.getCount() == 0) 8090b57cec5SDimitry Andric OS << " $$$$$:"; 8100b57cec5SDimitry Andric else 8110b57cec5SDimitry Andric OS << format("%9" PRIu64 ":", Block.getCount()); 8120b57cec5SDimitry Andric OS << format("%5u-block %2u\n", LineIndex + 1, BlockNo++); 8130b57cec5SDimitry Andric } 8140b57cec5SDimitry Andric 8150b57cec5SDimitry Andric /// printBranchInfo - Print conditional branch probabilities. 8160b57cec5SDimitry Andric void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block, 8170b57cec5SDimitry Andric GCOVCoverage &Coverage, uint32_t &EdgeNo) { 8180b57cec5SDimitry Andric SmallVector<uint64_t, 16> BranchCounts; 8190b57cec5SDimitry Andric uint64_t TotalCounts = 0; 8205ffd83dbSDimitry Andric for (const GCOVArc *Edge : Block.dsts()) { 8210b57cec5SDimitry Andric BranchCounts.push_back(Edge->Count); 8220b57cec5SDimitry Andric TotalCounts += Edge->Count; 8230b57cec5SDimitry Andric if (Block.getCount()) 8240b57cec5SDimitry Andric ++Coverage.BranchesExec; 8250b57cec5SDimitry Andric if (Edge->Count) 8260b57cec5SDimitry Andric ++Coverage.BranchesTaken; 8270b57cec5SDimitry Andric ++Coverage.Branches; 8280b57cec5SDimitry Andric 8290b57cec5SDimitry Andric if (Options.FuncCoverage) { 8300b57cec5SDimitry Andric const GCOVFunction *Function = &Block.getParent(); 8310b57cec5SDimitry Andric GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second; 8320b57cec5SDimitry Andric if (Block.getCount()) 8330b57cec5SDimitry Andric ++FuncCoverage.BranchesExec; 8340b57cec5SDimitry Andric if (Edge->Count) 8350b57cec5SDimitry Andric ++FuncCoverage.BranchesTaken; 8360b57cec5SDimitry Andric ++FuncCoverage.Branches; 8370b57cec5SDimitry Andric } 8380b57cec5SDimitry Andric } 8390b57cec5SDimitry Andric 8400b57cec5SDimitry Andric for (uint64_t N : BranchCounts) 8410b57cec5SDimitry Andric OS << format("branch %2u ", EdgeNo++) 8420b57cec5SDimitry Andric << formatBranchInfo(Options, N, TotalCounts) << "\n"; 8430b57cec5SDimitry Andric } 8440b57cec5SDimitry Andric 8450b57cec5SDimitry Andric /// printUncondBranchInfo - Print unconditional branch probabilities. 8460b57cec5SDimitry Andric void FileInfo::printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo, 8470b57cec5SDimitry Andric uint64_t Count) const { 8480b57cec5SDimitry Andric OS << format("unconditional %2u ", EdgeNo++) 8490b57cec5SDimitry Andric << formatBranchInfo(Options, Count, Count) << "\n"; 8500b57cec5SDimitry Andric } 8510b57cec5SDimitry Andric 8520b57cec5SDimitry Andric // printCoverage - Print generic coverage info used by both printFuncCoverage 8530b57cec5SDimitry Andric // and printFileCoverage. 8540b57cec5SDimitry Andric void FileInfo::printCoverage(raw_ostream &OS, 8550b57cec5SDimitry Andric const GCOVCoverage &Coverage) const { 8560b57cec5SDimitry Andric OS << format("Lines executed:%.2f%% of %u\n", 8570b57cec5SDimitry Andric double(Coverage.LinesExec) * 100 / Coverage.LogicalLines, 8580b57cec5SDimitry Andric Coverage.LogicalLines); 8590b57cec5SDimitry Andric if (Options.BranchInfo) { 8600b57cec5SDimitry Andric if (Coverage.Branches) { 8610b57cec5SDimitry Andric OS << format("Branches executed:%.2f%% of %u\n", 8620b57cec5SDimitry Andric double(Coverage.BranchesExec) * 100 / Coverage.Branches, 8630b57cec5SDimitry Andric Coverage.Branches); 8640b57cec5SDimitry Andric OS << format("Taken at least once:%.2f%% of %u\n", 8650b57cec5SDimitry Andric double(Coverage.BranchesTaken) * 100 / Coverage.Branches, 8660b57cec5SDimitry Andric Coverage.Branches); 8670b57cec5SDimitry Andric } else { 8680b57cec5SDimitry Andric OS << "No branches\n"; 8690b57cec5SDimitry Andric } 8700b57cec5SDimitry Andric OS << "No calls\n"; // to be consistent with gcov 8710b57cec5SDimitry Andric } 8720b57cec5SDimitry Andric } 8730b57cec5SDimitry Andric 8740b57cec5SDimitry Andric // printFuncCoverage - Print per-function coverage info. 8750b57cec5SDimitry Andric void FileInfo::printFuncCoverage(raw_ostream &OS) const { 8760b57cec5SDimitry Andric for (const auto &FC : FuncCoverages) { 8770b57cec5SDimitry Andric const GCOVCoverage &Coverage = FC.second; 8780b57cec5SDimitry Andric OS << "Function '" << Coverage.Name << "'\n"; 8790b57cec5SDimitry Andric printCoverage(OS, Coverage); 8800b57cec5SDimitry Andric OS << "\n"; 8810b57cec5SDimitry Andric } 8820b57cec5SDimitry Andric } 8830b57cec5SDimitry Andric 8840b57cec5SDimitry Andric // printFileCoverage - Print per-file coverage info. 8850b57cec5SDimitry Andric void FileInfo::printFileCoverage(raw_ostream &OS) const { 8865ffd83dbSDimitry Andric for (const SourceInfo &source : sources) { 8875ffd83dbSDimitry Andric const GCOVCoverage &Coverage = source.coverage; 8880b57cec5SDimitry Andric OS << "File '" << Coverage.Name << "'\n"; 8890b57cec5SDimitry Andric printCoverage(OS, Coverage); 8905ffd83dbSDimitry Andric if (!Options.NoOutput && !Options.Intermediate) 8915ffd83dbSDimitry Andric OS << "Creating '" << source.name << "'\n"; 8920b57cec5SDimitry Andric OS << "\n"; 8930b57cec5SDimitry Andric } 8940b57cec5SDimitry Andric } 895