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 |