Path: blob/main/contrib/llvm-project/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
35271 views
//===- CoverageMappingWriter.cpp - Code coverage mapping writer -----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains support for writing coverage mapping data for9// instrumentation based coverage.10//11//===----------------------------------------------------------------------===//1213#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"14#include "llvm/ADT/ArrayRef.h"15#include "llvm/ADT/SmallVector.h"16#include "llvm/ADT/StringExtras.h"17#include "llvm/ProfileData/InstrProf.h"18#include "llvm/Support/Compression.h"19#include "llvm/Support/LEB128.h"20#include "llvm/Support/raw_ostream.h"21#include <algorithm>22#include <cassert>23#include <limits>24#include <vector>2526using namespace llvm;27using namespace coverage;2829CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter(30ArrayRef<std::string> Filenames)31: Filenames(Filenames) {32#ifndef NDEBUG33StringSet<> NameSet;34for (StringRef Name : Filenames)35assert(NameSet.insert(Name).second && "Duplicate filename");36#endif37}3839void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) {40std::string FilenamesStr;41{42raw_string_ostream FilenamesOS{FilenamesStr};43for (const auto &Filename : Filenames) {44encodeULEB128(Filename.size(), FilenamesOS);45FilenamesOS << Filename;46}47}4849SmallVector<uint8_t, 128> CompressedStr;50bool doCompression = Compress && compression::zlib::isAvailable() &&51DoInstrProfNameCompression;52if (doCompression)53compression::zlib::compress(arrayRefFromStringRef(FilenamesStr),54CompressedStr,55compression::zlib::BestSizeCompression);5657// ::= <num-filenames>58// <uncompressed-len>59// <compressed-len-or-zero>60// (<compressed-filenames> | <uncompressed-filenames>)61encodeULEB128(Filenames.size(), OS);62encodeULEB128(FilenamesStr.size(), OS);63encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS);64OS << (doCompression ? toStringRef(CompressedStr) : StringRef(FilenamesStr));65}6667namespace {6869/// Gather only the expressions that are used by the mapping70/// regions in this function.71class CounterExpressionsMinimizer {72ArrayRef<CounterExpression> Expressions;73SmallVector<CounterExpression, 16> UsedExpressions;74std::vector<unsigned> AdjustedExpressionIDs;7576public:77CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,78ArrayRef<CounterMappingRegion> MappingRegions)79: Expressions(Expressions) {80AdjustedExpressionIDs.resize(Expressions.size(), 0);81for (const auto &I : MappingRegions) {82mark(I.Count);83mark(I.FalseCount);84}85for (const auto &I : MappingRegions) {86gatherUsed(I.Count);87gatherUsed(I.FalseCount);88}89}9091void mark(Counter C) {92if (!C.isExpression())93return;94unsigned ID = C.getExpressionID();95AdjustedExpressionIDs[ID] = 1;96mark(Expressions[ID].LHS);97mark(Expressions[ID].RHS);98}99100void gatherUsed(Counter C) {101if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()])102return;103AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size();104const auto &E = Expressions[C.getExpressionID()];105UsedExpressions.push_back(E);106gatherUsed(E.LHS);107gatherUsed(E.RHS);108}109110ArrayRef<CounterExpression> getExpressions() const { return UsedExpressions; }111112/// Adjust the given counter to correctly transition from the old113/// expression ids to the new expression ids.114Counter adjust(Counter C) const {115if (C.isExpression())116C = Counter::getExpression(AdjustedExpressionIDs[C.getExpressionID()]);117return C;118}119};120121} // end anonymous namespace122123/// Encode the counter.124///125/// The encoding uses the following format:126/// Low 2 bits - Tag:127/// Counter::Zero(0) - A Counter with kind Counter::Zero128/// Counter::CounterValueReference(1) - A counter with kind129/// Counter::CounterValueReference130/// Counter::Expression(2) + CounterExpression::Subtract(0) -131/// A counter with kind Counter::Expression and an expression132/// with kind CounterExpression::Subtract133/// Counter::Expression(2) + CounterExpression::Add(1) -134/// A counter with kind Counter::Expression and an expression135/// with kind CounterExpression::Add136/// Remaining bits - Counter/Expression ID.137static unsigned encodeCounter(ArrayRef<CounterExpression> Expressions,138Counter C) {139unsigned Tag = unsigned(C.getKind());140if (C.isExpression())141Tag += Expressions[C.getExpressionID()].Kind;142unsigned ID = C.getCounterID();143assert(ID <=144(std::numeric_limits<unsigned>::max() >> Counter::EncodingTagBits));145return Tag | (ID << Counter::EncodingTagBits);146}147148static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C,149raw_ostream &OS) {150encodeULEB128(encodeCounter(Expressions, C), OS);151}152153void CoverageMappingWriter::write(raw_ostream &OS) {154// Check that we don't have any bogus regions.155assert(all_of(MappingRegions,156[](const CounterMappingRegion &CMR) {157return CMR.startLoc() <= CMR.endLoc();158}) &&159"Source region does not begin before it ends");160161// Sort the regions in an ascending order by the file id and the starting162// location. Sort by region kinds to ensure stable order for tests.163llvm::stable_sort(MappingRegions, [](const CounterMappingRegion &LHS,164const CounterMappingRegion &RHS) {165if (LHS.FileID != RHS.FileID)166return LHS.FileID < RHS.FileID;167if (LHS.startLoc() != RHS.startLoc())168return LHS.startLoc() < RHS.startLoc();169170// Put `Decision` before `Expansion`.171auto getKindKey = [](CounterMappingRegion::RegionKind Kind) {172return (Kind == CounterMappingRegion::MCDCDecisionRegion173? 2 * CounterMappingRegion::ExpansionRegion - 1174: 2 * Kind);175};176177return getKindKey(LHS.Kind) < getKindKey(RHS.Kind);178});179180// Write out the fileid -> filename mapping.181encodeULEB128(VirtualFileMapping.size(), OS);182for (const auto &FileID : VirtualFileMapping)183encodeULEB128(FileID, OS);184185// Write out the expressions.186CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions);187auto MinExpressions = Minimizer.getExpressions();188encodeULEB128(MinExpressions.size(), OS);189for (const auto &E : MinExpressions) {190writeCounter(MinExpressions, Minimizer.adjust(E.LHS), OS);191writeCounter(MinExpressions, Minimizer.adjust(E.RHS), OS);192}193194// Write out the mapping regions.195// Split the regions into subarrays where each region in a196// subarray has a fileID which is the index of that subarray.197unsigned PrevLineStart = 0;198unsigned CurrentFileID = ~0U;199for (auto I = MappingRegions.begin(), E = MappingRegions.end(); I != E; ++I) {200if (I->FileID != CurrentFileID) {201// Ensure that all file ids have at least one mapping region.202assert(I->FileID == (CurrentFileID + 1));203// Find the number of regions with this file id.204unsigned RegionCount = 1;205for (auto J = I + 1; J != E && I->FileID == J->FileID; ++J)206++RegionCount;207// Start a new region sub-array.208encodeULEB128(RegionCount, OS);209210CurrentFileID = I->FileID;211PrevLineStart = 0;212}213Counter Count = Minimizer.adjust(I->Count);214Counter FalseCount = Minimizer.adjust(I->FalseCount);215bool ParamsShouldBeNull = true;216switch (I->Kind) {217case CounterMappingRegion::CodeRegion:218case CounterMappingRegion::GapRegion:219writeCounter(MinExpressions, Count, OS);220break;221case CounterMappingRegion::ExpansionRegion: {222assert(Count.isZero());223assert(I->ExpandedFileID <=224(std::numeric_limits<unsigned>::max() >>225Counter::EncodingCounterTagAndExpansionRegionTagBits));226// Mark an expansion region with a set bit that follows the counter tag,227// and pack the expanded file id into the remaining bits.228unsigned EncodedTagExpandedFileID =229(1 << Counter::EncodingTagBits) |230(I->ExpandedFileID231<< Counter::EncodingCounterTagAndExpansionRegionTagBits);232encodeULEB128(EncodedTagExpandedFileID, OS);233break;234}235case CounterMappingRegion::SkippedRegion:236assert(Count.isZero());237encodeULEB128(unsigned(I->Kind)238<< Counter::EncodingCounterTagAndExpansionRegionTagBits,239OS);240break;241case CounterMappingRegion::BranchRegion:242encodeULEB128(unsigned(I->Kind)243<< Counter::EncodingCounterTagAndExpansionRegionTagBits,244OS);245writeCounter(MinExpressions, Count, OS);246writeCounter(MinExpressions, FalseCount, OS);247break;248case CounterMappingRegion::MCDCBranchRegion:249encodeULEB128(unsigned(I->Kind)250<< Counter::EncodingCounterTagAndExpansionRegionTagBits,251OS);252writeCounter(MinExpressions, Count, OS);253writeCounter(MinExpressions, FalseCount, OS);254{255// They are written as internal values plus 1.256const auto &BranchParams = I->getBranchParams();257ParamsShouldBeNull = false;258unsigned ID1 = BranchParams.ID + 1;259unsigned TID1 = BranchParams.Conds[true] + 1;260unsigned FID1 = BranchParams.Conds[false] + 1;261encodeULEB128(ID1, OS);262encodeULEB128(TID1, OS);263encodeULEB128(FID1, OS);264}265break;266case CounterMappingRegion::MCDCDecisionRegion:267encodeULEB128(unsigned(I->Kind)268<< Counter::EncodingCounterTagAndExpansionRegionTagBits,269OS);270{271const auto &DecisionParams = I->getDecisionParams();272ParamsShouldBeNull = false;273encodeULEB128(static_cast<unsigned>(DecisionParams.BitmapIdx), OS);274encodeULEB128(static_cast<unsigned>(DecisionParams.NumConditions), OS);275}276break;277}278assert(I->LineStart >= PrevLineStart);279encodeULEB128(I->LineStart - PrevLineStart, OS);280encodeULEB128(I->ColumnStart, OS);281assert(I->LineEnd >= I->LineStart);282encodeULEB128(I->LineEnd - I->LineStart, OS);283encodeULEB128(I->ColumnEnd, OS);284PrevLineStart = I->LineStart;285assert((!ParamsShouldBeNull || std::get_if<0>(&I->MCDCParams)) &&286"MCDCParams should be empty");287(void)ParamsShouldBeNull;288}289// Ensure that all file ids have at least one mapping region.290assert(CurrentFileID == (VirtualFileMapping.size() - 1));291}292293void TestingFormatWriter::write(raw_ostream &OS, TestingFormatVersion Version) {294auto ByteSwap = [](uint64_t N) {295return support::endian::byte_swap<uint64_t, llvm::endianness::little>(N);296};297298// Output a 64bit magic number.299auto Magic = ByteSwap(TestingFormatMagic);300OS.write(reinterpret_cast<char *>(&Magic), sizeof(Magic));301302// Output a 64bit version field.303auto VersionLittle = ByteSwap(uint64_t(Version));304OS.write(reinterpret_cast<char *>(&VersionLittle), sizeof(VersionLittle));305306// Output the ProfileNames data.307encodeULEB128(ProfileNamesData.size(), OS);308encodeULEB128(ProfileNamesAddr, OS);309OS << ProfileNamesData;310311// Version2 adds an extra field to indicate the size of the312// CoverageMappingData.313if (Version == TestingFormatVersion::Version2)314encodeULEB128(CoverageMappingData.size(), OS);315316// Coverage mapping data is expected to have an alignment of 8.317for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)318OS.write(uint8_t(0));319OS << CoverageMappingData;320321// Coverage records data is expected to have an alignment of 8.322for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)323OS.write(uint8_t(0));324OS << CoverageRecordsData;325}326327328