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