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 | |
35 | namespace llvm { |
36 | |
37 | class GCOVFunction; |
38 | class GCOVBlock; |
39 | |
40 | namespace GCOV { |
41 | |
42 | enum GCOVVersion { V304, V407, V408, V800, V900, V1200 }; |
43 | |
44 | /// A struct for passing gcov options between functions. |
45 | struct 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. |
73 | class GCOVBuffer { |
74 | public: |
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 | |
184 | private: |
185 | MemoryBuffer *Buffer; |
186 | GCOV::GCOVVersion version{}; |
187 | }; |
188 | |
189 | /// GCOVFile - Collects coverage information for one pair of coverage file |
190 | /// (.gcno and .gcda). |
191 | class GCOVFile { |
192 | public: |
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 | |
204 | public: |
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 | |
219 | private: |
220 | unsigned addNormalizedPathToMap(StringRef filename); |
221 | }; |
222 | |
223 | struct 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. |
236 | class GCOVFunction { |
237 | public: |
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. |
274 | class GCOVBlock { |
275 | public: |
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 | |
308 | public: |
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 | |
318 | void 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 | |