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/DenseMap.h" 18 #include "llvm/ADT/MapVector.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/MemoryBuffer.h" 25 #include "llvm/Support/raw_ostream.h" 26 #include <algorithm> 27 #include <cassert> 28 #include <cstddef> 29 #include <cstdint> 30 #include <limits> 31 #include <memory> 32 #include <string> 33 #include <utility> 34 35 namespace llvm { 36 37 class GCOVFunction; 38 class GCOVBlock; 39 class FileInfo; 40 41 namespace GCOV { 42 43 enum GCOVVersion { V402, V404, V704 }; 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 L, bool N, bool X) 48 : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), 49 PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N), 50 HashFilenames(X) {} 51 52 bool AllBlocks; 53 bool BranchInfo; 54 bool BranchCount; 55 bool FuncCoverage; 56 bool PreservePaths; 57 bool UncondBranch; 58 bool LongFileNames; 59 bool NoOutput; 60 bool HashFilenames; 61 }; 62 63 } // end namespace GCOV 64 65 /// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific 66 /// read operations. 67 class GCOVBuffer { 68 public: GCOVBuffer(MemoryBuffer * B)69 GCOVBuffer(MemoryBuffer *B) : Buffer(B) {} 70 71 /// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer. readGCNOFormat()72 bool readGCNOFormat() { 73 StringRef File = Buffer->getBuffer().slice(0, 4); 74 if (File != "oncg") { 75 errs() << "Unexpected file type: " << File << ".\n"; 76 return false; 77 } 78 Cursor = 4; 79 return true; 80 } 81 82 /// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer. readGCDAFormat()83 bool readGCDAFormat() { 84 StringRef File = Buffer->getBuffer().slice(0, 4); 85 if (File != "adcg") { 86 errs() << "Unexpected file type: " << File << ".\n"; 87 return false; 88 } 89 Cursor = 4; 90 return true; 91 } 92 93 /// readGCOVVersion - Read GCOV version. readGCOVVersion(GCOV::GCOVVersion & Version)94 bool readGCOVVersion(GCOV::GCOVVersion &Version) { 95 StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4); 96 if (VersionStr == "*204") { 97 Cursor += 4; 98 Version = GCOV::V402; 99 return true; 100 } 101 if (VersionStr == "*404") { 102 Cursor += 4; 103 Version = GCOV::V404; 104 return true; 105 } 106 if (VersionStr == "*704") { 107 Cursor += 4; 108 Version = GCOV::V704; 109 return true; 110 } 111 errs() << "Unexpected version: " << VersionStr << ".\n"; 112 return false; 113 } 114 115 /// readFunctionTag - If cursor points to a function tag then increment the 116 /// cursor and return true otherwise return false. readFunctionTag()117 bool readFunctionTag() { 118 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 119 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || 120 Tag[3] != '\1') { 121 return false; 122 } 123 Cursor += 4; 124 return true; 125 } 126 127 /// readBlockTag - If cursor points to a block tag then increment the 128 /// cursor and return true otherwise return false. readBlockTag()129 bool readBlockTag() { 130 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 131 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' || 132 Tag[3] != '\x01') { 133 return false; 134 } 135 Cursor += 4; 136 return true; 137 } 138 139 /// readEdgeTag - If cursor points to an edge tag then increment the 140 /// cursor and return true otherwise return false. readEdgeTag()141 bool readEdgeTag() { 142 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 143 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' || 144 Tag[3] != '\x01') { 145 return false; 146 } 147 Cursor += 4; 148 return true; 149 } 150 151 /// readLineTag - If cursor points to a line tag then increment the 152 /// cursor and return true otherwise return false. readLineTag()153 bool readLineTag() { 154 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 155 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' || 156 Tag[3] != '\x01') { 157 return false; 158 } 159 Cursor += 4; 160 return true; 161 } 162 163 /// readArcTag - If cursor points to an gcda arc tag then increment the 164 /// cursor and return true otherwise return false. readArcTag()165 bool readArcTag() { 166 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 167 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' || 168 Tag[3] != '\1') { 169 return false; 170 } 171 Cursor += 4; 172 return true; 173 } 174 175 /// readObjectTag - If cursor points to an object summary tag then increment 176 /// the cursor and return true otherwise return false. readObjectTag()177 bool readObjectTag() { 178 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 179 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || 180 Tag[3] != '\xa1') { 181 return false; 182 } 183 Cursor += 4; 184 return true; 185 } 186 187 /// readProgramTag - If cursor points to a program summary tag then increment 188 /// the cursor and return true otherwise return false. readProgramTag()189 bool readProgramTag() { 190 StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); 191 if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || 192 Tag[3] != '\xa3') { 193 return false; 194 } 195 Cursor += 4; 196 return true; 197 } 198 readInt(uint32_t & Val)199 bool readInt(uint32_t &Val) { 200 if (Buffer->getBuffer().size() < Cursor + 4) { 201 errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n"; 202 return false; 203 } 204 StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4); 205 Cursor += 4; 206 Val = *(const uint32_t *)(Str.data()); 207 return true; 208 } 209 readInt64(uint64_t & Val)210 bool readInt64(uint64_t &Val) { 211 uint32_t Lo, Hi; 212 if (!readInt(Lo) || !readInt(Hi)) 213 return false; 214 Val = ((uint64_t)Hi << 32) | Lo; 215 return true; 216 } 217 readString(StringRef & Str)218 bool readString(StringRef &Str) { 219 uint32_t Len = 0; 220 // Keep reading until we find a non-zero length. This emulates gcov's 221 // behaviour, which appears to do the same. 222 while (Len == 0) 223 if (!readInt(Len)) 224 return false; 225 Len *= 4; 226 if (Buffer->getBuffer().size() < Cursor + Len) { 227 errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n"; 228 return false; 229 } 230 Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first; 231 Cursor += Len; 232 return true; 233 } 234 getCursor()235 uint64_t getCursor() const { return Cursor; } advanceCursor(uint32_t n)236 void advanceCursor(uint32_t n) { Cursor += n * 4; } 237 238 private: 239 MemoryBuffer *Buffer; 240 uint64_t Cursor = 0; 241 }; 242 243 /// GCOVFile - Collects coverage information for one pair of coverage file 244 /// (.gcno and .gcda). 245 class GCOVFile { 246 public: 247 GCOVFile() = default; 248 249 bool readGCNO(GCOVBuffer &Buffer); 250 bool readGCDA(GCOVBuffer &Buffer); getChecksum()251 uint32_t getChecksum() const { return Checksum; } 252 void print(raw_ostream &OS) const; 253 void dump() const; 254 void collectLineCounts(FileInfo &FI); 255 256 private: 257 bool GCNOInitialized = false; 258 GCOV::GCOVVersion Version; 259 uint32_t Checksum = 0; 260 SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions; 261 uint32_t RunCount = 0; 262 uint32_t ProgramCount = 0; 263 }; 264 265 /// GCOVEdge - Collects edge information. 266 struct GCOVEdge { GCOVEdgeGCOVEdge267 GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {} 268 269 GCOVBlock &Src; 270 GCOVBlock &Dst; 271 uint64_t Count = 0; 272 uint64_t CyclesCount = 0; 273 }; 274 275 /// GCOVFunction - Collects function information. 276 class GCOVFunction { 277 public: 278 using BlockIterator = pointee_iterator< 279 SmallVectorImpl<std::unique_ptr<GCOVBlock>>::const_iterator>; 280 GCOVFunction(GCOVFile & P)281 GCOVFunction(GCOVFile &P) : Parent(P) {} 282 283 bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version); 284 bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version); getName()285 StringRef getName() const { return Name; } getFilename()286 StringRef getFilename() const { return Filename; } getNumBlocks()287 size_t getNumBlocks() const { return Blocks.size(); } 288 uint64_t getEntryCount() const; 289 uint64_t getExitCount() const; 290 block_begin()291 BlockIterator block_begin() const { return Blocks.begin(); } block_end()292 BlockIterator block_end() const { return Blocks.end(); } blocks()293 iterator_range<BlockIterator> blocks() const { 294 return make_range(block_begin(), block_end()); 295 } 296 297 void print(raw_ostream &OS) const; 298 void dump() const; 299 void collectLineCounts(FileInfo &FI); 300 301 private: 302 GCOVFile &Parent; 303 uint32_t Ident = 0; 304 uint32_t Checksum; 305 uint32_t LineNumber = 0; 306 StringRef Name; 307 StringRef Filename; 308 SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks; 309 SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges; 310 }; 311 312 /// GCOVBlock - Collects block information. 313 class GCOVBlock { 314 struct EdgeWeight { EdgeWeightEdgeWeight315 EdgeWeight(GCOVBlock *D) : Dst(D) {} 316 317 GCOVBlock *Dst; 318 uint64_t Count = 0; 319 }; 320 321 public: 322 using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator; 323 using BlockVector = SmallVector<const GCOVBlock *, 4>; 324 using BlockVectorLists = SmallVector<BlockVector, 4>; 325 using Edges = SmallVector<GCOVEdge *, 4>; 326 GCOVBlock(GCOVFunction & P,uint32_t N)327 GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {} 328 ~GCOVBlock(); 329 getParent()330 const GCOVFunction &getParent() const { return Parent; } addLine(uint32_t N)331 void addLine(uint32_t N) { Lines.push_back(N); } getLastLine()332 uint32_t getLastLine() const { return Lines.back(); } 333 void addCount(size_t DstEdgeNo, uint64_t N); getCount()334 uint64_t getCount() const { return Counter; } 335 addSrcEdge(GCOVEdge * Edge)336 void addSrcEdge(GCOVEdge *Edge) { 337 assert(&Edge->Dst == this); // up to caller to ensure edge is valid 338 SrcEdges.push_back(Edge); 339 } 340 addDstEdge(GCOVEdge * Edge)341 void addDstEdge(GCOVEdge *Edge) { 342 assert(&Edge->Src == this); // up to caller to ensure edge is valid 343 // Check if adding this edge causes list to become unsorted. 344 if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number) 345 DstEdgesAreSorted = false; 346 DstEdges.push_back(Edge); 347 } 348 getNumSrcEdges()349 size_t getNumSrcEdges() const { return SrcEdges.size(); } getNumDstEdges()350 size_t getNumDstEdges() const { return DstEdges.size(); } 351 void sortDstEdges(); 352 src_begin()353 EdgeIterator src_begin() const { return SrcEdges.begin(); } src_end()354 EdgeIterator src_end() const { return SrcEdges.end(); } srcs()355 iterator_range<EdgeIterator> srcs() const { 356 return make_range(src_begin(), src_end()); 357 } 358 dst_begin()359 EdgeIterator dst_begin() const { return DstEdges.begin(); } dst_end()360 EdgeIterator dst_end() const { return DstEdges.end(); } dsts()361 iterator_range<EdgeIterator> dsts() const { 362 return make_range(dst_begin(), dst_end()); 363 } 364 365 void print(raw_ostream &OS) const; 366 void dump() const; 367 void collectLineCounts(FileInfo &FI); 368 369 static uint64_t getCycleCount(const Edges &Path); 370 static void unblock(const GCOVBlock *U, BlockVector &Blocked, 371 BlockVectorLists &BlockLists); 372 static bool lookForCircuit(const GCOVBlock *V, const GCOVBlock *Start, 373 Edges &Path, BlockVector &Blocked, 374 BlockVectorLists &BlockLists, 375 const BlockVector &Blocks, uint64_t &Count); 376 static void getCyclesCount(const BlockVector &Blocks, uint64_t &Count); 377 static uint64_t getLineCount(const BlockVector &Blocks); 378 379 private: 380 GCOVFunction &Parent; 381 uint32_t Number; 382 uint64_t Counter = 0; 383 bool DstEdgesAreSorted = true; 384 SmallVector<GCOVEdge *, 16> SrcEdges; 385 SmallVector<GCOVEdge *, 16> DstEdges; 386 SmallVector<uint32_t, 16> Lines; 387 }; 388 389 class FileInfo { 390 protected: 391 // It is unlikely--but possible--for multiple functions to be on the same 392 // line. 393 // Therefore this typedef allows LineData.Functions to store multiple 394 // functions 395 // per instance. This is rare, however, so optimize for the common case. 396 using FunctionVector = SmallVector<const GCOVFunction *, 1>; 397 using FunctionLines = DenseMap<uint32_t, FunctionVector>; 398 using BlockVector = SmallVector<const GCOVBlock *, 4>; 399 using BlockLines = DenseMap<uint32_t, BlockVector>; 400 401 struct LineData { 402 LineData() = default; 403 404 BlockLines Blocks; 405 FunctionLines Functions; 406 uint32_t LastLine = 0; 407 }; 408 409 struct GCOVCoverage { GCOVCoverageGCOVCoverage410 GCOVCoverage(StringRef Name) : Name(Name) {} 411 412 StringRef Name; 413 414 uint32_t LogicalLines = 0; 415 uint32_t LinesExec = 0; 416 417 uint32_t Branches = 0; 418 uint32_t BranchesExec = 0; 419 uint32_t BranchesTaken = 0; 420 }; 421 422 public: FileInfo(const GCOV::Options & Options)423 FileInfo(const GCOV::Options &Options) : Options(Options) {} 424 addBlockLine(StringRef Filename,uint32_t Line,const GCOVBlock * Block)425 void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) { 426 if (Line > LineInfo[Filename].LastLine) 427 LineInfo[Filename].LastLine = Line; 428 LineInfo[Filename].Blocks[Line - 1].push_back(Block); 429 } 430 addFunctionLine(StringRef Filename,uint32_t Line,const GCOVFunction * Function)431 void addFunctionLine(StringRef Filename, uint32_t Line, 432 const GCOVFunction *Function) { 433 if (Line > LineInfo[Filename].LastLine) 434 LineInfo[Filename].LastLine = Line; 435 LineInfo[Filename].Functions[Line - 1].push_back(Function); 436 } 437 setRunCount(uint32_t Runs)438 void setRunCount(uint32_t Runs) { RunCount = Runs; } setProgramCount(uint32_t Programs)439 void setProgramCount(uint32_t Programs) { ProgramCount = Programs; } 440 void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile, 441 StringRef GCDAFile); 442 443 protected: 444 std::string getCoveragePath(StringRef Filename, StringRef MainFilename); 445 std::unique_ptr<raw_ostream> openCoveragePath(StringRef CoveragePath); 446 void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const; 447 void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block, 448 uint32_t LineIndex, uint32_t &BlockNo) const; 449 void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block, 450 GCOVCoverage &Coverage, uint32_t &EdgeNo); 451 void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo, 452 uint64_t Count) const; 453 454 void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const; 455 void printFuncCoverage(raw_ostream &OS) const; 456 void printFileCoverage(raw_ostream &OS) const; 457 458 const GCOV::Options &Options; 459 StringMap<LineData> LineInfo; 460 uint32_t RunCount = 0; 461 uint32_t ProgramCount = 0; 462 463 using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>; 464 using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>; 465 466 FileCoverageList FileCoverages; 467 FuncCoverageMap FuncCoverages; 468 }; 469 470 } // end namespace llvm 471 472 #endif // LLVM_SUPPORT_GCOV_H 473