1 | //===- CoverageMapping.h - Code coverage mapping support --------*- 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 | // Code coverage mapping data is generated by clang and read by |
10 | // llvm-cov to show code coverage statistics for a file. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H |
15 | #define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H |
16 | |
17 | #include "llvm/ADT/ArrayRef.h" |
18 | #include "llvm/ADT/BitVector.h" |
19 | #include "llvm/ADT/DenseMap.h" |
20 | #include "llvm/ADT/DenseSet.h" |
21 | #include "llvm/ADT/Hashing.h" |
22 | #include "llvm/ADT/StringRef.h" |
23 | #include "llvm/ADT/iterator.h" |
24 | #include "llvm/ADT/iterator_range.h" |
25 | #include "llvm/Object/BuildID.h" |
26 | #include "llvm/ProfileData/Coverage/MCDCTypes.h" |
27 | #include "llvm/ProfileData/InstrProf.h" |
28 | #include "llvm/Support/Alignment.h" |
29 | #include "llvm/Support/Compiler.h" |
30 | #include "llvm/Support/Debug.h" |
31 | #include "llvm/Support/Endian.h" |
32 | #include "llvm/Support/Error.h" |
33 | #include "llvm/Support/raw_ostream.h" |
34 | #include <cassert> |
35 | #include <cstdint> |
36 | #include <iterator> |
37 | #include <memory> |
38 | #include <sstream> |
39 | #include <string> |
40 | #include <system_error> |
41 | #include <utility> |
42 | #include <vector> |
43 | |
44 | namespace llvm { |
45 | |
46 | class IndexedInstrProfReader; |
47 | |
48 | namespace object { |
49 | class BuildIDFetcher; |
50 | } // namespace object |
51 | |
52 | namespace vfs { |
53 | class FileSystem; |
54 | } // namespace vfs |
55 | |
56 | namespace coverage { |
57 | |
58 | class CoverageMappingReader; |
59 | struct CoverageMappingRecord; |
60 | |
61 | enum class coveragemap_error { |
62 | success = 0, |
63 | eof, |
64 | no_data_found, |
65 | unsupported_version, |
66 | truncated, |
67 | malformed, |
68 | decompression_failed, |
69 | invalid_or_missing_arch_specifier |
70 | }; |
71 | |
72 | const std::error_category &coveragemap_category(); |
73 | |
74 | inline std::error_code make_error_code(coveragemap_error E) { |
75 | return std::error_code(static_cast<int>(E), coveragemap_category()); |
76 | } |
77 | |
78 | class CoverageMapError : public ErrorInfo<CoverageMapError> { |
79 | public: |
80 | CoverageMapError(coveragemap_error Err, const Twine &ErrStr = Twine()) |
81 | : Err(Err), Msg(ErrStr.str()) { |
82 | assert(Err != coveragemap_error::success && "Not an error" ); |
83 | } |
84 | |
85 | std::string message() const override; |
86 | |
87 | void log(raw_ostream &OS) const override { OS << message(); } |
88 | |
89 | std::error_code convertToErrorCode() const override { |
90 | return make_error_code(E: Err); |
91 | } |
92 | |
93 | coveragemap_error get() const { return Err; } |
94 | const std::string &getMessage() const { return Msg; } |
95 | |
96 | static char ID; |
97 | |
98 | private: |
99 | coveragemap_error Err; |
100 | std::string Msg; |
101 | }; |
102 | |
103 | /// A Counter is an abstract value that describes how to compute the |
104 | /// execution count for a region of code using the collected profile count data. |
105 | struct Counter { |
106 | /// The CounterExpression kind (Add or Subtract) is encoded in bit 0 next to |
107 | /// the CounterKind. This means CounterKind has to leave bit 0 free. |
108 | enum CounterKind { Zero, CounterValueReference, Expression }; |
109 | static const unsigned EncodingTagBits = 2; |
110 | static const unsigned EncodingTagMask = 0x3; |
111 | static const unsigned EncodingCounterTagAndExpansionRegionTagBits = |
112 | EncodingTagBits + 1; |
113 | |
114 | private: |
115 | CounterKind Kind = Zero; |
116 | unsigned ID = 0; |
117 | |
118 | Counter(CounterKind Kind, unsigned ID) : Kind(Kind), ID(ID) {} |
119 | |
120 | public: |
121 | Counter() = default; |
122 | |
123 | CounterKind getKind() const { return Kind; } |
124 | |
125 | bool isZero() const { return Kind == Zero; } |
126 | |
127 | bool isExpression() const { return Kind == Expression; } |
128 | |
129 | unsigned getCounterID() const { return ID; } |
130 | |
131 | unsigned getExpressionID() const { return ID; } |
132 | |
133 | friend bool operator==(const Counter &LHS, const Counter &RHS) { |
134 | return LHS.Kind == RHS.Kind && LHS.ID == RHS.ID; |
135 | } |
136 | |
137 | friend bool operator!=(const Counter &LHS, const Counter &RHS) { |
138 | return !(LHS == RHS); |
139 | } |
140 | |
141 | friend bool operator<(const Counter &LHS, const Counter &RHS) { |
142 | return std::tie(args: LHS.Kind, args: LHS.ID) < std::tie(args: RHS.Kind, args: RHS.ID); |
143 | } |
144 | |
145 | /// Return the counter that represents the number zero. |
146 | static Counter getZero() { return Counter(); } |
147 | |
148 | /// Return the counter that corresponds to a specific profile counter. |
149 | static Counter getCounter(unsigned CounterId) { |
150 | return Counter(CounterValueReference, CounterId); |
151 | } |
152 | |
153 | /// Return the counter that corresponds to a specific addition counter |
154 | /// expression. |
155 | static Counter getExpression(unsigned ExpressionId) { |
156 | return Counter(Expression, ExpressionId); |
157 | } |
158 | }; |
159 | |
160 | /// A Counter expression is a value that represents an arithmetic operation |
161 | /// with two counters. |
162 | struct CounterExpression { |
163 | enum ExprKind { Subtract, Add }; |
164 | ExprKind Kind; |
165 | Counter LHS, RHS; |
166 | |
167 | CounterExpression(ExprKind Kind, Counter LHS, Counter RHS) |
168 | : Kind(Kind), LHS(LHS), RHS(RHS) {} |
169 | }; |
170 | |
171 | /// A Counter expression builder is used to construct the counter expressions. |
172 | /// It avoids unnecessary duplication and simplifies algebraic expressions. |
173 | class CounterExpressionBuilder { |
174 | /// A list of all the counter expressions |
175 | std::vector<CounterExpression> Expressions; |
176 | |
177 | /// A lookup table for the index of a given expression. |
178 | DenseMap<CounterExpression, unsigned> ExpressionIndices; |
179 | |
180 | /// Return the counter which corresponds to the given expression. |
181 | /// |
182 | /// If the given expression is already stored in the builder, a counter |
183 | /// that references that expression is returned. Otherwise, the given |
184 | /// expression is added to the builder's collection of expressions. |
185 | Counter get(const CounterExpression &E); |
186 | |
187 | /// Represents a term in a counter expression tree. |
188 | struct Term { |
189 | unsigned CounterID; |
190 | int Factor; |
191 | |
192 | Term(unsigned CounterID, int Factor) |
193 | : CounterID(CounterID), Factor(Factor) {} |
194 | }; |
195 | |
196 | /// Gather the terms of the expression tree for processing. |
197 | /// |
198 | /// This collects each addition and subtraction referenced by the counter into |
199 | /// a sequence that can be sorted and combined to build a simplified counter |
200 | /// expression. |
201 | void (Counter C, int Sign, SmallVectorImpl<Term> &Terms); |
202 | |
203 | /// Simplifies the given expression tree |
204 | /// by getting rid of algebraically redundant operations. |
205 | Counter simplify(Counter ExpressionTree); |
206 | |
207 | public: |
208 | ArrayRef<CounterExpression> getExpressions() const { return Expressions; } |
209 | |
210 | /// Return a counter that represents the expression that adds LHS and RHS. |
211 | Counter add(Counter LHS, Counter RHS, bool Simplify = true); |
212 | |
213 | /// Return a counter that represents the expression that subtracts RHS from |
214 | /// LHS. |
215 | Counter subtract(Counter LHS, Counter RHS, bool Simplify = true); |
216 | }; |
217 | |
218 | using LineColPair = std::pair<unsigned, unsigned>; |
219 | |
220 | /// A Counter mapping region associates a source range with a specific counter. |
221 | struct CounterMappingRegion { |
222 | enum RegionKind { |
223 | /// A CodeRegion associates some code with a counter |
224 | CodeRegion, |
225 | |
226 | /// An ExpansionRegion represents a file expansion region that associates |
227 | /// a source range with the expansion of a virtual source file, such as |
228 | /// for a macro instantiation or #include file. |
229 | ExpansionRegion, |
230 | |
231 | /// A SkippedRegion represents a source range with code that was skipped |
232 | /// by a preprocessor or similar means. |
233 | SkippedRegion, |
234 | |
235 | /// A GapRegion is like a CodeRegion, but its count is only set as the |
236 | /// line execution count when its the only region in the line. |
237 | GapRegion, |
238 | |
239 | /// A BranchRegion represents leaf-level boolean expressions and is |
240 | /// associated with two counters, each representing the number of times the |
241 | /// expression evaluates to true or false. |
242 | BranchRegion, |
243 | |
244 | /// A DecisionRegion represents a top-level boolean expression and is |
245 | /// associated with a variable length bitmap index and condition number. |
246 | MCDCDecisionRegion, |
247 | |
248 | /// A Branch Region can be extended to include IDs to facilitate MC/DC. |
249 | MCDCBranchRegion |
250 | }; |
251 | |
252 | /// Primary Counter that is also used for Branch Regions (TrueCount). |
253 | Counter Count; |
254 | |
255 | /// Secondary Counter used for Branch Regions (FalseCount). |
256 | Counter FalseCount; |
257 | |
258 | /// Parameters used for Modified Condition/Decision Coverage |
259 | mcdc::Parameters MCDCParams; |
260 | |
261 | template <class MaybeConstInnerParameters, class MaybeConstMCDCParameters> |
262 | static auto &getParams(MaybeConstMCDCParameters &MCDCParams) { |
263 | using InnerParameters = |
264 | typename std::remove_const<MaybeConstInnerParameters>::type; |
265 | MaybeConstInnerParameters *Params = |
266 | std::get_if<InnerParameters>(&MCDCParams); |
267 | assert(Params && "InnerParameters unavailable" ); |
268 | return *Params; |
269 | } |
270 | |
271 | const auto &getDecisionParams() const { |
272 | return getParams<const mcdc::DecisionParameters>(MCDCParams); |
273 | } |
274 | |
275 | const auto &getBranchParams() const { |
276 | return getParams<const mcdc::BranchParameters>(MCDCParams); |
277 | } |
278 | |
279 | unsigned FileID = 0; |
280 | unsigned ExpandedFileID = 0; |
281 | unsigned LineStart, ColumnStart, LineEnd, ColumnEnd; |
282 | |
283 | RegionKind Kind; |
284 | |
285 | CounterMappingRegion(Counter Count, unsigned FileID, unsigned ExpandedFileID, |
286 | unsigned LineStart, unsigned ColumnStart, |
287 | unsigned LineEnd, unsigned ColumnEnd, RegionKind Kind) |
288 | : Count(Count), FileID(FileID), ExpandedFileID(ExpandedFileID), |
289 | LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd), |
290 | ColumnEnd(ColumnEnd), Kind(Kind) {} |
291 | |
292 | CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID, |
293 | unsigned ExpandedFileID, unsigned LineStart, |
294 | unsigned ColumnStart, unsigned LineEnd, |
295 | unsigned ColumnEnd, RegionKind Kind, |
296 | const mcdc::Parameters &MCDCParams = std::monostate()) |
297 | : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams), |
298 | FileID(FileID), ExpandedFileID(ExpandedFileID), LineStart(LineStart), |
299 | ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd), |
300 | Kind(Kind) {} |
301 | |
302 | CounterMappingRegion(const mcdc::DecisionParameters &MCDCParams, |
303 | unsigned FileID, unsigned LineStart, |
304 | unsigned ColumnStart, unsigned LineEnd, |
305 | unsigned ColumnEnd, RegionKind Kind) |
306 | : MCDCParams(MCDCParams), FileID(FileID), LineStart(LineStart), |
307 | ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd), |
308 | Kind(Kind) {} |
309 | |
310 | static CounterMappingRegion |
311 | makeRegion(Counter Count, unsigned FileID, unsigned LineStart, |
312 | unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { |
313 | return CounterMappingRegion(Count, FileID, 0, LineStart, ColumnStart, |
314 | LineEnd, ColumnEnd, CodeRegion); |
315 | } |
316 | |
317 | static CounterMappingRegion |
318 | makeExpansion(unsigned FileID, unsigned ExpandedFileID, unsigned LineStart, |
319 | unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { |
320 | return CounterMappingRegion(Counter(), FileID, ExpandedFileID, LineStart, |
321 | ColumnStart, LineEnd, ColumnEnd, |
322 | ExpansionRegion); |
323 | } |
324 | |
325 | static CounterMappingRegion |
326 | makeSkipped(unsigned FileID, unsigned LineStart, unsigned ColumnStart, |
327 | unsigned LineEnd, unsigned ColumnEnd) { |
328 | return CounterMappingRegion(Counter(), FileID, 0, LineStart, ColumnStart, |
329 | LineEnd, ColumnEnd, SkippedRegion); |
330 | } |
331 | |
332 | static CounterMappingRegion |
333 | makeGapRegion(Counter Count, unsigned FileID, unsigned LineStart, |
334 | unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { |
335 | return CounterMappingRegion(Count, FileID, 0, LineStart, ColumnStart, |
336 | LineEnd, (1U << 31) | ColumnEnd, GapRegion); |
337 | } |
338 | |
339 | static CounterMappingRegion |
340 | makeBranchRegion(Counter Count, Counter FalseCount, unsigned FileID, |
341 | unsigned LineStart, unsigned ColumnStart, unsigned LineEnd, |
342 | unsigned ColumnEnd, |
343 | const mcdc::Parameters &MCDCParams = std::monostate()) { |
344 | return CounterMappingRegion( |
345 | Count, FalseCount, FileID, 0, LineStart, ColumnStart, LineEnd, |
346 | ColumnEnd, |
347 | (std::get_if<mcdc::BranchParameters>(ptr: &MCDCParams) ? MCDCBranchRegion |
348 | : BranchRegion), |
349 | MCDCParams); |
350 | } |
351 | |
352 | static CounterMappingRegion |
353 | makeDecisionRegion(const mcdc::DecisionParameters &MCDCParams, |
354 | unsigned FileID, unsigned LineStart, unsigned ColumnStart, |
355 | unsigned LineEnd, unsigned ColumnEnd) { |
356 | return CounterMappingRegion(MCDCParams, FileID, LineStart, ColumnStart, |
357 | LineEnd, ColumnEnd, MCDCDecisionRegion); |
358 | } |
359 | |
360 | inline LineColPair startLoc() const { |
361 | return LineColPair(LineStart, ColumnStart); |
362 | } |
363 | |
364 | inline LineColPair endLoc() const { return LineColPair(LineEnd, ColumnEnd); } |
365 | }; |
366 | |
367 | /// Associates a source range with an execution count. |
368 | struct CountedRegion : public CounterMappingRegion { |
369 | uint64_t ExecutionCount; |
370 | uint64_t FalseExecutionCount; |
371 | bool Folded; |
372 | |
373 | CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount) |
374 | : CounterMappingRegion(R), ExecutionCount(ExecutionCount), |
375 | FalseExecutionCount(0), Folded(false) {} |
376 | |
377 | CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount, |
378 | uint64_t FalseExecutionCount) |
379 | : CounterMappingRegion(R), ExecutionCount(ExecutionCount), |
380 | FalseExecutionCount(FalseExecutionCount), Folded(false) {} |
381 | }; |
382 | |
383 | /// MCDC Record grouping all information together. |
384 | struct MCDCRecord { |
385 | /// CondState represents the evaluation of a condition in an executed test |
386 | /// vector, which can be True or False. A DontCare is used to mask an |
387 | /// unevaluatable condition resulting from short-circuit behavior of logical |
388 | /// operators in languages like C/C++. When comparing the evaluation of a |
389 | /// condition across executed test vectors, comparisons against a DontCare |
390 | /// are effectively ignored. |
391 | enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 }; |
392 | |
393 | using TestVector = llvm::SmallVector<CondState>; |
394 | using TestVectors = llvm::SmallVector<TestVector>; |
395 | using BoolVector = llvm::SmallVector<bool>; |
396 | using TVRowPair = std::pair<unsigned, unsigned>; |
397 | using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>; |
398 | using CondIDMap = llvm::DenseMap<unsigned, unsigned>; |
399 | using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>; |
400 | |
401 | private: |
402 | CounterMappingRegion Region; |
403 | TestVectors TV; |
404 | TVPairMap IndependencePairs; |
405 | BoolVector Folded; |
406 | CondIDMap PosToID; |
407 | LineColPairMap CondLoc; |
408 | |
409 | public: |
410 | MCDCRecord(const CounterMappingRegion &Region, TestVectors &&TV, |
411 | TVPairMap &&IndependencePairs, BoolVector &&Folded, |
412 | CondIDMap &&PosToID, LineColPairMap &&CondLoc) |
413 | : Region(Region), TV(std::move(TV)), |
414 | IndependencePairs(std::move(IndependencePairs)), |
415 | Folded(std::move(Folded)), PosToID(std::move(PosToID)), |
416 | CondLoc(std::move(CondLoc)){}; |
417 | |
418 | CounterMappingRegion getDecisionRegion() const { return Region; } |
419 | unsigned getNumConditions() const { |
420 | unsigned NumConditions = Region.getDecisionParams().NumConditions; |
421 | assert(NumConditions != 0 && |
422 | "In MC/DC, NumConditions should never be zero!" ); |
423 | return NumConditions; |
424 | } |
425 | unsigned getNumTestVectors() const { return TV.size(); } |
426 | bool isCondFolded(unsigned Condition) const { return Folded[Condition]; } |
427 | |
428 | /// Return the evaluation of a condition (indicated by Condition) in an |
429 | /// executed test vector (indicated by TestVectorIndex), which will be True, |
430 | /// False, or DontCare if the condition is unevaluatable. Because condition |
431 | /// IDs are not associated based on their position in the expression, |
432 | /// accessing conditions in the TestVectors requires a translation from a |
433 | /// ordinal position to actual condition ID. This is done via PosToID[]. |
434 | CondState getTVCondition(unsigned TestVectorIndex, unsigned Condition) { |
435 | return TV[TestVectorIndex][PosToID[Condition]]; |
436 | } |
437 | |
438 | /// Return the Result evaluation for an executed test vector. |
439 | /// See MCDCRecordProcessor::RecordTestVector(). |
440 | CondState getTVResult(unsigned TestVectorIndex) { |
441 | return TV[TestVectorIndex][getNumConditions()]; |
442 | } |
443 | |
444 | /// Determine whether a given condition (indicated by Condition) is covered |
445 | /// by an Independence Pair. Because condition IDs are not associated based |
446 | /// on their position in the expression, accessing conditions in the |
447 | /// TestVectors requires a translation from a ordinal position to actual |
448 | /// condition ID. This is done via PosToID[]. |
449 | bool isConditionIndependencePairCovered(unsigned Condition) const { |
450 | auto It = PosToID.find(Val: Condition); |
451 | if (It != PosToID.end()) |
452 | return IndependencePairs.contains(Val: It->second); |
453 | llvm_unreachable("Condition ID without an Ordinal mapping" ); |
454 | } |
455 | |
456 | /// Return the Independence Pair that covers the given condition. Because |
457 | /// condition IDs are not associated based on their position in the |
458 | /// expression, accessing conditions in the TestVectors requires a |
459 | /// translation from a ordinal position to actual condition ID. This is done |
460 | /// via PosToID[]. |
461 | TVRowPair getConditionIndependencePair(unsigned Condition) { |
462 | assert(isConditionIndependencePairCovered(Condition)); |
463 | return IndependencePairs[PosToID[Condition]]; |
464 | } |
465 | |
466 | float getPercentCovered() const { |
467 | unsigned Folded = 0; |
468 | unsigned Covered = 0; |
469 | for (unsigned C = 0; C < getNumConditions(); C++) { |
470 | if (isCondFolded(Condition: C)) |
471 | Folded++; |
472 | else if (isConditionIndependencePairCovered(Condition: C)) |
473 | Covered++; |
474 | } |
475 | |
476 | unsigned Total = getNumConditions() - Folded; |
477 | if (Total == 0) |
478 | return 0.0; |
479 | return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0; |
480 | } |
481 | |
482 | std::string (unsigned Condition) { |
483 | std::ostringstream OS; |
484 | OS << "Condition C" << Condition + 1 << " --> (" ; |
485 | OS << CondLoc[Condition].first << ":" << CondLoc[Condition].second; |
486 | OS << ")\n" ; |
487 | return OS.str(); |
488 | } |
489 | |
490 | std::string () const { |
491 | std::ostringstream OS; |
492 | if (getNumTestVectors() == 0) { |
493 | OS << "None.\n" ; |
494 | return OS.str(); |
495 | } |
496 | const auto NumConditions = getNumConditions(); |
497 | for (unsigned I = 0; I < NumConditions; I++) { |
498 | OS << "C" << I + 1; |
499 | if (I != NumConditions - 1) |
500 | OS << ", " ; |
501 | } |
502 | OS << " Result\n" ; |
503 | return OS.str(); |
504 | } |
505 | |
506 | std::string getTestVectorString(unsigned TestVectorIndex) { |
507 | assert(TestVectorIndex < getNumTestVectors() && |
508 | "TestVector index out of bounds!" ); |
509 | std::ostringstream OS; |
510 | const auto NumConditions = getNumConditions(); |
511 | // Add individual condition values to the string. |
512 | OS << " " << TestVectorIndex + 1 << " { " ; |
513 | for (unsigned Condition = 0; Condition < NumConditions; Condition++) { |
514 | if (isCondFolded(Condition)) |
515 | OS << "C" ; |
516 | else { |
517 | switch (getTVCondition(TestVectorIndex, Condition)) { |
518 | case MCDCRecord::MCDC_DontCare: |
519 | OS << "-" ; |
520 | break; |
521 | case MCDCRecord::MCDC_True: |
522 | OS << "T" ; |
523 | break; |
524 | case MCDCRecord::MCDC_False: |
525 | OS << "F" ; |
526 | break; |
527 | } |
528 | } |
529 | if (Condition != NumConditions - 1) |
530 | OS << ", " ; |
531 | } |
532 | |
533 | // Add result value to the string. |
534 | OS << " = " ; |
535 | if (getTVResult(TestVectorIndex) == MCDC_True) |
536 | OS << "T" ; |
537 | else |
538 | OS << "F" ; |
539 | OS << " }\n" ; |
540 | |
541 | return OS.str(); |
542 | } |
543 | |
544 | std::string getConditionCoverageString(unsigned Condition) { |
545 | assert(Condition < getNumConditions() && |
546 | "Condition index is out of bounds!" ); |
547 | std::ostringstream OS; |
548 | |
549 | OS << " C" << Condition + 1 << "-Pair: " ; |
550 | if (isCondFolded(Condition)) { |
551 | OS << "constant folded\n" ; |
552 | } else if (isConditionIndependencePairCovered(Condition)) { |
553 | TVRowPair rows = getConditionIndependencePair(Condition); |
554 | OS << "covered: (" << rows.first << "," ; |
555 | OS << rows.second << ")\n" ; |
556 | } else |
557 | OS << "not covered\n" ; |
558 | |
559 | return OS.str(); |
560 | } |
561 | }; |
562 | |
563 | /// A Counter mapping context is used to connect the counters, expressions |
564 | /// and the obtained counter values. |
565 | class CounterMappingContext { |
566 | ArrayRef<CounterExpression> Expressions; |
567 | ArrayRef<uint64_t> CounterValues; |
568 | BitVector Bitmap; |
569 | |
570 | public: |
571 | CounterMappingContext(ArrayRef<CounterExpression> Expressions, |
572 | ArrayRef<uint64_t> CounterValues = std::nullopt) |
573 | : Expressions(Expressions), CounterValues(CounterValues) {} |
574 | |
575 | void setCounts(ArrayRef<uint64_t> Counts) { CounterValues = Counts; } |
576 | void setBitmap(BitVector &&Bitmap_) { Bitmap = std::move(Bitmap_); } |
577 | |
578 | void dump(const Counter &C, raw_ostream &OS) const; |
579 | void dump(const Counter &C) const { dump(C, OS&: dbgs()); } |
580 | |
581 | /// Return the number of times that a region of code associated with this |
582 | /// counter was executed. |
583 | Expected<int64_t> evaluate(const Counter &C) const; |
584 | |
585 | /// Return an MCDC record that indicates executed test vectors and condition |
586 | /// pairs. |
587 | Expected<MCDCRecord> |
588 | evaluateMCDCRegion(const CounterMappingRegion &Region, |
589 | ArrayRef<const CounterMappingRegion *> Branches); |
590 | |
591 | unsigned getMaxCounterID(const Counter &C) const; |
592 | }; |
593 | |
594 | /// Code coverage information for a single function. |
595 | struct FunctionRecord { |
596 | /// Raw function name. |
597 | std::string Name; |
598 | /// Mapping from FileID (i.e. vector index) to filename. Used to support |
599 | /// macro expansions within a function in which the macro and function are |
600 | /// defined in separate files. |
601 | /// |
602 | /// TODO: Uniquing filenames across all function records may be a performance |
603 | /// optimization. |
604 | std::vector<std::string> Filenames; |
605 | /// Regions in the function along with their counts. |
606 | std::vector<CountedRegion> CountedRegions; |
607 | /// Branch Regions in the function along with their counts. |
608 | std::vector<CountedRegion> CountedBranchRegions; |
609 | /// MCDC Records record a DecisionRegion and associated BranchRegions. |
610 | std::vector<MCDCRecord> MCDCRecords; |
611 | /// The number of times this function was executed. |
612 | uint64_t ExecutionCount = 0; |
613 | |
614 | FunctionRecord(StringRef Name, ArrayRef<StringRef> Filenames) |
615 | : Name(Name), Filenames(Filenames.begin(), Filenames.end()) {} |
616 | |
617 | FunctionRecord(FunctionRecord &&FR) = default; |
618 | FunctionRecord &operator=(FunctionRecord &&) = default; |
619 | |
620 | void pushMCDCRecord(MCDCRecord &&Record) { |
621 | MCDCRecords.push_back(x: std::move(Record)); |
622 | } |
623 | |
624 | void pushRegion(CounterMappingRegion Region, uint64_t Count, |
625 | uint64_t FalseCount) { |
626 | if (Region.Kind == CounterMappingRegion::BranchRegion || |
627 | Region.Kind == CounterMappingRegion::MCDCBranchRegion) { |
628 | CountedBranchRegions.emplace_back(args&: Region, args&: Count, args&: FalseCount); |
629 | // If both counters are hard-coded to zero, then this region represents a |
630 | // constant-folded branch. |
631 | if (Region.Count.isZero() && Region.FalseCount.isZero()) |
632 | CountedBranchRegions.back().Folded = true; |
633 | return; |
634 | } |
635 | if (CountedRegions.empty()) |
636 | ExecutionCount = Count; |
637 | CountedRegions.emplace_back(args&: Region, args&: Count, args&: FalseCount); |
638 | } |
639 | }; |
640 | |
641 | /// Iterator over Functions, optionally filtered to a single file. |
642 | class FunctionRecordIterator |
643 | : public iterator_facade_base<FunctionRecordIterator, |
644 | std::forward_iterator_tag, FunctionRecord> { |
645 | ArrayRef<FunctionRecord> Records; |
646 | ArrayRef<FunctionRecord>::iterator Current; |
647 | StringRef Filename; |
648 | |
649 | /// Skip records whose primary file is not \c Filename. |
650 | void skipOtherFiles(); |
651 | |
652 | public: |
653 | FunctionRecordIterator(ArrayRef<FunctionRecord> Records_, |
654 | StringRef Filename = "" ) |
655 | : Records(Records_), Current(Records.begin()), Filename(Filename) { |
656 | skipOtherFiles(); |
657 | } |
658 | |
659 | FunctionRecordIterator() : Current(Records.begin()) {} |
660 | |
661 | bool operator==(const FunctionRecordIterator &RHS) const { |
662 | return Current == RHS.Current && Filename == RHS.Filename; |
663 | } |
664 | |
665 | const FunctionRecord &operator*() const { return *Current; } |
666 | |
667 | FunctionRecordIterator &operator++() { |
668 | assert(Current != Records.end() && "incremented past end" ); |
669 | ++Current; |
670 | skipOtherFiles(); |
671 | return *this; |
672 | } |
673 | }; |
674 | |
675 | /// Coverage information for a macro expansion or #included file. |
676 | /// |
677 | /// When covered code has pieces that can be expanded for more detail, such as a |
678 | /// preprocessor macro use and its definition, these are represented as |
679 | /// expansions whose coverage can be looked up independently. |
680 | struct ExpansionRecord { |
681 | /// The abstract file this expansion covers. |
682 | unsigned FileID; |
683 | /// The region that expands to this record. |
684 | const CountedRegion &Region; |
685 | /// Coverage for the expansion. |
686 | const FunctionRecord &Function; |
687 | |
688 | ExpansionRecord(const CountedRegion &Region, |
689 | const FunctionRecord &Function) |
690 | : FileID(Region.ExpandedFileID), Region(Region), Function(Function) {} |
691 | }; |
692 | |
693 | /// The execution count information starting at a point in a file. |
694 | /// |
695 | /// A sequence of CoverageSegments gives execution counts for a file in format |
696 | /// that's simple to iterate through for processing. |
697 | struct CoverageSegment { |
698 | /// The line where this segment begins. |
699 | unsigned Line; |
700 | /// The column where this segment begins. |
701 | unsigned Col; |
702 | /// The execution count, or zero if no count was recorded. |
703 | uint64_t Count; |
704 | /// When false, the segment was uninstrumented or skipped. |
705 | bool HasCount; |
706 | /// Whether this enters a new region or returns to a previous count. |
707 | bool IsRegionEntry; |
708 | /// Whether this enters a gap region. |
709 | bool IsGapRegion; |
710 | |
711 | CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry) |
712 | : Line(Line), Col(Col), Count(0), HasCount(false), |
713 | IsRegionEntry(IsRegionEntry), IsGapRegion(false) {} |
714 | |
715 | CoverageSegment(unsigned Line, unsigned Col, uint64_t Count, |
716 | bool IsRegionEntry, bool IsGapRegion = false, |
717 | bool IsBranchRegion = false) |
718 | : Line(Line), Col(Col), Count(Count), HasCount(true), |
719 | IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {} |
720 | |
721 | friend bool operator==(const CoverageSegment &L, const CoverageSegment &R) { |
722 | return std::tie(args: L.Line, args: L.Col, args: L.Count, args: L.HasCount, args: L.IsRegionEntry, |
723 | args: L.IsGapRegion) == std::tie(args: R.Line, args: R.Col, args: R.Count, |
724 | args: R.HasCount, args: R.IsRegionEntry, |
725 | args: R.IsGapRegion); |
726 | } |
727 | }; |
728 | |
729 | /// An instantiation group contains a \c FunctionRecord list, such that each |
730 | /// record corresponds to a distinct instantiation of the same function. |
731 | /// |
732 | /// Note that it's possible for a function to have more than one instantiation |
733 | /// (consider C++ template specializations or static inline functions). |
734 | class InstantiationGroup { |
735 | friend class CoverageMapping; |
736 | |
737 | unsigned Line; |
738 | unsigned Col; |
739 | std::vector<const FunctionRecord *> Instantiations; |
740 | |
741 | InstantiationGroup(unsigned Line, unsigned Col, |
742 | std::vector<const FunctionRecord *> Instantiations) |
743 | : Line(Line), Col(Col), Instantiations(std::move(Instantiations)) {} |
744 | |
745 | public: |
746 | InstantiationGroup(const InstantiationGroup &) = delete; |
747 | InstantiationGroup(InstantiationGroup &&) = default; |
748 | |
749 | /// Get the number of instantiations in this group. |
750 | size_t size() const { return Instantiations.size(); } |
751 | |
752 | /// Get the line where the common function was defined. |
753 | unsigned getLine() const { return Line; } |
754 | |
755 | /// Get the column where the common function was defined. |
756 | unsigned getColumn() const { return Col; } |
757 | |
758 | /// Check if the instantiations in this group have a common mangled name. |
759 | bool hasName() const { |
760 | for (unsigned I = 1, E = Instantiations.size(); I < E; ++I) |
761 | if (Instantiations[I]->Name != Instantiations[0]->Name) |
762 | return false; |
763 | return true; |
764 | } |
765 | |
766 | /// Get the common mangled name for instantiations in this group. |
767 | StringRef getName() const { |
768 | assert(hasName() && "Instantiations don't have a shared name" ); |
769 | return Instantiations[0]->Name; |
770 | } |
771 | |
772 | /// Get the total execution count of all instantiations in this group. |
773 | uint64_t getTotalExecutionCount() const { |
774 | uint64_t Count = 0; |
775 | for (const FunctionRecord *F : Instantiations) |
776 | Count += F->ExecutionCount; |
777 | return Count; |
778 | } |
779 | |
780 | /// Get the instantiations in this group. |
781 | ArrayRef<const FunctionRecord *> getInstantiations() const { |
782 | return Instantiations; |
783 | } |
784 | }; |
785 | |
786 | /// Coverage information to be processed or displayed. |
787 | /// |
788 | /// This represents the coverage of an entire file, expansion, or function. It |
789 | /// provides a sequence of CoverageSegments to iterate through, as well as the |
790 | /// list of expansions that can be further processed. |
791 | class CoverageData { |
792 | friend class CoverageMapping; |
793 | |
794 | std::string Filename; |
795 | std::vector<CoverageSegment> Segments; |
796 | std::vector<ExpansionRecord> Expansions; |
797 | std::vector<CountedRegion> BranchRegions; |
798 | std::vector<MCDCRecord> MCDCRecords; |
799 | |
800 | public: |
801 | CoverageData() = default; |
802 | |
803 | CoverageData(StringRef Filename) : Filename(Filename) {} |
804 | |
805 | /// Get the name of the file this data covers. |
806 | StringRef getFilename() const { return Filename; } |
807 | |
808 | /// Get an iterator over the coverage segments for this object. The segments |
809 | /// are guaranteed to be uniqued and sorted by location. |
810 | std::vector<CoverageSegment>::const_iterator begin() const { |
811 | return Segments.begin(); |
812 | } |
813 | |
814 | std::vector<CoverageSegment>::const_iterator end() const { |
815 | return Segments.end(); |
816 | } |
817 | |
818 | bool empty() const { return Segments.empty(); } |
819 | |
820 | /// Expansions that can be further processed. |
821 | ArrayRef<ExpansionRecord> getExpansions() const { return Expansions; } |
822 | |
823 | /// Branches that can be further processed. |
824 | ArrayRef<CountedRegion> getBranches() const { return BranchRegions; } |
825 | |
826 | /// MCDC Records that can be further processed. |
827 | ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; } |
828 | }; |
829 | |
830 | /// The mapping of profile information to coverage data. |
831 | /// |
832 | /// This is the main interface to get coverage information, using a profile to |
833 | /// fill out execution counts. |
834 | class CoverageMapping { |
835 | DenseMap<size_t, DenseSet<size_t>> RecordProvenance; |
836 | std::vector<FunctionRecord> Functions; |
837 | DenseMap<size_t, SmallVector<unsigned, 0>> FilenameHash2RecordIndices; |
838 | std::vector<std::pair<std::string, uint64_t>> FuncHashMismatches; |
839 | |
840 | CoverageMapping() = default; |
841 | |
842 | // Load coverage records from readers. |
843 | static Error loadFromReaders( |
844 | ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders, |
845 | IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage); |
846 | |
847 | // Load coverage records from file. |
848 | static Error |
849 | loadFromFile(StringRef Filename, StringRef Arch, StringRef CompilationDir, |
850 | IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage, |
851 | bool &DataFound, |
852 | SmallVectorImpl<object::BuildID> *FoundBinaryIDs = nullptr); |
853 | |
854 | /// Add a function record corresponding to \p Record. |
855 | Error loadFunctionRecord(const CoverageMappingRecord &Record, |
856 | IndexedInstrProfReader &ProfileReader); |
857 | |
858 | /// Look up the indices for function records which are at least partially |
859 | /// defined in the specified file. This is guaranteed to return a superset of |
860 | /// such records: extra records not in the file may be included if there is |
861 | /// a hash collision on the filename. Clients must be robust to collisions. |
862 | ArrayRef<unsigned> |
863 | getImpreciseRecordIndicesForFilename(StringRef Filename) const; |
864 | |
865 | public: |
866 | CoverageMapping(const CoverageMapping &) = delete; |
867 | CoverageMapping &operator=(const CoverageMapping &) = delete; |
868 | |
869 | /// Load the coverage mapping using the given readers. |
870 | static Expected<std::unique_ptr<CoverageMapping>> |
871 | load(ArrayRef<std::unique_ptr<CoverageMappingReader>> CoverageReaders, |
872 | IndexedInstrProfReader &ProfileReader); |
873 | |
874 | /// Load the coverage mapping from the given object files and profile. If |
875 | /// \p Arches is non-empty, it must specify an architecture for each object. |
876 | /// Ignores non-instrumented object files unless all are not instrumented. |
877 | static Expected<std::unique_ptr<CoverageMapping>> |
878 | load(ArrayRef<StringRef> ObjectFilenames, StringRef ProfileFilename, |
879 | vfs::FileSystem &FS, ArrayRef<StringRef> Arches = std::nullopt, |
880 | StringRef CompilationDir = "" , |
881 | const object::BuildIDFetcher *BIDFetcher = nullptr, |
882 | bool CheckBinaryIDs = false); |
883 | |
884 | /// The number of functions that couldn't have their profiles mapped. |
885 | /// |
886 | /// This is a count of functions whose profile is out of date or otherwise |
887 | /// can't be associated with any coverage information. |
888 | unsigned getMismatchedCount() const { return FuncHashMismatches.size(); } |
889 | |
890 | /// A hash mismatch occurs when a profile record for a symbol does not have |
891 | /// the same hash as a coverage mapping record for the same symbol. This |
892 | /// returns a list of hash mismatches, where each mismatch is a pair of the |
893 | /// symbol name and its coverage mapping hash. |
894 | ArrayRef<std::pair<std::string, uint64_t>> getHashMismatches() const { |
895 | return FuncHashMismatches; |
896 | } |
897 | |
898 | /// Returns a lexicographically sorted, unique list of files that are |
899 | /// covered. |
900 | std::vector<StringRef> getUniqueSourceFiles() const; |
901 | |
902 | /// Get the coverage for a particular file. |
903 | /// |
904 | /// The given filename must be the name as recorded in the coverage |
905 | /// information. That is, only names returned from getUniqueSourceFiles will |
906 | /// yield a result. |
907 | CoverageData getCoverageForFile(StringRef Filename) const; |
908 | |
909 | /// Get the coverage for a particular function. |
910 | CoverageData getCoverageForFunction(const FunctionRecord &Function) const; |
911 | |
912 | /// Get the coverage for an expansion within a coverage set. |
913 | CoverageData getCoverageForExpansion(const ExpansionRecord &Expansion) const; |
914 | |
915 | /// Gets all of the functions covered by this profile. |
916 | iterator_range<FunctionRecordIterator> getCoveredFunctions() const { |
917 | return make_range(x: FunctionRecordIterator(Functions), |
918 | y: FunctionRecordIterator()); |
919 | } |
920 | |
921 | /// Gets all of the functions in a particular file. |
922 | iterator_range<FunctionRecordIterator> |
923 | getCoveredFunctions(StringRef Filename) const { |
924 | return make_range(x: FunctionRecordIterator(Functions, Filename), |
925 | y: FunctionRecordIterator()); |
926 | } |
927 | |
928 | /// Get the list of function instantiation groups in a particular file. |
929 | /// |
930 | /// Every instantiation group in a program is attributed to exactly one file: |
931 | /// the file in which the definition for the common function begins. |
932 | std::vector<InstantiationGroup> |
933 | getInstantiationGroups(StringRef Filename) const; |
934 | }; |
935 | |
936 | /// Coverage statistics for a single line. |
937 | class LineCoverageStats { |
938 | uint64_t ExecutionCount; |
939 | bool HasMultipleRegions; |
940 | bool Mapped; |
941 | unsigned Line; |
942 | ArrayRef<const CoverageSegment *> LineSegments; |
943 | const CoverageSegment *WrappedSegment; |
944 | |
945 | friend class LineCoverageIterator; |
946 | LineCoverageStats() = default; |
947 | |
948 | public: |
949 | LineCoverageStats(ArrayRef<const CoverageSegment *> LineSegments, |
950 | const CoverageSegment *WrappedSegment, unsigned Line); |
951 | |
952 | uint64_t getExecutionCount() const { return ExecutionCount; } |
953 | |
954 | bool hasMultipleRegions() const { return HasMultipleRegions; } |
955 | |
956 | bool isMapped() const { return Mapped; } |
957 | |
958 | unsigned getLine() const { return Line; } |
959 | |
960 | ArrayRef<const CoverageSegment *> getLineSegments() const { |
961 | return LineSegments; |
962 | } |
963 | |
964 | const CoverageSegment *getWrappedSegment() const { return WrappedSegment; } |
965 | }; |
966 | |
967 | /// An iterator over the \c LineCoverageStats objects for lines described by |
968 | /// a \c CoverageData instance. |
969 | class LineCoverageIterator |
970 | : public iterator_facade_base<LineCoverageIterator, |
971 | std::forward_iterator_tag, |
972 | const LineCoverageStats> { |
973 | public: |
974 | LineCoverageIterator(const CoverageData &CD) |
975 | : LineCoverageIterator(CD, CD.begin()->Line) {} |
976 | |
977 | LineCoverageIterator(const CoverageData &CD, unsigned Line) |
978 | : CD(CD), WrappedSegment(nullptr), Next(CD.begin()), Ended(false), |
979 | Line(Line) { |
980 | this->operator++(); |
981 | } |
982 | |
983 | bool operator==(const LineCoverageIterator &R) const { |
984 | return &CD == &R.CD && Next == R.Next && Ended == R.Ended; |
985 | } |
986 | |
987 | const LineCoverageStats &operator*() const { return Stats; } |
988 | |
989 | LineCoverageIterator &operator++(); |
990 | |
991 | LineCoverageIterator getEnd() const { |
992 | auto EndIt = *this; |
993 | EndIt.Next = CD.end(); |
994 | EndIt.Ended = true; |
995 | return EndIt; |
996 | } |
997 | |
998 | private: |
999 | const CoverageData &CD; |
1000 | const CoverageSegment *WrappedSegment; |
1001 | std::vector<CoverageSegment>::const_iterator Next; |
1002 | bool Ended; |
1003 | unsigned Line; |
1004 | SmallVector<const CoverageSegment *, 4> Segments; |
1005 | LineCoverageStats Stats; |
1006 | }; |
1007 | |
1008 | /// Get a \c LineCoverageIterator range for the lines described by \p CD. |
1009 | static inline iterator_range<LineCoverageIterator> |
1010 | getLineCoverageStats(const coverage::CoverageData &CD) { |
1011 | auto Begin = LineCoverageIterator(CD); |
1012 | auto End = Begin.getEnd(); |
1013 | return make_range(x: Begin, y: End); |
1014 | } |
1015 | |
1016 | // Coverage mappping data (V2) has the following layout: |
1017 | // IPSK_covmap: |
1018 | // [CoverageMapFileHeader] |
1019 | // [ArrayStart] |
1020 | // [CovMapFunctionRecordV2] |
1021 | // [CovMapFunctionRecordV2] |
1022 | // ... |
1023 | // [ArrayEnd] |
1024 | // [Encoded Filenames and Region Mapping Data] |
1025 | // |
1026 | // Coverage mappping data (V3) has the following layout: |
1027 | // IPSK_covmap: |
1028 | // [CoverageMapFileHeader] |
1029 | // [Encoded Filenames] |
1030 | // IPSK_covfun: |
1031 | // [ArrayStart] |
1032 | // odr_name_1: [CovMapFunctionRecordV3] |
1033 | // odr_name_2: [CovMapFunctionRecordV3] |
1034 | // ... |
1035 | // [ArrayEnd] |
1036 | // |
1037 | // Both versions of the coverage mapping format encode the same information, |
1038 | // but the V3 format does so more compactly by taking advantage of linkonce_odr |
1039 | // semantics (it allows exactly 1 function record per name reference). |
1040 | |
1041 | /// This namespace defines accessors shared by different versions of coverage |
1042 | /// mapping records. |
1043 | namespace accessors { |
1044 | |
1045 | /// Return the structural hash associated with the function. |
1046 | template <class FuncRecordTy, llvm::endianness Endian> |
1047 | uint64_t getFuncHash(const FuncRecordTy *Record) { |
1048 | return support::endian::byte_swap<uint64_t, Endian>(Record->FuncHash); |
1049 | } |
1050 | |
1051 | /// Return the coverage map data size for the function. |
1052 | template <class FuncRecordTy, llvm::endianness Endian> |
1053 | uint64_t getDataSize(const FuncRecordTy *Record) { |
1054 | return support::endian::byte_swap<uint32_t, Endian>(Record->DataSize); |
1055 | } |
1056 | |
1057 | /// Return the function lookup key. The value is considered opaque. |
1058 | template <class FuncRecordTy, llvm::endianness Endian> |
1059 | uint64_t getFuncNameRef(const FuncRecordTy *Record) { |
1060 | return support::endian::byte_swap<uint64_t, Endian>(Record->NameRef); |
1061 | } |
1062 | |
1063 | /// Return the PGO name of the function. Used for formats in which the name is |
1064 | /// a hash. |
1065 | template <class FuncRecordTy, llvm::endianness Endian> |
1066 | Error getFuncNameViaRef(const FuncRecordTy *Record, |
1067 | InstrProfSymtab &ProfileNames, StringRef &FuncName) { |
1068 | uint64_t NameRef = getFuncNameRef<FuncRecordTy, Endian>(Record); |
1069 | FuncName = ProfileNames.getFuncOrVarName(MD5Hash: NameRef); |
1070 | return Error::success(); |
1071 | } |
1072 | |
1073 | /// Read coverage mapping out-of-line, from \p MappingBuf. This is used when the |
1074 | /// coverage mapping is attached to the file header, instead of to the function |
1075 | /// record. |
1076 | template <class FuncRecordTy, llvm::endianness Endian> |
1077 | StringRef getCoverageMappingOutOfLine(const FuncRecordTy *Record, |
1078 | const char *MappingBuf) { |
1079 | return {MappingBuf, size_t(getDataSize<FuncRecordTy, Endian>(Record))}; |
1080 | } |
1081 | |
1082 | /// Advance to the next out-of-line coverage mapping and its associated |
1083 | /// function record. |
1084 | template <class FuncRecordTy, llvm::endianness Endian> |
1085 | std::pair<const char *, const FuncRecordTy *> |
1086 | advanceByOneOutOfLine(const FuncRecordTy *Record, const char *MappingBuf) { |
1087 | return {MappingBuf + getDataSize<FuncRecordTy, Endian>(Record), Record + 1}; |
1088 | } |
1089 | |
1090 | } // end namespace accessors |
1091 | |
1092 | LLVM_PACKED_START |
1093 | template <class IntPtrT> |
1094 | struct CovMapFunctionRecordV1 { |
1095 | using ThisT = CovMapFunctionRecordV1<IntPtrT>; |
1096 | |
1097 | #define COVMAP_V1 |
1098 | #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; |
1099 | #include "llvm/ProfileData/InstrProfData.inc" |
1100 | #undef COVMAP_V1 |
1101 | CovMapFunctionRecordV1() = delete; |
1102 | |
1103 | template <llvm::endianness Endian> uint64_t getFuncHash() const { |
1104 | return accessors::getFuncHash<ThisT, Endian>(this); |
1105 | } |
1106 | |
1107 | template <llvm::endianness Endian> uint64_t getDataSize() const { |
1108 | return accessors::getDataSize<ThisT, Endian>(this); |
1109 | } |
1110 | |
1111 | /// Return function lookup key. The value is consider opaque. |
1112 | template <llvm::endianness Endian> IntPtrT getFuncNameRef() const { |
1113 | return support::endian::byte_swap<IntPtrT, Endian>(NamePtr); |
1114 | } |
1115 | |
1116 | /// Return the PGO name of the function. |
1117 | template <llvm::endianness Endian> |
1118 | Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { |
1119 | IntPtrT NameRef = getFuncNameRef<Endian>(); |
1120 | uint32_t NameS = support::endian::byte_swap<uint32_t, Endian>(NameSize); |
1121 | FuncName = ProfileNames.getFuncName(FuncNameAddress: NameRef, NameSize: NameS); |
1122 | if (NameS && FuncName.empty()) |
1123 | return make_error<CoverageMapError>(Args: coveragemap_error::malformed, |
1124 | Args: "function name is empty" ); |
1125 | return Error::success(); |
1126 | } |
1127 | |
1128 | template <llvm::endianness Endian> |
1129 | std::pair<const char *, const ThisT *> |
1130 | advanceByOne(const char *MappingBuf) const { |
1131 | return accessors::advanceByOneOutOfLine<ThisT, Endian>(this, MappingBuf); |
1132 | } |
1133 | |
1134 | template <llvm::endianness Endian> uint64_t getFilenamesRef() const { |
1135 | llvm_unreachable("V1 function format does not contain a filenames ref" ); |
1136 | } |
1137 | |
1138 | template <llvm::endianness Endian> |
1139 | StringRef getCoverageMapping(const char *MappingBuf) const { |
1140 | return accessors::getCoverageMappingOutOfLine<ThisT, Endian>(this, |
1141 | MappingBuf); |
1142 | } |
1143 | }; |
1144 | |
1145 | struct CovMapFunctionRecordV2 { |
1146 | using ThisT = CovMapFunctionRecordV2; |
1147 | |
1148 | #define COVMAP_V2 |
1149 | #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; |
1150 | #include "llvm/ProfileData/InstrProfData.inc" |
1151 | #undef COVMAP_V2 |
1152 | CovMapFunctionRecordV2() = delete; |
1153 | |
1154 | template <llvm::endianness Endian> uint64_t getFuncHash() const { |
1155 | return accessors::getFuncHash<ThisT, Endian>(this); |
1156 | } |
1157 | |
1158 | template <llvm::endianness Endian> uint64_t getDataSize() const { |
1159 | return accessors::getDataSize<ThisT, Endian>(this); |
1160 | } |
1161 | |
1162 | template <llvm::endianness Endian> uint64_t getFuncNameRef() const { |
1163 | return accessors::getFuncNameRef<ThisT, Endian>(this); |
1164 | } |
1165 | |
1166 | template <llvm::endianness Endian> |
1167 | Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { |
1168 | return accessors::getFuncNameViaRef<ThisT, Endian>(this, ProfileNames, |
1169 | FuncName); |
1170 | } |
1171 | |
1172 | template <llvm::endianness Endian> |
1173 | std::pair<const char *, const ThisT *> |
1174 | advanceByOne(const char *MappingBuf) const { |
1175 | return accessors::advanceByOneOutOfLine<ThisT, Endian>(this, MappingBuf); |
1176 | } |
1177 | |
1178 | template <llvm::endianness Endian> uint64_t getFilenamesRef() const { |
1179 | llvm_unreachable("V2 function format does not contain a filenames ref" ); |
1180 | } |
1181 | |
1182 | template <llvm::endianness Endian> |
1183 | StringRef getCoverageMapping(const char *MappingBuf) const { |
1184 | return accessors::getCoverageMappingOutOfLine<ThisT, Endian>(this, |
1185 | MappingBuf); |
1186 | } |
1187 | }; |
1188 | |
1189 | struct CovMapFunctionRecordV3 { |
1190 | using ThisT = CovMapFunctionRecordV3; |
1191 | |
1192 | #define COVMAP_V3 |
1193 | #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; |
1194 | #include "llvm/ProfileData/InstrProfData.inc" |
1195 | #undef COVMAP_V3 |
1196 | CovMapFunctionRecordV3() = delete; |
1197 | |
1198 | template <llvm::endianness Endian> uint64_t getFuncHash() const { |
1199 | return accessors::getFuncHash<ThisT, Endian>(this); |
1200 | } |
1201 | |
1202 | template <llvm::endianness Endian> uint64_t getDataSize() const { |
1203 | return accessors::getDataSize<ThisT, Endian>(this); |
1204 | } |
1205 | |
1206 | template <llvm::endianness Endian> uint64_t getFuncNameRef() const { |
1207 | return accessors::getFuncNameRef<ThisT, Endian>(this); |
1208 | } |
1209 | |
1210 | template <llvm::endianness Endian> |
1211 | Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { |
1212 | return accessors::getFuncNameViaRef<ThisT, Endian>(this, ProfileNames, |
1213 | FuncName); |
1214 | } |
1215 | |
1216 | /// Get the filename set reference. |
1217 | template <llvm::endianness Endian> uint64_t getFilenamesRef() const { |
1218 | return support::endian::byte_swap<uint64_t, Endian>(FilenamesRef); |
1219 | } |
1220 | |
1221 | /// Read the inline coverage mapping. Ignore the buffer parameter, it is for |
1222 | /// out-of-line coverage mapping data only. |
1223 | template <llvm::endianness Endian> |
1224 | StringRef getCoverageMapping(const char *) const { |
1225 | return StringRef(&CoverageMapping, getDataSize<Endian>()); |
1226 | } |
1227 | |
1228 | // Advance to the next inline coverage mapping and its associated function |
1229 | // record. Ignore the out-of-line coverage mapping buffer. |
1230 | template <llvm::endianness Endian> |
1231 | std::pair<const char *, const CovMapFunctionRecordV3 *> |
1232 | advanceByOne(const char *) const { |
1233 | assert(isAddrAligned(Align(8), this) && "Function record not aligned" ); |
1234 | const char *Next = ((const char *)this) + sizeof(CovMapFunctionRecordV3) - |
1235 | sizeof(char) + getDataSize<Endian>(); |
1236 | // Each function record has an alignment of 8, so we need to adjust |
1237 | // alignment before reading the next record. |
1238 | Next += offsetToAlignedAddr(Addr: Next, Alignment: Align(8)); |
1239 | return {nullptr, reinterpret_cast<const CovMapFunctionRecordV3 *>(Next)}; |
1240 | } |
1241 | }; |
1242 | |
1243 | // Per module coverage mapping data header, i.e. CoverageMapFileHeader |
1244 | // documented above. |
1245 | struct { |
1246 | #define (Type, LLVMType, Name, Init) Type Name; |
1247 | #include "llvm/ProfileData/InstrProfData.inc" |
1248 | template <llvm::endianness Endian> uint32_t () const { |
1249 | return support::endian::byte_swap<uint32_t, Endian>(NRecords); |
1250 | } |
1251 | |
1252 | template <llvm::endianness Endian> uint32_t () const { |
1253 | return support::endian::byte_swap<uint32_t, Endian>(FilenamesSize); |
1254 | } |
1255 | |
1256 | template <llvm::endianness Endian> uint32_t () const { |
1257 | return support::endian::byte_swap<uint32_t, Endian>(CoverageSize); |
1258 | } |
1259 | |
1260 | template <llvm::endianness Endian> uint32_t () const { |
1261 | return support::endian::byte_swap<uint32_t, Endian>(Version); |
1262 | } |
1263 | }; |
1264 | |
1265 | LLVM_PACKED_END |
1266 | |
1267 | enum CovMapVersion { |
1268 | Version1 = 0, |
1269 | // Function's name reference from CovMapFuncRecord is changed from raw |
1270 | // name string pointer to MD5 to support name section compression. Name |
1271 | // section is also compressed. |
1272 | Version2 = 1, |
1273 | // A new interpretation of the columnEnd field is added in order to mark |
1274 | // regions as gap areas. |
1275 | Version3 = 2, |
1276 | // Function records are named, uniqued, and moved to a dedicated section. |
1277 | Version4 = 3, |
1278 | // Branch regions referring to two counters are added |
1279 | Version5 = 4, |
1280 | // Compilation directory is stored separately and combined with relative |
1281 | // filenames to produce an absolute file path. |
1282 | Version6 = 5, |
1283 | // Branch regions extended and Decision Regions added for MC/DC. |
1284 | Version7 = 6, |
1285 | // The current version is Version7. |
1286 | CurrentVersion = INSTR_PROF_COVMAP_VERSION |
1287 | }; |
1288 | |
1289 | // Correspond to "llvmcovm", in little-endian. |
1290 | constexpr uint64_t TestingFormatMagic = 0x6d766f636d766c6c; |
1291 | |
1292 | enum class TestingFormatVersion : uint64_t { |
1293 | // The first version's number corresponds to the string "testdata" in |
1294 | // little-endian. This is for a historical reason. |
1295 | Version1 = 0x6174616474736574, |
1296 | // Version1 has a defect that it can't store multiple file records. Version2 |
1297 | // fix this problem by adding a new field before the file records section. |
1298 | Version2 = 1, |
1299 | // The current testing format version is Version2. |
1300 | CurrentVersion = Version2 |
1301 | }; |
1302 | |
1303 | template <int CovMapVersion, class IntPtrT> struct CovMapTraits { |
1304 | using CovMapFuncRecordType = CovMapFunctionRecordV3; |
1305 | using NameRefType = uint64_t; |
1306 | }; |
1307 | |
1308 | template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version3, IntPtrT> { |
1309 | using CovMapFuncRecordType = CovMapFunctionRecordV2; |
1310 | using NameRefType = uint64_t; |
1311 | }; |
1312 | |
1313 | template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version2, IntPtrT> { |
1314 | using CovMapFuncRecordType = CovMapFunctionRecordV2; |
1315 | using NameRefType = uint64_t; |
1316 | }; |
1317 | |
1318 | template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version1, IntPtrT> { |
1319 | using CovMapFuncRecordType = CovMapFunctionRecordV1<IntPtrT>; |
1320 | using NameRefType = IntPtrT; |
1321 | }; |
1322 | |
1323 | } // end namespace coverage |
1324 | |
1325 | /// Provide DenseMapInfo for CounterExpression |
1326 | template<> struct DenseMapInfo<coverage::CounterExpression> { |
1327 | static inline coverage::CounterExpression getEmptyKey() { |
1328 | using namespace coverage; |
1329 | |
1330 | return CounterExpression(CounterExpression::ExprKind::Subtract, |
1331 | Counter::getCounter(CounterId: ~0U), |
1332 | Counter::getCounter(CounterId: ~0U)); |
1333 | } |
1334 | |
1335 | static inline coverage::CounterExpression getTombstoneKey() { |
1336 | using namespace coverage; |
1337 | |
1338 | return CounterExpression(CounterExpression::ExprKind::Add, |
1339 | Counter::getCounter(CounterId: ~0U), |
1340 | Counter::getCounter(CounterId: ~0U)); |
1341 | } |
1342 | |
1343 | static unsigned getHashValue(const coverage::CounterExpression &V) { |
1344 | return static_cast<unsigned>( |
1345 | hash_combine(args: V.Kind, args: V.LHS.getKind(), args: V.LHS.getCounterID(), |
1346 | args: V.RHS.getKind(), args: V.RHS.getCounterID())); |
1347 | } |
1348 | |
1349 | static bool isEqual(const coverage::CounterExpression &LHS, |
1350 | const coverage::CounterExpression &RHS) { |
1351 | return LHS.Kind == RHS.Kind && LHS.LHS == RHS.LHS && LHS.RHS == RHS.RHS; |
1352 | } |
1353 | }; |
1354 | |
1355 | } // end namespace llvm |
1356 | |
1357 | #endif // LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H |
1358 | |