1e78d131aSEugene Zelenko //===- CoverageMappingWriter.cpp - Code coverage mapping writer -----------===// 2dc707122SEaswaran Raman // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6dc707122SEaswaran Raman // 7dc707122SEaswaran Raman //===----------------------------------------------------------------------===// 8dc707122SEaswaran Raman // 9dc707122SEaswaran Raman // This file contains support for writing coverage mapping data for 10dc707122SEaswaran Raman // instrumentation based coverage. 11dc707122SEaswaran Raman // 12dc707122SEaswaran Raman //===----------------------------------------------------------------------===// 13dc707122SEaswaran Raman 14dd1ea9deSVedant Kumar #include "llvm/ProfileData/InstrProf.h" 156bda14b3SChandler Carruth #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" 16e78d131aSEugene Zelenko #include "llvm/ADT/ArrayRef.h" 17e78d131aSEugene Zelenko #include "llvm/ADT/SmallVector.h" 18dd1ea9deSVedant Kumar #include "llvm/Support/Compression.h" 19dc707122SEaswaran Raman #include "llvm/Support/LEB128.h" 20e78d131aSEugene Zelenko #include "llvm/Support/raw_ostream.h" 21e78d131aSEugene Zelenko #include <algorithm> 22e78d131aSEugene Zelenko #include <cassert> 23e78d131aSEugene Zelenko #include <limits> 24e78d131aSEugene Zelenko #include <vector> 25dc707122SEaswaran Raman 26dc707122SEaswaran Raman using namespace llvm; 27dc707122SEaswaran Raman using namespace coverage; 28dc707122SEaswaran Raman 2995de2497SVedant Kumar CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter( 305fbd1a33SPetr Hosek ArrayRef<std::string> Filenames) 3195de2497SVedant Kumar : Filenames(Filenames) { 3295de2497SVedant Kumar #ifndef NDEBUG 3395de2497SVedant Kumar StringSet<> NameSet; 3495de2497SVedant Kumar for (StringRef Name : Filenames) 3595de2497SVedant Kumar assert(NameSet.insert(Name).second && "Duplicate filename"); 3695de2497SVedant Kumar #endif 3795de2497SVedant Kumar } 3895de2497SVedant Kumar 39dd1ea9deSVedant Kumar void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) { 40dd1ea9deSVedant Kumar std::string FilenamesStr; 41dd1ea9deSVedant Kumar { 42dd1ea9deSVedant Kumar raw_string_ostream FilenamesOS{FilenamesStr}; 4333888717SVedant Kumar for (const auto &Filename : Filenames) { 44dd1ea9deSVedant Kumar encodeULEB128(Filename.size(), FilenamesOS); 45dd1ea9deSVedant Kumar FilenamesOS << Filename; 4633888717SVedant Kumar } 4799317124SVedant Kumar } 4899317124SVedant Kumar 49dd1ea9deSVedant Kumar SmallString<128> CompressedStr; 50dd1ea9deSVedant Kumar bool doCompression = 51dd1ea9deSVedant Kumar Compress && zlib::isAvailable() && DoInstrProfNameCompression; 52dd1ea9deSVedant Kumar if (doCompression) { 53dd1ea9deSVedant Kumar auto E = 54dd1ea9deSVedant Kumar zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression); 55dd1ea9deSVedant Kumar if (E) 56dd1ea9deSVedant Kumar report_bad_alloc_error("Failed to zlib compress coverage data"); 57dd1ea9deSVedant Kumar } 58dd1ea9deSVedant Kumar 59dd1ea9deSVedant Kumar // ::= <num-filenames> 60dd1ea9deSVedant Kumar // <uncompressed-len> 61dd1ea9deSVedant Kumar // <compressed-len-or-zero> 62dd1ea9deSVedant Kumar // (<compressed-filenames> | <uncompressed-filenames>) 63dd1ea9deSVedant Kumar encodeULEB128(Filenames.size(), OS); 64dd1ea9deSVedant Kumar encodeULEB128(FilenamesStr.size(), OS); 65dd1ea9deSVedant Kumar encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS); 66*1def2579SDavid Blaikie OS << (doCompression ? CompressedStr.str() : StringRef(FilenamesStr)); 67dd1ea9deSVedant Kumar } 68dd1ea9deSVedant Kumar 69dc707122SEaswaran Raman namespace { 70e78d131aSEugene Zelenko 715f8f34e4SAdrian Prantl /// Gather only the expressions that are used by the mapping 72dc707122SEaswaran Raman /// regions in this function. 73dc707122SEaswaran Raman class CounterExpressionsMinimizer { 74dc707122SEaswaran Raman ArrayRef<CounterExpression> Expressions; 75e78d131aSEugene Zelenko SmallVector<CounterExpression, 16> UsedExpressions; 76dc707122SEaswaran Raman std::vector<unsigned> AdjustedExpressionIDs; 77dc707122SEaswaran Raman 78dc707122SEaswaran Raman public: 79e78d131aSEugene Zelenko CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions, 80e78d131aSEugene Zelenko ArrayRef<CounterMappingRegion> MappingRegions) 81e78d131aSEugene Zelenko : Expressions(Expressions) { 82e78d131aSEugene Zelenko AdjustedExpressionIDs.resize(Expressions.size(), 0); 839f2967bcSAlan Phipps for (const auto &I : MappingRegions) { 84e78d131aSEugene Zelenko mark(I.Count); 859f2967bcSAlan Phipps mark(I.FalseCount); 869f2967bcSAlan Phipps } 879f2967bcSAlan Phipps for (const auto &I : MappingRegions) { 88e78d131aSEugene Zelenko gatherUsed(I.Count); 899f2967bcSAlan Phipps gatherUsed(I.FalseCount); 909f2967bcSAlan Phipps } 91e78d131aSEugene Zelenko } 92e78d131aSEugene Zelenko 93dc707122SEaswaran Raman void mark(Counter C) { 94dc707122SEaswaran Raman if (!C.isExpression()) 95dc707122SEaswaran Raman return; 96dc707122SEaswaran Raman unsigned ID = C.getExpressionID(); 97dc707122SEaswaran Raman AdjustedExpressionIDs[ID] = 1; 98dc707122SEaswaran Raman mark(Expressions[ID].LHS); 99dc707122SEaswaran Raman mark(Expressions[ID].RHS); 100dc707122SEaswaran Raman } 101dc707122SEaswaran Raman 102dc707122SEaswaran Raman void gatherUsed(Counter C) { 103dc707122SEaswaran Raman if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()]) 104dc707122SEaswaran Raman return; 105dc707122SEaswaran Raman AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size(); 106dc707122SEaswaran Raman const auto &E = Expressions[C.getExpressionID()]; 107dc707122SEaswaran Raman UsedExpressions.push_back(E); 108dc707122SEaswaran Raman gatherUsed(E.LHS); 109dc707122SEaswaran Raman gatherUsed(E.RHS); 110dc707122SEaswaran Raman } 111dc707122SEaswaran Raman 112dc707122SEaswaran Raman ArrayRef<CounterExpression> getExpressions() const { return UsedExpressions; } 113dc707122SEaswaran Raman 1145f8f34e4SAdrian Prantl /// Adjust the given counter to correctly transition from the old 115dc707122SEaswaran Raman /// expression ids to the new expression ids. 116dc707122SEaswaran Raman Counter adjust(Counter C) const { 117dc707122SEaswaran Raman if (C.isExpression()) 118dc707122SEaswaran Raman C = Counter::getExpression(AdjustedExpressionIDs[C.getExpressionID()]); 119dc707122SEaswaran Raman return C; 120dc707122SEaswaran Raman } 121dc707122SEaswaran Raman }; 122e78d131aSEugene Zelenko 123e78d131aSEugene Zelenko } // end anonymous namespace 124dc707122SEaswaran Raman 1255f8f34e4SAdrian Prantl /// Encode the counter. 126dc707122SEaswaran Raman /// 127dc707122SEaswaran Raman /// The encoding uses the following format: 128dc707122SEaswaran Raman /// Low 2 bits - Tag: 129dc707122SEaswaran Raman /// Counter::Zero(0) - A Counter with kind Counter::Zero 130dc707122SEaswaran Raman /// Counter::CounterValueReference(1) - A counter with kind 131dc707122SEaswaran Raman /// Counter::CounterValueReference 132dc707122SEaswaran Raman /// Counter::Expression(2) + CounterExpression::Subtract(0) - 133dc707122SEaswaran Raman /// A counter with kind Counter::Expression and an expression 134dc707122SEaswaran Raman /// with kind CounterExpression::Subtract 135dc707122SEaswaran Raman /// Counter::Expression(2) + CounterExpression::Add(1) - 136dc707122SEaswaran Raman /// A counter with kind Counter::Expression and an expression 137dc707122SEaswaran Raman /// with kind CounterExpression::Add 138dc707122SEaswaran Raman /// Remaining bits - Counter/Expression ID. 139dc707122SEaswaran Raman static unsigned encodeCounter(ArrayRef<CounterExpression> Expressions, 140dc707122SEaswaran Raman Counter C) { 141dc707122SEaswaran Raman unsigned Tag = unsigned(C.getKind()); 142dc707122SEaswaran Raman if (C.isExpression()) 143dc707122SEaswaran Raman Tag += Expressions[C.getExpressionID()].Kind; 144dc707122SEaswaran Raman unsigned ID = C.getCounterID(); 145dc707122SEaswaran Raman assert(ID <= 146dc707122SEaswaran Raman (std::numeric_limits<unsigned>::max() >> Counter::EncodingTagBits)); 147dc707122SEaswaran Raman return Tag | (ID << Counter::EncodingTagBits); 148dc707122SEaswaran Raman } 149dc707122SEaswaran Raman 150dc707122SEaswaran Raman static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C, 151dc707122SEaswaran Raman raw_ostream &OS) { 152dc707122SEaswaran Raman encodeULEB128(encodeCounter(Expressions, C), OS); 153dc707122SEaswaran Raman } 154dc707122SEaswaran Raman 155dc707122SEaswaran Raman void CoverageMappingWriter::write(raw_ostream &OS) { 156bae83970SVedant Kumar // Check that we don't have any bogus regions. 157bae83970SVedant Kumar assert(all_of(MappingRegions, 158bae83970SVedant Kumar [](const CounterMappingRegion &CMR) { 159bae83970SVedant Kumar return CMR.startLoc() <= CMR.endLoc(); 160bae83970SVedant Kumar }) && 161bae83970SVedant Kumar "Source region does not begin before it ends"); 162bae83970SVedant Kumar 163dc707122SEaswaran Raman // Sort the regions in an ascending order by the file id and the starting 164f3c8a9cfSIgor Kudrin // location. Sort by region kinds to ensure stable order for tests. 165efd94c56SFangrui Song llvm::stable_sort(MappingRegions, [](const CounterMappingRegion &LHS, 166efd94c56SFangrui Song const CounterMappingRegion &RHS) { 167f3c8a9cfSIgor Kudrin if (LHS.FileID != RHS.FileID) 168f3c8a9cfSIgor Kudrin return LHS.FileID < RHS.FileID; 169f3c8a9cfSIgor Kudrin if (LHS.startLoc() != RHS.startLoc()) 170f3c8a9cfSIgor Kudrin return LHS.startLoc() < RHS.startLoc(); 171f3c8a9cfSIgor Kudrin return LHS.Kind < RHS.Kind; 172f3c8a9cfSIgor Kudrin }); 173dc707122SEaswaran Raman 174dc707122SEaswaran Raman // Write out the fileid -> filename mapping. 175dc707122SEaswaran Raman encodeULEB128(VirtualFileMapping.size(), OS); 176dc707122SEaswaran Raman for (const auto &FileID : VirtualFileMapping) 177dc707122SEaswaran Raman encodeULEB128(FileID, OS); 178dc707122SEaswaran Raman 179dc707122SEaswaran Raman // Write out the expressions. 180dc707122SEaswaran Raman CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions); 181dc707122SEaswaran Raman auto MinExpressions = Minimizer.getExpressions(); 182dc707122SEaswaran Raman encodeULEB128(MinExpressions.size(), OS); 183dc707122SEaswaran Raman for (const auto &E : MinExpressions) { 184dc707122SEaswaran Raman writeCounter(MinExpressions, Minimizer.adjust(E.LHS), OS); 185dc707122SEaswaran Raman writeCounter(MinExpressions, Minimizer.adjust(E.RHS), OS); 186dc707122SEaswaran Raman } 187dc707122SEaswaran Raman 188dc707122SEaswaran Raman // Write out the mapping regions. 189dc707122SEaswaran Raman // Split the regions into subarrays where each region in a 190dc707122SEaswaran Raman // subarray has a fileID which is the index of that subarray. 191dc707122SEaswaran Raman unsigned PrevLineStart = 0; 192dc707122SEaswaran Raman unsigned CurrentFileID = ~0U; 193dc707122SEaswaran Raman for (auto I = MappingRegions.begin(), E = MappingRegions.end(); I != E; ++I) { 194dc707122SEaswaran Raman if (I->FileID != CurrentFileID) { 195dc707122SEaswaran Raman // Ensure that all file ids have at least one mapping region. 196dc707122SEaswaran Raman assert(I->FileID == (CurrentFileID + 1)); 197dc707122SEaswaran Raman // Find the number of regions with this file id. 198dc707122SEaswaran Raman unsigned RegionCount = 1; 199dc707122SEaswaran Raman for (auto J = I + 1; J != E && I->FileID == J->FileID; ++J) 200dc707122SEaswaran Raman ++RegionCount; 201dc707122SEaswaran Raman // Start a new region sub-array. 202dc707122SEaswaran Raman encodeULEB128(RegionCount, OS); 203dc707122SEaswaran Raman 204dc707122SEaswaran Raman CurrentFileID = I->FileID; 205dc707122SEaswaran Raman PrevLineStart = 0; 206dc707122SEaswaran Raman } 207dc707122SEaswaran Raman Counter Count = Minimizer.adjust(I->Count); 2089f2967bcSAlan Phipps Counter FalseCount = Minimizer.adjust(I->FalseCount); 209dc707122SEaswaran Raman switch (I->Kind) { 210dc707122SEaswaran Raman case CounterMappingRegion::CodeRegion: 211ad8f637bSVedant Kumar case CounterMappingRegion::GapRegion: 212dc707122SEaswaran Raman writeCounter(MinExpressions, Count, OS); 213dc707122SEaswaran Raman break; 214dc707122SEaswaran Raman case CounterMappingRegion::ExpansionRegion: { 215dc707122SEaswaran Raman assert(Count.isZero()); 216dc707122SEaswaran Raman assert(I->ExpandedFileID <= 217dc707122SEaswaran Raman (std::numeric_limits<unsigned>::max() >> 218dc707122SEaswaran Raman Counter::EncodingCounterTagAndExpansionRegionTagBits)); 219dc707122SEaswaran Raman // Mark an expansion region with a set bit that follows the counter tag, 220dc707122SEaswaran Raman // and pack the expanded file id into the remaining bits. 221dc707122SEaswaran Raman unsigned EncodedTagExpandedFileID = 222dc707122SEaswaran Raman (1 << Counter::EncodingTagBits) | 223dc707122SEaswaran Raman (I->ExpandedFileID 224dc707122SEaswaran Raman << Counter::EncodingCounterTagAndExpansionRegionTagBits); 225dc707122SEaswaran Raman encodeULEB128(EncodedTagExpandedFileID, OS); 226dc707122SEaswaran Raman break; 227dc707122SEaswaran Raman } 228dc707122SEaswaran Raman case CounterMappingRegion::SkippedRegion: 229dc707122SEaswaran Raman assert(Count.isZero()); 230dc707122SEaswaran Raman encodeULEB128(unsigned(I->Kind) 231dc707122SEaswaran Raman << Counter::EncodingCounterTagAndExpansionRegionTagBits, 232dc707122SEaswaran Raman OS); 233dc707122SEaswaran Raman break; 2349f2967bcSAlan Phipps case CounterMappingRegion::BranchRegion: 2359f2967bcSAlan Phipps encodeULEB128(unsigned(I->Kind) 2369f2967bcSAlan Phipps << Counter::EncodingCounterTagAndExpansionRegionTagBits, 2379f2967bcSAlan Phipps OS); 2389f2967bcSAlan Phipps writeCounter(MinExpressions, Count, OS); 2399f2967bcSAlan Phipps writeCounter(MinExpressions, FalseCount, OS); 2409f2967bcSAlan Phipps break; 241dc707122SEaswaran Raman } 242dc707122SEaswaran Raman assert(I->LineStart >= PrevLineStart); 243dc707122SEaswaran Raman encodeULEB128(I->LineStart - PrevLineStart, OS); 244dc707122SEaswaran Raman encodeULEB128(I->ColumnStart, OS); 245dc707122SEaswaran Raman assert(I->LineEnd >= I->LineStart); 246dc707122SEaswaran Raman encodeULEB128(I->LineEnd - I->LineStart, OS); 247dc707122SEaswaran Raman encodeULEB128(I->ColumnEnd, OS); 248dc707122SEaswaran Raman PrevLineStart = I->LineStart; 249dc707122SEaswaran Raman } 250dc707122SEaswaran Raman // Ensure that all file ids have at least one mapping region. 251dc707122SEaswaran Raman assert(CurrentFileID == (VirtualFileMapping.size() - 1)); 252dc707122SEaswaran Raman } 253