1//===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===//
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// This header provides the interface to read and write coverage files that
10// use 'gcov' format.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_PROFILEDATA_GCOV_H
15#define LLVM_PROFILEDATA_GCOV_H
16
17#include "llvm/ADT/DenseSet.h"
18#include "llvm/ADT/SmallString.h"
19#include "llvm/ADT/SmallVector.h"
20#include "llvm/ADT/StringMap.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/ADT/iterator.h"
23#include "llvm/ADT/iterator_range.h"
24#include "llvm/Support/DataExtractor.h"
25#include "llvm/Support/MemoryBuffer.h"
26#include "llvm/Support/raw_ostream.h"
27#include <algorithm>
28#include <cstddef>
29#include <cstdint>
30#include <map>
31#include <memory>
32#include <string>
33#include <utility>
34
35namespace llvm {
36
37class GCOVFunction;
38class GCOVBlock;
39
40namespace GCOV {
41
42enum GCOVVersion { V304, V407, V408, V800, V900, V1200 };
43
44/// A struct for passing gcov options between functions.
45struct Options {
46 Options(bool A, bool B, bool C, bool F, bool P, bool U, bool I, bool L,
47 bool M, bool N, bool R, bool T, bool X, std::string SourcePrefix)
48 : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F),
49 PreservePaths(P), UncondBranch(U), Intermediate(I), LongFileNames(L),
50 Demangle(M), NoOutput(N), RelativeOnly(R), UseStdout(T),
51 HashFilenames(X), SourcePrefix(std::move(SourcePrefix)) {}
52
53 bool AllBlocks;
54 bool BranchInfo;
55 bool BranchCount;
56 bool FuncCoverage;
57 bool PreservePaths;
58 bool UncondBranch;
59 bool Intermediate;
60 bool LongFileNames;
61 bool Demangle;
62 bool NoOutput;
63 bool RelativeOnly;
64 bool UseStdout;
65 bool HashFilenames;
66 std::string SourcePrefix;
67};
68
69} // end namespace GCOV
70
71/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
72/// read operations.
73class GCOVBuffer {
74public:
75 GCOVBuffer(MemoryBuffer *B) : Buffer(B) {}
76 ~GCOVBuffer() { consumeError(Err: cursor.takeError()); }
77
78 /// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
79 bool readGCNOFormat() {
80 StringRef buf = Buffer->getBuffer();
81 StringRef magic = buf.substr(Start: 0, N: 4);
82 if (magic == "gcno") {
83 de = DataExtractor(buf.substr(Start: 4), false, 0);
84 } else if (magic == "oncg") {
85 de = DataExtractor(buf.substr(Start: 4), true, 0);
86 } else {
87 errs() << "unexpected magic: " << magic << "\n";
88 return false;
89 }
90 return true;
91 }
92
93 /// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
94 bool readGCDAFormat() {
95 StringRef buf = Buffer->getBuffer();
96 StringRef magic = buf.substr(Start: 0, N: 4);
97 if (magic == "gcda") {
98 de = DataExtractor(buf.substr(Start: 4), false, 0);
99 } else if (magic == "adcg") {
100 de = DataExtractor(buf.substr(Start: 4), true, 0);
101 } else {
102 return false;
103 }
104 return true;
105 }
106
107 /// readGCOVVersion - Read GCOV version.
108 bool readGCOVVersion(GCOV::GCOVVersion &version) {
109 std::string str(de.getBytes(C&: cursor, Length: 4));
110 if (str.size() != 4)
111 return false;
112 if (de.isLittleEndian())
113 std::reverse(first: str.begin(), last: str.end());
114 int ver = str[0] >= 'A'
115 ? (str[0] - 'A') * 100 + (str[1] - '0') * 10 + str[2] - '0'
116 : (str[0] - '0') * 10 + str[2] - '0';
117 if (ver >= 120) {
118 this->version = version = GCOV::V1200;
119 return true;
120 } else if (ver >= 90) {
121 // PR gcov-profile/84846, r269678
122 this->version = version = GCOV::V900;
123 return true;
124 } else if (ver >= 80) {
125 // PR gcov-profile/48463
126 this->version = version = GCOV::V800;
127 return true;
128 } else if (ver >= 48) {
129 // r189778: the exit block moved from the last to the second.
130 this->version = version = GCOV::V408;
131 return true;
132 } else if (ver >= 47) {
133 // r173147: split checksum into cfg checksum and line checksum.
134 this->version = version = GCOV::V407;
135 return true;
136 } else if (ver >= 34) {
137 this->version = 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(C&: cursor); }
145 StringRef getString() {
146 uint32_t len;
147 if (!readInt(Val&: len) || len == 0)
148 return {};
149 return de.getBytes(C&: cursor, Length: len * 4).split(Separator: '\0').first;
150 }
151
152 bool readInt(uint32_t &Val) {
153 if (cursor.tell() + 4 > de.size()) {
154 Val = 0;
155 errs() << "unexpected end of memory buffer: " << cursor.tell() << "\n";
156 return false;
157 }
158 Val = de.getU32(C&: cursor);
159 return true;
160 }
161
162 bool readInt64(uint64_t &Val) {
163 uint32_t Lo, Hi;
164 if (!readInt(Val&: Lo) || !readInt(Val&: 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(Val&: len) || len == 0)
173 return false;
174 if (version >= GCOV::V1200)
175 str = de.getBytes(C&: cursor, Length: len).drop_back();
176 else
177 str = de.getBytes(C&: cursor, Length: len * 4).split(Separator: '\0').first;
178 return bool(cursor);
179 }
180
181 DataExtractor de{ArrayRef<uint8_t>{}, false, 0};
182 DataExtractor::Cursor cursor{0};
183
184private:
185 MemoryBuffer *Buffer;
186 GCOV::GCOVVersion version{};
187};
188
189/// GCOVFile - Collects coverage information for one pair of coverage file
190/// (.gcno and .gcda).
191class GCOVFile {
192public:
193 GCOVFile() = default;
194
195 bool readGCNO(GCOVBuffer &Buffer);
196 bool readGCDA(GCOVBuffer &Buffer);
197 GCOV::GCOVVersion getVersion() const { return version; }
198 void print(raw_ostream &OS) const;
199 void dump() const;
200
201 std::vector<std::string> filenames;
202 StringMap<unsigned> filenameToIdx;
203
204public:
205 bool GCNOInitialized = false;
206 GCOV::GCOVVersion version{};
207 uint32_t checksum = 0;
208 StringRef cwd;
209 SmallVector<std::unique_ptr<GCOVFunction>, 16> functions;
210 std::map<uint32_t, GCOVFunction *> identToFunction;
211 uint32_t runCount = 0;
212 uint32_t programCount = 0;
213
214 using iterator = pointee_iterator<
215 SmallVectorImpl<std::unique_ptr<GCOVFunction>>::const_iterator>;
216 iterator begin() const { return iterator(functions.begin()); }
217 iterator end() const { return iterator(functions.end()); }
218
219private:
220 unsigned addNormalizedPathToMap(StringRef filename);
221};
222
223struct GCOVArc {
224 GCOVArc(GCOVBlock &src, GCOVBlock &dst, uint32_t flags)
225 : src(src), dst(dst), flags(flags) {}
226 bool onTree() const;
227
228 GCOVBlock &src;
229 GCOVBlock &dst;
230 uint32_t flags;
231 uint64_t count = 0;
232 uint64_t cycleCount = 0;
233};
234
235/// GCOVFunction - Collects function information.
236class GCOVFunction {
237public:
238 using BlockIterator = pointee_iterator<
239 SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>;
240
241 GCOVFunction(GCOVFile &file) : file(file) {}
242
243 StringRef getName(bool demangle) const;
244 StringRef getFilename() const;
245 uint64_t getEntryCount() const;
246 GCOVBlock &getExitBlock() const;
247
248 iterator_range<BlockIterator> blocksRange() const {
249 return make_range(x: blocks.begin(), y: blocks.end());
250 }
251
252 void propagateCounts(const GCOVBlock &v, GCOVArc *pred);
253 void print(raw_ostream &OS) const;
254 void dump() const;
255
256 GCOVFile &file;
257 uint32_t ident = 0;
258 uint32_t linenoChecksum;
259 uint32_t cfgChecksum = 0;
260 uint32_t startLine = 0;
261 uint32_t startColumn = 0;
262 uint32_t endLine = 0;
263 uint32_t endColumn = 0;
264 uint8_t artificial = 0;
265 StringRef Name;
266 mutable SmallString<0> demangled;
267 unsigned srcIdx;
268 SmallVector<std::unique_ptr<GCOVBlock>, 0> blocks;
269 SmallVector<std::unique_ptr<GCOVArc>, 0> arcs, treeArcs;
270 DenseSet<const GCOVBlock *> visited;
271};
272
273/// GCOVBlock - Collects block information.
274class GCOVBlock {
275public:
276 using EdgeIterator = SmallVectorImpl<GCOVArc *>::const_iterator;
277 using BlockVector = SmallVector<const GCOVBlock *, 1>;
278 using BlockVectorLists = SmallVector<BlockVector, 4>;
279 using Edges = SmallVector<GCOVArc *, 4>;
280
281 GCOVBlock(uint32_t N) : number(N) {}
282
283 void addLine(uint32_t N) { lines.push_back(Elt: N); }
284 uint32_t getLastLine() const { return lines.back(); }
285 uint64_t getCount() const { return count; }
286
287 void addSrcEdge(GCOVArc *Edge) { pred.push_back(Elt: Edge); }
288
289 void addDstEdge(GCOVArc *Edge) { succ.push_back(Elt: Edge); }
290
291 iterator_range<EdgeIterator> srcs() const {
292 return make_range(x: pred.begin(), y: pred.end());
293 }
294
295 iterator_range<EdgeIterator> dsts() const {
296 return make_range(x: succ.begin(), y: succ.end());
297 }
298
299 void print(raw_ostream &OS) const;
300 void dump() const;
301
302 static uint64_t
303 augmentOneCycle(GCOVBlock *src,
304 std::vector<std::pair<GCOVBlock *, size_t>> &stack);
305 static uint64_t getCyclesCount(const BlockVector &blocks);
306 static uint64_t getLineCount(const BlockVector &Blocks);
307
308public:
309 uint32_t number;
310 uint64_t count = 0;
311 SmallVector<GCOVArc *, 2> pred;
312 SmallVector<GCOVArc *, 2> succ;
313 SmallVector<uint32_t, 4> lines;
314 bool traversable = false;
315 GCOVArc *incoming = nullptr;
316};
317
318void gcovOneInput(const GCOV::Options &options, StringRef filename,
319 StringRef gcno, StringRef gcda, GCOVFile &file);
320
321} // end namespace llvm
322
323#endif // LLVM_PROFILEDATA_GCOV_H
324

source code of llvm/include/llvm/ProfileData/GCOV.h