LLVM 22.0.0git
GCOV.cpp
Go to the documentation of this file.
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
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/SmallSet.h"
17#include "llvm/Config/llvm-config.h"
19#include "llvm/Support/Debug.h"
21#include "llvm/Support/Format.h"
22#include "llvm/Support/MD5.h"
23#include "llvm/Support/Path.h"
25#include <algorithm>
26#include <optional>
27#include <system_error>
28
29using namespace llvm;
30
31enum : uint32_t {
34
35 GCOV_TAG_FUNCTION = 0x01000000,
36 GCOV_TAG_BLOCKS = 0x01410000,
37 GCOV_TAG_ARCS = 0x01430000,
38 GCOV_TAG_LINES = 0x01450000,
40 // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9.
43};
44
45namespace {
46struct Summary {
48
50 uint64_t lines = 0;
51 uint64_t linesExec = 0;
53 uint64_t branchesExec = 0;
54 uint64_t branchesTaken = 0;
55};
56
57struct LineInfo {
59 uint64_t count = 0;
60 bool exists = false;
61};
62
63struct SourceInfo {
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
72class Context {
73public:
75 void print(StringRef filename, StringRef gcno, StringRef gcda,
76 GCOVFile &file);
77
78private:
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
94 std::vector<SourceInfo> sources;
95};
96} // namespace
97
98//===----------------------------------------------------------------------===//
99// GCOVFile implementation.
100
101/// readGCNO - Read GCNO buffer.
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 Block.addFile(addNormalizedPathToMap(filename));
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().
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 } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) {
241 buf.readInt(dummy);
242 buf.readInt(dummy);
243 buf.readInt(runCount);
244 ++programCount;
245 } else if (tag == GCOV_TAG_FUNCTION) {
246 if (length == 0) // Placeholder
247 continue;
248 if (length < 2 || !buf.readInt(ident))
249 return false;
250 auto It = identToFunction.find(ident);
251 uint32_t linenoChecksum, cfgChecksum = 0;
252 buf.readInt(linenoChecksum);
253 if (version >= GCOV::V407)
254 buf.readInt(cfgChecksum);
255 if (It != identToFunction.end()) {
256 fn = It->second;
257 if (linenoChecksum != fn->linenoChecksum ||
258 cfgChecksum != fn->cfgChecksum) {
259 errs() << fn->Name
260 << format(": checksum mismatch, (%u, %u) != (%u, %u)\n",
261 linenoChecksum, cfgChecksum, fn->linenoChecksum,
262 fn->cfgChecksum);
263 return false;
264 }
265 }
266 } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) {
267 uint32_t expected = 2 * fn->arcs.size();
268 if (version >= GCOV::V1200)
269 expected *= 4;
270 if (length != expected) {
271 errs() << fn->Name
272 << format(
273 ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n",
274 length, expected);
275 return false;
276 }
277 for (std::unique_ptr<GCOVArc> &arc : fn->arcs) {
278 if (!buf.readInt64(arc->count))
279 return false;
280 arc->src.count += arc->count;
281 }
282
283 if (fn->blocks.size() >= 2) {
284 GCOVBlock &src = *fn->blocks[0];
285 GCOVBlock &sink =
286 version < GCOV::V408 ? *fn->blocks.back() : *fn->blocks[1];
287 auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE);
288 sink.addDstEdge(arc.get());
289 src.addSrcEdge(arc.get());
290 fn->treeArcs.push_back(std::move(arc));
291
292 for (GCOVBlock &block : fn->blocksRange())
293 fn->propagateCounts(block, nullptr);
294 for (size_t i = fn->treeArcs.size() - 1; i; --i)
295 fn->treeArcs[i - 1]->src.count += fn->treeArcs[i - 1]->count;
296 }
297 }
298 pos += version >= GCOV::V1200 ? length : 4 * length;
299 if (pos < buf.cursor.tell())
300 return false;
301 buf.de.skip(buf.cursor, pos - buf.cursor.tell());
302 }
303
304 return true;
305}
306
308 for (const GCOVFunction &f : *this)
309 f.print(OS);
310}
311
312#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
313/// dump - Dump GCOVFile content to dbgs() for debugging purposes.
315#endif
316
317unsigned GCOVFile::addNormalizedPathToMap(StringRef filename) {
318 // unify filename, as the same path can have different form
319 SmallString<256> P(filename);
321 filename = P.str();
322
323 auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size());
324 if (r.second)
325 filenames.emplace_back(filename);
326
327 return r.first->second;
328}
329
330bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; }
331
332//===----------------------------------------------------------------------===//
333// GCOVFunction implementation.
334
336 if (!demangle)
337 return Name;
338 if (demangled.empty()) {
339 do {
340 if (Name.starts_with("_Z")) {
341 // Name is guaranteed to be NUL-terminated.
342 if (char *res = itaniumDemangle(Name.data())) {
343 demangled = res;
344 free(res);
345 break;
346 }
347 }
348 demangled = Name;
349 } while (false);
350 }
351 return demangled;
352}
354
355/// getEntryCount - Get the number of times the function was called by
356/// retrieving the entry block's count.
358 return blocks.front()->getCount();
359}
360
362 return file.getVersion() < GCOV::V408 ? *blocks.back() : *blocks[1];
363}
364
365// For each basic block, the sum of incoming edge counts equals the sum of
366// outgoing edge counts by Kirchoff's circuit law. If the unmeasured arcs form a
367// spanning tree, the count for each unmeasured arc (GCOV_ARC_ON_TREE) can be
368// uniquely identified. Use an iterative algorithm to decrease stack usage for
369// library users in threads. See the edge propagation algorithm in Optimally
370// Profiling and Tracing Programs, ACM Transactions on Programming Languages and
371// Systems, 1994.
373 struct Elem {
374 const GCOVBlock &v;
375 GCOVArc *pred;
376 bool inDst;
377 size_t i = 0;
378 uint64_t excess = 0;
379 };
380
382 stack.push_back({v, pred, false});
383 for (;;) {
384 Elem &u = stack.back();
385 // If GCOV_ARC_ON_TREE edges do form a tree, visited is not needed;
386 // otherwise, this prevents infinite recursion for bad input.
387 if (u.i == 0 && !visited.insert(&u.v).second) {
388 stack.pop_back();
389 if (stack.empty())
390 break;
391 continue;
392 }
393 if (u.i < u.v.pred.size()) {
394 GCOVArc *e = u.v.pred[u.i++];
395 if (e != u.pred) {
396 if (e->onTree())
397 stack.push_back({e->src, e, /*inDst=*/false});
398 else
399 u.excess += e->count;
400 }
401 } else if (u.i < u.v.pred.size() + u.v.succ.size()) {
402 GCOVArc *e = u.v.succ[u.i++ - u.v.pred.size()];
403 if (e != u.pred) {
404 if (e->onTree())
405 stack.push_back({e->dst, e, /*inDst=*/true});
406 else
407 u.excess -= e->count;
408 }
409 } else {
410 uint64_t excess = u.excess;
411 if (static_cast<int64_t>(excess) < 0)
412 excess = -excess;
413 if (u.pred)
414 u.pred->count = excess;
415 bool inDst = u.inDst;
416 stack.pop_back();
417 if (stack.empty())
418 break;
419 stack.back().excess += inDst ? -excess : excess;
420 }
421 }
422}
423
425 OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":"
426 << startLine << "\n";
427 for (const auto &Block : blocks)
428 Block->print(OS);
429}
430
431#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
432/// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
434#endif
435
436/// collectLineCounts - Collect line counts. This must be used after
437/// reading .gcno and .gcda files.
438
439//===----------------------------------------------------------------------===//
440// GCOVBlock implementation.
441
443 OS << "Block : " << number << " Counter : " << count << "\n";
444 if (!pred.empty()) {
445 OS << "\tSource Edges : ";
446 for (const GCOVArc *Edge : pred)
447 OS << Edge->src.number << " (" << Edge->count << "), ";
448 OS << "\n";
449 }
450 if (!succ.empty()) {
451 OS << "\tDestination Edges : ";
452 for (const GCOVArc *Edge : succ) {
453 if (Edge->flags & GCOV_ARC_ON_TREE)
454 OS << '*';
455 OS << Edge->dst.number << " (" << Edge->count << "), ";
456 }
457 OS << "\n";
458 }
459 if (!locations.empty()) {
460 for (const GCOVBlockLocation &loc : locations) {
461 OS << "\tFile: " << loc.srcIdx << ": ";
462 for (uint32_t N : loc.lines)
463 OS << (N) << ",";
464 OS << "\n";
465 }
466 }
467}
468
469#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
470/// dump - Dump GCOVBlock content to dbgs() for debugging purposes.
472#endif
473
476 std::vector<std::pair<GCOVBlock *, size_t>> &stack) {
477 GCOVBlock *u;
478 size_t i;
479 stack.clear();
480 stack.emplace_back(src, 0);
481 src->incoming = (GCOVArc *)1; // Mark u available for cycle detection
482 for (;;) {
483 std::tie(u, i) = stack.back();
484 if (i == u->succ.size()) {
485 u->traversable = false;
486 stack.pop_back();
487 if (stack.empty())
488 break;
489 continue;
490 }
491 ++stack.back().second;
492 GCOVArc *succ = u->succ[i];
493 // Ignore saturated arcs (cycleCount has been reduced to 0) and visited
494 // blocks. Ignore self arcs to guard against bad input (.gcno has no
495 // self arcs).
496 if (succ->cycleCount == 0 || !succ->dst.traversable || &succ->dst == u)
497 continue;
498 if (succ->dst.incoming == nullptr) {
499 succ->dst.incoming = succ;
500 stack.emplace_back(&succ->dst, 0);
501 continue;
502 }
503 uint64_t minCount = succ->cycleCount;
504 for (GCOVBlock *v = u;;) {
505 minCount = std::min(minCount, v->incoming->cycleCount);
506 v = &v->incoming->src;
507 if (v == &succ->dst)
508 break;
509 }
510 succ->cycleCount -= minCount;
511 for (GCOVBlock *v = u;;) {
512 v->incoming->cycleCount -= minCount;
513 v = &v->incoming->src;
514 if (v == &succ->dst)
515 break;
516 }
517 return minCount;
518 }
519 return 0;
520}
521
522// Get the total execution count of loops among blocks on the same line.
523// Assuming a reducible flow graph, the count is the sum of back edge counts.
524// Identifying loops is complex, so we simply find cycles and perform cycle
525// cancelling iteratively.
527 std::vector<std::pair<GCOVBlock *, size_t>> stack;
528 uint64_t count = 0, d;
529 for (;;) {
530 // Make blocks on the line traversable and try finding a cycle.
531 for (const auto *b : blocks) {
532 const_cast<GCOVBlock *>(b)->traversable = true;
533 const_cast<GCOVBlock *>(b)->incoming = nullptr;
534 }
535 d = 0;
536 for (const auto *block : blocks) {
537 auto *b = const_cast<GCOVBlock *>(block);
538 if (b->traversable && (d = augmentOneCycle(b, stack)) > 0)
539 break;
540 }
541 if (d == 0)
542 break;
543 count += d;
544 }
545 // If there is no more loop, all traversable bits should have been cleared.
546 // This property is needed by subsequent calls.
547 for (const auto *b : blocks) {
548 assert(!b->traversable);
549 (void)b;
550 }
551 return count;
552}
553
554//===----------------------------------------------------------------------===//
555// FileInfo implementation.
556
557// Format dividend/divisor as a percentage. Return 1 if the result is greater
558// than 0% and less than 1%.
559static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) {
560 if (!dividend || !divisor)
561 return 0;
562 dividend *= 100;
563 return dividend < divisor ? 1 : dividend / divisor;
564}
565
566// This custom division function mimics gcov's branch ouputs:
567// - Round to closest whole number
568// - Only output 0% or 100% if it's exactly that value
569static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) {
570 if (!Numerator)
571 return 0;
572 if (Numerator == Divisor)
573 return 100;
574
575 uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
576 if (Res == 0)
577 return 1;
578 if (Res == 100)
579 return 99;
580 return Res;
581}
582
583namespace {
584struct formatBranchInfo {
585 formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total)
586 : Options(Options), Count(Count), Total(Total) {}
587
588 void print(raw_ostream &OS) const {
589 if (!Total)
590 OS << "never executed";
591 else if (Options.BranchCount)
592 OS << "taken " << Count;
593 else
594 OS << "taken " << branchDiv(Count, Total) << "%";
595 }
596
597 const GCOV::Options &Options;
598 uint64_t Count;
600};
601
602static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
603 FBI.print(OS);
604 return OS;
605}
606
607class LineConsumer {
608 std::unique_ptr<MemoryBuffer> Buffer;
609 StringRef Remaining;
610
611public:
612 LineConsumer() = default;
613 LineConsumer(StringRef Filename) {
614 // Open source files without requiring a NUL terminator. The concurrent
615 // modification may nullify the NUL terminator condition.
617 MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/false,
618 /*RequiresNullTerminator=*/false);
619 if (std::error_code EC = BufferOrErr.getError()) {
620 errs() << Filename << ": " << EC.message() << "\n";
621 Remaining = "";
622 } else {
623 Buffer = std::move(BufferOrErr.get());
624 Remaining = Buffer->getBuffer();
625 }
626 }
627 bool empty() { return Remaining.empty(); }
628 void printNext(raw_ostream &OS, uint32_t LineNum) {
630 if (empty())
631 Line = "/*EOF*/";
632 else
633 std::tie(Line, Remaining) = Remaining.split("\n");
634 OS << format("%5u:", LineNum) << Line << "\n";
635 }
636};
637} // end anonymous namespace
638
639/// Convert a path to a gcov filename. If PreservePaths is true, this
640/// translates "/" to "#", ".." to "^", and drops ".", to match gcov.
641static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
642 if (!PreservePaths)
643 return sys::path::filename(Filename).str();
644
645 // This behaviour is defined by gcov in terms of text replacements, so it's
646 // not likely to do anything useful on filesystems with different textual
647 // conventions.
648 llvm::SmallString<256> Result("");
650 for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) {
651 if (*I != '/')
652 continue;
653
654 if (I - S == 1 && *S == '.') {
655 // ".", the current directory, is skipped.
656 } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
657 // "..", the parent directory, is replaced with "^".
658 Result.append("^#");
659 } else {
660 if (S < I)
661 // Leave other components intact,
662 Result.append(S, I);
663 // And separate with "#".
664 Result.push_back('#');
665 }
666 S = I + 1;
667 }
668
669 if (S < I)
670 Result.append(S, I);
671 return std::string(Result);
672}
673
674std::string Context::getCoveragePath(StringRef filename,
675 StringRef mainFilename) const {
676 if (options.NoOutput)
677 // This is probably a bug in gcov, but when -n is specified, paths aren't
678 // mangled at all, and the -l and -p options are ignored. Here, we do the
679 // same.
680 return std::string(filename);
681
682 std::string CoveragePath;
683 if (options.LongFileNames && filename != mainFilename)
684 CoveragePath =
685 mangleCoveragePath(mainFilename, options.PreservePaths) + "##";
686 CoveragePath += mangleCoveragePath(filename, options.PreservePaths);
687 if (options.HashFilenames) {
688 MD5 Hasher;
690 Hasher.update(filename.str());
691 Hasher.final(Result);
692 CoveragePath += "##" + std::string(Result.digest());
693 }
694 CoveragePath += ".gcov";
695 return CoveragePath;
696}
697
698void Context::collectFunction(GCOVFunction &f, Summary &summary) {
699 SourceInfo &si = sources[f.srcIdx];
700 if (f.startLine >= si.startLineToFunctions.size())
701 si.startLineToFunctions.resize(f.startLine + 1);
702 si.startLineToFunctions[f.startLine].push_back(&f);
704 SmallSet<uint32_t, 16> linesExec;
705 for (const GCOVBlock &b : f.blocksRange()) {
706 if (b.locations.empty())
707 continue;
708 for (const GCOVBlockLocation &loc : b.locations) {
709 SourceInfo &locSource = sources[loc.srcIdx];
710 uint32_t maxLineNum = *llvm::max_element(loc.lines);
711 if (maxLineNum >= locSource.lines.size())
712 locSource.lines.resize(maxLineNum + 1);
713 for (uint32_t lineNum : loc.lines) {
714 LineInfo &line = locSource.lines[lineNum];
715 line.exists = true;
716 line.count += b.count;
717 line.blocks.push_back(&b);
718 if (f.srcIdx == loc.srcIdx) {
719 if (lines.insert(lineNum).second)
720 ++summary.lines;
721 if (b.count && linesExec.insert(lineNum).second)
722 ++summary.linesExec;
723 }
724 }
725 }
726 }
727}
728
729void Context::collectSourceLine(SourceInfo &si, Summary *summary,
730 LineInfo &line, size_t lineNum) const {
731 uint64_t count = 0;
732 for (const GCOVBlock *b : line.blocks) {
733 if (b->number == 0) {
734 // For nonstandard control flows, arcs into the exit block may be
735 // duplicately counted (fork) or not be counted (abnormal exit), and thus
736 // the (exit,entry) counter may be inaccurate. Count the entry block with
737 // the outgoing arcs.
738 for (const GCOVArc *arc : b->succ)
739 count += arc->count;
740 } else {
741 // Add counts from predecessors that are not on the same line.
742 for (const GCOVArc *arc : b->pred)
743 if (!llvm::is_contained(line.blocks, &arc->src))
744 count += arc->count;
745 }
746 for (GCOVArc *arc : b->succ)
747 arc->cycleCount = arc->count;
748 }
749
750 count += GCOVBlock::getCyclesCount(line.blocks);
751 line.count = count;
752 if (line.exists) {
753 ++summary->lines;
754 if (line.count != 0)
755 ++summary->linesExec;
756 }
757
758 if (options.BranchInfo)
759 for (const GCOVBlock *b : line.blocks) {
760 if (b->getLastLine() != lineNum)
761 continue;
762 int branches = 0, execBranches = 0, takenBranches = 0;
763 for (const GCOVArc *arc : b->succ) {
764 ++branches;
765 if (count != 0)
766 ++execBranches;
767 if (arc->count != 0)
768 ++takenBranches;
769 }
770 if (branches > 1) {
771 summary->branches += branches;
772 summary->branchesExec += execBranches;
773 summary->branchesTaken += takenBranches;
774 }
775 }
776}
777
778void Context::collectSource(SourceInfo &si, Summary &summary) const {
779 size_t lineNum = 0;
780 for (LineInfo &line : si.lines) {
781 collectSourceLine(si, &summary, line, lineNum);
782 ++lineNum;
783 }
784}
785
786void Context::annotateSource(SourceInfo &si, const GCOVFile &file,
787 StringRef gcno, StringRef gcda,
788 raw_ostream &os) const {
789 auto source =
790 options.Intermediate ? LineConsumer() : LineConsumer(si.filename);
791
792 os << " -: 0:Source:" << si.displayName << '\n';
793 os << " -: 0:Graph:" << gcno << '\n';
794 os << " -: 0:Data:" << gcda << '\n';
795 os << " -: 0:Runs:" << file.runCount << '\n';
796 if (file.version < GCOV::V900)
797 os << " -: 0:Programs:" << file.programCount << '\n';
798
799 for (size_t lineNum = 1; !source.empty(); ++lineNum) {
800 if (lineNum >= si.lines.size()) {
801 os << " -:";
802 source.printNext(os, lineNum);
803 continue;
804 }
805
806 const LineInfo &line = si.lines[lineNum];
807 if (options.BranchInfo && lineNum < si.startLineToFunctions.size())
808 for (const auto *f : si.startLineToFunctions[lineNum])
809 printFunctionDetails(*f, os);
810 if (!line.exists)
811 os << " -:";
812 else if (line.count == 0)
813 os << " #####:";
814 else
815 os << format("%9" PRIu64 ":", line.count);
816 source.printNext(os, lineNum);
817
818 uint32_t blockIdx = 0, edgeIdx = 0;
819 for (const GCOVBlock *b : line.blocks) {
820 if (b->getLastLine() != lineNum)
821 continue;
822 if (options.AllBlocks) {
823 if (b->getCount() == 0)
824 os << " $$$$$:";
825 else
826 os << format("%9" PRIu64 ":", b->count);
827 os << format("%5u-block %2u\n", lineNum, blockIdx++);
828 }
829 if (options.BranchInfo) {
830 size_t NumEdges = b->succ.size();
831 if (NumEdges > 1)
832 printBranchInfo(*b, edgeIdx, os);
833 else if (options.UncondBranch && NumEdges == 1) {
834 uint64_t count = b->succ[0]->count;
835 os << format("unconditional %2u ", edgeIdx++)
836 << formatBranchInfo(options, count, count) << '\n';
837 }
838 }
839 }
840 }
841}
842
843void Context::printSourceToIntermediate(const SourceInfo &si,
844 raw_ostream &os) const {
845 os << "file:" << si.filename << '\n';
846 for (const auto &fs : si.startLineToFunctions)
847 for (const GCOVFunction *f : fs)
848 os << "function:" << f->startLine << ',' << f->getEntryCount() << ','
849 << f->getName(options.Demangle) << '\n';
850 for (size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) {
851 const LineInfo &line = si.lines[lineNum];
852 if (line.blocks.empty())
853 continue;
854 // GCC 8 (r254259) added third third field for Ada:
855 // lcount:<line>,<count>,<has_unexecuted_blocks>
856 // We don't need the third field.
857 os << "lcount:" << lineNum << ',' << line.count << '\n';
858
859 if (!options.BranchInfo)
860 continue;
861 for (const GCOVBlock *b : line.blocks) {
862 if (b->succ.size() < 2 || b->getLastLine() != lineNum)
863 continue;
864 for (const GCOVArc *arc : b->succ) {
865 const char *type =
866 b->getCount() ? arc->count ? "taken" : "nottaken" : "notexec";
867 os << "branch:" << lineNum << ',' << type << '\n';
868 }
869 }
870 }
871}
872
873void Context::print(StringRef filename, StringRef gcno, StringRef gcda,
874 GCOVFile &file) {
875 for (StringRef filename : file.filenames) {
876 sources.emplace_back(filename);
877 SourceInfo &si = sources.back();
878 si.displayName = si.filename;
879 if (!options.SourcePrefix.empty() &&
880 sys::path::replace_path_prefix(si.displayName, options.SourcePrefix,
881 "") &&
882 !si.displayName.empty()) {
883 // TODO replace_path_prefix may strip the prefix even if the remaining
884 // part does not start with a separator.
885 if (sys::path::is_separator(si.displayName[0]))
886 si.displayName.erase(si.displayName.begin());
887 else
888 si.displayName = si.filename;
889 }
890 if (options.RelativeOnly && sys::path::is_absolute(si.displayName))
891 si.ignored = true;
892 }
893
894 raw_ostream &os = llvm::outs();
895 for (GCOVFunction &f : make_pointee_range(file.functions)) {
896 Summary summary(f.getName(options.Demangle));
897 collectFunction(f, summary);
898 if (options.FuncCoverage && !options.UseStdout) {
899 os << "Function '" << summary.Name << "'\n";
900 printSummary(summary, os);
901 os << '\n';
902 }
903 }
904
905 for (SourceInfo &si : sources) {
906 if (si.ignored)
907 continue;
908 Summary summary(si.displayName);
909 collectSource(si, summary);
910
911 // Print file summary unless -t is specified.
912 std::string gcovName = getCoveragePath(si.filename, filename);
913 if (!options.UseStdout) {
914 os << "File '" << summary.Name << "'\n";
915 printSummary(summary, os);
916 if (!options.NoOutput && !options.Intermediate)
917 os << "Creating '" << gcovName << "'\n";
918 os << '\n';
919 }
920
921 if (options.NoOutput || options.Intermediate)
922 continue;
923 std::optional<raw_fd_ostream> os;
924 if (!options.UseStdout) {
925 std::error_code ec;
926 os.emplace(gcovName, ec, sys::fs::OF_TextWithCRLF);
927 if (ec) {
928 errs() << ec.message() << '\n';
929 continue;
930 }
931 }
932 annotateSource(si, file, gcno, gcda,
933 options.UseStdout ? llvm::outs() : *os);
934 }
935
936 if (options.Intermediate && !options.NoOutput) {
937 // gcov 7.* unexpectedly create multiple .gcov files, which was fixed in 8.0
938 // (PR GCC/82702). We create just one file.
939 std::string outputPath(sys::path::filename(filename));
940 std::error_code ec;
941 raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_TextWithCRLF);
942 if (ec) {
943 errs() << ec.message() << '\n';
944 return;
945 }
946
947 for (const SourceInfo &si : sources)
948 printSourceToIntermediate(si, os);
949 }
950}
951
952void Context::printFunctionDetails(const GCOVFunction &f,
953 raw_ostream &os) const {
954 const uint64_t entryCount = f.getEntryCount();
955 uint32_t blocksExec = 0;
956 const GCOVBlock &exitBlock = f.getExitBlock();
957 uint64_t exitCount = 0;
958 for (const GCOVArc *arc : exitBlock.pred)
959 exitCount += arc->count;
960 for (const GCOVBlock &b : f.blocksRange())
961 if (b.number != 0 && &b != &exitBlock && b.getCount())
962 ++blocksExec;
963
964 os << "function " << f.getName(options.Demangle) << " called " << entryCount
965 << " returned " << formatPercentage(exitCount, entryCount)
966 << "% blocks executed "
967 << formatPercentage(blocksExec, f.blocks.size() - 2) << "%\n";
968}
969
970/// printBranchInfo - Print conditional branch probabilities.
971void Context::printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx,
972 raw_ostream &os) const {
973 uint64_t total = 0;
974 for (const GCOVArc *arc : Block.dsts())
975 total += arc->count;
976 for (const GCOVArc *arc : Block.dsts())
977 os << format("branch %2u ", edgeIdx++)
978 << formatBranchInfo(options, arc->count, total) << '\n';
979}
980
981void Context::printSummary(const Summary &summary, raw_ostream &os) const {
982 os << format("Lines executed:%.2f%% of %" PRIu64 "\n",
983 double(summary.linesExec) * 100 / summary.lines, summary.lines);
984 if (options.BranchInfo) {
985 if (summary.branches == 0) {
986 os << "No branches\n";
987 } else {
988 os << format("Branches executed:%.2f%% of %" PRIu64 "\n",
989 double(summary.branchesExec) * 100 / summary.branches,
990 summary.branches);
991 os << format("Taken at least once:%.2f%% of %" PRIu64 "\n",
992 double(summary.branchesTaken) * 100 / summary.branches,
993 summary.branches);
994 }
995 os << "No calls\n";
996 }
997}
998
999void llvm::gcovOneInput(const GCOV::Options &options, StringRef filename,
1000 StringRef gcno, StringRef gcda, GCOVFile &file) {
1001 Context fi(options);
1002 fi.print(filename, gcno, gcda, file);
1003}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
arc branch ARC finalize branches
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val)
bbsections Prepares for basic block by splitting functions into clusters of basic blocks
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition: Compiler.h:638
Looks at all the uses of the given value Returns the Liveness deduced from the uses of this value Adds all uses that cause the result to be MaybeLive to MaybeLiveRetUses If the result is MaybeLiveUses might be modified but its content should be ignored(since it might not be complete). DeadArgumentEliminationPass
static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths)
Convert a path to a gcov filename.
Definition: GCOV.cpp:641
static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor)
Definition: GCOV.cpp:559
static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor)
Definition: GCOV.cpp:569
@ GCOV_TAG_LINES
Definition: GCOV.cpp:38
@ GCOV_TAG_COUNTER_ARCS
Definition: GCOV.cpp:39
@ GCOV_TAG_PROGRAM_SUMMARY
Definition: GCOV.cpp:42
@ GCOV_ARC_FALLTHROUGH
Definition: GCOV.cpp:33
@ GCOV_TAG_OBJECT_SUMMARY
Definition: GCOV.cpp:41
@ GCOV_ARC_ON_TREE
Definition: GCOV.cpp:32
@ GCOV_TAG_ARCS
Definition: GCOV.cpp:37
@ GCOV_TAG_FUNCTION
Definition: GCOV.cpp:35
@ GCOV_TAG_BLOCKS
Definition: GCOV.cpp:36
static bool sink(Instruction &I, LoopInfo *LI, DominatorTree *DT, const Loop *CurLoop, ICFLoopSafetyInfo *SafetyInfo, MemorySSAUpdater &MSSAU, OptimizationRemarkEmitter *ORE)
When an instruction is found to only be used outside of the loop, this function moves it to the exit ...
Definition: LICM.cpp:1578
static LVOptions Options
Definition: LVOptions.cpp:25
#define I(x, y, z)
Definition: MD5.cpp:58
#define P(N)
dot regions Print regions of function to dot file(with no function bodies)"
std::pair< BasicBlock *, BasicBlock * > Edge
This file contains some templates that are useful if you are working with the STL at all.
raw_pwrite_stream & OS
This file defines the SmallSet class.
unify loop Fixup each natural loop to have a single exit block
uint64_t tell() const
Return the current position of this Cursor.
Definition: DataExtractor.h:72
LLVM_ABI void skip(Cursor &C, uint64_t Length) const
Advance the Cursor position by the given number of bytes.
Represents either an error or a value T.
Definition: ErrorOr.h:56
reference get()
Definition: ErrorOr.h:149
std::error_code getError() const
Definition: ErrorOr.h:152
GCOVBlock - Collects block information.
Definition: GCOV.h:283
static LLVM_ABI uint64_t getCyclesCount(const BlockVector &blocks)
Definition: GCOV.cpp:526
void addDstEdge(GCOVArc *Edge)
Definition: GCOV.h:303
SmallVector< GCOVBlockLocation > locations
Definition: GCOV.h:327
uint32_t number
Definition: GCOV.h:323
void addSrcEdge(GCOVArc *Edge)
Definition: GCOV.h:301
bool traversable
Definition: GCOV.h:329
LLVM_ABI void print(raw_ostream &OS) const
collectLineCounts - Collect line counts.
Definition: GCOV.cpp:442
GCOVArc * incoming
Definition: GCOV.h:330
static LLVM_ABI uint64_t augmentOneCycle(GCOVBlock *src, std::vector< std::pair< GCOVBlock *, size_t > > &stack)
Definition: GCOV.cpp:475
SmallVector< GCOVArc *, 2 > pred
Definition: GCOV.h:325
LLVM_ABI void dump() const
dump - Dump GCOVBlock content to dbgs() for debugging purposes.
Definition: GCOV.cpp:471
uint64_t count
Definition: GCOV.h:324
SmallVector< GCOVArc *, 2 > succ
Definition: GCOV.h:326
GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific read operations.
Definition: GCOV.h:74
bool readInt(uint32_t &Val)
Definition: GCOV.h:153
DataExtractor::Cursor cursor
Definition: GCOV.h:183
bool readInt64(uint64_t &Val)
Definition: GCOV.h:163
bool readGCOVVersion(GCOV::GCOVVersion &version)
readGCOVVersion - Read GCOV version.
Definition: GCOV.h:109
bool readString(StringRef &str)
Definition: GCOV.h:171
DataExtractor de
Definition: GCOV.h:182
uint32_t getWord()
Definition: GCOV.h:145
bool readGCNOFormat()
readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
Definition: GCOV.h:80
bool readGCDAFormat()
readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
Definition: GCOV.h:95
GCOVFile - Collects coverage information for one pair of coverage file (.gcno and ....
Definition: GCOV.h:192
SmallVector< std::unique_ptr< GCOVFunction >, 16 > functions
Definition: GCOV.h:210
uint32_t checksum
Definition: GCOV.h:208
LLVM_ABI void print(raw_ostream &OS) const
Definition: GCOV.cpp:307
uint32_t programCount
Definition: GCOV.h:213
uint32_t runCount
Definition: GCOV.h:212
GCOV::GCOVVersion version
Definition: GCOV.h:207
std::vector< std::string > filenames
Definition: GCOV.h:202
GCOV::GCOVVersion getVersion() const
Definition: GCOV.h:198
LLVM_ABI void dump() const
dump - Dump GCOVFile content to dbgs() for debugging purposes.
Definition: GCOV.cpp:314
std::map< uint32_t, GCOVFunction * > identToFunction
Definition: GCOV.h:211
StringRef cwd
Definition: GCOV.h:209
bool GCNOInitialized
Definition: GCOV.h:206
StringMap< unsigned > filenameToIdx
Definition: GCOV.h:203
LLVM_ABI bool readGCNO(GCOVBuffer &Buffer)
readGCNO - Read GCNO buffer.
Definition: GCOV.cpp:102
LLVM_ABI bool readGCDA(GCOVBuffer &Buffer)
readGCDA - Read GCDA buffer.
Definition: GCOV.cpp:210
GCOVFunction - Collects function information.
Definition: GCOV.h:237
LLVM_ABI uint64_t getEntryCount() const
getEntryCount - Get the number of times the function was called by retrieving the entry block's count...
Definition: GCOV.cpp:357
uint32_t endColumn
Definition: GCOV.h:264
SmallVector< std::unique_ptr< GCOVArc >, 0 > treeArcs
Definition: GCOV.h:270
LLVM_ABI StringRef getName(bool demangle) const
Definition: GCOV.cpp:335
uint32_t cfgChecksum
Definition: GCOV.h:260
LLVM_ABI void dump() const
dump - Dump GCOVFunction content to dbgs() for debugging purposes.
Definition: GCOV.cpp:433
StringRef Name
Definition: GCOV.h:266
uint8_t artificial
Definition: GCOV.h:265
LLVM_ABI void propagateCounts(const GCOVBlock &v, GCOVArc *pred)
Definition: GCOV.cpp:372
uint32_t endLine
Definition: GCOV.h:263
SmallVector< std::unique_ptr< GCOVBlock >, 0 > blocks
Definition: GCOV.h:269
LLVM_ABI GCOVBlock & getExitBlock() const
Definition: GCOV.cpp:361
LLVM_ABI StringRef getFilename() const
Definition: GCOV.cpp:353
DenseSet< const GCOVBlock * > visited
Definition: GCOV.h:271
uint32_t startLine
Definition: GCOV.h:261
SmallString< 0 > demangled
Definition: GCOV.h:267
LLVM_ABI void print(raw_ostream &OS) const
Definition: GCOV.cpp:424
uint32_t startColumn
Definition: GCOV.h:262
uint32_t linenoChecksum
Definition: GCOV.h:259
iterator_range< BlockIterator > blocksRange() const
Definition: GCOV.h:249
SmallVector< std::unique_ptr< GCOVArc >, 0 > arcs
Definition: GCOV.h:270
uint32_t ident
Definition: GCOV.h:258
unsigned srcIdx
Definition: GCOV.h:268
GCOVFile & file
Definition: GCOV.h:257
Definition: MD5.h:42
LLVM_ABI void update(ArrayRef< uint8_t > Data)
Updates the hash for the byte stream provided.
Definition: MD5.cpp:189
LLVM_ABI void final(MD5Result &Result)
Finishes off the hash and puts the result in result.
Definition: MD5.cpp:234
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:134
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:182
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
bool empty() const
Definition: SmallVector.h:82
void push_back(const T &Elt)
Definition: SmallVector.h:414
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
unsigned size() const
Definition: StringMap.h:109
std::pair< iterator, bool > try_emplace(StringRef Key, ArgsTy &&...Args)
Emplace a new element for the specified key into the map if the key isn't already in the map.
Definition: StringMap.h:372
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition: StringRef.h:710
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:233
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:269
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:151
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:148
A raw_ostream that writes to a file descriptor.
Definition: raw_ostream.h:461
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:53
GCOVVersion
Definition: GCOV.h:43
@ V407
Definition: GCOV.h:43
@ V800
Definition: GCOV.h:43
@ V408
Definition: GCOV.h:43
@ V900
Definition: GCOV.h:43
@ V1200
Definition: GCOV.h:43
LVOptions & options()
Definition: LVOptions.h:448
LLVM_ABI bool exists(const basic_file_status &status)
Does file exist?
Definition: Path.cpp:1077
@ OF_TextWithCRLF
The file should be opened in text mode and use a carriage linefeed '\r '.
Definition: FileSystem.h:771
LLVM_ABI bool remove_dots(SmallVectorImpl< char > &path, bool remove_dot_dot=false, Style style=Style::native)
In-place remove any '.
Definition: Path.cpp:715
LLVM_ABI StringRef filename(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get filename.
Definition: Path.cpp:577
LLVM_ABI bool is_absolute(const Twine &path, Style style=Style::native)
Is path absolute?
Definition: Path.cpp:671
LLVM_ABI bool replace_path_prefix(SmallVectorImpl< char > &Path, StringRef OldPrefix, StringRef NewPrefix, Style style=Style::native)
Replace matching path prefix with another path.
Definition: Path.cpp:518
LLVM_ABI bool is_separator(char value, Style style=Style::native)
Check whether the given char is a path separator on the host OS.
Definition: Path.cpp:601
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
LLVM_ABI raw_fd_ostream & outs()
This returns a reference to a raw_fd_ostream for standard output.
iterator_range< pointee_iterator< WrappedIteratorT > > make_pointee_range(RangeT &&Range)
Definition: iterator.h:336
DEMANGLE_ABI char * itaniumDemangle(std::string_view mangled_name, bool ParseParams=true)
Returns a non-NULL pointer to a NUL-terminated C style string that should be explicitly freed,...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:207
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition: Format.h:126
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
Definition: STLExtras.h:1973
auto max_element(R &&Range)
Provide wrappers to std::max_element which take ranges instead of having to pass begin/end explicitly...
Definition: STLExtras.h:2049
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
Definition: APFixedPoint.h:312
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition: STLExtras.h:1916
LLVM_ABI void gcovOneInput(const GCOV::Options &options, StringRef filename, StringRef gcno, StringRef gcda, GCOVFile &file)
Definition: GCOV.cpp:999
DEMANGLE_ABI std::string demangle(std::string_view MangledName)
Attempt to demangle a string using different demangling schemes.
Definition: Demangle.cpp:20
#define N
uint64_t count
Definition: GCOV.h:232
LLVM_ABI bool onTree() const
Definition: GCOV.cpp:330
GCOVBlock & src
Definition: GCOV.h:229
uint64_t cycleCount
Definition: GCOV.h:233
uint32_t flags
Definition: GCOV.h:231
Represent file of lines same with block_location_info in gcc.
Definition: GCOV.h:275
SmallVector< uint32_t, 4 > lines
Definition: GCOV.h:279
A struct for passing gcov options between functions.
Definition: GCOV.h:46