10b57cec5SDimitry Andric //===- CoverageMappingWriter.cpp - Code coverage mapping writer -----------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file contains support for writing coverage mapping data for
100b57cec5SDimitry Andric // instrumentation based coverage.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
150b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h"
160b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
1706c3fb27SDimitry Andric #include "llvm/ADT/StringExtras.h"
1806c3fb27SDimitry Andric #include "llvm/ProfileData/InstrProf.h"
195ffd83dbSDimitry Andric #include "llvm/Support/Compression.h"
200b57cec5SDimitry Andric #include "llvm/Support/LEB128.h"
210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
220b57cec5SDimitry Andric #include <algorithm>
230b57cec5SDimitry Andric #include <cassert>
240b57cec5SDimitry Andric #include <limits>
250b57cec5SDimitry Andric #include <vector>
260b57cec5SDimitry Andric
270b57cec5SDimitry Andric using namespace llvm;
280b57cec5SDimitry Andric using namespace coverage;
290b57cec5SDimitry Andric
CoverageFilenamesSectionWriter(ArrayRef<std::string> Filenames)308bcb0991SDimitry Andric CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter(
31fe6060f1SDimitry Andric ArrayRef<std::string> Filenames)
328bcb0991SDimitry Andric : Filenames(Filenames) {
338bcb0991SDimitry Andric #ifndef NDEBUG
348bcb0991SDimitry Andric StringSet<> NameSet;
358bcb0991SDimitry Andric for (StringRef Name : Filenames)
368bcb0991SDimitry Andric assert(NameSet.insert(Name).second && "Duplicate filename");
378bcb0991SDimitry Andric #endif
388bcb0991SDimitry Andric }
398bcb0991SDimitry Andric
write(raw_ostream & OS,bool Compress)405ffd83dbSDimitry Andric void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) {
415ffd83dbSDimitry Andric std::string FilenamesStr;
425ffd83dbSDimitry Andric {
435ffd83dbSDimitry Andric raw_string_ostream FilenamesOS{FilenamesStr};
440b57cec5SDimitry Andric for (const auto &Filename : Filenames) {
455ffd83dbSDimitry Andric encodeULEB128(Filename.size(), FilenamesOS);
465ffd83dbSDimitry Andric FilenamesOS << Filename;
470b57cec5SDimitry Andric }
480b57cec5SDimitry Andric }
490b57cec5SDimitry Andric
50753f127fSDimitry Andric SmallVector<uint8_t, 128> CompressedStr;
51753f127fSDimitry Andric bool doCompression = Compress && compression::zlib::isAvailable() &&
52753f127fSDimitry Andric DoInstrProfNameCompression;
5381ad6265SDimitry Andric if (doCompression)
54753f127fSDimitry Andric compression::zlib::compress(arrayRefFromStringRef(FilenamesStr),
55753f127fSDimitry Andric CompressedStr,
56753f127fSDimitry Andric compression::zlib::BestSizeCompression);
575ffd83dbSDimitry Andric
585ffd83dbSDimitry Andric // ::= <num-filenames>
595ffd83dbSDimitry Andric // <uncompressed-len>
605ffd83dbSDimitry Andric // <compressed-len-or-zero>
615ffd83dbSDimitry Andric // (<compressed-filenames> | <uncompressed-filenames>)
625ffd83dbSDimitry Andric encodeULEB128(Filenames.size(), OS);
635ffd83dbSDimitry Andric encodeULEB128(FilenamesStr.size(), OS);
645ffd83dbSDimitry Andric encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS);
65753f127fSDimitry Andric OS << (doCompression ? toStringRef(CompressedStr) : StringRef(FilenamesStr));
665ffd83dbSDimitry Andric }
675ffd83dbSDimitry Andric
680b57cec5SDimitry Andric namespace {
690b57cec5SDimitry Andric
700b57cec5SDimitry Andric /// Gather only the expressions that are used by the mapping
710b57cec5SDimitry Andric /// regions in this function.
720b57cec5SDimitry Andric class CounterExpressionsMinimizer {
730b57cec5SDimitry Andric ArrayRef<CounterExpression> Expressions;
740b57cec5SDimitry Andric SmallVector<CounterExpression, 16> UsedExpressions;
750b57cec5SDimitry Andric std::vector<unsigned> AdjustedExpressionIDs;
760b57cec5SDimitry Andric
770b57cec5SDimitry Andric public:
CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,ArrayRef<CounterMappingRegion> MappingRegions)780b57cec5SDimitry Andric CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,
790b57cec5SDimitry Andric ArrayRef<CounterMappingRegion> MappingRegions)
800b57cec5SDimitry Andric : Expressions(Expressions) {
810b57cec5SDimitry Andric AdjustedExpressionIDs.resize(Expressions.size(), 0);
82e8d8bef9SDimitry Andric for (const auto &I : MappingRegions) {
830b57cec5SDimitry Andric mark(I.Count);
84e8d8bef9SDimitry Andric mark(I.FalseCount);
85e8d8bef9SDimitry Andric }
86e8d8bef9SDimitry Andric for (const auto &I : MappingRegions) {
870b57cec5SDimitry Andric gatherUsed(I.Count);
88e8d8bef9SDimitry Andric gatherUsed(I.FalseCount);
89e8d8bef9SDimitry Andric }
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric
mark(Counter C)920b57cec5SDimitry Andric void mark(Counter C) {
930b57cec5SDimitry Andric if (!C.isExpression())
940b57cec5SDimitry Andric return;
950b57cec5SDimitry Andric unsigned ID = C.getExpressionID();
960b57cec5SDimitry Andric AdjustedExpressionIDs[ID] = 1;
970b57cec5SDimitry Andric mark(Expressions[ID].LHS);
980b57cec5SDimitry Andric mark(Expressions[ID].RHS);
990b57cec5SDimitry Andric }
1000b57cec5SDimitry Andric
gatherUsed(Counter C)1010b57cec5SDimitry Andric void gatherUsed(Counter C) {
1020b57cec5SDimitry Andric if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()])
1030b57cec5SDimitry Andric return;
1040b57cec5SDimitry Andric AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size();
1050b57cec5SDimitry Andric const auto &E = Expressions[C.getExpressionID()];
1060b57cec5SDimitry Andric UsedExpressions.push_back(E);
1070b57cec5SDimitry Andric gatherUsed(E.LHS);
1080b57cec5SDimitry Andric gatherUsed(E.RHS);
1090b57cec5SDimitry Andric }
1100b57cec5SDimitry Andric
getExpressions() const1110b57cec5SDimitry Andric ArrayRef<CounterExpression> getExpressions() const { return UsedExpressions; }
1120b57cec5SDimitry Andric
1130b57cec5SDimitry Andric /// Adjust the given counter to correctly transition from the old
1140b57cec5SDimitry Andric /// expression ids to the new expression ids.
adjust(Counter C) const1150b57cec5SDimitry Andric Counter adjust(Counter C) const {
1160b57cec5SDimitry Andric if (C.isExpression())
1170b57cec5SDimitry Andric C = Counter::getExpression(AdjustedExpressionIDs[C.getExpressionID()]);
1180b57cec5SDimitry Andric return C;
1190b57cec5SDimitry Andric }
1200b57cec5SDimitry Andric };
1210b57cec5SDimitry Andric
1220b57cec5SDimitry Andric } // end anonymous namespace
1230b57cec5SDimitry Andric
1240b57cec5SDimitry Andric /// Encode the counter.
1250b57cec5SDimitry Andric ///
1260b57cec5SDimitry Andric /// The encoding uses the following format:
1270b57cec5SDimitry Andric /// Low 2 bits - Tag:
1280b57cec5SDimitry Andric /// Counter::Zero(0) - A Counter with kind Counter::Zero
1290b57cec5SDimitry Andric /// Counter::CounterValueReference(1) - A counter with kind
1300b57cec5SDimitry Andric /// Counter::CounterValueReference
1310b57cec5SDimitry Andric /// Counter::Expression(2) + CounterExpression::Subtract(0) -
1320b57cec5SDimitry Andric /// A counter with kind Counter::Expression and an expression
1330b57cec5SDimitry Andric /// with kind CounterExpression::Subtract
1340b57cec5SDimitry Andric /// Counter::Expression(2) + CounterExpression::Add(1) -
1350b57cec5SDimitry Andric /// A counter with kind Counter::Expression and an expression
1360b57cec5SDimitry Andric /// with kind CounterExpression::Add
1370b57cec5SDimitry Andric /// Remaining bits - Counter/Expression ID.
encodeCounter(ArrayRef<CounterExpression> Expressions,Counter C)1380b57cec5SDimitry Andric static unsigned encodeCounter(ArrayRef<CounterExpression> Expressions,
1390b57cec5SDimitry Andric Counter C) {
1400b57cec5SDimitry Andric unsigned Tag = unsigned(C.getKind());
1410b57cec5SDimitry Andric if (C.isExpression())
1420b57cec5SDimitry Andric Tag += Expressions[C.getExpressionID()].Kind;
1430b57cec5SDimitry Andric unsigned ID = C.getCounterID();
1440b57cec5SDimitry Andric assert(ID <=
1450b57cec5SDimitry Andric (std::numeric_limits<unsigned>::max() >> Counter::EncodingTagBits));
1460b57cec5SDimitry Andric return Tag | (ID << Counter::EncodingTagBits);
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric
writeCounter(ArrayRef<CounterExpression> Expressions,Counter C,raw_ostream & OS)1490b57cec5SDimitry Andric static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C,
1500b57cec5SDimitry Andric raw_ostream &OS) {
1510b57cec5SDimitry Andric encodeULEB128(encodeCounter(Expressions, C), OS);
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric
write(raw_ostream & OS)1540b57cec5SDimitry Andric void CoverageMappingWriter::write(raw_ostream &OS) {
1550b57cec5SDimitry Andric // Check that we don't have any bogus regions.
1560b57cec5SDimitry Andric assert(all_of(MappingRegions,
1570b57cec5SDimitry Andric [](const CounterMappingRegion &CMR) {
1580b57cec5SDimitry Andric return CMR.startLoc() <= CMR.endLoc();
1590b57cec5SDimitry Andric }) &&
1600b57cec5SDimitry Andric "Source region does not begin before it ends");
1610b57cec5SDimitry Andric
1620b57cec5SDimitry Andric // Sort the regions in an ascending order by the file id and the starting
1630b57cec5SDimitry Andric // location. Sort by region kinds to ensure stable order for tests.
1640b57cec5SDimitry Andric llvm::stable_sort(MappingRegions, [](const CounterMappingRegion &LHS,
1650b57cec5SDimitry Andric const CounterMappingRegion &RHS) {
1660b57cec5SDimitry Andric if (LHS.FileID != RHS.FileID)
1670b57cec5SDimitry Andric return LHS.FileID < RHS.FileID;
1680b57cec5SDimitry Andric if (LHS.startLoc() != RHS.startLoc())
1690b57cec5SDimitry Andric return LHS.startLoc() < RHS.startLoc();
170b3edf446SDimitry Andric
171b3edf446SDimitry Andric // Put `Decision` before `Expansion`.
172b3edf446SDimitry Andric auto getKindKey = [](CounterMappingRegion::RegionKind Kind) {
173b3edf446SDimitry Andric return (Kind == CounterMappingRegion::MCDCDecisionRegion
174b3edf446SDimitry Andric ? 2 * CounterMappingRegion::ExpansionRegion - 1
175b3edf446SDimitry Andric : 2 * Kind);
176b3edf446SDimitry Andric };
177b3edf446SDimitry Andric
178b3edf446SDimitry Andric return getKindKey(LHS.Kind) < getKindKey(RHS.Kind);
1790b57cec5SDimitry Andric });
1800b57cec5SDimitry Andric
1810b57cec5SDimitry Andric // Write out the fileid -> filename mapping.
1820b57cec5SDimitry Andric encodeULEB128(VirtualFileMapping.size(), OS);
1830b57cec5SDimitry Andric for (const auto &FileID : VirtualFileMapping)
1840b57cec5SDimitry Andric encodeULEB128(FileID, OS);
1850b57cec5SDimitry Andric
1860b57cec5SDimitry Andric // Write out the expressions.
1870b57cec5SDimitry Andric CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions);
1880b57cec5SDimitry Andric auto MinExpressions = Minimizer.getExpressions();
1890b57cec5SDimitry Andric encodeULEB128(MinExpressions.size(), OS);
1900b57cec5SDimitry Andric for (const auto &E : MinExpressions) {
1910b57cec5SDimitry Andric writeCounter(MinExpressions, Minimizer.adjust(E.LHS), OS);
1920b57cec5SDimitry Andric writeCounter(MinExpressions, Minimizer.adjust(E.RHS), OS);
1930b57cec5SDimitry Andric }
1940b57cec5SDimitry Andric
1950b57cec5SDimitry Andric // Write out the mapping regions.
1960b57cec5SDimitry Andric // Split the regions into subarrays where each region in a
1970b57cec5SDimitry Andric // subarray has a fileID which is the index of that subarray.
1980b57cec5SDimitry Andric unsigned PrevLineStart = 0;
1990b57cec5SDimitry Andric unsigned CurrentFileID = ~0U;
2000b57cec5SDimitry Andric for (auto I = MappingRegions.begin(), E = MappingRegions.end(); I != E; ++I) {
2010b57cec5SDimitry Andric if (I->FileID != CurrentFileID) {
2020b57cec5SDimitry Andric // Ensure that all file ids have at least one mapping region.
2030b57cec5SDimitry Andric assert(I->FileID == (CurrentFileID + 1));
2040b57cec5SDimitry Andric // Find the number of regions with this file id.
2050b57cec5SDimitry Andric unsigned RegionCount = 1;
2060b57cec5SDimitry Andric for (auto J = I + 1; J != E && I->FileID == J->FileID; ++J)
2070b57cec5SDimitry Andric ++RegionCount;
2080b57cec5SDimitry Andric // Start a new region sub-array.
2090b57cec5SDimitry Andric encodeULEB128(RegionCount, OS);
2100b57cec5SDimitry Andric
2110b57cec5SDimitry Andric CurrentFileID = I->FileID;
2120b57cec5SDimitry Andric PrevLineStart = 0;
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric Counter Count = Minimizer.adjust(I->Count);
215e8d8bef9SDimitry Andric Counter FalseCount = Minimizer.adjust(I->FalseCount);
216*0fca6ea1SDimitry Andric bool ParamsShouldBeNull = true;
2170b57cec5SDimitry Andric switch (I->Kind) {
2180b57cec5SDimitry Andric case CounterMappingRegion::CodeRegion:
2190b57cec5SDimitry Andric case CounterMappingRegion::GapRegion:
2200b57cec5SDimitry Andric writeCounter(MinExpressions, Count, OS);
2210b57cec5SDimitry Andric break;
2220b57cec5SDimitry Andric case CounterMappingRegion::ExpansionRegion: {
2230b57cec5SDimitry Andric assert(Count.isZero());
2240b57cec5SDimitry Andric assert(I->ExpandedFileID <=
2250b57cec5SDimitry Andric (std::numeric_limits<unsigned>::max() >>
2260b57cec5SDimitry Andric Counter::EncodingCounterTagAndExpansionRegionTagBits));
2270b57cec5SDimitry Andric // Mark an expansion region with a set bit that follows the counter tag,
2280b57cec5SDimitry Andric // and pack the expanded file id into the remaining bits.
2290b57cec5SDimitry Andric unsigned EncodedTagExpandedFileID =
2300b57cec5SDimitry Andric (1 << Counter::EncodingTagBits) |
2310b57cec5SDimitry Andric (I->ExpandedFileID
2320b57cec5SDimitry Andric << Counter::EncodingCounterTagAndExpansionRegionTagBits);
2330b57cec5SDimitry Andric encodeULEB128(EncodedTagExpandedFileID, OS);
2340b57cec5SDimitry Andric break;
2350b57cec5SDimitry Andric }
2360b57cec5SDimitry Andric case CounterMappingRegion::SkippedRegion:
2370b57cec5SDimitry Andric assert(Count.isZero());
2380b57cec5SDimitry Andric encodeULEB128(unsigned(I->Kind)
2390b57cec5SDimitry Andric << Counter::EncodingCounterTagAndExpansionRegionTagBits,
2400b57cec5SDimitry Andric OS);
2410b57cec5SDimitry Andric break;
242e8d8bef9SDimitry Andric case CounterMappingRegion::BranchRegion:
243e8d8bef9SDimitry Andric encodeULEB128(unsigned(I->Kind)
244e8d8bef9SDimitry Andric << Counter::EncodingCounterTagAndExpansionRegionTagBits,
245e8d8bef9SDimitry Andric OS);
246e8d8bef9SDimitry Andric writeCounter(MinExpressions, Count, OS);
247e8d8bef9SDimitry Andric writeCounter(MinExpressions, FalseCount, OS);
248e8d8bef9SDimitry Andric break;
2495f757f3fSDimitry Andric case CounterMappingRegion::MCDCBranchRegion:
2505f757f3fSDimitry Andric encodeULEB128(unsigned(I->Kind)
2515f757f3fSDimitry Andric << Counter::EncodingCounterTagAndExpansionRegionTagBits,
2525f757f3fSDimitry Andric OS);
2535f757f3fSDimitry Andric writeCounter(MinExpressions, Count, OS);
2545f757f3fSDimitry Andric writeCounter(MinExpressions, FalseCount, OS);
255*0fca6ea1SDimitry Andric {
256*0fca6ea1SDimitry Andric // They are written as internal values plus 1.
257*0fca6ea1SDimitry Andric const auto &BranchParams = I->getBranchParams();
258*0fca6ea1SDimitry Andric ParamsShouldBeNull = false;
259*0fca6ea1SDimitry Andric unsigned ID1 = BranchParams.ID + 1;
260*0fca6ea1SDimitry Andric unsigned TID1 = BranchParams.Conds[true] + 1;
261*0fca6ea1SDimitry Andric unsigned FID1 = BranchParams.Conds[false] + 1;
262*0fca6ea1SDimitry Andric encodeULEB128(ID1, OS);
263*0fca6ea1SDimitry Andric encodeULEB128(TID1, OS);
264*0fca6ea1SDimitry Andric encodeULEB128(FID1, OS);
265*0fca6ea1SDimitry Andric }
2665f757f3fSDimitry Andric break;
2675f757f3fSDimitry Andric case CounterMappingRegion::MCDCDecisionRegion:
2685f757f3fSDimitry Andric encodeULEB128(unsigned(I->Kind)
2695f757f3fSDimitry Andric << Counter::EncodingCounterTagAndExpansionRegionTagBits,
2705f757f3fSDimitry Andric OS);
271*0fca6ea1SDimitry Andric {
272*0fca6ea1SDimitry Andric const auto &DecisionParams = I->getDecisionParams();
273*0fca6ea1SDimitry Andric ParamsShouldBeNull = false;
274*0fca6ea1SDimitry Andric encodeULEB128(static_cast<unsigned>(DecisionParams.BitmapIdx), OS);
275*0fca6ea1SDimitry Andric encodeULEB128(static_cast<unsigned>(DecisionParams.NumConditions), OS);
276*0fca6ea1SDimitry Andric }
2775f757f3fSDimitry Andric break;
2780b57cec5SDimitry Andric }
2790b57cec5SDimitry Andric assert(I->LineStart >= PrevLineStart);
2800b57cec5SDimitry Andric encodeULEB128(I->LineStart - PrevLineStart, OS);
2810b57cec5SDimitry Andric encodeULEB128(I->ColumnStart, OS);
2820b57cec5SDimitry Andric assert(I->LineEnd >= I->LineStart);
2830b57cec5SDimitry Andric encodeULEB128(I->LineEnd - I->LineStart, OS);
2840b57cec5SDimitry Andric encodeULEB128(I->ColumnEnd, OS);
2850b57cec5SDimitry Andric PrevLineStart = I->LineStart;
286*0fca6ea1SDimitry Andric assert((!ParamsShouldBeNull || std::get_if<0>(&I->MCDCParams)) &&
287*0fca6ea1SDimitry Andric "MCDCParams should be empty");
288*0fca6ea1SDimitry Andric (void)ParamsShouldBeNull;
2890b57cec5SDimitry Andric }
2900b57cec5SDimitry Andric // Ensure that all file ids have at least one mapping region.
2910b57cec5SDimitry Andric assert(CurrentFileID == (VirtualFileMapping.size() - 1));
2920b57cec5SDimitry Andric }
2935f757f3fSDimitry Andric
write(raw_ostream & OS,TestingFormatVersion Version)2945f757f3fSDimitry Andric void TestingFormatWriter::write(raw_ostream &OS, TestingFormatVersion Version) {
2955f757f3fSDimitry Andric auto ByteSwap = [](uint64_t N) {
2965f757f3fSDimitry Andric return support::endian::byte_swap<uint64_t, llvm::endianness::little>(N);
2975f757f3fSDimitry Andric };
2985f757f3fSDimitry Andric
2995f757f3fSDimitry Andric // Output a 64bit magic number.
3005f757f3fSDimitry Andric auto Magic = ByteSwap(TestingFormatMagic);
3015f757f3fSDimitry Andric OS.write(reinterpret_cast<char *>(&Magic), sizeof(Magic));
3025f757f3fSDimitry Andric
3035f757f3fSDimitry Andric // Output a 64bit version field.
3045f757f3fSDimitry Andric auto VersionLittle = ByteSwap(uint64_t(Version));
3055f757f3fSDimitry Andric OS.write(reinterpret_cast<char *>(&VersionLittle), sizeof(VersionLittle));
3065f757f3fSDimitry Andric
3075f757f3fSDimitry Andric // Output the ProfileNames data.
3085f757f3fSDimitry Andric encodeULEB128(ProfileNamesData.size(), OS);
3095f757f3fSDimitry Andric encodeULEB128(ProfileNamesAddr, OS);
3105f757f3fSDimitry Andric OS << ProfileNamesData;
3115f757f3fSDimitry Andric
3125f757f3fSDimitry Andric // Version2 adds an extra field to indicate the size of the
3135f757f3fSDimitry Andric // CoverageMappingData.
3145f757f3fSDimitry Andric if (Version == TestingFormatVersion::Version2)
3155f757f3fSDimitry Andric encodeULEB128(CoverageMappingData.size(), OS);
3165f757f3fSDimitry Andric
3175f757f3fSDimitry Andric // Coverage mapping data is expected to have an alignment of 8.
3185f757f3fSDimitry Andric for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)
3195f757f3fSDimitry Andric OS.write(uint8_t(0));
3205f757f3fSDimitry Andric OS << CoverageMappingData;
3215f757f3fSDimitry Andric
3225f757f3fSDimitry Andric // Coverage records data is expected to have an alignment of 8.
3235f757f3fSDimitry Andric for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)
3245f757f3fSDimitry Andric OS.write(uint8_t(0));
3255f757f3fSDimitry Andric OS << CoverageRecordsData;
3265f757f3fSDimitry Andric }
327