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