clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name GCOV.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -ffunction-sections -fdata-sections -resource-dir /usr/lib/llvm-12/lib/clang/12.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I /build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05/build-llvm/lib/ProfileData -I /build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05/llvm/lib/ProfileData -I /build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05/build-llvm/include -I /build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05/llvm/include -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-12/lib/clang/12.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir /build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05/build-llvm/lib/ProfileData -fdebug-prefix-map=/build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05=. -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -o /tmp/scan-build-2021-01-04-143208-9200-1 -x c++ /build/llvm-toolchain-snapshot-12~++20210104111112+e43b3d1f5e05/llvm/lib/ProfileData/GCOV.cpp
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "llvm/ProfileData/GCOV.h" |
15 | #include "llvm/ADT/STLExtras.h" |
16 | #include "llvm/Config/llvm-config.h" |
17 | #include "llvm/Demangle/Demangle.h" |
18 | #include "llvm/Support/Debug.h" |
19 | #include "llvm/Support/FileSystem.h" |
20 | #include "llvm/Support/Format.h" |
21 | #include "llvm/Support/MD5.h" |
22 | #include "llvm/Support/Path.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | #include <algorithm> |
25 | #include <system_error> |
26 | #include <unordered_map> |
27 | |
28 | using namespace llvm; |
29 | |
30 | enum : uint32_t { |
31 | GCOV_ARC_ON_TREE = 1 << 0, |
32 | GCOV_ARC_FALLTHROUGH = 1 << 2, |
33 | |
34 | GCOV_TAG_FUNCTION = 0x01000000, |
35 | GCOV_TAG_BLOCKS = 0x01410000, |
36 | GCOV_TAG_ARCS = 0x01430000, |
37 | GCOV_TAG_LINES = 0x01450000, |
38 | GCOV_TAG_COUNTER_ARCS = 0x01a10000, |
39 | |
40 | GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, |
41 | GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, |
42 | }; |
43 | |
44 | namespace { |
45 | struct Summary { |
46 | Summary(StringRef Name) : Name(Name) {} |
47 | |
48 | StringRef Name; |
49 | uint64_t lines = 0; |
50 | uint64_t linesExec = 0; |
51 | uint64_t branches = 0; |
52 | uint64_t branchesExec = 0; |
53 | uint64_t branchesTaken = 0; |
54 | }; |
55 | |
56 | struct LineInfo { |
57 | SmallVector<const GCOVBlock *, 1> blocks; |
58 | uint64_t count = 0; |
59 | bool exists = false; |
60 | }; |
61 | |
62 | struct SourceInfo { |
63 | StringRef filename; |
64 | SmallString<0> displayName; |
65 | std::vector<std::vector<const GCOVFunction *>> startLineToFunctions; |
66 | std::vector<LineInfo> lines; |
67 | bool ignored = false; |
68 | SourceInfo(StringRef filename) : filename(filename) {} |
69 | }; |
70 | |
71 | class Context { |
72 | public: |
73 | Context(const GCOV::Options &Options) : options(Options) {} |
74 | void print(StringRef filename, StringRef gcno, StringRef gcda, |
75 | GCOVFile &file); |
76 | |
77 | private: |
78 | std::string getCoveragePath(StringRef filename, StringRef mainFilename) const; |
79 | void printFunctionDetails(const GCOVFunction &f, raw_ostream &os) const; |
80 | void printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, |
81 | raw_ostream &OS) const; |
82 | void printSummary(const Summary &summary, raw_ostream &os) const; |
83 | |
84 | void collectFunction(GCOVFunction &f, Summary &summary); |
85 | void collectSourceLine(SourceInfo &si, Summary *summary, LineInfo &line, |
86 | size_t lineNum) const; |
87 | void collectSource(SourceInfo &si, Summary &summary) const; |
88 | void annotateSource(SourceInfo &si, const GCOVFile &file, StringRef gcno, |
89 | StringRef gcda, raw_ostream &os) const; |
90 | void printSourceToIntermediate(const SourceInfo &si, raw_ostream &os) const; |
91 | |
92 | const GCOV::Options &options; |
93 | std::vector<SourceInfo> sources; |
94 | }; |
95 | } |
96 | |
97 | |
98 | |
99 | |
100 | |
101 | bool GCOVFile::readGCNO(GCOVBuffer &buf) { |
102 | if (!buf.readGCNOFormat()) |
| 1 | Calling 'GCOVBuffer::readGCNOFormat' | |
|
| 5 | | Returning from 'GCOVBuffer::readGCNOFormat' | |
|
| |
103 | return false; |
104 | if (!buf.readGCOVVersion(Version)) |
| 7 | | Calling 'GCOVBuffer::readGCOVVersion' | |
|
| 20 | | Returning from 'GCOVBuffer::readGCOVVersion' | |
|
| |
105 | return false; |
106 | |
107 | Checksum = buf.getWord(); |
108 | if (Version >= GCOV::V900) |
| |
109 | cwd = buf.getString(); |
110 | if (Version >= GCOV::V800) |
| |
111 | buf.getWord(); |
112 | |
113 | uint32_t tag, length; |
114 | GCOVFunction *fn; |
| 24 | | 'fn' declared without an initial value | |
|
115 | while ((tag = buf.getWord())) { |
| 25 | | Loop condition is true. Entering loop body | |
|
116 | if (!buf.readInt(length)) |
| 26 | | Calling 'GCOVBuffer::readInt' | |
|
| 30 | | Returning from 'GCOVBuffer::readInt' | |
|
| |
117 | return false; |
118 | if (tag == GCOV_TAG_FUNCTION) { |
| 32 | | Assuming 'tag' is not equal to GCOV_TAG_FUNCTION | |
|
| |
119 | functions.push_back(std::make_unique<GCOVFunction>(*this)); |
120 | fn = functions.back().get(); |
121 | fn->ident = buf.getWord(); |
122 | fn->linenoChecksum = buf.getWord(); |
123 | if (Version >= GCOV::V407) |
124 | fn->cfgChecksum = buf.getWord(); |
125 | buf.readString(fn->Name); |
126 | StringRef filename; |
127 | if (Version < GCOV::V800) { |
128 | filename = buf.getString(); |
129 | fn->startLine = buf.getWord(); |
130 | } else { |
131 | fn->artificial = buf.getWord(); |
132 | filename = buf.getString(); |
133 | fn->startLine = buf.getWord(); |
134 | fn->startColumn = buf.getWord(); |
135 | fn->endLine = buf.getWord(); |
136 | if (Version >= GCOV::V900) |
137 | fn->endColumn = buf.getWord(); |
138 | } |
139 | auto r = filenameToIdx.try_emplace(filename, filenameToIdx.size()); |
140 | if (r.second) |
141 | filenames.emplace_back(filename); |
142 | fn->srcIdx = r.first->second; |
143 | IdentToFunction[fn->ident] = fn; |
144 | } else if (tag == GCOV_TAG_BLOCKS && fn) { |
| 34 | | Assuming 'tag' is not equal to GCOV_TAG_BLOCKS | |
|
145 | if (Version < GCOV::V800) { |
146 | for (uint32_t i = 0; i != length; ++i) { |
147 | buf.getWord(); |
148 | fn->blocks.push_back(std::make_unique<GCOVBlock>(i)); |
149 | } |
150 | } else { |
151 | uint32_t num = buf.getWord(); |
152 | for (uint32_t i = 0; i != num; ++i) |
153 | fn->blocks.push_back(std::make_unique<GCOVBlock>(i)); |
154 | } |
155 | } else if (tag == GCOV_TAG_ARCS && fn) { |
| 35 | | Assuming 'tag' is not equal to GCOV_TAG_ARCS | |
|
156 | uint32_t srcNo = buf.getWord(); |
157 | if (srcNo >= fn->blocks.size()) { |
158 | errs() << "unexpected block number: " << srcNo << " (in " |
159 | << fn->blocks.size() << ")\n"; |
160 | return false; |
161 | } |
162 | GCOVBlock *src = fn->blocks[srcNo].get(); |
163 | for (uint32_t i = 0, e = (length - 1) / 2; i != e; ++i) { |
164 | uint32_t dstNo = buf.getWord(), flags = buf.getWord(); |
165 | GCOVBlock *dst = fn->blocks[dstNo].get(); |
166 | auto arc = std::make_unique<GCOVArc>(*src, *dst, flags); |
167 | src->addDstEdge(arc.get()); |
168 | dst->addSrcEdge(arc.get()); |
169 | if (arc->onTree()) |
170 | fn->treeArcs.push_back(std::move(arc)); |
171 | else |
172 | fn->arcs.push_back(std::move(arc)); |
173 | } |
174 | } else if (tag == GCOV_TAG_LINES && fn) { |
| 36 | | Assuming 'tag' is equal to GCOV_TAG_LINES | |
|
| 37 | | Branch condition evaluates to a garbage value |
|
175 | uint32_t srcNo = buf.getWord(); |
176 | if (srcNo >= fn->blocks.size()) { |
177 | errs() << "unexpected block number: " << srcNo << " (in " |
178 | << fn->blocks.size() << ")\n"; |
179 | return false; |
180 | } |
181 | GCOVBlock &Block = *fn->blocks[srcNo]; |
182 | for (;;) { |
183 | uint32_t line = buf.getWord(); |
184 | if (line) |
185 | Block.addLine(line); |
186 | else { |
187 | StringRef filename = buf.getString(); |
188 | if (filename.empty()) |
189 | break; |
190 | |
191 | } |
192 | } |
193 | } |
194 | } |
195 | |
196 | GCNOInitialized = true; |
197 | return true; |
198 | } |
199 | |
200 | |
201 | |
202 | bool GCOVFile::readGCDA(GCOVBuffer &buf) { |
203 | assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()"); |
204 | if (!buf.readGCDAFormat()) |
205 | return false; |
206 | GCOV::GCOVVersion GCDAVersion; |
207 | if (!buf.readGCOVVersion(GCDAVersion)) |
208 | return false; |
209 | if (Version != GCDAVersion) { |
210 | errs() << "GCOV versions do not match.\n"; |
211 | return false; |
212 | } |
213 | |
214 | uint32_t GCDAChecksum; |
215 | if (!buf.readInt(GCDAChecksum)) |
216 | return false; |
217 | if (Checksum != GCDAChecksum) { |
218 | errs() << "File checksums do not match: " << Checksum |
219 | << " != " << GCDAChecksum << ".\n"; |
220 | return false; |
221 | } |
222 | uint32_t dummy, tag, length; |
223 | uint32_t ident; |
224 | GCOVFunction *fn = nullptr; |
225 | while ((tag = buf.getWord())) { |
226 | if (!buf.readInt(length)) |
227 | return false; |
228 | uint32_t pos = buf.cursor.tell(); |
229 | if (tag == GCOV_TAG_OBJECT_SUMMARY) { |
230 | buf.readInt(RunCount); |
231 | buf.readInt(dummy); |
232 | |
233 | if (length == 9) |
234 | buf.readInt(RunCount); |
235 | } else if (tag == GCOV_TAG_PROGRAM_SUMMARY) { |
236 | |
237 | if (length > 0) { |
238 | buf.readInt(dummy); |
239 | buf.readInt(dummy); |
240 | buf.readInt(RunCount); |
241 | } |
242 | ++ProgramCount; |
243 | } else if (tag == GCOV_TAG_FUNCTION) { |
244 | if (length == 0) |
245 | continue; |
246 | |
247 | |
248 | |
249 | if (length < 2 || !buf.readInt(ident)) |
250 | return false; |
251 | auto It = IdentToFunction.find(ident); |
252 | uint32_t linenoChecksum, cfgChecksum = 0; |
253 | buf.readInt(linenoChecksum); |
254 | if (Version >= GCOV::V407) |
255 | buf.readInt(cfgChecksum); |
256 | if (It != IdentToFunction.end()) { |
257 | fn = It->second; |
258 | if (linenoChecksum != fn->linenoChecksum || |
259 | cfgChecksum != fn->cfgChecksum) { |
260 | errs() << fn->Name |
261 | << format(": checksum mismatch, (%u, %u) != (%u, %u)\n", |
262 | linenoChecksum, cfgChecksum, fn->linenoChecksum, |
263 | fn->cfgChecksum); |
264 | return false; |
265 | } |
266 | } |
267 | } else if (tag == GCOV_TAG_COUNTER_ARCS && fn) { |
268 | if (length != 2 * fn->arcs.size()) { |
269 | errs() << fn->Name |
270 | << format( |
271 | ": GCOV_TAG_COUNTER_ARCS mismatch, got %u, expected %u\n", |
272 | length, unsigned(2 * fn->arcs.size())); |
273 | return false; |
274 | } |
275 | for (std::unique_ptr<GCOVArc> &arc : fn->arcs) { |
276 | if (!buf.readInt64(arc->count)) |
277 | return false; |
278 | arc->src.count += arc->count; |
279 | } |
280 | |
281 | if (fn->blocks.size() >= 2) { |
282 | GCOVBlock &src = *fn->blocks[0]; |
283 | GCOVBlock &sink = |
284 | Version < GCOV::V408 ? *fn->blocks.back() : *fn->blocks[1]; |
285 | auto arc = std::make_unique<GCOVArc>(sink, src, GCOV_ARC_ON_TREE); |
286 | sink.addDstEdge(arc.get()); |
287 | src.addSrcEdge(arc.get()); |
288 | fn->treeArcs.push_back(std::move(arc)); |
289 | |
290 | for (GCOVBlock &block : fn->blocksRange()) |
291 | fn->propagateCounts(block, nullptr); |
292 | for (size_t i = fn->treeArcs.size() - 1; i; --i) |
293 | fn->treeArcs[i - 1]->src.count += fn->treeArcs[i - 1]->count; |
294 | } |
295 | } |
296 | pos += 4 * length; |
297 | if (pos < buf.cursor.tell()) |
298 | return false; |
299 | buf.de.skip(buf.cursor, pos - buf.cursor.tell()); |
300 | } |
301 | |
302 | return true; |
303 | } |
304 | |
305 | void GCOVFile::print(raw_ostream &OS) const { |
306 | for (const GCOVFunction &f : *this) |
307 | f.print(OS); |
308 | } |
309 | |
310 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
311 | |
312 | LLVM_DUMP_METHOD void GCOVFile::dump() const { print(dbgs()); } |
313 | #endif |
314 | |
315 | bool GCOVArc::onTree() const { return flags & GCOV_ARC_ON_TREE; } |
316 | |
317 | |
318 | |
319 | |
320 | StringRef GCOVFunction::getName(bool demangle) const { |
321 | if (!demangle) |
322 | return Name; |
323 | if (demangled.empty()) { |
324 | do { |
325 | if (Name.startswith("_Z")) { |
326 | int status = 0; |
327 | |
328 | char *res = itaniumDemangle(Name.data(), nullptr, nullptr, &status); |
329 | if (status == 0) { |
330 | demangled = res; |
331 | free(res); |
332 | break; |
333 | } |
334 | } |
335 | demangled = Name; |
336 | } while (0); |
337 | } |
338 | return demangled; |
339 | } |
340 | StringRef GCOVFunction::getFilename() const { return file.filenames[srcIdx]; } |
341 | |
342 | |
343 | |
344 | uint64_t GCOVFunction::getEntryCount() const { |
345 | return blocks.front()->getCount(); |
346 | } |
347 | |
348 | GCOVBlock &GCOVFunction::getExitBlock() const { |
349 | return file.getVersion() < GCOV::V408 ? *blocks.back() : *blocks[1]; |
350 | } |
351 | |
352 | |
353 | |
354 | |
355 | |
356 | uint64_t GCOVFunction::propagateCounts(const GCOVBlock &v, GCOVArc *pred) { |
357 | |
358 | |
359 | if (!visited.insert(&v).second) |
360 | return 0; |
361 | |
362 | uint64_t excess = 0; |
363 | for (GCOVArc *e : v.srcs()) |
364 | if (e != pred) |
365 | excess += e->onTree() ? propagateCounts(e->src, e) : e->count; |
366 | for (GCOVArc *e : v.dsts()) |
367 | if (e != pred) |
368 | excess -= e->onTree() ? propagateCounts(e->dst, e) : e->count; |
369 | if (int64_t(excess) < 0) |
370 | excess = -excess; |
371 | if (pred) |
372 | pred->count = excess; |
373 | return excess; |
374 | } |
375 | |
376 | void GCOVFunction::print(raw_ostream &OS) const { |
377 | OS << "===== " << Name << " (" << ident << ") @ " << getFilename() << ":" |
378 | << startLine << "\n"; |
379 | for (const auto &Block : blocks) |
380 | Block->print(OS); |
381 | } |
382 | |
383 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
384 | |
385 | LLVM_DUMP_METHOD void GCOVFunction::dump() const { print(dbgs()); } |
386 | #endif |
387 | |
388 | |
389 | |
390 | |
391 | |
392 | |
393 | |
394 | void GCOVBlock::print(raw_ostream &OS) const { |
395 | OS << "Block : " << number << " Counter : " << count << "\n"; |
396 | if (!pred.empty()) { |
397 | OS << "\tSource Edges : "; |
398 | for (const GCOVArc *Edge : pred) |
399 | OS << Edge->src.number << " (" << Edge->count << "), "; |
400 | OS << "\n"; |
401 | } |
402 | if (!succ.empty()) { |
403 | OS << "\tDestination Edges : "; |
404 | for (const GCOVArc *Edge : succ) { |
405 | if (Edge->flags & GCOV_ARC_ON_TREE) |
406 | OS << '*'; |
407 | OS << Edge->dst.number << " (" << Edge->count << "), "; |
408 | } |
409 | OS << "\n"; |
410 | } |
411 | if (!lines.empty()) { |
412 | OS << "\tLines : "; |
413 | for (uint32_t N : lines) |
414 | OS << (N) << ","; |
415 | OS << "\n"; |
416 | } |
417 | } |
418 | |
419 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
420 | |
421 | LLVM_DUMP_METHOD void GCOVBlock::dump() const { print(dbgs()); } |
422 | #endif |
423 | |
424 | uint64_t |
425 | GCOVBlock::augmentOneCycle(GCOVBlock *src, |
426 | std::vector<std::pair<GCOVBlock *, size_t>> &stack) { |
427 | GCOVBlock *u; |
428 | size_t i; |
429 | stack.clear(); |
430 | stack.emplace_back(src, 0); |
431 | src->incoming = (GCOVArc *)1; |
432 | for (;;) { |
433 | std::tie(u, i) = stack.back(); |
434 | if (i == u->succ.size()) { |
435 | u->traversable = false; |
436 | stack.pop_back(); |
437 | if (stack.empty()) |
438 | break; |
439 | continue; |
440 | } |
441 | ++stack.back().second; |
442 | GCOVArc *succ = u->succ[i]; |
443 | |
444 | |
445 | |
446 | if (succ->cycleCount == 0 || !succ->dst.traversable || &succ->dst == u) |
447 | continue; |
448 | if (succ->dst.incoming == nullptr) { |
449 | succ->dst.incoming = succ; |
450 | stack.emplace_back(&succ->dst, 0); |
451 | continue; |
452 | } |
453 | uint64_t minCount = succ->cycleCount; |
454 | for (GCOVBlock *v = u;;) { |
455 | minCount = std::min(minCount, v->incoming->cycleCount); |
456 | v = &v->incoming->src; |
457 | if (v == &succ->dst) |
458 | break; |
459 | } |
460 | succ->cycleCount -= minCount; |
461 | for (GCOVBlock *v = u;;) { |
462 | v->incoming->cycleCount -= minCount; |
463 | v = &v->incoming->src; |
464 | if (v == &succ->dst) |
465 | break; |
466 | } |
467 | return minCount; |
468 | } |
469 | return 0; |
470 | } |
471 | |
472 | |
473 | |
474 | |
475 | |
476 | uint64_t GCOVBlock::getCyclesCount(const BlockVector &blocks) { |
477 | std::vector<std::pair<GCOVBlock *, size_t>> stack; |
478 | uint64_t count = 0, d; |
479 | for (;;) { |
480 | |
481 | for (auto b : blocks) { |
482 | const_cast<GCOVBlock *>(b)->traversable = true; |
483 | const_cast<GCOVBlock *>(b)->incoming = nullptr; |
484 | } |
485 | d = 0; |
486 | for (auto block : blocks) { |
487 | auto *b = const_cast<GCOVBlock *>(block); |
488 | if (b->traversable && (d = augmentOneCycle(b, stack)) > 0) |
489 | break; |
490 | } |
491 | if (d == 0) |
492 | break; |
493 | count += d; |
494 | } |
495 | |
496 | |
497 | for (auto b : blocks) { |
498 | assert(!b->traversable); |
499 | (void)b; |
500 | } |
501 | return count; |
502 | } |
503 | |
504 | |
505 | |
506 | |
507 | |
508 | |
509 | static uint32_t formatPercentage(uint64_t dividend, uint64_t divisor) { |
510 | if (!dividend || !divisor) |
511 | return 0; |
512 | dividend *= 100; |
513 | return dividend < divisor ? 1 : dividend / divisor; |
514 | } |
515 | |
516 | |
517 | |
518 | |
519 | static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) { |
520 | if (!Numerator) |
521 | return 0; |
522 | if (Numerator == Divisor) |
523 | return 100; |
524 | |
525 | uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor; |
526 | if (Res == 0) |
527 | return 1; |
528 | if (Res == 100) |
529 | return 99; |
530 | return Res; |
531 | } |
532 | |
533 | namespace { |
534 | struct formatBranchInfo { |
535 | formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total) |
536 | : Options(Options), Count(Count), Total(Total) {} |
537 | |
538 | void print(raw_ostream &OS) const { |
539 | if (!Total) |
540 | OS << "never executed"; |
541 | else if (Options.BranchCount) |
542 | OS << "taken " << Count; |
543 | else |
544 | OS << "taken " << branchDiv(Count, Total) << "%"; |
545 | } |
546 | |
547 | const GCOV::Options &Options; |
548 | uint64_t Count; |
549 | uint64_t Total; |
550 | }; |
551 | |
552 | static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) { |
553 | FBI.print(OS); |
554 | return OS; |
555 | } |
556 | |
557 | class LineConsumer { |
558 | std::unique_ptr<MemoryBuffer> Buffer; |
559 | StringRef Remaining; |
560 | |
561 | public: |
562 | LineConsumer() = default; |
563 | LineConsumer(StringRef Filename) { |
564 | |
565 | |
566 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = |
567 | MemoryBuffer::getFileOrSTDIN(Filename, -1, |
568 | false); |
569 | if (std::error_code EC = BufferOrErr.getError()) { |
570 | errs() << Filename << ": " << EC.message() << "\n"; |
571 | Remaining = ""; |
572 | } else { |
573 | Buffer = std::move(BufferOrErr.get()); |
574 | Remaining = Buffer->getBuffer(); |
575 | } |
576 | } |
577 | bool empty() { return Remaining.empty(); } |
578 | void printNext(raw_ostream &OS, uint32_t LineNum) { |
579 | StringRef Line; |
580 | if (empty()) |
581 | Line = "/*EOF*/"; |
582 | else |
583 | std::tie(Line, Remaining) = Remaining.split("\n"); |
584 | OS << format("%5u:", LineNum) << Line << "\n"; |
585 | } |
586 | }; |
587 | } |
588 | |
589 | |
590 | |
591 | static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) { |
592 | if (!PreservePaths) |
593 | return sys::path::filename(Filename).str(); |
594 | |
595 | |
596 | |
597 | |
598 | llvm::SmallString<256> Result(""); |
599 | StringRef::iterator I, S, E; |
600 | for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) { |
601 | if (*I != '/') |
602 | continue; |
603 | |
604 | if (I - S == 1 && *S == '.') { |
605 | |
606 | } else if (I - S == 2 && *S == '.' && *(S + 1) == '.') { |
607 | |
608 | Result.append("^#"); |
609 | } else { |
610 | if (S < I) |
611 | |
612 | Result.append(S, I); |
613 | |
614 | Result.push_back('#'); |
615 | } |
616 | S = I + 1; |
617 | } |
618 | |
619 | if (S < I) |
620 | Result.append(S, I); |
621 | return std::string(Result.str()); |
622 | } |
623 | |
624 | std::string Context::getCoveragePath(StringRef filename, |
625 | StringRef mainFilename) const { |
626 | if (options.NoOutput) |
627 | |
628 | |
629 | |
630 | return std::string(filename); |
631 | |
632 | std::string CoveragePath; |
633 | if (options.LongFileNames && !filename.equals(mainFilename)) |
634 | CoveragePath = |
635 | mangleCoveragePath(mainFilename, options.PreservePaths) + "##"; |
636 | CoveragePath += mangleCoveragePath(filename, options.PreservePaths); |
637 | if (options.HashFilenames) { |
638 | MD5 Hasher; |
639 | MD5::MD5Result Result; |
640 | Hasher.update(filename.str()); |
641 | Hasher.final(Result); |
642 | CoveragePath += "##" + std::string(Result.digest()); |
643 | } |
644 | CoveragePath += ".gcov"; |
645 | return CoveragePath; |
646 | } |
647 | |
648 | void Context::collectFunction(GCOVFunction &f, Summary &summary) { |
649 | SourceInfo &si = sources[f.srcIdx]; |
650 | if (f.startLine >= si.startLineToFunctions.size()) |
651 | si.startLineToFunctions.resize(f.startLine + 1); |
652 | si.startLineToFunctions[f.startLine].push_back(&f); |
653 | for (const GCOVBlock &b : f.blocksRange()) { |
654 | if (b.lines.empty()) |
655 | continue; |
656 | uint32_t maxLineNum = *std::max_element(b.lines.begin(), b.lines.end()); |
657 | if (maxLineNum >= si.lines.size()) |
658 | si.lines.resize(maxLineNum + 1); |
659 | for (uint32_t lineNum : b.lines) { |
660 | LineInfo &line = si.lines[lineNum]; |
661 | if (!line.exists) |
662 | ++summary.lines; |
663 | if (line.count == 0 && b.count) |
664 | ++summary.linesExec; |
665 | line.exists = true; |
666 | line.count += b.count; |
667 | line.blocks.push_back(&b); |
668 | } |
669 | } |
670 | } |
671 | |
672 | void Context::collectSourceLine(SourceInfo &si, Summary *summary, |
673 | LineInfo &line, size_t lineNum) const { |
674 | uint64_t count = 0; |
675 | for (const GCOVBlock *b : line.blocks) { |
676 | if (b->number == 0) { |
677 | |
678 | |
679 | |
680 | |
681 | for (const GCOVArc *arc : b->succ) |
682 | count += arc->count; |
683 | } else { |
684 | |
685 | for (const GCOVArc *arc : b->pred) |
686 | if (!llvm::is_contained(line.blocks, &arc->src)) |
687 | count += arc->count; |
688 | } |
689 | for (GCOVArc *arc : b->succ) |
690 | arc->cycleCount = arc->count; |
691 | } |
692 | |
693 | count += GCOVBlock::getCyclesCount(line.blocks); |
694 | line.count = count; |
695 | if (line.exists) { |
696 | ++summary->lines; |
697 | if (line.count != 0) |
698 | ++summary->linesExec; |
699 | } |
700 | |
701 | if (options.BranchInfo) |
702 | for (const GCOVBlock *b : line.blocks) { |
703 | if (b->getLastLine() != lineNum) |
704 | continue; |
705 | int branches = 0, execBranches = 0, takenBranches = 0; |
706 | for (const GCOVArc *arc : b->succ) { |
707 | ++branches; |
708 | if (count != 0) |
709 | ++execBranches; |
710 | if (arc->count != 0) |
711 | ++takenBranches; |
712 | } |
713 | if (branches > 1) { |
714 | summary->branches += branches; |
715 | summary->branchesExec += execBranches; |
716 | summary->branchesTaken += takenBranches; |
717 | } |
718 | } |
719 | } |
720 | |
721 | void Context::collectSource(SourceInfo &si, Summary &summary) const { |
722 | size_t lineNum = 0; |
723 | for (LineInfo &line : si.lines) { |
724 | collectSourceLine(si, &summary, line, lineNum); |
725 | ++lineNum; |
726 | } |
727 | } |
728 | |
729 | void Context::annotateSource(SourceInfo &si, const GCOVFile &file, |
730 | StringRef gcno, StringRef gcda, |
731 | raw_ostream &os) const { |
732 | auto source = |
733 | options.Intermediate ? LineConsumer() : LineConsumer(si.filename); |
734 | |
735 | os << " -: 0:Source:" << si.displayName << '\n'; |
736 | os << " -: 0:Graph:" << gcno << '\n'; |
737 | os << " -: 0:Data:" << gcda << '\n'; |
738 | os << " -: 0:Runs:" << file.RunCount << '\n'; |
739 | if (file.Version < GCOV::V900) |
740 | os << " -: 0:Programs:" << file.ProgramCount << '\n'; |
741 | |
742 | for (size_t lineNum = 1; !source.empty(); ++lineNum) { |
743 | if (lineNum >= si.lines.size()) { |
744 | os << " -:"; |
745 | source.printNext(os, lineNum); |
746 | continue; |
747 | } |
748 | |
749 | const LineInfo &line = si.lines[lineNum]; |
750 | if (options.BranchInfo && lineNum < si.startLineToFunctions.size()) |
751 | for (const auto *f : si.startLineToFunctions[lineNum]) |
752 | printFunctionDetails(*f, os); |
753 | if (!line.exists) |
754 | os << " -:"; |
755 | else if (line.count == 0) |
756 | os << " #####:"; |
757 | else |
758 | os << format("%9" PRIu64 ":", line.count); |
759 | source.printNext(os, lineNum); |
760 | |
761 | uint32_t blockIdx = 0, edgeIdx = 0; |
762 | for (const GCOVBlock *b : line.blocks) { |
763 | if (b->getLastLine() != lineNum) |
764 | continue; |
765 | if (options.AllBlocks) { |
766 | if (b->getCount() == 0) |
767 | os << " $$$$$:"; |
768 | else |
769 | os << format("%9" PRIu64 ":", b->count); |
770 | os << format("%5u-block %2u\n", lineNum, blockIdx++); |
771 | } |
772 | if (options.BranchInfo) { |
773 | size_t NumEdges = b->succ.size(); |
774 | if (NumEdges > 1) |
775 | printBranchInfo(*b, edgeIdx, os); |
776 | else if (options.UncondBranch && NumEdges == 1) { |
777 | uint64_t count = b->succ[0]->count; |
778 | os << format("unconditional %2u ", edgeIdx++) |
779 | << formatBranchInfo(options, count, count) << '\n'; |
780 | } |
781 | } |
782 | } |
783 | } |
784 | } |
785 | |
786 | void Context::printSourceToIntermediate(const SourceInfo &si, |
787 | raw_ostream &os) const { |
788 | os << "file:" << si.filename << '\n'; |
789 | for (const auto &fs : si.startLineToFunctions) |
790 | for (const GCOVFunction *f : fs) |
791 | os << "function:" << f->startLine << ',' << f->getEntryCount() << ',' |
792 | << f->getName(options.Demangle) << '\n'; |
793 | for (size_t lineNum = 1, size = si.lines.size(); lineNum < size; ++lineNum) { |
794 | const LineInfo &line = si.lines[lineNum]; |
795 | if (line.blocks.empty()) |
796 | continue; |
797 | |
798 | |
799 | |
800 | os << "lcount:" << lineNum << ',' << line.count << '\n'; |
801 | |
802 | if (!options.BranchInfo) |
803 | continue; |
804 | for (const GCOVBlock *b : line.blocks) { |
805 | if (b->succ.size() < 2 || b->getLastLine() != lineNum) |
806 | continue; |
807 | for (const GCOVArc *arc : b->succ) { |
808 | const char *type = |
809 | b->getCount() ? arc->count ? "taken" : "nottaken" : "notexec"; |
810 | os << "branch:" << lineNum << ',' << type << '\n'; |
811 | } |
812 | } |
813 | } |
814 | } |
815 | |
816 | void Context::print(StringRef filename, StringRef gcno, StringRef gcda, |
817 | GCOVFile &file) { |
818 | for (StringRef filename : file.filenames) { |
819 | sources.emplace_back(filename); |
820 | SourceInfo &si = sources.back(); |
821 | si.displayName = si.filename; |
822 | if (!options.SourcePrefix.empty() && |
823 | sys::path::replace_path_prefix(si.displayName, options.SourcePrefix, |
824 | "") && |
825 | !si.displayName.empty()) { |
826 | |
827 | |
828 | if (sys::path::is_separator(si.displayName[0])) |
829 | si.displayName.erase(si.displayName.begin()); |
830 | else |
831 | si.displayName = si.filename; |
832 | } |
833 | if (options.RelativeOnly && sys::path::is_absolute(si.displayName)) |
834 | si.ignored = true; |
835 | } |
836 | |
837 | raw_ostream &os = llvm::outs(); |
838 | for (GCOVFunction &f : make_pointee_range(file.functions)) { |
839 | Summary summary(f.getName(options.Demangle)); |
840 | collectFunction(f, summary); |
841 | if (options.FuncCoverage && !options.UseStdout) { |
842 | os << "Function '" << summary.Name << "'\n"; |
843 | printSummary(summary, os); |
844 | os << '\n'; |
845 | } |
846 | } |
847 | |
848 | for (SourceInfo &si : sources) { |
849 | if (si.ignored) |
850 | continue; |
851 | Summary summary(si.displayName); |
852 | collectSource(si, summary); |
853 | |
854 | |
855 | std::string gcovName = getCoveragePath(si.filename, filename); |
856 | if (!options.UseStdout) { |
857 | os << "File '" << summary.Name << "'\n"; |
858 | printSummary(summary, os); |
859 | if (!options.NoOutput && !options.Intermediate) |
860 | os << "Creating '" << gcovName << "'\n"; |
861 | os << '\n'; |
862 | } |
863 | |
864 | if (options.NoOutput || options.Intermediate) |
865 | continue; |
866 | Optional<raw_fd_ostream> os; |
867 | if (!options.UseStdout) { |
868 | std::error_code ec; |
869 | os.emplace(gcovName, ec, sys::fs::OF_Text); |
870 | if (ec) { |
871 | errs() << ec.message() << '\n'; |
872 | continue; |
873 | } |
874 | } |
875 | annotateSource(si, file, gcno, gcda, |
876 | options.UseStdout ? llvm::outs() : *os); |
877 | } |
878 | |
879 | if (options.Intermediate && !options.NoOutput) { |
880 | |
881 | |
882 | std::string outputPath(sys::path::filename(filename)); |
883 | std::error_code ec; |
884 | raw_fd_ostream os(outputPath + ".gcov", ec, sys::fs::OF_Text); |
885 | if (ec) { |
886 | errs() << ec.message() << '\n'; |
887 | return; |
888 | } |
889 | |
890 | for (const SourceInfo &si : sources) |
891 | printSourceToIntermediate(si, os); |
892 | } |
893 | } |
894 | |
895 | void Context::printFunctionDetails(const GCOVFunction &f, |
896 | raw_ostream &os) const { |
897 | const uint64_t entryCount = f.getEntryCount(); |
898 | uint32_t blocksExec = 0; |
899 | const GCOVBlock &exitBlock = f.getExitBlock(); |
900 | uint64_t exitCount = 0; |
901 | for (const GCOVArc *arc : exitBlock.pred) |
902 | exitCount += arc->count; |
903 | for (const GCOVBlock &b : f.blocksRange()) |
904 | if (b.number != 0 && &b != &exitBlock && b.getCount()) |
905 | ++blocksExec; |
906 | |
907 | os << "function " << f.getName(options.Demangle) << " called " << entryCount |
908 | << " returned " << formatPercentage(exitCount, entryCount) |
909 | << "% blocks executed " |
910 | << formatPercentage(blocksExec, f.blocks.size() - 2) << "%\n"; |
911 | } |
912 | |
913 | |
914 | void Context::printBranchInfo(const GCOVBlock &Block, uint32_t &edgeIdx, |
915 | raw_ostream &os) const { |
916 | uint64_t total = 0; |
917 | for (const GCOVArc *arc : Block.dsts()) |
918 | total += arc->count; |
919 | for (const GCOVArc *arc : Block.dsts()) |
920 | os << format("branch %2u ", edgeIdx++) |
921 | << formatBranchInfo(options, arc->count, total) << '\n'; |
922 | } |
923 | |
924 | void Context::printSummary(const Summary &summary, raw_ostream &os) const { |
925 | os << format("Lines executed:%.2f%% of %" PRIu64 "\n", |
926 | double(summary.linesExec) * 100 / summary.lines, summary.lines); |
927 | if (options.BranchInfo) { |
928 | if (summary.branches == 0) { |
929 | os << "No branches\n"; |
930 | } else { |
931 | os << format("Branches executed:%.2f%% of %" PRIu64 "\n", |
932 | double(summary.branchesExec) * 100 / summary.branches, |
933 | summary.branches); |
934 | os << format("Taken at least once:%.2f%% of %" PRIu64 "\n", |
935 | double(summary.branchesTaken) * 100 / summary.branches, |
936 | summary.branches); |
937 | } |
938 | os << "No calls\n"; |
939 | } |
940 | } |
941 | |
942 | void llvm::gcovOneInput(const GCOV::Options &options, StringRef filename, |
943 | StringRef gcno, StringRef gcda, GCOVFile &file) { |
944 | Context fi(options); |
945 | fi.print(filename, gcno, gcda, file); |
946 | } |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #ifndef LLVM_PROFILEDATA_GCOV_H |
15 | #define LLVM_PROFILEDATA_GCOV_H |
16 | |
17 | #include "llvm/ADT/DenseMap.h" |
18 | #include "llvm/ADT/DenseSet.h" |
19 | #include "llvm/ADT/MapVector.h" |
20 | #include "llvm/ADT/SmallVector.h" |
21 | #include "llvm/ADT/StringMap.h" |
22 | #include "llvm/ADT/StringRef.h" |
23 | #include "llvm/ADT/iterator.h" |
24 | #include "llvm/ADT/iterator_range.h" |
25 | #include "llvm/Support/DataExtractor.h" |
26 | #include "llvm/Support/MemoryBuffer.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | #include <algorithm> |
29 | #include <cassert> |
30 | #include <cstddef> |
31 | #include <cstdint> |
32 | #include <limits> |
33 | #include <map> |
34 | #include <memory> |
35 | #include <string> |
36 | #include <utility> |
37 | |
38 | namespace llvm { |
39 | |
40 | class GCOVFunction; |
41 | class GCOVBlock; |
42 | |
43 | namespace GCOV { |
44 | |
45 | enum GCOVVersion { V304, V407, V408, V800, V900 }; |
46 | |
47 | |
48 | struct Options { |
49 | Options(bool A, bool B, bool C, bool F, bool P, bool U, bool I, bool L, |
50 | bool M, bool N, bool R, bool T, bool X, std::string SourcePrefix) |
51 | : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), |
52 | PreservePaths(P), UncondBranch(U), Intermediate(I), LongFileNames(L), |
53 | Demangle(M), NoOutput(N), RelativeOnly(R), UseStdout(T), |
54 | HashFilenames(X), SourcePrefix(std::move(SourcePrefix)) {} |
55 | |
56 | bool AllBlocks; |
57 | bool BranchInfo; |
58 | bool BranchCount; |
59 | bool FuncCoverage; |
60 | bool PreservePaths; |
61 | bool UncondBranch; |
62 | bool Intermediate; |
63 | bool LongFileNames; |
64 | bool Demangle; |
65 | bool NoOutput; |
66 | bool RelativeOnly; |
67 | bool UseStdout; |
68 | bool HashFilenames; |
69 | std::string SourcePrefix; |
70 | }; |
71 | |
72 | } |
73 | |
74 | |
75 | |
76 | class GCOVBuffer { |
77 | public: |
78 | GCOVBuffer(MemoryBuffer *B) : Buffer(B) {} |
79 | ~GCOVBuffer() { consumeError(cursor.takeError()); } |
80 | |
81 | |
82 | bool readGCNOFormat() { |
83 | StringRef buf = Buffer->getBuffer(); |
84 | StringRef magic = buf.substr(0, 4); |
85 | if (magic == "gcno") { |
| 2 | | Assuming the condition is true | |
|
| |
86 | de = DataExtractor(buf.substr(4), false, 0); |
87 | } else if (magic == "oncg") { |
88 | de = DataExtractor(buf.substr(4), true, 0); |
89 | } else { |
90 | errs() << "unexpected magic: " << magic << "\n"; |
91 | return false; |
92 | } |
93 | return true; |
| 4 | | Returning the value 1, which participates in a condition later | |
|
94 | } |
95 | |
96 | |
97 | bool readGCDAFormat() { |
98 | StringRef buf = Buffer->getBuffer(); |
99 | StringRef magic = buf.substr(0, 4); |
100 | if (magic == "gcda") { |
101 | de = DataExtractor(buf.substr(4), false, 0); |
102 | } else if (magic == "adcg") { |
103 | de = DataExtractor(buf.substr(4), true, 0); |
104 | } else { |
105 | return false; |
106 | } |
107 | return true; |
108 | } |
109 | |
110 | |
111 | bool readGCOVVersion(GCOV::GCOVVersion &Version) { |
112 | std::string str(de.getBytes(cursor, 4)); |
113 | if (str.size() != 4) |
| 8 | | Assuming the condition is false | |
|
| |
114 | return false; |
115 | if (de.isLittleEndian()) |
| |
116 | std::reverse(str.begin(), str.end()); |
117 | int ver = str[0] >= 'A' |
| 11 | | Assuming the condition is false | |
|
| |
118 | ? (str[0] - 'A') * 100 + (str[1] - '0') * 10 + str[2] - '0' |
119 | : (str[0] - '0') * 10 + str[2] - '0'; |
120 | if (ver >= 90) { |
| |
| |
121 | |
122 | Version = GCOV::V900; |
123 | return true; |
124 | } else if (ver >= 80) { |
| |
| |
125 | |
126 | Version = GCOV::V800; |
127 | return true; |
128 | } else if (ver >= 48) { |
| 17 | | Assuming 'ver' is >= 48 | |
|
| |
129 | |
130 | Version = GCOV::V408; |
131 | return true; |
| 19 | | Returning the value 1, which participates in a condition later | |
|
132 | } else if (ver >= 47) { |
133 | |
134 | Version = GCOV::V407; |
135 | return true; |
136 | } else if (ver >= 34) { |
137 | Version = GCOV::V304; |
138 | return true; |
139 | } |
140 | errs() << "unexpected version: " << str << "\n"; |
141 | return false; |
142 | } |
143 | |
144 | uint32_t getWord() { return de.getU32(cursor); } |
145 | StringRef getString() { |
146 | uint32_t len; |
147 | if (!readInt(len) || len == 0) |
148 | return {}; |
149 | return de.getBytes(cursor, len * 4).split('\0').first; |
150 | } |
151 | |
152 | bool readInt(uint32_t &Val) { |
153 | if (cursor.tell() + 4 > de.size()) { |
| 27 | | Assuming the condition is false | |
|
| |
154 | Val = 0; |
155 | errs() << "unexpected end of memory buffer: " << cursor.tell() << "\n"; |
156 | return false; |
157 | } |
158 | Val = de.getU32(cursor); |
159 | return true; |
| 29 | | Returning the value 1, which participates in a condition later | |
|
160 | } |
161 | |
162 | bool readInt64(uint64_t &Val) { |
163 | uint32_t Lo, Hi; |
164 | if (!readInt(Lo) || !readInt(Hi)) |
165 | return false; |
166 | Val = ((uint64_t)Hi << 32) | Lo; |
167 | return true; |
168 | } |
169 | |
170 | bool readString(StringRef &Str) { |
171 | uint32_t len; |
172 | if (!readInt(len) || len == 0) |
173 | return false; |
174 | Str = de.getBytes(cursor, len * 4).split('\0').first; |
175 | return bool(cursor); |
176 | } |
177 | |
178 | DataExtractor de{ArrayRef<uint8_t>{}, false, 0}; |
179 | DataExtractor::Cursor cursor{0}; |
180 | |
181 | private: |
182 | MemoryBuffer *Buffer; |
183 | }; |
184 | |
185 | |
186 | |
187 | class GCOVFile { |
188 | public: |
189 | GCOVFile() = default; |
190 | |
191 | bool readGCNO(GCOVBuffer &Buffer); |
192 | bool readGCDA(GCOVBuffer &Buffer); |
193 | GCOV::GCOVVersion getVersion() const { return Version; } |
194 | void print(raw_ostream &OS) const; |
195 | void dump() const; |
196 | |
197 | std::vector<std::string> filenames; |
198 | StringMap<unsigned> filenameToIdx; |
199 | |
200 | public: |
201 | bool GCNOInitialized = false; |
202 | GCOV::GCOVVersion Version; |
203 | uint32_t Checksum = 0; |
204 | StringRef cwd; |
205 | SmallVector<std::unique_ptr<GCOVFunction>, 16> functions; |
206 | std::map<uint32_t, GCOVFunction *> IdentToFunction; |
207 | uint32_t RunCount = 0; |
208 | uint32_t ProgramCount = 0; |
209 | |
210 | using iterator = pointee_iterator< |
211 | SmallVectorImpl<std::unique_ptr<GCOVFunction>>::const_iterator>; |
212 | iterator begin() const { return iterator(functions.begin()); } |
213 | iterator end() const { return iterator(functions.end()); } |
214 | }; |
215 | |
216 | struct GCOVArc { |
217 | GCOVArc(GCOVBlock &src, GCOVBlock &dst, uint32_t flags) |
218 | : src(src), dst(dst), flags(flags) {} |
219 | bool onTree() const; |
220 | |
221 | GCOVBlock &src; |
222 | GCOVBlock &dst; |
223 | uint32_t flags; |
224 | uint64_t count = 0; |
225 | uint64_t cycleCount = 0; |
226 | }; |
227 | |
228 | |
229 | class GCOVFunction { |
230 | public: |
231 | using BlockIterator = pointee_iterator< |
232 | SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>; |
233 | |
234 | GCOVFunction(GCOVFile &file) : file(file) {} |
235 | |
236 | StringRef getName(bool demangle) const; |
237 | StringRef getFilename() const; |
238 | uint64_t getEntryCount() const; |
239 | GCOVBlock &getExitBlock() const; |
240 | |
241 | iterator_range<BlockIterator> blocksRange() const { |
242 | return make_range(blocks.begin(), blocks.end()); |
243 | } |
244 | |
245 | uint64_t propagateCounts(const GCOVBlock &v, GCOVArc *pred); |
246 | void print(raw_ostream &OS) const; |
247 | void dump() const; |
248 | |
249 | GCOVFile &file; |
250 | uint32_t ident = 0; |
251 | uint32_t linenoChecksum; |
252 | uint32_t cfgChecksum = 0; |
253 | uint32_t startLine = 0; |
254 | uint32_t startColumn = 0; |
255 | uint32_t endLine = 0; |
256 | uint32_t endColumn = 0; |
257 | uint8_t artificial = 0; |
258 | StringRef Name; |
259 | mutable SmallString<0> demangled; |
260 | unsigned srcIdx; |
261 | SmallVector<std::unique_ptr<GCOVBlock>, 0> blocks; |
262 | SmallVector<std::unique_ptr<GCOVArc>, 0> arcs, treeArcs; |
263 | DenseSet<const GCOVBlock *> visited; |
264 | }; |
265 | |
266 | |
267 | class GCOVBlock { |
268 | public: |
269 | using EdgeIterator = SmallVectorImpl<GCOVArc *>::const_iterator; |
270 | using BlockVector = SmallVector<const GCOVBlock *, 1>; |
271 | using BlockVectorLists = SmallVector<BlockVector, 4>; |
272 | using Edges = SmallVector<GCOVArc *, 4>; |
273 | |
274 | GCOVBlock(uint32_t N) : number(N) {} |
275 | |
276 | void addLine(uint32_t N) { lines.push_back(N); } |
277 | uint32_t getLastLine() const { return lines.back(); } |
278 | uint64_t getCount() const { return count; } |
279 | |
280 | void addSrcEdge(GCOVArc *Edge) { pred.push_back(Edge); } |
281 | |
282 | void addDstEdge(GCOVArc *Edge) { succ.push_back(Edge); } |
283 | |
284 | iterator_range<EdgeIterator> srcs() const { |
285 | return make_range(pred.begin(), pred.end()); |
286 | } |
287 | |
288 | iterator_range<EdgeIterator> dsts() const { |
289 | return make_range(succ.begin(), succ.end()); |
290 | } |
291 | |
292 | void print(raw_ostream &OS) const; |
293 | void dump() const; |
294 | |
295 | static uint64_t |
296 | augmentOneCycle(GCOVBlock *src, |
297 | std::vector<std::pair<GCOVBlock *, size_t>> &stack); |
298 | static uint64_t getCyclesCount(const BlockVector &blocks); |
299 | static uint64_t getLineCount(const BlockVector &Blocks); |
300 | |
301 | public: |
302 | uint32_t number; |
303 | uint64_t count = 0; |
304 | SmallVector<GCOVArc *, 2> pred; |
305 | SmallVector<GCOVArc *, 2> succ; |
306 | SmallVector<uint32_t, 4> lines; |
307 | bool traversable = false; |
308 | GCOVArc *incoming = nullptr; |
309 | }; |
310 | |
311 | void gcovOneInput(const GCOV::Options &options, StringRef filename, |
312 | StringRef gcno, StringRef gcda, GCOVFile &file); |
313 | |
314 | } |
315 | |
316 | #endif // LLVM_SUPPORT_GCOV_H |