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
320