xref: /llvm-project/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp (revision 8039e92ac5e7b0756e0d1f88156546030e7e3fff)
1 //=-- CoverageMappingWriter.cpp - Code coverage mapping writer -------------=//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file contains support for writing coverage mapping data for
11 // instrumentation based coverage.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
16 #include "llvm/Support/LEB128.h"
17 
18 using namespace llvm;
19 using namespace coverage;
20 
21 namespace {
22 /// \brief Gather only the expressions that are used by the mapping
23 /// regions in this function.
24 class CounterExpressionsMinimizer {
25   ArrayRef<CounterExpression> Expressions;
26   llvm::SmallVector<CounterExpression, 16> UsedExpressions;
27   std::vector<unsigned> AdjustedExpressionIDs;
28 
29 public:
30   void mark(Counter C) {
31     if (!C.isExpression())
32       return;
33     unsigned ID = C.getExpressionID();
34     AdjustedExpressionIDs[ID] = 1;
35     mark(Expressions[ID].LHS);
36     mark(Expressions[ID].RHS);
37   }
38 
39   void gatherUsed(Counter C) {
40     if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()])
41       return;
42     AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size();
43     const auto &E = Expressions[C.getExpressionID()];
44     UsedExpressions.push_back(E);
45     gatherUsed(E.LHS);
46     gatherUsed(E.RHS);
47   }
48 
49   CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,
50                               ArrayRef<CounterMappingRegion> MappingRegions)
51       : Expressions(Expressions) {
52     AdjustedExpressionIDs.resize(Expressions.size(), 0);
53     for (const auto &I : MappingRegions)
54       mark(I.Count);
55     for (const auto &I : MappingRegions)
56       gatherUsed(I.Count);
57   }
58 
59   ArrayRef<CounterExpression> getExpressions() const { return UsedExpressions; }
60 
61   /// \brief Adjust the given counter to correctly transition from the old
62   /// expression ids to the new expression ids.
63   Counter adjust(Counter C) const {
64     if (C.isExpression())
65       C = Counter::getExpression(AdjustedExpressionIDs[C.getExpressionID()]);
66     return C;
67   }
68 };
69 }
70 
71 /// \brief Encode the counter.
72 ///
73 /// The encoding uses the following format:
74 /// Low 2 bits - Tag:
75 ///   Counter::Zero(0) - A Counter with kind Counter::Zero
76 ///   Counter::CounterValueReference(1) - A counter with kind
77 ///     Counter::CounterValueReference
78 ///   Counter::Expression(2) + CounterExpression::Subtract(0) -
79 ///     A counter with kind Counter::Expression and an expression
80 ///     with kind CounterExpression::Subtract
81 ///   Counter::Expression(2) + CounterExpression::Add(1) -
82 ///     A counter with kind Counter::Expression and an expression
83 ///     with kind CounterExpression::Add
84 /// Remaining bits - Counter/Expression ID.
85 static unsigned encodeCounter(ArrayRef<CounterExpression> Expressions,
86                               Counter C) {
87   unsigned Tag = unsigned(C.getKind());
88   if (C.isExpression())
89     Tag += Expressions[C.getExpressionID()].Kind;
90   unsigned ID = C.getCounterID();
91   assert(ID <=
92          (std::numeric_limits<unsigned>::max() >> Counter::EncodingTagBits));
93   return Tag | (ID << Counter::EncodingTagBits);
94 }
95 
96 static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C,
97                          raw_ostream &OS) {
98   encodeULEB128(encodeCounter(Expressions, C), OS);
99 }
100 
101 void CoverageMappingWriter::write(raw_ostream &OS) {
102   // Sort the regions in an ascending order by the file id and the starting
103   // location.
104   std::stable_sort(MappingRegions.begin(), MappingRegions.end());
105 
106   // Write out the fileid -> filename mapping.
107   encodeULEB128(VirtualFileMapping.size(), OS);
108   for (const auto &FileID : VirtualFileMapping)
109     encodeULEB128(FileID, OS);
110 
111   // Write out the expressions.
112   CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions);
113   auto MinExpressions = Minimizer.getExpressions();
114   encodeULEB128(MinExpressions.size(), OS);
115   for (const auto &E : MinExpressions) {
116     writeCounter(MinExpressions, Minimizer.adjust(E.LHS), OS);
117     writeCounter(MinExpressions, Minimizer.adjust(E.RHS), OS);
118   }
119 
120   // Write out the mapping regions.
121   // Split the regions into subarrays where each region in a
122   // subarray has a fileID which is the index of that subarray.
123   unsigned PrevLineStart = 0;
124   unsigned CurrentFileID = ~0U;
125   for (auto I = MappingRegions.begin(), E = MappingRegions.end(); I != E; ++I) {
126     if (I->FileID != CurrentFileID) {
127       // Ensure that all file ids have at least one mapping region.
128       assert(I->FileID == (CurrentFileID + 1));
129       // Find the number of regions with this file id.
130       unsigned RegionCount = 1;
131       for (auto J = I + 1; J != E && I->FileID == J->FileID; ++J)
132         ++RegionCount;
133       // Start a new region sub-array.
134       encodeULEB128(RegionCount, OS);
135 
136       CurrentFileID = I->FileID;
137       PrevLineStart = 0;
138     }
139     Counter Count = Minimizer.adjust(I->Count);
140     switch (I->Kind) {
141     case CounterMappingRegion::CodeRegion:
142       writeCounter(MinExpressions, Count, OS);
143       break;
144     case CounterMappingRegion::ExpansionRegion: {
145       assert(Count.isZero());
146       assert(I->ExpandedFileID <=
147              (std::numeric_limits<unsigned>::max() >>
148               Counter::EncodingCounterTagAndExpansionRegionTagBits));
149       // Mark an expansion region with a set bit that follows the counter tag,
150       // and pack the expanded file id into the remaining bits.
151       unsigned EncodedTagExpandedFileID =
152           (1 << Counter::EncodingTagBits) |
153           (I->ExpandedFileID
154            << Counter::EncodingCounterTagAndExpansionRegionTagBits);
155       encodeULEB128(EncodedTagExpandedFileID, OS);
156       break;
157     }
158     case CounterMappingRegion::SkippedRegion:
159       assert(Count.isZero());
160       encodeULEB128(unsigned(I->Kind)
161                         << Counter::EncodingCounterTagAndExpansionRegionTagBits,
162                     OS);
163       break;
164     }
165     assert(I->LineStart >= PrevLineStart);
166     encodeULEB128(I->LineStart - PrevLineStart, OS);
167     encodeULEB128(I->ColumnStart, OS);
168     assert(I->LineEnd >= I->LineStart);
169     encodeULEB128(I->LineEnd - I->LineStart, OS);
170     encodeULEB128(I->ColumnEnd, OS);
171     PrevLineStart = I->LineStart;
172   }
173   // Ensure that all file ids have at least one mapping region.
174   assert(CurrentFileID == (VirtualFileMapping.size() - 1));
175 }
176 
177 /// \brief Encode coverage data into \p OS.
178 static void encodeCoverageData(ArrayRef<std::string> Filenames,
179                                ArrayRef<std::string> CoverageMappings,
180                                size_t &FilenamesSize,
181                                size_t &CoverageMappingsSize, raw_ostream &OS) {
182   size_t OSOffset = OS.GetNumBytesInBuffer();
183 
184   // Encode the filenames.
185   encodeULEB128(Filenames.size(), OS);
186   for (const auto &Filename : Filenames) {
187     encodeULEB128(Filename.size(), OS);
188     OS << Filename;
189   }
190 
191   FilenamesSize = OS.GetNumBytesInBuffer() - OSOffset;
192 
193   // Encode the coverage mappings.
194   for (const auto &RawMapping : CoverageMappings)
195     OS << RawMapping;
196 
197   // Pad the output stream to an 8-byte boundary. Account for the padding bytes
198   // in \p CoverageMappingsSize.
199   if (size_t Rem = OS.GetNumBytesInBuffer() % 8) {
200     CoverageMappingsSize += 8 - Rem;
201     for (size_t I = 0, S = 8 - Rem; I < S; ++I)
202       OS << '\0';
203   }
204 
205   CoverageMappingsSize = OS.GetNumBytesInBuffer() - FilenamesSize - OSOffset;
206 }
207 
208 namespace llvm {
209 namespace coverage {
210 
211 Expected<std::string> encodeFilenamesAndRawMappings(
212     ArrayRef<std::string> Filenames, ArrayRef<std::string> CoverageMappings,
213     size_t &FilenamesSize, size_t &CoverageMappingsSize) {
214   std::string CoverageData;
215   {
216     raw_string_ostream OS{CoverageData};
217     encodeCoverageData(Filenames, CoverageMappings, FilenamesSize,
218                        CoverageMappingsSize, OS);
219   }
220   return std::move(CoverageData);
221 }
222 
223 } // end namespace coverage
224 } // end namespace llvm
225