1 //===- GCOV.cpp - LLVM coverage tool --------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // GCOV implements the interface to read and write coverage files that use 10 // 'gcov' format. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/ProfileData/GCOV.h" 15 #include "llvm/ADT/STLExtras.h" 16 #include "llvm/ADT/SmallSet.h" 17 #include "llvm/Config/llvm-config.h" 18 #include "llvm/Demangle/Demangle.h" 19 #include "llvm/Support/Debug.h" 20 #include "llvm/Support/FileSystem.h" 21 #include "llvm/Support/Format.h" 22 #include "llvm/Support/MD5.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/raw_ostream.h" 25 #include <algorithm> 26 #include <optional> 27 #include <system_error> 28 29 using namespace llvm; 30 31 enum : uint32_t { 32 GCOV_ARC_ON_TREE = 1 << 0, 33 GCOV_ARC_FALLTHROUGH = 1 << 2, 34 35 GCOV_TAG_FUNCTION = 0x01000000, 36 GCOV_TAG_BLOCKS = 0x01410000, 37 GCOV_TAG_ARCS = 0x01430000, 38 GCOV_TAG_LINES = 0x01450000, 39 GCOV_TAG_COUNTER_ARCS = 0x01a10000, 40 // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9. 41 GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, 42 GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, 43 }; 44 45 namespace { 46 struct Summary { 47 Summary(StringRef Name) : Name(Name) {} 48 49 StringRef Name; 50 uint64_t lines = 0; 51 uint64_t linesExec = 0; 52 uint64_t branches = 0; 53 uint64_t branchesExec = 0; 54 uint64_t branchesTaken = 0; 55 }; 56 57 struct LineInfo { 58 SmallVector<const GCOVBlock *, 1> blocks; 59 uint64_t count = 0; 60 bool exists = false; 61 }; 62 63 struct SourceInfo { 64 StringRef filename; 65 SmallString<0> displayName; 66 std::vector<std::vector<const GCOVFunction *>> startLineToFunctions; 67 std::vector<LineInfo> lines; 68 bool ignored = false; 69 SourceInfo(StringRef filename) : filename(filename) {} 70 }; 71 72 class Context { 73 public: 74 Context(const GCOV::Options &Options) : options(Options) {} 75 void print(StringRef filename, StringRef gcno, StringRef gcda, 76 GCOVFile &file); 77 78 private: 79 std::string getCoveragePath(StringRef filename, StringRef mainFilename) const; 80 void printFunctionDetails(const GCOVFunction &f, raw_ostream &os) const; 81 void printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, 82 raw_ostream &OS) const; 83 void printSummary(const Summary &summary, raw_ostream &os) const; 84 85 void collectFunction(GCOVFunction &f, Summary &summary); 86 void collectSourceLine(SourceInfo &si, Summary *summary, LineInfo &line, 87 size_t lineNum) const; 88 void collectSource(SourceInfo &si, Summary &summary) const; 89 void annotateSource(SourceInfo &si, const GCOVFile &file, StringRef gcno, 90 StringRef gcda, raw_ostream &os) const; 91 void printSourceToIntermediate(const SourceInfo &si, raw_ostream &os) const; 92 93 const GCOV::Options &options; 94 std::vector<SourceInfo> sources; 95 }; 96 } // namespace 97 98 //===----------------------------------------------------------------------===// 99 // GCOVFile implementation. 100 101 /// readGCNO - Read GCNO buffer. 102 bool GCOVFile::readGCNO(GCOVBuffer &buf) { 103 if (!buf.readGCNOFormat()) 104 return false; 105 if (!buf.readGCOVVersion(version)) 106 return false; 107 108 checksum = buf.getWord(); 109 if (version >= GCOV::V900 && !buf.readString(cwd)) 110 return false; 111 if (version >= GCOV::V800) 112 buf.getWord(); // hasUnexecutedBlocks 113 114 uint32_t tag, length; 115 GCOVFunction *fn = nullptr; 116 while ((tag = buf.getWord())) { 117 if (!buf.readInt(length)) 118 return false; 119 uint32_t pos = buf.cursor.tell(); 120 if (tag == GCOV_TAG_FUNCTION) { 121 functions.push_back(std::make_unique<GCOVFunction>(*this)); 122 fn = functions.back().get(); 123 fn->ident = buf.getWord(); 124 fn->linenoChecksum = buf.getWord(); 125 if (version >= GCOV::V407) 126 fn->cfgChecksum = buf.getWord(); 127 buf.readString(fn->Name); 128 StringRef filename; 129 if (version < GCOV::V800) { 130 if (!buf.readString(filename)) 131 return false; 132 fn->startLine = buf.getWord(); 133 } else { 134 fn->artificial = buf.getWord(); 135 if (!buf.readString(filename)) 136 return false; 137 fn->startLine = buf.getWord(); 138 fn->startColumn = buf.getWord(); 139 fn->endLine = buf.getWord(); 140 if (version >= GCOV::V900) 141 fn->endColumn = buf.getWord(); 142 } 143 fn->srcIdx = addNormalizedPathToMap(filename); 144 identToFunction[fn->ident] = fn; 145 } else if (tag == GCOV_TAG_BLOCKS && fn) { 146 if (version < GCOV::V800) { 147 for (uint32_t i = 0; i != length; ++i) { 148 buf.getWord(); // Ignored block flags 149 fn->blocks.push_back(std::make_unique<GCOVBlock>(i)); 150 } 151 } else { 152 uint32_t num = buf.getWord(); 153 for (uint32_t i = 0; i != num; ++i) 154 fn->blocks.push_back(std::make_unique<GCOVBlock>(i)); 155 } 156 } else if (tag == GCOV_TAG_ARCS && fn) { 157 uint32_t srcNo = buf.getWord(); 158 if (srcNo >= fn->blocks.size()) { 159 errs() << "unexpected block number: " << srcNo << " (in " 160 << fn->blocks.size() << ")\n"; 161 return false; 162 } 163 GCOVBlock *src = fn->blocks[srcNo].get(); 164 const uint32_t e = 165 version >= GCOV::V1200 ? (length / 4 - 1) / 2 : (length - 1) / 2; 166 for (uint32_t i = 0; i != e; ++i) { 167 uint32_t dstNo = buf.getWord(), flags = buf.getWord(); 168 GCOVBlock *dst = fn->blocks[dstNo].get(); 169 auto arc = std::make_unique<GCOVArc>(*src, *dst, flags); 170 src->addDstEdge(arc.get()); 171 dst->addSrcEdge(arc.get()); 172 if (arc->onTree()) 173 fn->treeArcs.push_back(std::move(arc)); 174 else 175 fn->arcs.push_back(std::move(arc)); 176 } 177 } else if (tag == GCOV_TAG_LINES && fn) { 178 uint32_t srcNo = buf.getWord(); 179 if (srcNo >= fn->blocks.size()) { 180 errs() << "unexpected block number: " << srcNo << " (in " 181 << fn->blocks.size() << ")\n"; 182 return false; 183 } 184 GCOVBlock &Block = *fn->blocks[srcNo]; 185 for (;;) { 186 uint32_t line = buf.getWord(); 187 if (line) 188 Block.addLine(line); 189 else { 190 StringRef filename; 191 buf.readString(filename); 192 if (filename.empty()) 193 break; 194 // TODO Unhandled 195 } 196 } 197 } 198 pos += version >= GCOV::V1200 ? length : 4 * length; 199 if (pos < buf.cursor.tell()) 200 return false; 201 buf.de.skip(buf.cursor, pos - buf.cursor.tell()); 202 } 203 204 GCNOInitialized = true; 205 return true; 206 } 207 208 /// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be 209 /// called after readGCNO(). 210 bool GCOVFile::readGCDA(GCOVBuffer &buf) { 211 assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()"); 212 if (!buf.readGCDAFormat()) 213 return false; 214 GCOV::GCOVVersion GCDAVersion; 215 if (!buf.readGCOVVersion(GCDAVersion)) 216 return false; 217 if (version != GCDAVersion) { 218 errs() << "GCOV versions do not match.\n"; 219 return false; 220 } 221 222 uint32_t GCDAChecksum; 223 if (!buf.readInt(GCDAChecksum)) 224 return false; 225 if (checksum != GCDAChecksum) { 226 errs() << "file checksums do not match: " << checksum 227 << " != " << GCDAChecksum << "\n"; 228 return false; 229 } 230 uint32_t dummy, tag, length; 231 uint32_t ident; 232 GCOVFunction *fn = nullptr; 233 while ((tag = buf.getWord())) { 234 if (!buf.readInt(length)) 235 return false; 236 uint32_t pos = buf.cursor.tell(); 237 if (tag == GCOV_TAG_OBJECT_SUMMARY) { 238 buf.readInt(runCount); 239 buf.readInt(dummy); 240 // clang<11 uses a fake 4.2 format which sets length to 9. 241 if (length == 9) 242 buf.readInt(runCount); 243 } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) { 244 // clang<11 uses a fake 4.2 format which sets length to 0. 245 if (length > 0) { 246 buf.readInt(dummy); 247 buf.readInt(dummy); 248 buf.readInt(runCount); 249 } 250 ++programCount; 251 } else if (tag == GCOV_TAG_FUNCTION) { 252 if (length == 0) // Placeholder 253 continue; 254 // As of GCC 10, GCOV_TAG_FUNCTION_LENGTH has never been larger than 3. 255 // However, clang<11 uses a fake 4.2 format which may set length larger 256 // than 3. 257 if (length < 2 || !buf.readInt(ident)) 258 return false; 259 auto It = identToFunction.find(ident); 260 uint32_t linenoChecksum, cfgChecksum = 0; 261 buf.readInt(linenoChecksum); 262 if (version >= GCOV::V407) 263 buf.readInt(cfgChecksum); 264 if (It != identToFunction.end()) { 265 fn = It->second; 266 if (linenoChecksum != fn->linenoChecksum || 267 cfgChecksum != fn->cfgChecksum) { 268 errs() << fn->Name 269 << format(": checksum mismatch, (%u, %u) != (%u, %u)\n", 270 linenoChecksum, cfgChecksum, fn->linenoChecksum, 271 fn->cfgChecksum); 272 return false; 273 } 274 } 275 } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) { 276 uint32_t expected = 2 * fn->arcs.size(); 277 if (version >= GCOV::V1200) 278 expected *= 4; 279 if (length != expected) { 280 errs() << fn->Name 281 << format( 282 ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n", 283 length, expected); 284 return false; 285 } 286 for (std::unique_ptr<GCOVArc> &arc : fn->arcs) { 287 if (!buf.readInt64(arc->count)) 288 return false; 289 arc->src.count += arc->count; 290 } 291 292 if (fn->blocks.size() >= 2) { 293 GCOVBlock &src = *fn->blocks[0]; 294 GCOVBlock &sink = 295 version < GCOV::V408 ? *fn->blocks.back() : *fn->blocks[1]; 296 auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE); 297 sink.addDstEdge(arc.get()); 298 src.addSrcEdge(arc.get()); 299 fn->treeArcs.push_back(std::move(arc)); 300 301 for (GCOVBlock &block : fn->blocksRange()) 302 fn->propagateCounts(block, nullptr); 303 for (size_t i = fn->treeArcs.size() - 1; i; --i) 304 fn->treeArcs[i - 1]->src.count += fn->treeArcs[i - 1]->count; 305 } 306 } 307 pos += version >= GCOV::V1200 ? length : 4 * length; 308 if (pos < buf.cursor.tell()) 309 return false; 310 buf.de.skip(buf.cursor, pos - buf.cursor.tell()); 311 } 312 313 return true; 314 } 315 316 void GCOVFile::print(raw_ostream &OS) const { 317 for (const GCOVFunction &f : *this) 318 f.print(OS); 319 } 320 321 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 322 /// dump - Dump GCOVFile content to dbgs() for debugging purposes. 323 LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } 324 #endif 325 326 unsigned GCOVFile::addNormalizedPathToMap(StringRef filename) { 327 // unify filename, as the same path can have different form 328 SmallString<256> P(filename); 329 sys::path::remove_dots(P, true); 330 filename = P.str(); 331 332 auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size()); 333 if (r.second) 334 filenames.emplace_back(filename); 335 336 return r.first->second; 337 } 338 339 bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; } 340 341 //===----------------------------------------------------------------------===// 342 // GCOVFunction implementation. 343 344 StringRef GCOVFunction::getName(bool demangle) const { 345 if (!demangle) 346 return Name; 347 if (demangled.empty()) { 348 do { 349 if (Name.startswith("_Z")) { 350 // Name is guaranteed to be NUL-terminated. 351 if (char *res = itaniumDemangle(Name.data())) { 352 demangled = res; 353 free(res); 354 break; 355 } 356 } 357 demangled = Name; 358 } while (false); 359 } 360 return demangled; 361 } 362 StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; } 363 364 /// getEntryCount - Get the number of times the function was called by 365 /// retrieving the entry block's count. 366 uint64_t GCOVFunction::getEntryCount() const { 367 return blocks.front()->getCount(); 368 } 369 370 GCOVBlock &GCOVFunction::getExitBlock() const { 371 return file.getVersion() < GCOV::V408 ? *blocks.back() : *blocks[1]; 372 } 373 374 // For each basic block, the sum of incoming edge counts equals the sum of 375 // outgoing edge counts by Kirchoff's circuit law. If the unmeasured arcs form a 376 // spanning tree, the count for each unmeasured arc (GCOV_ARC_ON_TREE) can be 377 // uniquely identified. 378 uint64_t GCOVFunction::propagateCounts(const GCOVBlock &v, GCOVArc *pred) { 379 // If GCOV_ARC_ON_TREE edges do form a tree, visited is not needed; otherwise 380 // this prevents infinite recursion. 381 if (!visited.insert(&v).second) 382 return 0; 383 384 uint64_t excess = 0; 385 for (GCOVArc *e : v.srcs()) 386 if (e != pred) 387 excess += e->onTree() ? propagateCounts(e->src, e) : e->count; 388 for (GCOVArc *e : v.dsts()) 389 if (e != pred) 390 excess -= e->onTree() ? propagateCounts(e->dst, e) : e->count; 391 if (int64_t(excess) < 0) 392 excess = -excess; 393 if (pred) 394 pred->count = excess; 395 return excess; 396 } 397 398 void GCOVFunction::print(raw_ostream &OS) const { 399 OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":" 400 << startLine << "\n"; 401 for (const auto &Block : blocks) 402 Block->print(OS); 403 } 404 405 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 406 /// dump - Dump GCOVFunction content to dbgs() for debugging purposes. 407 LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } 408 #endif 409 410 /// collectLineCounts - Collect line counts. This must be used after 411 /// reading .gcno and .gcda files. 412 413 //===----------------------------------------------------------------------===// 414 // GCOVBlock implementation. 415 416 void GCOVBlock::print(raw_ostream &OS) const { 417 OS << "Block : " << number << " Counter : " << count << "\n"; 418 if (!pred.empty()) { 419 OS << "\tSource Edges : "; 420 for (const GCOVArc *Edge : pred) 421 OS << Edge->src.number << " (" << Edge->count << "), "; 422 OS << "\n"; 423 } 424 if (!succ.empty()) { 425 OS << "\tDestination Edges : "; 426 for (const GCOVArc *Edge : succ) { 427 if (Edge->flags & GCOV_ARC_ON_TREE) 428 OS << '*'; 429 OS << Edge->dst.number << " (" << Edge->count << "), "; 430 } 431 OS << "\n"; 432 } 433 if (!lines.empty()) { 434 OS << "\tLines : "; 435 for (uint32_t N : lines) 436 OS << (N) << ","; 437 OS << "\n"; 438 } 439 } 440 441 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 442 /// dump - Dump GCOVBlock content to dbgs() for debugging purposes. 443 LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } 444 #endif 445 446 uint64_t 447 GCOVBlock::augmentOneCycle(GCOVBlock *src, 448 std::vector<std::pair<GCOVBlock *, size_t>> &stack) { 449 GCOVBlock *u; 450 size_t i; 451 stack.clear(); 452 stack.emplace_back(src, 0); 453 src->incoming = (GCOVArc *)1; // Mark u available for cycle detection 454 for (;;) { 455 std::tie(u, i) = stack.back(); 456 if (i == u->succ.size()) { 457 u->traversable = false; 458 stack.pop_back(); 459 if (stack.empty()) 460 break; 461 continue; 462 } 463 ++stack.back().second; 464 GCOVArc *succ = u->succ[i]; 465 // Ignore saturated arcs (cycleCount has been reduced to 0) and visited 466 // blocks. Ignore self arcs to guard against bad input (.gcno has no 467 // self arcs). 468 if (succ->cycleCount == 0 || !succ->dst.traversable || &succ->dst == u) 469 continue; 470 if (succ->dst.incoming == nullptr) { 471 succ->dst.incoming = succ; 472 stack.emplace_back(&succ->dst, 0); 473 continue; 474 } 475 uint64_t minCount = succ->cycleCount; 476 for (GCOVBlock *v = u;;) { 477 minCount = std::min(minCount, v->incoming->cycleCount); 478 v = &v->incoming->src; 479 if (v == &succ->dst) 480 break; 481 } 482 succ->cycleCount -= minCount; 483 for (GCOVBlock *v = u;;) { 484 v->incoming->cycleCount -= minCount; 485 v = &v->incoming->src; 486 if (v == &succ->dst) 487 break; 488 } 489 return minCount; 490 } 491 return 0; 492 } 493 494 // Get the total execution count of loops among blocks on the same line. 495 // Assuming a reducible flow graph, the count is the sum of back edge counts. 496 // Identifying loops is complex, so we simply find cycles and perform cycle 497 // cancelling iteratively. 498 uint64_t GCOVBlock::getCyclesCount(const BlockVector &blocks) { 499 std::vector<std::pair<GCOVBlock *, size_t>> stack; 500 uint64_t count = 0, d; 501 for (;;) { 502 // Make blocks on the line traversable and try finding a cycle. 503 for (const auto *b : blocks) { 504 const_cast<GCOVBlock *>(b)->traversable = true; 505 const_cast<GCOVBlock *>(b)->incoming = nullptr; 506 } 507 d = 0; 508 for (const auto *block : blocks) { 509 auto *b = const_cast<GCOVBlock *>(block); 510 if (b->traversable && (d = augmentOneCycle(b, stack)) > 0) 511 break; 512 } 513 if (d == 0) 514 break; 515 count += d; 516 } 517 // If there is no more loop, all traversable bits should have been cleared. 518 // This property is needed by subsequent calls. 519 for (const auto *b : blocks) { 520 assert(!b->traversable); 521 (void)b; 522 } 523 return count; 524 } 525 526 //===----------------------------------------------------------------------===// 527 // FileInfo implementation. 528 529 // Format dividend/divisor as a percentage. Return 1 if the result is greater 530 // than 0% and less than 1%. 531 static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) { 532 if (!dividend || !divisor) 533 return 0; 534 dividend *= 100; 535 return dividend < divisor ? 1 : dividend / divisor; 536 } 537 538 // This custom division function mimics gcov's branch ouputs: 539 // - Round to closest whole number 540 // - Only output 0% or 100% if it's exactly that value 541 static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) { 542 if (!Numerator) 543 return 0; 544 if (Numerator == Divisor) 545 return 100; 546 547 uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor; 548 if (Res == 0) 549 return 1; 550 if (Res == 100) 551 return 99; 552 return Res; 553 } 554 555 namespace { 556 struct formatBranchInfo { 557 formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) 558 : Options(Options), Count(Count), Total(Total) {} 559 560 void print(raw_ostream &OS) const { 561 if (!Total) 562 OS << "never executed"; 563 else if (Options.BranchCount) 564 OS << "taken " << Count; 565 else 566 OS << "taken " << branchDiv(Count, Total) << "%"; 567 } 568 569 const GCOV::Options &Options; 570 uint64_t Count; 571 uint64_t Total; 572 }; 573 574 static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { 575 FBI.print(OS); 576 return OS; 577 } 578 579 class LineConsumer { 580 std::unique_ptr<MemoryBuffer> Buffer; 581 StringRef Remaining; 582 583 public: 584 LineConsumer() = default; 585 LineConsumer(StringRef Filename) { 586 // Open source files without requiring a NUL terminator. The concurrent 587 // modification may nullify the NUL terminator condition. 588 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = 589 MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/false, 590 /*RequiresNullTerminator=*/false); 591 if (std::error_code EC = BufferOrErr.getError()) { 592 errs() << Filename << ": " << EC.message() << "\n"; 593 Remaining = ""; 594 } else { 595 Buffer = std::move(BufferOrErr.get()); 596 Remaining = Buffer->getBuffer(); 597 } 598 } 599 bool empty() { return Remaining.empty(); } 600 void printNext(raw_ostream &OS, uint32_t LineNum) { 601 StringRef Line; 602 if (empty()) 603 Line = "/*EOF*/"; 604 else 605 std::tie(Line, Remaining) = Remaining.split("\n"); 606 OS << format("%5u:", LineNum) << Line << "\n"; 607 } 608 }; 609 } // end anonymous namespace 610 611 /// Convert a path to a gcov filename. If PreservePaths is true, this 612 /// translates "/" to "#", ".." to "^", and drops ".", to match gcov. 613 static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { 614 if (!PreservePaths) 615 return sys::path::filename(Filename).str(); 616 617 // This behaviour is defined by gcov in terms of text replacements, so it's 618 // not likely to do anything useful on filesystems with different textual 619 // conventions. 620 llvm::SmallString<256> Result(""); 621 StringRef::iterator I, S, E; 622 for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { 623 if (*I != '/') 624 continue; 625 626 if (I - S == 1 && *S == '.') { 627 // ".", the current directory, is skipped. 628 } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { 629 // "..", the parent directory, is replaced with "^". 630 Result.append("^#"); 631 } else { 632 if (S < I) 633 // Leave other components intact, 634 Result.append(S, I); 635 // And separate with "#". 636 Result.push_back('#'); 637 } 638 S = I + 1; 639 } 640 641 if (S < I) 642 Result.append(S, I); 643 return std::string(Result.str()); 644 } 645 646 std::string Context::getCoveragePath(StringRef filename, 647 StringRef mainFilename) const { 648 if (options.NoOutput) 649 // This is probably a bug in gcov, but when -n is specified, paths aren't 650 // mangled at all, and the -l and -p options are ignored. Here, we do the 651 // same. 652 return std::string(filename); 653 654 std::string CoveragePath; 655 if (options.LongFileNames && !filename.equals(mainFilename)) 656 CoveragePath = 657 mangleCoveragePath(mainFilename, options.PreservePaths) + "##"; 658 CoveragePath += mangleCoveragePath(filename, options.PreservePaths); 659 if (options.HashFilenames) { 660 MD5 Hasher; 661 MD5::MD5Result Result; 662 Hasher.update(filename.str()); 663 Hasher.final(Result); 664 CoveragePath += "##" + std::string(Result.digest()); 665 } 666 CoveragePath += ".gcov"; 667 return CoveragePath; 668 } 669 670 void Context::collectFunction(GCOVFunction &f, Summary &summary) { 671 SourceInfo &si = sources[f.srcIdx]; 672 if (f.startLine >= si.startLineToFunctions.size()) 673 si.startLineToFunctions.resize(f.startLine + 1); 674 si.startLineToFunctions[f.startLine].push_back(&f); 675 SmallSet<uint32_t, 16> lines; 676 SmallSet<uint32_t, 16> linesExec; 677 for (const GCOVBlock &b : f.blocksRange()) { 678 if (b.lines.empty()) 679 continue; 680 uint32_t maxLineNum = *std::max_element(b.lines.begin(), b.lines.end()); 681 if (maxLineNum >= si.lines.size()) 682 si.lines.resize(maxLineNum + 1); 683 for (uint32_t lineNum : b.lines) { 684 LineInfo &line = si.lines[lineNum]; 685 if (lines.insert(lineNum).second) 686 ++summary.lines; 687 if (b.count && linesExec.insert(lineNum).second) 688 ++summary.linesExec; 689 line.exists = true; 690 line.count += b.count; 691 line.blocks.push_back(&b); 692 } 693 } 694 } 695 696 void Context::collectSourceLine(SourceInfo &si, Summary *summary, 697 LineInfo &line, size_t lineNum) const { 698 uint64_t count = 0; 699 for (const GCOVBlock *b : line.blocks) { 700 if (b->number == 0) { 701 // For nonstandard control flows, arcs into the exit block may be 702 // duplicately counted (fork) or not be counted (abnormal exit), and thus 703 // the (exit,entry) counter may be inaccurate. Count the entry block with 704 // the outgoing arcs. 705 for (const GCOVArc *arc : b->succ) 706 count += arc->count; 707 } else { 708 // Add counts from predecessors that are not on the same line. 709 for (const GCOVArc *arc : b->pred) 710 if (!llvm::is_contained(line.blocks, &arc->src)) 711 count += arc->count; 712 } 713 for (GCOVArc *arc : b->succ) 714 arc->cycleCount = arc->count; 715 } 716 717 count += GCOVBlock::getCyclesCount(line.blocks); 718 line.count = count; 719 if (line.exists) { 720 ++summary->lines; 721 if (line.count != 0) 722 ++summary->linesExec; 723 } 724 725 if (options.BranchInfo) 726 for (const GCOVBlock *b : line.blocks) { 727 if (b->getLastLine() != lineNum) 728 continue; 729 int branches = 0, execBranches = 0, takenBranches = 0; 730 for (const GCOVArc *arc : b->succ) { 731 ++branches; 732 if (count != 0) 733 ++execBranches; 734 if (arc->count != 0) 735 ++takenBranches; 736 } 737 if (branches > 1) { 738 summary->branches += branches; 739 summary->branchesExec += execBranches; 740 summary->branchesTaken += takenBranches; 741 } 742 } 743 } 744 745 void Context::collectSource(SourceInfo &si, Summary &summary) const { 746 size_t lineNum = 0; 747 for (LineInfo &line : si.lines) { 748 collectSourceLine(si, &summary, line, lineNum); 749 ++lineNum; 750 } 751 } 752 753 void Context::annotateSource(SourceInfo &si, const GCOVFile &file, 754 StringRef gcno, StringRef gcda, 755 raw_ostream &os) const { 756 auto source = 757 options.Intermediate ? LineConsumer() : LineConsumer(si.filename); 758 759 os << " -: 0:Source:" << si.displayName << '\n'; 760 os << " -: 0:Graph:" << gcno << '\n'; 761 os << " -: 0:Data:" << gcda << '\n'; 762 os << " -: 0:Runs:" << file.runCount << '\n'; 763 if (file.version < GCOV::V900) 764 os << " -: 0:Programs:" << file.programCount << '\n'; 765 766 for (size_t lineNum = 1; !source.empty(); ++lineNum) { 767 if (lineNum >= si.lines.size()) { 768 os << " -:"; 769 source.printNext(os, lineNum); 770 continue; 771 } 772 773 const LineInfo &line = si.lines[lineNum]; 774 if (options.BranchInfo && lineNum < si.startLineToFunctions.size()) 775 for (const auto *f : si.startLineToFunctions[lineNum]) 776 printFunctionDetails(*f, os); 777 if (!line.exists) 778 os << " -:"; 779 else if (line.count == 0) 780 os << " #####:"; 781 else 782 os << format("%9" PRIu64 ":", line.count); 783 source.printNext(os, lineNum); 784 785 uint32_t blockIdx = 0, edgeIdx = 0; 786 for (const GCOVBlock *b : line.blocks) { 787 if (b->getLastLine() != lineNum) 788 continue; 789 if (options.AllBlocks) { 790 if (b->getCount() == 0) 791 os << " $$$$$:"; 792 else 793 os << format("%9" PRIu64 ":", b->count); 794 os << format("%5u-block %2u\n", lineNum, blockIdx++); 795 } 796 if (options.BranchInfo) { 797 size_t NumEdges = b->succ.size(); 798 if (NumEdges > 1) 799 printBranchInfo(*b, edgeIdx, os); 800 else if (options.UncondBranch && NumEdges == 1) { 801 uint64_t count = b->succ[0]->count; 802 os << format("unconditional %2u ", edgeIdx++) 803 << formatBranchInfo(options, count, count) << '\n'; 804 } 805 } 806 } 807 } 808 } 809 810 void Context::printSourceToIntermediate(const SourceInfo &si, 811 raw_ostream &os) const { 812 os << "file:" << si.filename << '\n'; 813 for (const auto &fs : si.startLineToFunctions) 814 for (const GCOVFunction *f : fs) 815 os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' 816 << f->getName(options.Demangle) << '\n'; 817 for (size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) { 818 const LineInfo &line = si.lines[lineNum]; 819 if (line.blocks.empty()) 820 continue; 821 // GCC 8 (r254259) added third third field for Ada: 822 // lcount:<line>,<count>,<has_unexecuted_blocks> 823 // We don't need the third field. 824 os << "lcount:" << lineNum << ',' << line.count << '\n'; 825 826 if (!options.BranchInfo) 827 continue; 828 for (const GCOVBlock *b : line.blocks) { 829 if (b->succ.size() < 2 || b->getLastLine() != lineNum) 830 continue; 831 for (const GCOVArc *arc : b->succ) { 832 const char *type = 833 b->getCount() ? arc->count ? "taken" : "nottaken" : "notexec"; 834 os << "branch:" << lineNum << ',' << type << '\n'; 835 } 836 } 837 } 838 } 839 840 void Context::print(StringRef filename, StringRef gcno, StringRef gcda, 841 GCOVFile &file) { 842 for (StringRef filename : file.filenames) { 843 sources.emplace_back(filename); 844 SourceInfo &si = sources.back(); 845 si.displayName = si.filename; 846 if (!options.SourcePrefix.empty() && 847 sys::path::replace_path_prefix(si.displayName, options.SourcePrefix, 848 "") && 849 !si.displayName.empty()) { 850 // TODO replace_path_prefix may strip the prefix even if the remaining 851 // part does not start with a separator. 852 if (sys::path::is_separator(si.displayName[0])) 853 si.displayName.erase(si.displayName.begin()); 854 else 855 si.displayName = si.filename; 856 } 857 if (options.RelativeOnly && sys::path::is_absolute(si.displayName)) 858 si.ignored = true; 859 } 860 861 raw_ostream &os = llvm::outs(); 862 for (GCOVFunction &f : make_pointee_range(file.functions)) { 863 Summary summary(f.getName(options.Demangle)); 864 collectFunction(f, summary); 865 if (options.FuncCoverage && !options.UseStdout) { 866 os << "Function '" << summary.Name << "'\n"; 867 printSummary(summary, os); 868 os << '\n'; 869 } 870 } 871 872 for (SourceInfo &si : sources) { 873 if (si.ignored) 874 continue; 875 Summary summary(si.displayName); 876 collectSource(si, summary); 877 878 // Print file summary unless -t is specified. 879 std::string gcovName = getCoveragePath(si.filename, filename); 880 if (!options.UseStdout) { 881 os << "File '" << summary.Name << "'\n"; 882 printSummary(summary, os); 883 if (!options.NoOutput && !options.Intermediate) 884 os << "Creating '" << gcovName << "'\n"; 885 os << '\n'; 886 } 887 888 if (options.NoOutput || options.Intermediate) 889 continue; 890 std::optional<raw_fd_ostream> os; 891 if (!options.UseStdout) { 892 std::error_code ec; 893 os.emplace(gcovName, ec, sys::fs::OF_TextWithCRLF); 894 if (ec) { 895 errs() << ec.message() << '\n'; 896 continue; 897 } 898 } 899 annotateSource(si, file, gcno, gcda, 900 options.UseStdout ? llvm::outs() : *os); 901 } 902 903 if (options.Intermediate && !options.NoOutput) { 904 // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0 905 // (PR GCC/82702). We create just one file. 906 std::string outputPath(sys::path::filename(filename)); 907 std::error_code ec; 908 raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_TextWithCRLF); 909 if (ec) { 910 errs() << ec.message() << '\n'; 911 return; 912 } 913 914 for (const SourceInfo &si : sources) 915 printSourceToIntermediate(si, os); 916 } 917 } 918 919 void Context::printFunctionDetails(const GCOVFunction &f, 920 raw_ostream &os) const { 921 const uint64_t entryCount = f.getEntryCount(); 922 uint32_t blocksExec = 0; 923 const GCOVBlock &exitBlock = f.getExitBlock(); 924 uint64_t exitCount = 0; 925 for (const GCOVArc *arc : exitBlock.pred) 926 exitCount += arc->count; 927 for (const GCOVBlock &b : f.blocksRange()) 928 if (b.number != 0 && &b != &exitBlock && b.getCount()) 929 ++blocksExec; 930 931 os << "function " << f.getName(options.Demangle) << " called " << entryCount 932 << " returned " << formatPercentage(exitCount, entryCount) 933 << "% blocks executed " 934 << formatPercentage(blocksExec, f.blocks.size() - 2) << "%\n"; 935 } 936 937 /// printBranchInfo - Print conditional branch probabilities. 938 void Context::printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, 939 raw_ostream &os) const { 940 uint64_t total = 0; 941 for (const GCOVArc *arc : Block.dsts()) 942 total += arc->count; 943 for (const GCOVArc *arc : Block.dsts()) 944 os << format("branch %2u ", edgeIdx++) 945 << formatBranchInfo(options, arc->count, total) << '\n'; 946 } 947 948 void Context::printSummary(const Summary &summary, raw_ostream &os) const { 949 os << format("Lines executed:%.2f%% of %" PRIu64 "\n", 950 double(summary.linesExec) * 100 / summary.lines, summary.lines); 951 if (options.BranchInfo) { 952 if (summary.branches == 0) { 953 os << "No branches\n"; 954 } else { 955 os << format("Branches executed:%.2f%% of %" PRIu64 "\n", 956 double(summary.branchesExec) * 100 / summary.branches, 957 summary.branches); 958 os << format("Taken at least once:%.2f%% of %" PRIu64 "\n", 959 double(summary.branchesTaken) * 100 / summary.branches, 960 summary.branches); 961 } 962 os << "No calls\n"; 963 } 964 } 965 966 void llvm::gcovOneInput(const GCOV::Options &options, StringRef filename, 967 StringRef gcno, StringRef gcda, GCOVFile &file) { 968 Context fi(options); 969 fi.print(filename, gcno, gcda, file); 970 } 971