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