Index: include/llvm-c/ProfileData.h =================================================================== --- /dev/null +++ include/llvm-c/ProfileData.h @@ -0,0 +1,104 @@ +/*===-- llvm-c/ProfileData.h - ProfileData Library C Interface ----*- C -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header declares the C interface to libProfileData.a, which implements *| +|* the LLVM profile data format. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_C_PROFILEDATA_H +#define LLVM_C_PROFILEDATA_H + +#include "llvm-c/Support.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup LLVMC LLVM-C: C interface to LLVM + * + * This module exposes parts of the LLVM library as a C API. + * + * @{ + */ + +// Opaque type wrappers +typedef struct LLVMOpaqueIndexedInstrProfReader *LLVMIndexedInstrProfReaderRef; +typedef struct LLVMOpaqueObjectFileCoverageMappingReader * +LLVMObjectFileCoverageMappingReaderRef; + +// IndexedInstrProfReader creation +LLVMIndexedInstrProfReaderRef +LLVMCreateIndexedInstrProfReader(const char *Path); +void LLVMDisposeIndexedInstrProfReader(LLVMIndexedInstrProfReaderRef Reader); + +// Function profiling counts extraction +void LLVMGetFunctionCounts(LLVMIndexedInstrProfReaderRef Reader, + const char *FuncName, uint64_t **Values, + size_t *ValuesSize); +void LLVMDisposeFunctionCounts(uint64_t *Values); + +// ObjectFileCoverageMappingReader creation +LLVMObjectFileCoverageMappingReaderRef +LLVMCreateObjectFileCoverageMappingReader(LLVMMemoryBufferRef MemBuf); +void LLVMDisposeObjectFileCoverageMappingReader( + LLVMObjectFileCoverageMappingReaderRef Reader); + +/// This provides a C-visible enumerator to +/// manage coverage mapping region kinds. +/// This should map exactly onto the C++ enumerator +/// CounterMappingRegion::RegionKind. +typedef enum { + LLVMCoverageMappingCodeRegion, + LLVMCoverageMappingExpansionRegion, + LLVMCoverageMappingEmptyRegion, + LLVMCoverageMappingSkippedRegion, +} LLVMCoverageMappingRegionKind; + +/// \brief A coverage mapping region. +typedef struct { + uint64_t ExecutionCount; + unsigned FileID, ExpandedFileID; + unsigned LineStart, ColumnStart; + unsigned LineEnd, ColumnEnd; + LLVMCoverageMappingRegionKind Kind; +} LLVMCoverageMappingRegion; + +/// \brief A coverage mapping data record for a single function. +typedef struct { + const char *Name; + const char **Filenames; + size_t FilenamesSize; + LLVMCoverageMappingRegion *MappingRegions; + size_t MappingRegionsSize; +} LLVMFunctionCoverageRecord; + +// Coverage mapping data extraction +LLVMBool LLVMLoadFunctionCoverageMappingData( + LLVMObjectFileCoverageMappingReaderRef CovReader, + LLVMIndexedInstrProfReaderRef ProfReader, + LLVMFunctionCoverageRecord **FunctionCoverage, + size_t *FunctionCoverageSize); +void LLVMDisposeFunctionCoverageMappingData( + LLVMFunctionCoverageRecord *FunctionCoverage, size_t FunctionCoverageSize); + +// Coverage mapping region operations +LLVMBool LLVMHasCoverageMappingRegionExecutionCountError( + LLVMCoverageMappingRegion *Region); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif /* !defined(__cplusplus) */ + +#endif /* !defined(LLVM_C_PROFILEDATA_H) */ Index: include/llvm/ProfileData/CoverageMapping.h =================================================================== --- /dev/null +++ include/llvm/ProfileData/CoverageMapping.h @@ -0,0 +1,225 @@ +//=-- CoverageMapping.h - Code coverage mapping support ---------*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Code coverage mapping data is genererated by clang and read by +// llvm-cov to show code coverage statistics for a file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_COVERAGEMAPPING_H_ +#define LLVM_PROFILEDATA_COVERAGEMAPPING_H_ + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { + +struct CounterExpressions; + +/// A Counter is an abstract value that will allow +/// the user to compute the number of times that a particular region +/// of code has been visited when the user has access to +/// the collected profile counters. +struct Counter { + enum CounterKind { + Zero, + CounterValueReference, + AdditionExpression, + SubtractionExpression + }; + static const unsigned EncodingTagBits = 2; + static const unsigned EncodingTagMask = 0x3; + +private: + CounterKind Kind; + unsigned ID; + + Counter(CounterKind Kind, unsigned ID) : Kind(Kind), ID(ID) {} + +public: + Counter() : Kind(Zero), ID(0) {} + + CounterKind getKind() const { return Kind; } + + bool isZero() const { return Kind == Zero; } + + bool isExpression() const { + return Kind == AdditionExpression || Kind == SubtractionExpression; + } + + unsigned getCounterID() const { return ID; } + + unsigned getExpressionID() const { return ID; } + + /// Encodes the counter into a raw integer value + unsigned Encode() const; + + /// Decodes the counter from a raw integer value + bool Decode(unsigned Value); + + bool operator==(const Counter &Other) const { + return Kind == Other.Kind && ID == Other.ID; + } + + /// Return the counter that represents the + /// number zero. + static Counter getZero() { return Counter(); } + + /// Return the counter that corresponds to a + /// specific profile counter. + static Counter getCounter(unsigned CounterId) { + return Counter(CounterValueReference, CounterId); + } + + /// Return the counter that corresponds to a + /// specific addition counter expression. + static Counter getExpression(CounterKind Kind, unsigned ExpressionId) { + return Counter(Kind, ExpressionId); + } +}; + +/// A Counter expression is a value that +/// represent an arithmetic operation with two counters. +struct CounterExpression { + enum ExprKind { Unknown, Subtract, Add }; + ExprKind Kind; + Counter LHS, RHS; + + CounterExpression(ExprKind Kind, Counter LHS, Counter RHS) + : Kind(Kind), LHS(LHS), RHS(RHS) {} + + bool operator==(const CounterExpression &Other) const { + return Kind == Other.Kind && LHS == Other.LHS && RHS == Other.RHS; + } +}; + +/// A Counter expression builder is used to construct the +/// counter expression. It avoids unecessary duplication +/// and simplifies algebraic expression to minimize the +/// number of necessary expressions and mapping regions. +class CounterExpressionBuilder { + /// A list of all the counter expressions + llvm::SmallVector Expressions; + /// An array of terms used in expression simplification. + llvm::SmallVector Terms; + + /// Return the counter which + /// corresponds to the given expression. Use a linear + /// search to try to find the given expression. + Counter get(Counter::CounterKind Kind, const CounterExpression &E); + + /// Convert the expression tree into a polynomial + /// in the form of K1Counter1 + .. + KNCounterN + /// where K is an integer constant that is stored + /// in the Terms array. + void ExtractTerms(Counter C, int Sign = 1); + + /// Simplies the given expression tree + /// by getting rid of algebraicly redundant + /// operations. + Counter Simplify(Counter ExpressionTree); + +public: + CounterExpressionBuilder(unsigned NumCounters); + + ArrayRef getExpressions() const { return Expressions; } + + /// Return a counter that represents the + /// the exression that adds lhs and rhs. + Counter Add(Counter LHS, Counter RHS); + + /// Return a counter that represents the + /// expression that subracts rhs from lhs. + Counter Subtract(Counter LHS, Counter RHS); +}; + +/// A Counter mapping region associates a source range with +/// a specific counter. +struct CounterMappingRegion { + enum RegionKind { + /// A CounterRegion associates some code with a counter + CodeRegion, + + /// An ExpansionRegion represents + /// a file expansion region that associates + /// a source range with the expansion of a + /// virtual source file. + /// Can be used to mark the usages of macroes + /// and #included files in coverage for the C sources. + ExpansionRegion, + + /// An EmptyRegion represents a source range without + /// code, but with a distinct counter. + EmptyRegion, + + /// A SkippedRegion represents a source range with + /// code that was skipped by a preprocessor + /// or similar means. + SkippedRegion + }; + + Counter Count; + unsigned FileID, ExpandedFileID; + unsigned LineStart, ColumnStart, LineEnd, ColumnEnd; + RegionKind Kind; + + CounterMappingRegion(Counter Count, unsigned FileID, unsigned LineStart, + unsigned ColumnStart, unsigned LineEnd, + unsigned ColumnEnd, RegionKind Kind = CodeRegion) + : Count(Count), FileID(FileID), ExpandedFileID(0), LineStart(LineStart), + ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd), + Kind(Kind) {} + + bool operator<(const CounterMappingRegion &Other) const { + if (FileID != Other.FileID) + return FileID < Other.FileID; + if (LineStart == Other.LineStart) + return ColumnStart < Other.ColumnStart; + return LineStart < Other.LineStart; + } +}; + +/// A MappingRegion associates a source range +/// with an execution count. +struct MappingRegion : public CounterMappingRegion { + uint64_t ExecutionCount; + + MappingRegion(const CounterMappingRegion &R, uint64_t ExecutionCount) + : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {} +}; + +/// A Counter mapping context is used +/// to connect the counters, expressions and the +/// obtained counter values. +class CounterMappingContext { + ArrayRef Expressions; + ArrayRef CounterValues; + +public: + CounterMappingContext(ArrayRef Expressions, + ArrayRef CounterValues = ArrayRef()) + : Expressions(Expressions), CounterValues(CounterValues) {} + + void dump(const Counter &C, llvm::raw_ostream &OS) const; + void dump(const Counter &C) const { dump(C, llvm::outs()); } + + /// Return the number of times that a + /// region of code associated with this + /// counter was executed. + int64_t evaluate(const Counter &C, std::error_code *Error) const; + int64_t evaluate(const Counter &C, std::error_code &Error) const { + Error.clear(); + return evaluate(C, &Error); + } +}; + +} // end namespace llvm + +#endif // LLVM_PROFILEDATA_COVERAGEMAPPING_H_ Index: include/llvm/ProfileData/CoverageMappingReader.h =================================================================== --- /dev/null +++ include/llvm/ProfileData/CoverageMappingReader.h @@ -0,0 +1,178 @@ +//=-- CoverageMappingReader.h - Code coverage mapping reader ------*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for reading coverage mapping data for +// instrumentation based coverage. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_COVERAGEMAPPING_READER_H_ +#define LLVM_PROFILEDATA_COVERAGEMAPPING_READER_H_ + +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/FileSystem.h" + +#include + +namespace llvm { + +class CoverageMappingReader; + +/// Coverage mapping information for a single function. +struct CoverageMappingRecord { + StringRef FunctionName; + ArrayRef Filenames; + ArrayRef Expressions; + ArrayRef MappingRegions; +}; + +/// A file format agnostic iterator over coverage mapping data. +class CoverageMappingIterator + : public std::iterator { + CoverageMappingReader *Reader; + CoverageMappingRecord Record; + + void Increment(); + +public: + CoverageMappingIterator() : Reader(nullptr) {} + CoverageMappingIterator(CoverageMappingReader *Reader) : Reader(Reader) { + Increment(); + } + + CoverageMappingIterator &operator++() { + Increment(); + return *this; + } + bool operator==(const CoverageMappingIterator &RHS) { + return Reader == RHS.Reader; + } + bool operator!=(const CoverageMappingIterator &RHS) { + return Reader != RHS.Reader; + } + CoverageMappingRecord &operator*() { return Record; } + CoverageMappingRecord *operator->() { return &Record; } +}; + +/// Base class and interface for reading coverage mapping data of any known +/// format. Provides an iterator over CoverageMappingRecords. +class CoverageMappingReader { + std::error_code LastError; + +public: + CoverageMappingReader() : LastError(instrprof_error::success) {} + virtual ~CoverageMappingReader() {} + + /// Read the header. Required before reading first record. + virtual std::error_code readHeader() = 0; + /// Read a single record. + virtual std::error_code readNextRecord(CoverageMappingRecord &Record) = 0; + /// Iterator over profile data. + CoverageMappingIterator begin() { return CoverageMappingIterator(this); } + CoverageMappingIterator end() { return CoverageMappingIterator(); } + +protected: + /// Set the current error_code and return same. + std::error_code error(std::error_code EC) { + LastError = EC; + return EC; + } + + /// Clear the current error code and return a successful one. + std::error_code success() { return error(instrprof_error::success); } + +public: + /// Return true if the reader has finished reading the profile data. + bool isEOF() { return LastError == instrprof_error::eof; } + /// Return true if the reader encountered an error reading profiling data. + bool hasError() { return LastError && !isEOF(); } + /// Get the current error code. + std::error_code getError() { return LastError; } +}; + +/// Reader for the raw coverage mapping binary format from +/// CoverageMappingWriter. +class RawCoverageMappingReader : public CoverageMappingReader { + StringRef FunctionName; + StringRef MappingData; + std::vector &Filenames; + std::vector &Expressions; + std::vector &MappingRegions; + + RawCoverageMappingReader(const RawCoverageMappingReader &) + LLVM_DELETED_FUNCTION; + RawCoverageMappingReader & + operator=(const RawCoverageMappingReader &) LLVM_DELETED_FUNCTION; + +public: + RawCoverageMappingReader(StringRef FunctionName, StringRef MappingData, + std::vector &Filenames, + std::vector &Expressions, + std::vector &MappingRegions) + : FunctionName(FunctionName), MappingData(MappingData), + Filenames(Filenames), Expressions(Expressions), + MappingRegions(MappingRegions) {} + + std::error_code readHeader() override; + std::error_code readNextRecord(CoverageMappingRecord &Record) override; + +private: + std::error_code readULEB128(uint64_t &Result); + std::error_code readIntMax(uint64_t &Result, uint64_t MaxPlus1); + std::error_code readSize(uint64_t &Result); + std::error_code readString(StringRef &Result); + std::error_code readCounter(Counter &C); + std::error_code + readMappingRegionsSubArray(std::vector &MappingRegions, + unsigned &InferredFileID, size_t VFMsize); +}; + +/// Reader for the coverage mapping data that is emitted by the +/// frontend and stored in an object file. +class ObjectFileCoverageMappingReader : public CoverageMappingReader { +public: + struct ProfileMappingRecord { + StringRef FunctionName; + StringRef CoverageMapping; + + ProfileMappingRecord(StringRef FunctionName, StringRef CoverageMapping) + : FunctionName(FunctionName), CoverageMapping(CoverageMapping) {} + }; + +private: + std::unique_ptr Object; + std::vector MappingRecords; + size_t CurrentRecord; + std::vector Filenames; + std::vector Expressions; + std::vector MappingRegions; + + ObjectFileCoverageMappingReader(const ObjectFileCoverageMappingReader &) + LLVM_DELETED_FUNCTION; + ObjectFileCoverageMappingReader & + operator=(const ObjectFileCoverageMappingReader &) LLVM_DELETED_FUNCTION; + +public: + ObjectFileCoverageMappingReader(StringRef FileName); + ObjectFileCoverageMappingReader( + std::unique_ptr &ObjectBuffer, + sys::fs::file_magic Type = sys::fs::file_magic::unknown); + + std::error_code readHeader() override; + std::error_code readNextRecord(CoverageMappingRecord &Record) override; +}; + +} // end namespace llvm + +#endif // LLVM_PROFILEDATA_COVERAGEMAPPING_READER_H_ Index: include/llvm/ProfileData/CoverageMappingWriter.h =================================================================== --- /dev/null +++ include/llvm/ProfileData/CoverageMappingWriter.h @@ -0,0 +1,53 @@ +//=-- CoverageMappingWriter.h - Code coverage mapping writer ------*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing coverage mapping data for +// instrumentation based coverage. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_COVERAGEMAPPING_WRITER_H_ +#define LLVM_PROFILEDATA_COVERAGEMAPPING_WRITER_H_ + +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// Writer for instrumentation based coverage mapping data. +class CoverageMappingWriter { + ArrayRef Filenames; + ArrayRef VirtualFileMapping; + ArrayRef Expressions; + MutableArrayRef MappingRegions; + +public: + CoverageMappingWriter(ArrayRef Filenames, + ArrayRef VirtualFileMapping, + ArrayRef Expressions, + MutableArrayRef MappingRegions) + : Filenames(Filenames), VirtualFileMapping(VirtualFileMapping), + Expressions(Expressions), MappingRegions(MappingRegions) {} + + CoverageMappingWriter(ArrayRef Filenames, + ArrayRef Expressions, + MutableArrayRef MappingRegions) + : Filenames(Filenames), Expressions(Expressions), + MappingRegions(MappingRegions) {} + + /// Write encoded coverage mapping data to the given + /// output stream. + void write(raw_ostream &OS); +}; + +} // end namespace llvm + +#endif // LLVM_PROFILE_COVERAGEMAPPING_WRITER_H_ Index: lib/ProfileData/CMakeLists.txt =================================================================== --- lib/ProfileData/CMakeLists.txt +++ lib/ProfileData/CMakeLists.txt @@ -2,4 +2,8 @@ InstrProf.cpp InstrProfReader.cpp InstrProfWriter.cpp + CoverageMapping.cpp + CoverageMappingWriter.cpp + CoverageMappingReader.cpp + ProfileData.cpp ) Index: lib/ProfileData/CoverageMapping.cpp =================================================================== --- /dev/null +++ lib/ProfileData/CoverageMapping.cpp @@ -0,0 +1,175 @@ +//=-- CoverageMapping.cpp - Code coverage mapping support ---------*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for clang's and llvm's instrumentation based +// code coverage. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/CoverageMapping.h" + +using namespace llvm; + +// Encode the counter in the following way: +// Low 2 bits - tag/counter kind. +// Remaining bits - Counter/Expression ID. +unsigned Counter::Encode() const { + assert(ID <= (std::numeric_limits::max() >> EncodingTagBits)); + + return unsigned(Kind) | (ID << EncodingTagBits); +} + +bool Counter::Decode(unsigned Value) { + Kind = CounterKind(Value & EncodingTagMask); + ID = Value >> EncodingTagBits; + return false; +} + +CounterExpressionBuilder::CounterExpressionBuilder(unsigned NumCounters) { + Terms.resize(NumCounters); +} + +Counter CounterExpressionBuilder::get(Counter::CounterKind Kind, + const CounterExpression &E) { + for (unsigned I = 0, S = Expressions.size(); I < S; ++I) { + if (Expressions[I] == E) + return Counter::getExpression(Kind, I); + } + Expressions.push_back(E); + return Counter::getExpression(Kind, Expressions.size() - 1); +} + +void CounterExpressionBuilder::ExtractTerms(Counter C, int Sign) { + switch (C.getKind()) { + case Counter::Zero: + break; + case Counter::CounterValueReference: + Terms[C.getCounterID()] += Sign; + break; + case Counter::AdditionExpression: + ExtractTerms(Expressions[C.getExpressionID()].LHS, Sign); + ExtractTerms(Expressions[C.getExpressionID()].RHS, Sign); + break; + case Counter::SubtractionExpression: + ExtractTerms(Expressions[C.getExpressionID()].LHS, Sign); + ExtractTerms(Expressions[C.getExpressionID()].RHS, Sign == 1 ? -1 : 1); + break; + } +} + +Counter CounterExpressionBuilder::Simplify(Counter ExpressionTree) { + // Gather constant terms + for (auto &I : Terms) + I = 0; + ExtractTerms(ExpressionTree); + + Counter C; + // Create additions + // Note: the additions are created first + // to avoid creation of a tree like ((0 - X) + Y) + // instead of (Y - X). + for (unsigned I = 0, S = Terms.size(); I < S; ++I) { + if (Terms[I] <= 0) + continue; + for (int J = 0; J < Terms[I]; ++J) { + if (C.isZero()) + C = Counter::getCounter(I); + else + C = get(Counter::AdditionExpression, + CounterExpression(CounterExpression::Add, C, + Counter::getCounter(I))); + } + } + + // Create subtractions + for (unsigned I = 0, S = Terms.size(); I < S; ++I) { + if (Terms[I] >= 0) + continue; + for (int J = 0; J < (-Terms[I]); ++J) + C = get(Counter::SubtractionExpression, + CounterExpression(CounterExpression::Subtract, C, + Counter::getCounter(I))); + } + return C; +} + +Counter CounterExpressionBuilder::Add(Counter LHS, Counter RHS) { + return Simplify(get(Counter::AdditionExpression, + CounterExpression(CounterExpression::Add, LHS, RHS))); +} + +Counter CounterExpressionBuilder::Subtract(Counter LHS, Counter RHS) { + return Simplify( + get(Counter::SubtractionExpression, + CounterExpression(CounterExpression::Subtract, LHS, RHS))); +} + +void CounterMappingContext::dump(const Counter &C, + llvm::raw_ostream &OS) const { + switch (C.getKind()) { + default: + OS << '0'; + return; + case Counter::CounterValueReference: + OS << '#' << C.getCounterID(); + break; + case Counter::SubtractionExpression: + case Counter::AdditionExpression: { + if (C.getExpressionID() >= Expressions.size()) + return; + const auto &E = Expressions[C.getExpressionID()]; + OS << '('; + dump(E.LHS); + OS << (C.getKind() == Counter::SubtractionExpression ? " - " : " + "); + dump(E.RHS); + OS << ')'; + break; + } + } + if (CounterValues.empty()) + return; + std::error_code Error; + auto Value = evaluate(C, Error); + if (Error) + return; + OS << '[' << Value << ']'; +} + +int64_t CounterMappingContext::evaluate(const Counter &C, + std::error_code *EC) const { + switch (C.getKind()) { + case Counter::Zero: + return 0; + case Counter::CounterValueReference: + if (C.getCounterID() >= CounterValues.size()) { + if (EC) + *EC = std::make_error_code(std::errc::argument_out_of_domain); + break; + } + return CounterValues[C.getCounterID()]; + case Counter::SubtractionExpression: + case Counter::AdditionExpression: { + if (C.getExpressionID() >= Expressions.size()) { + if (EC) + *EC = std::make_error_code(std::errc::argument_out_of_domain); + break; + } + const auto &E = Expressions[C.getExpressionID()]; + auto LHS = evaluate(E.LHS, EC); + if (EC && *EC) + return 0; + auto RHS = evaluate(E.RHS, EC); + if (EC && *EC) + return 0; + return C.getKind() == Counter::SubtractionExpression ? LHS - RHS + : LHS + RHS; + } + } + return 0; +} Index: lib/ProfileData/CoverageMappingReader.cpp =================================================================== --- /dev/null +++ lib/ProfileData/CoverageMappingReader.cpp @@ -0,0 +1,394 @@ +//=-- CoverageMappingReader.cpp - Code coverage mapping reader ----*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for reading coverage mapping data for +// instrumentation based coverage. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/CoverageMappingReader.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; +using namespace llvm::object; + +void CoverageMappingIterator::Increment() { + if (Reader->readNextRecord(Record)) + *this = CoverageMappingIterator(); +} + +std::error_code RawCoverageMappingReader::readULEB128(uint64_t &Result) { + if (MappingData.size() < 1) + return error(instrprof_error::truncated); + unsigned N = 0; + Result = + decodeULEB128(reinterpret_cast(MappingData.data()), &N); + if (N > MappingData.size()) + return error(instrprof_error::malformed); + MappingData = MappingData.substr(N); + return success(); +} + +std::error_code RawCoverageMappingReader::readIntMax(uint64_t &Result, + uint64_t MaxPlus1) { + auto Err = readULEB128(Result); + if (Err) + return Err; + if (Result >= MaxPlus1) + return error(instrprof_error::malformed); + return success(); +} + +std::error_code RawCoverageMappingReader::readSize(uint64_t &Result) { + auto Err = readULEB128(Result); + if (Err) + return Err; + // Sanity check the number + if (Result > MappingData.size()) + return error(instrprof_error::malformed); + return success(); +} + +std::error_code RawCoverageMappingReader::readString(StringRef &Result) { + uint64_t Length; + auto Err = readSize(Length); + if (Err) + return Err; + Result = MappingData.substr(0, Length); + MappingData = MappingData.substr(Length); + return success(); +} + +std::error_code RawCoverageMappingReader::readCounter(Counter &C) { + uint64_t IntValue; + auto Err = readIntMax(IntValue, std::numeric_limits::max()); + if (Err) + return Err; + if (C.Decode(IntValue)) + return error(instrprof_error::malformed); + return success(); +} + +std::error_code RawCoverageMappingReader::readHeader() { return success(); } + +static unsigned const EncodingCounterTagAndExpansionRegionTagBits = + Counter::EncodingTagBits + 1; + +std::error_code RawCoverageMappingReader::readMappingRegionsSubArray( + std::vector &MappingRegions, unsigned &InferredFileID, + size_t VFMsize) { + uint64_t IntValue; + auto Err = readSize(IntValue); + if (Err) + return Err; + unsigned PrevLineStart = 0; + for (size_t I = 0, S = IntValue; I < S; ++I) { + Counter C; + CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion; + + // Read the combined counter + region kind + uint64_t IntValue; + auto Err = readIntMax(IntValue, std::numeric_limits::max()); + if (Err) + return Err; + unsigned Tag = IntValue & Counter::EncodingTagMask; + uint64_t ExpandedFileID = 0; + if (Tag != Counter::Zero) { + if (C.Decode(IntValue)) + return error(instrprof_error::malformed); + } else { + // Is it an expansion region? + if ((IntValue >> Counter::EncodingTagBits) & 1) { + Kind = CounterMappingRegion::ExpansionRegion; + ExpandedFileID = + IntValue >> EncodingCounterTagAndExpansionRegionTagBits; + } else { + switch (IntValue >> EncodingCounterTagAndExpansionRegionTagBits) { + case CounterMappingRegion::CodeRegion: + break; + case CounterMappingRegion::EmptyRegion: + Kind = CounterMappingRegion::EmptyRegion; + break; + case CounterMappingRegion::SkippedRegion: + Kind = CounterMappingRegion::SkippedRegion; + break; + default: + return error(instrprof_error::malformed); + } + } + } + + // Read the source range + uint64_t LocationInfo[4]; + for (unsigned J = 0; J < 4; ++J) { + Err = readIntMax(LocationInfo[J], std::numeric_limits::max()); + if (Err) + return Err; + } + PrevLineStart += LocationInfo[0]; + // Adjust the column locations for the empty regions that + // are supposed to cover whole lines. + if (LocationInfo[1] == 0 && LocationInfo[3] == 0) { + LocationInfo[1] = 1; + LocationInfo[3] = std::numeric_limits::max(); + } + MappingRegions.push_back(CounterMappingRegion( + C, InferredFileID, PrevLineStart, LocationInfo[1], + PrevLineStart + LocationInfo[2], LocationInfo[3], Kind)); + MappingRegions.back().ExpandedFileID = ExpandedFileID; + } + return success(); +} + +std::error_code +RawCoverageMappingReader::readNextRecord(CoverageMappingRecord &Record) { + uint64_t IntValue; + + // Read the filenames + llvm::SmallVector UniqueFilenames; + auto Err = readSize(IntValue); + if (Err) + return Err; + for (size_t I = 0, S = IntValue; I < S; ++I) { + StringRef Filename; + auto Err = readString(Filename); + if (Err) + return Err; + UniqueFilenames.push_back(Filename); + } + + // Read the virtual file mapping + llvm::SmallVector VirtualFileMapping; + Err = readSize(IntValue); + if (Err) + return Err; + if (IntValue == 0) { + // Construct the identity virtual file mapping + for (unsigned I = 0, S = UniqueFilenames.size(); I < S; ++I) + VirtualFileMapping.push_back(I); + } else { + for (size_t I = 0, S = IntValue; I < S; ++I) { + auto Err = readIntMax(IntValue, UniqueFilenames.size()); + if (Err) + return Err; + VirtualFileMapping.push_back(IntValue); + } + } + + // Construct the files using unique filenames + // and virtual file mapping + for (auto I : VirtualFileMapping) { + Filenames.push_back(UniqueFilenames[I]); + } + + // Read the expressions + Err = readSize(IntValue); + if (Err) + return Err; + Expressions.reserve(IntValue); + for (size_t I = 0, S = IntValue; I < S; ++I) { + Counter LHS, RHS; + Err = readCounter(LHS); + if (Err) + return Err; + Err = readCounter(RHS); + if (Err) + return Err; + Expressions.push_back( + CounterExpression(CounterExpression::Unknown, LHS, RHS)); + } + + // Read the mapping regions sub-arrays + for (unsigned InferredFileID = 0, S = VirtualFileMapping.size(); + InferredFileID < S; ++InferredFileID) { + Err = readMappingRegionsSubArray(MappingRegions, InferredFileID, + VirtualFileMapping.size()); + if (Err) + return Err; + } + + // Set the counters for the expansion regions + // i.e. Counter of expansion region = counter of the first region + // from the expanded file. + for (auto &I : MappingRegions) { + if (I.Kind == CounterMappingRegion::ExpansionRegion) { + for (const auto &J : MappingRegions) { + if (J.FileID == I.ExpandedFileID) { + I.Count = J.Count; + break; + } + } + } + } + + Record.FunctionName = FunctionName; + Record.Filenames = Filenames; + Record.Expressions = Expressions; + Record.MappingRegions = MappingRegions; + return success(); +} + +ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader( + StringRef FileName) + : CurrentRecord(0) { + auto File = llvm::object::ObjectFile::createObjectFile(FileName); + if (!File) + error(File.getError()); + else + Object.reset(File.get()); +} + +ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader( + std::unique_ptr &ObjectBuffer, sys::fs::file_magic Type) + : CurrentRecord(0) { + auto File = llvm::object::ObjectFile::createObjectFile(ObjectBuffer, Type); + if (!File) + error(File.getError()); + else + Object.reset(File.get()); +} + +/// A structure which represents +/// the CoverageMappingRecord +/// which points to both the +/// mapping data and the function's name +template struct CoverageMappingRecord { + IntPtrT CoverageMappingPtr; + IntPtrT FunctionNamePtr; + uint32_t CoverageMappingSize; + uint32_t FunctionNameSize; +}; + +/// A helper structure to access the data +/// from the sections in an object file. +struct SectionData { + StringRef Data; + uint64_t Address; + + std::error_code load(SectionRef &Section) { + auto Err = Section.getContents(Data); + if (Err) + return Err; + return Section.getAddress(Address); + } + + std::error_code get(uint64_t Pointer, size_t Size, StringRef &Result) { + if (Pointer < Address) + return instrprof_error::malformed; + auto Offset = Pointer - Address; + if (Offset + Size > Data.size()) + return instrprof_error::malformed; + Result = Data.substr(Pointer - Address, Size); + return instrprof_error::success; + } +}; + +template +std::error_code readCoverageMappingData( + SectionRef &ProfileNames, SectionRef &CoverageMapping, + SectionRef &CoverageData, + std::vector & + Records) { + // Get the contents of the given sections + StringRef Data; + auto Err = CoverageData.getContents(Data); + if (Err) + return Err; + SectionData CoverageMappingData, ProfileNamesData; + Err = CoverageMappingData.load(CoverageMapping); + if (Err) + return Err; + Err = ProfileNamesData.load(ProfileNames); + if (Err) + return Err; + + // Read the records in the coverage data section + while (!Data.empty()) { + if (Data.size() < sizeof(CoverageMappingRecord)) + return instrprof_error::malformed; + auto MappingRecord = + reinterpret_cast *>(Data.data()); + StringRef Mapping; + Err = CoverageMappingData.get(MappingRecord->CoverageMappingPtr, + MappingRecord->CoverageMappingSize, Mapping); + if (Err) + return Err; + StringRef FunctionName; + Err = ProfileNamesData.get(MappingRecord->FunctionNamePtr, + MappingRecord->FunctionNameSize, FunctionName); + if (Err) + return Err; + Records.push_back(ObjectFileCoverageMappingReader::ProfileMappingRecord( + FunctionName, Mapping)); + Data = Data.substr(sizeof(CoverageMappingRecord)); + } + + return instrprof_error::success; +} + +std::error_code ObjectFileCoverageMappingReader::readHeader() { + if (!Object) + return getError(); + auto BytesInAddress = Object->getBytesInAddress(); + if (BytesInAddress != 4 && BytesInAddress != 8) + return error(instrprof_error::malformed); + + // Look for the sections that we are interested in + int FoundSectionCount = 0; + SectionRef ProfileNames, CoverageMapping, CoverageData; + for (const auto &Section : Object->sections()) { + StringRef Name; + auto Err = Section.getName(Name); + if (Err) + return Err; + if (Name == "__llvm_prf_names") { + ProfileNames = Section; + } else if (Name == "__llvm_prf_cvmap") { + CoverageMapping = Section; + } else if (Name == "__llvm_prf_cvdat") { + CoverageData = Section; + } else + continue; + ++FoundSectionCount; + } + if (FoundSectionCount != 3) + return error(instrprof_error::malformed); + + // Load the data from the found sections + std::error_code Err; + if (BytesInAddress == 4) + Err = readCoverageMappingData(ProfileNames, CoverageMapping, + CoverageData, MappingRecords); + else + Err = readCoverageMappingData(ProfileNames, CoverageMapping, + CoverageData, MappingRecords); + if (Err) + return error(Err); + + return success(); +} + +std::error_code +ObjectFileCoverageMappingReader::readNextRecord(CoverageMappingRecord &Record) { + if (CurrentRecord >= MappingRecords.size()) + return error(instrprof_error::eof); + + Filenames.clear(); + Expressions.clear(); + MappingRegions.clear(); + auto Err = RawCoverageMappingReader( + MappingRecords[CurrentRecord].FunctionName, + MappingRecords[CurrentRecord].CoverageMapping, Filenames, + Expressions, MappingRegions).readNextRecord(Record); + if (Err) + return Err; + ++CurrentRecord; + return success(); +} Index: lib/ProfileData/CoverageMappingWriter.cpp =================================================================== --- /dev/null +++ lib/ProfileData/CoverageMappingWriter.cpp @@ -0,0 +1,193 @@ +//=-- CoverageMappingWriter.cpp - Code coverage mapping writer -------------=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing coverage mapping data for +// instrumentation based coverage. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/CoverageMappingWriter.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; + +/// Determine if the virtual file mapping has a one to one +/// consecutive mapping with the filenames +static bool +isIdentityVirtualFileMapping(ArrayRef Filenames, + ArrayRef VirtualFileMapping) { + if (VirtualFileMapping.empty()) + return true; + if (Filenames.size() != VirtualFileMapping.size()) + return false; + unsigned I = 0; + for (auto J : VirtualFileMapping) { + if (J != I) + return false; + ++I; + } + return true; +} + +namespace { +/// Gather only the expressions that are used by the mapping +/// regions in this function. +class CounterExpressionsMinimizer { + ArrayRef Expressions; + llvm::SmallVector UsedExpressions; + std::vector AdjustedExpressionIDs; + +public: + void mark(Counter C) { + if (!C.isExpression()) + return; + unsigned ID = C.getExpressionID(); + AdjustedExpressionIDs[ID] = 1; + mark(Expressions[ID].LHS); + mark(Expressions[ID].RHS); + } + + void gatherUsed(Counter C) { + if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()]) + return; + AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size(); + const auto &E = Expressions[C.getExpressionID()]; + UsedExpressions.push_back(E); + gatherUsed(E.LHS); + gatherUsed(E.RHS); + } + + CounterExpressionsMinimizer(ArrayRef Expressions, + ArrayRef MappingRegions) + : Expressions(Expressions) { + AdjustedExpressionIDs.resize(Expressions.size(), 0); + for (const auto &I : MappingRegions) + mark(I.Count); + for (const auto &I : MappingRegions) + gatherUsed(I.Count); + } + + ArrayRef getExpressions() const { return UsedExpressions; } + + /// Adjust the given counter to + /// correctly transition from the old + /// expression ids to the new expression ids. + Counter adjust(Counter C) const { + if (C.isExpression()) + C = Counter::getExpression(C.getKind(), + AdjustedExpressionIDs[C.getExpressionID()]); + return C; + } +}; +} + +static void writeCounter(Counter C, raw_ostream &OS) { + encodeULEB128(C.Encode(), OS); +} + +static unsigned const EncodingCounterTagAndExpansionRegionTagBits = + Counter::EncodingTagBits + 1; + +/// Return the number of regions that have the given FileID +static unsigned countFileIDs(ArrayRef Regions, + unsigned FileID) { + unsigned Result = 0; + for (const auto &I : Regions) { + if (I.FileID == FileID) + ++Result; + if (I.FileID > FileID) + break; + } + return Result; +} + +void CoverageMappingWriter::write(raw_ostream &OS) { + // Sort the regions in an ascending order by the file id and the starting + // location + std::sort(MappingRegions.begin(), MappingRegions.end()); + + // Write out the file names + encodeULEB128(Filenames.size(), OS); + for (const auto &Filename : Filenames) { + encodeULEB128(Filename.size(), OS); + OS << Filename; + } + + // Write out the virtual file mapping (if necessary) + if (!isIdentityVirtualFileMapping(Filenames, VirtualFileMapping)) { + encodeULEB128(VirtualFileMapping.size(), OS); + for (const auto &FileID : VirtualFileMapping) + encodeULEB128(FileID, OS); + } else + encodeULEB128(0, OS); + + // Write out the expressions. + CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions); + encodeULEB128(Minimizer.getExpressions().size(), OS); + for (const auto &E : Minimizer.getExpressions()) { + writeCounter(Minimizer.adjust(E.LHS), OS); + writeCounter(Minimizer.adjust(E.RHS), OS); + } + + // Write out the mapping regions + // Split the regions into subarrays where each region in a + // subarray has a fileID which is the index of that subarray. + unsigned PrevLineStart = 0; + unsigned CurrentFileID = MappingRegions.front().FileID; + assert(CurrentFileID == 0); + encodeULEB128(countFileIDs(MappingRegions, CurrentFileID), OS); + for (const auto &I : MappingRegions) { + if (I.FileID != CurrentFileID) { + // Ensure that all file ids have at least one mapping region + assert(I.FileID == (CurrentFileID + 1)); + // Start a new region sub-array + CurrentFileID = I.FileID; + encodeULEB128(countFileIDs(MappingRegions, CurrentFileID), OS); + PrevLineStart = 0; + } + Counter Count = Minimizer.adjust(I.Count); + switch (I.Kind) { + case CounterMappingRegion::CodeRegion: + writeCounter(Count, OS); + break; + case CounterMappingRegion::ExpansionRegion: { + assert(Count.isZero()); + assert(I.ExpandedFileID <= (std::numeric_limits::max() >> + EncodingCounterTagAndExpansionRegionTagBits)); + // Mark an expansion region with a set bit that follows the counter tag, + // and pack the expanded file id into the remaining bits. + unsigned EncodedTagExpandedFileID = + (1 << Counter::EncodingTagBits) | + (I.ExpandedFileID << EncodingCounterTagAndExpansionRegionTagBits); + encodeULEB128(EncodedTagExpandedFileID, OS); + break; + } + case CounterMappingRegion::EmptyRegion: + assert(Count.isZero()); + encodeULEB128( + unsigned(I.Kind) << EncodingCounterTagAndExpansionRegionTagBits, OS); + break; + case CounterMappingRegion::SkippedRegion: + assert(Count.isZero()); + assert(Count.Encode() == Counter::Zero); + encodeULEB128( + unsigned(I.Kind) << EncodingCounterTagAndExpansionRegionTagBits, OS); + break; + } + assert(I.LineStart >= PrevLineStart); + encodeULEB128(I.LineStart - PrevLineStart, OS); + encodeULEB128(I.ColumnStart, OS); + assert(I.LineEnd >= I.LineStart); + encodeULEB128(I.LineEnd - I.LineStart, OS); + encodeULEB128(I.ColumnEnd, OS); + PrevLineStart = I.LineStart; + } + // Ensure that all file ids have at least one mapping region + assert(CurrentFileID == (VirtualFileMapping.size() - 1)); +} Index: lib/ProfileData/LLVMBuild.txt =================================================================== --- lib/ProfileData/LLVMBuild.txt +++ lib/ProfileData/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Library name = ProfileData parent = Libraries -required_libraries = Support +required_libraries = Support Object Index: lib/ProfileData/ProfileData.cpp =================================================================== --- /dev/null +++ lib/ProfileData/ProfileData.cpp @@ -0,0 +1,163 @@ +//===- ProfileData.cpp - C bindings to the profile data library-*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the C bindings to the profile data library. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/ProfileData.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/CoverageMappingReader.h" + +using namespace llvm; + +inline IndexedInstrProfReader *unwrap(LLVMIndexedInstrProfReaderRef Reader) { + return reinterpret_cast(Reader); +} + +inline LLVMIndexedInstrProfReaderRef wrap(const IndexedInstrProfReader *Reader) { + return reinterpret_cast(const_cast(Reader)); +} + +inline ObjectFileCoverageMappingReader *unwrap(LLVMObjectFileCoverageMappingReaderRef Reader) { + return reinterpret_cast(Reader); +} + +inline LLVMObjectFileCoverageMappingReaderRef wrap(const ObjectFileCoverageMappingReader *Reader) { + return reinterpret_cast(const_cast(Reader)); +} + +LLVMIndexedInstrProfReaderRef LLVMCreateIndexedInstrProfReader(const char *Path) { + std::unique_ptr PGOReader; + if (IndexedInstrProfReader::create(Path, PGOReader)) + return nullptr; + return wrap(PGOReader.release()); +} + +void LLVMDisposeIndexedInstrProfReader(LLVMIndexedInstrProfReaderRef Reader) { + delete unwrap(Reader); +} + +void LLVMGetFunctionCounts(LLVMIndexedInstrProfReaderRef Reader, const char *FuncName, + uint64_t **Values, size_t *ValuesSize) { + uint64_t FuncHash; + std::vector Counts; + auto EC = unwrap(Reader)->getFunctionCounts(FuncName, FuncHash, Counts); + if (EC) { + *Values = nullptr; + *ValuesSize = 0; + return; + } + *Values = new uint64_t[Counts.size()]; + *ValuesSize = Counts.size(); + memcpy(*Values, Counts.data(), Counts.size()*sizeof(uint64_t)); +} + +void LLVMDisposeFunctionCounts(uint64_t *Values) { + if (Values) + delete[] Values; +} + +LLVMObjectFileCoverageMappingReaderRef LLVMCreateObjectFileCoverageMappingReader(LLVMMemoryBufferRef MemBuf) { + std::unique_ptr Buffer(unwrap(MemBuf)); + auto Reader = new ObjectFileCoverageMappingReader(Buffer); + if (Reader->readHeader()) { + return nullptr; + } + return wrap(Reader); +} + +void LLVMDisposeObjectFileCoverageMappingReader(LLVMObjectFileCoverageMappingReaderRef Reader) { + if (!Reader) + return; + delete unwrap(Reader); +} + +const char *CopyString(StringRef Str) { + char *S = new char[Str.size() + 1]; + memcpy(S, Str.data(), Str.size()); + S[Str.size()] = '\0'; + return S; +} + +void CopyStringArray(const char **&Dest, size_t &SizeDest, ArrayRef Src) { + Dest = new const char *[Src.size()]; + SizeDest = Src.size(); + for (size_t I = 0, S = Src.size(); I < S; ++I) + Dest[I] = CopyString(Src[I]); +} + +LLVMBool LLVMLoadFunctionCoverageMappingData(LLVMObjectFileCoverageMappingReaderRef CovReader, + LLVMIndexedInstrProfReaderRef ProfReader, + LLVMFunctionCoverageRecord **FunctionCoverage, + size_t *FunctionCoverageSize) { + std::vector Results; + std::vector Counts; + auto PGOReader = unwrap(ProfReader); + bool RegionError = false; + + for (const auto &I : *unwrap(CovReader)) { + LLVMFunctionCoverageRecord FunctionEntry; + FunctionEntry.Name = CopyString(I.FunctionName); + CopyStringArray(FunctionEntry.Filenames, FunctionEntry.FilenamesSize, I.Filenames); + + // Get the function counts + Counts.clear(); + if (PGOReader) { + uint64_t FuncHash; + PGOReader->getFunctionCounts(I.FunctionName, FuncHash, Counts); + } + + // Convert the mapping regions into the LLVMCoverageMappingRegion structs + FunctionEntry.MappingRegions = new LLVMCoverageMappingRegion[I.MappingRegions.size()]; + FunctionEntry.MappingRegionsSize = I.MappingRegions.size(); + CounterMappingContext Ctx(I.Expressions, Counts); + for (size_t J = 0; J < FunctionEntry.MappingRegionsSize; ++J) { + const auto &R = I.MappingRegions[J]; + auto &Dest = FunctionEntry.MappingRegions[J]; + std::error_code Error; + Dest.ExecutionCount = PGOReader? Ctx.evaluate(R.Count, Error) : 0; + if (Error) { + Dest.ExecutionCount = std::numeric_limits::max(); + RegionError = true; + } + Dest.FileID = R.FileID; + Dest.ExpandedFileID = R.ExpandedFileID; + Dest.LineStart = R.LineStart; + Dest.ColumnStart = R.ColumnStart; + Dest.LineEnd = R.LineEnd; + Dest.ColumnEnd = R.ColumnEnd; + Dest.Kind = LLVMCoverageMappingRegionKind(R.Kind); + } + + Results.push_back(FunctionEntry); + } + *FunctionCoverage = new LLVMFunctionCoverageRecord[Results.size()]; + *FunctionCoverageSize = Results.size(); + memcpy(*FunctionCoverage, Results.data(), Results.size()*sizeof(LLVMFunctionCoverageRecord)); + return RegionError; +} + +void LLVMDisposeFunctionCoverageMappingData(LLVMFunctionCoverageRecord *FunctionCoverage, + size_t FunctionCoverageSize) { + if (!FunctionCoverage) + return; + for (size_t I = 0; I < FunctionCoverageSize; ++I) { + delete[] FunctionCoverage[I].Name; + for (size_t J = 0; J < FunctionCoverage[I].FilenamesSize; ++J) + delete[] FunctionCoverage[I].Filenames[J]; + delete[] FunctionCoverage[I].Filenames; + delete[] FunctionCoverage[I].MappingRegions; + } + delete[] FunctionCoverage; +} + +LLVMBool LLVMHasCoverageMappingRegionExecutionCountError(LLVMCoverageMappingRegion *Region) { + return Region->ExecutionCount == std::numeric_limits::max(); +}