1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +01:00

Revert "[Coverage] Revise format to reduce binary size"

This reverts commit 99317124e1c772e9a9de41a0cd56e1db049b4ea4. This is
still busted on Windows:

http://lab.llvm.org:8011/builders/lld-x86_64-win7/builds/40873

The llvm-cov tests report 'error: Could not load coverage information'.
This commit is contained in:
Vedant Kumar 2020-02-28 18:03:15 -08:00
parent ddbbf4cb94
commit 52738a45b0
16 changed files with 240 additions and 622 deletions

View File

@ -15,23 +15,46 @@ LLVM's code coverage mapping format is used to provide code coverage
analysis using LLVM's and Clang's instrumentation based profiling
(Clang's ``-fprofile-instr-generate`` option).
This document is aimed at those who would like to know how LLVM's code coverage
mapping works under the hood. A prior knowledge of how Clang's profile guided
optimization works is useful, but not required. For those interested in using
LLVM to provide code coverage analysis for their own programs, see the `Clang
documentation <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html>`.
This document is aimed at those who use LLVM's code coverage mapping to provide
code coverage analysis for their own programs, and for those who would like
to know how it works under the hood. A prior knowledge of how Clang's profile
guided optimization works is useful, but not required.
We start by briefly describing LLVM's code coverage mapping format and the
We start by showing how to use LLVM and Clang for code coverage analysis,
then we briefly describe LLVM's code coverage mapping format and the
way that Clang and LLVM's code coverage tool work with this format. After
the basics are down, more advanced features of the coverage mapping format
are discussed - such as the data structures, LLVM IR representation and
the binary encoding.
Quick Start
===========
Here's a short story that describes how to generate code coverage overview
for a sample source file called *test.c*.
* First, compile an instrumented version of your program using Clang's
``-fprofile-instr-generate`` option with the additional ``-fcoverage-mapping``
option:
``clang -o test -fprofile-instr-generate -fcoverage-mapping test.c``
* Then, run the instrumented binary. The runtime will produce a file called
*default.profraw* containing the raw profile instrumentation data:
``./test``
* After that, merge the profile data using the *llvm-profdata* tool:
``llvm-profdata merge -o test.profdata default.profraw``
* Finally, run LLVM's code coverage tool (*llvm-cov*) to produce the code
coverage overview for the sample source file:
``llvm-cov show ./test -instr-profile=test.profdata test.c``
High Level Overview
===================
LLVM's code coverage mapping format is designed to be a self contained
data format that can be embedded into the LLVM IR and into object files.
data format, that can be embedded into the LLVM IR and object files.
It's described in this document as a **mapping** format because its goal is
to store the data that is required for a code coverage tool to map between
the specific source ranges in a file and the execution counts obtained
@ -201,9 +224,9 @@ executed, as it doesn't possess the frontend's knowledge.
LLVM IR Representation
======================
The coverage mapping data is stored in the LLVM IR using a global constant
structure variable called *__llvm_coverage_mapping* with the *IPSK_covmap*
section specifier (i.e. ".lcovmap$M" on Windows and "__llvm_covmap" elsewhere).
The coverage mapping data is stored in the LLVM IR using a single global
constant structure variable called *__llvm_coverage_mapping*
with the *__llvm_covmap* section specifier.
For example, lets consider a C file and how it gets compiled to LLVM:
@ -218,45 +241,42 @@ For example, lets consider a C file and how it gets compiled to LLVM:
return 13;
}
The coverage mapping variable generated by Clang has 2 fields:
The coverage mapping variable generated by Clang has 3 fields:
* Coverage mapping header.
* An optionally compressed list of filenames present in the translation unit.
* An array of function records.
The variable has 8-byte alignment because ld64 cannot always pack symbols from
different object files tightly (the word-level alignment assumption is baked in
too deeply).
* Coverage mapping data which is an array of bytes. Zero paddings are added at the end to force 8 byte alignment.
.. code-block:: llvm
@__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [32 x i8] }
{
@__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [2 x { i64, i32, i64 }], [40 x i8] }
{
{ i32, i32, i32, i32 } ; Coverage map header
{
i32 0, ; Always 0. In prior versions, the number of affixed function records
i32 32, ; The length of the string that contains the encoded translation unit filenames
i32 0, ; Always 0. In prior versions, the length of the affixed string that contains the encoded coverage mapping data
i32 3, ; Coverage mapping format version
i32 2, ; The number of function records
i32 20, ; The length of the string that contains the encoded translation unit filenames
i32 20, ; The length of the string that contains the encoded coverage mapping data
i32 2, ; Coverage mapping format version
},
[32 x i8] c"..." ; Encoded data (dissected later)
[2 x { i64, i32, i64 }] [ ; Function records
{ i64, i32, i64 } {
i64 0x5cf8c24cdb18bdac, ; Function's name MD5
i32 9, ; Function's encoded coverage mapping data string length
i64 0 ; Function's structural hash
},
{ i64, i32, i64 } {
i64 0xe413754a191db537, ; Function's name MD5
i32 9, ; Function's encoded coverage mapping data string length
i64 0 ; Function's structural hash
}],
[40 x i8] c"..." ; Encoded data (dissected later)
}, section "__llvm_covmap", align 8
The current version of the format is version 4. There are two differences from version 3:
The current version of the format is version 3. The only difference from version 2 is that a special encoding for column end locations was introduced to indicate gap regions.
* Function records are now named symbols, and are marked *linkonce_odr*. This
allows linkers to merge duplicate function records. Merging of duplicate
*dummy* records (emitted for functions included-but-not-used in a translation
unit) reduces size bloat in the coverage mapping data. As part of this
change, region mapping information for a function is now included within the
function record, instead of being affixed to the coverage header.
* The filename list for a translation unit may optionally be zlib-compressed.
The only difference between versions 3 and 2 is that a special encoding for
column end locations was introduced to indicate gap regions.
In version 1, the function record for *foo* was defined as follows:
The function record layout has evolved since version 1. In version 1, the function record for *foo* is defined as follows:
.. code-block:: llvm
@ -266,27 +286,19 @@ In version 1, the function record for *foo* was defined as follows:
i64 0 ; Function's structural hash
}
In version 2, the function record for *foo* was defined as follows:
.. code-block:: llvm
{ i64, i32, i64 } {
i64 0x5cf8c24cdb18bdac, ; Function's name MD5
i32 9, ; Function's encoded coverage mapping data string length
i64 0 ; Function's structural hash
Coverage Mapping Header:
------------------------
The coverage mapping header has the following fields:
* The number of function records affixed to the coverage header. Always 0, but present for backwards compatibility.
* The number of function records.
* The length of the string in the third field of *__llvm_coverage_mapping* that contains the encoded translation unit filenames.
* The length of the string in the third field of *__llvm_coverage_mapping* that contains any encoded coverage mapping data affixed to the coverage header. Always 0, but present for backwards compatibility.
* The length of the string in the third field of *__llvm_coverage_mapping* that contains the encoded coverage mapping data.
* The format version. The current version is 4 (encoded as a 3).
* The format version. The current version is 3 (encoded as a 2).
.. _function records:
@ -297,11 +309,24 @@ A function record is a structure of the following type:
.. code-block:: llvm
{ i64, i32, i64, i64, [? x i8] }
{ i64, i32, i64 }
It contains the function name's MD5, the length of the encoded mapping data for
that function, the function's structural hash value, the hash of the filenames
in the function's translation unit, and the encoded mapping data.
It contains function name's MD5, the length of the encoded mapping data for that function, and function's
structural hash value.
Encoded data:
-------------
The encoded data is stored in a single string that contains
the encoded filenames used by this translation unit and the encoded coverage
mapping data for each function in this translation unit.
The encoded data has the following structure:
``[filenames, coverageMappingDataForFunctionRecord0, coverageMappingDataForFunctionRecord1, ..., padding]``
If necessary, the encoded data is padded with zeroes so that the size
of the data string is rounded up to the nearest multiple of 8 bytes.
Dissecting the sample:
^^^^^^^^^^^^^^^^^^^^^^
@ -314,18 +339,32 @@ IR for the `coverage mapping sample`_ that was shown earlier:
.. code-block:: llvm
c"\01\15\1Dx\DA\13\D1\0F-N-*\D6/+\CE\D6/\C9-\D0O\CB\CF\D7K\06\00N+\07]"
c"\01\12/Users/alex/test.c\01\00\00\01\01\01\0C\02\02\01\00\00\01\01\04\0C\02\02\00\00"
* The string contains values that are encoded in the LEB128 format, which is
used throughout for storing integers. It also contains a compressed payload.
used throughout for storing integers. It also contains a string value.
* The first three LEB128-encoded numbers in the sample specify the number of
filenames, the length of the uncompressed filenames, and the length of the
compressed payload (or 0 if compression is disabled). In this sample, there
is 1 filename that is 21 bytes in length (uncompressed), and stored in 29
bytes (compressed).
* The length of the substring that contains the encoded translation unit
filenames is the value of the second field in the *__llvm_coverage_mapping*
structure, which is 20, thus the filenames are encoded in this string:
* The coverage mapping from the first function record is encoded in this string:
.. code-block:: llvm
c"\01\12/Users/alex/test.c"
This string contains the following data:
* Its first byte has a value of ``0x01``. It stores the number of filenames
contained in this string.
* Its second byte stores the length of the first filename in this string.
* The remaining 18 bytes are used to store the first filename.
* The length of the substring that contains the encoded coverage mapping data
for the first function is the value of the third field in the first
structure in an array of `function records`_ stored in the
third field of the *__llvm_coverage_mapping* structure, which is the 9.
Therefore, the coverage mapping for the first function record is encoded
in this string:
.. code-block:: llvm

View File

@ -24,7 +24,6 @@
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
@ -55,8 +54,7 @@ enum class coveragemap_error {
no_data_found,
unsupported_version,
truncated,
malformed,
decompression_failed
malformed
};
const std::error_category &coveragemap_category();
@ -680,119 +678,37 @@ getLineCoverageStats(const coverage::CoverageData &CD) {
return make_range(Begin, End);
}
// Coverage mappping data (V2) has the following layout:
// IPSK_covmap:
// [CoverageMapFileHeader]
// [ArrayStart]
// [CovMapFunctionRecordV2]
// [CovMapFunctionRecordV2]
// ...
// [ArrayEnd]
// [Encoded Filenames and Region Mapping Data]
//
// Coverage mappping data (V3) has the following layout:
// IPSK_covmap:
// [CoverageMapFileHeader]
// [Encoded Filenames]
// IPSK_covfun:
// [ArrayStart]
// odr_name_1: [CovMapFunctionRecordV3]
// odr_name_2: [CovMapFunctionRecordV3]
// ...
// [ArrayEnd]
//
// Both versions of the coverage mapping format encode the same information,
// but the V3 format does so more compactly by taking advantage of linkonce_odr
// semantics (it allows exactly 1 function record per name reference).
/// This namespace defines accessors (*not* data) shared by different versions
/// of coverage mapping records. The functionality is CRTP'd in.
namespace accessors {
/// Accessors for a 64-bit function hash and a 64-bit coverage mapping data
/// size.
template <class FuncRecordTy> struct FuncHashAndDataSize {
using ThisTy = const FuncRecordTy *;
// Return the structural hash associated with the function.
template <support::endianness Endian> uint64_t getFuncHash() const {
return support::endian::byte_swap<uint64_t, Endian>(
static_cast<ThisTy>(this)->FuncHash);
}
// Return the coverage map data size for the function.
template <support::endianness Endian> uint32_t getDataSize() const {
return support::endian::byte_swap<uint32_t, Endian>(
static_cast<ThisTy>(this)->DataSize);
}
};
/// Accessors for a hashed function name.
template <class FuncRecordTy> struct HashedNameRef {
using ThisTy = const FuncRecordTy *;
// Return the function lookup key. The value is considered opaque.
template <support::endianness Endian> uint64_t getFuncNameRef() const {
return support::endian::byte_swap<uint64_t, Endian>(
static_cast<ThisTy>(this)->NameRef);
}
// Return the PGO name of the function.
template <support::endianness Endian>
Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
uint64_t NameRef = getFuncNameRef<Endian>();
FuncName = ProfileNames.getFuncName(NameRef);
return Error::success();
}
};
/// Accessors for an out-of-line coverage mapping: this is affixed to the
/// coverage map file header, instead of to the function record.
template <class FuncRecordTy> struct OutOfLineCovMapping {
using ThisTy = const FuncRecordTy *;
// Return an invalid filename set reference. When an out-of-line coverage
// mapping string is expected, the function record doen't contain a filenames
// ref.
template <support::endianness Endian> uint64_t getFilenamesRef() const {
llvm_unreachable("Record does not contain a reference to filenames");
}
// Read coverage mapping out-of-line, from \p MappingBuf.
template <support::endianness Endian>
StringRef getCoverageMapping(const char *MappingBuf) const {
return {MappingBuf,
static_cast<ThisTy>(this)->template getDataSize<Endian>()};
}
// Advance to the next out-of-line coverage mapping and its associated
// function record.
template <support::endianness Endian>
std::pair<const char *, const FuncRecordTy *>
advanceByOne(const char *MappingBuf) const {
auto *CFR = static_cast<ThisTy>(this);
return {MappingBuf + CFR->template getDataSize<Endian>(), CFR + 1};
}
};
} // end namespace accessors
// Profile coverage map has the following layout:
// [CoverageMapFileHeader]
// [ArrayStart]
// [CovMapFunctionRecord]
// [CovMapFunctionRecord]
// ...
// [ArrayEnd]
// [Encoded Region Mapping Data]
LLVM_PACKED_START
template <class IntPtrT>
struct CovMapFunctionRecordV1
: accessors::FuncHashAndDataSize<CovMapFunctionRecordV1<IntPtrT>>,
accessors::OutOfLineCovMapping<CovMapFunctionRecordV1<IntPtrT>> {
template <class IntPtrT> struct CovMapFunctionRecordV1 {
#define COVMAP_V1
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
#include "llvm/ProfileData/InstrProfData.inc"
#undef COVMAP_V1
CovMapFunctionRecordV1() = delete;
// Return the structural hash associated with the function.
template <support::endianness Endian> uint64_t getFuncHash() const {
return support::endian::byte_swap<uint64_t, Endian>(FuncHash);
}
// Return the coverage map data size for the funciton.
template <support::endianness Endian> uint32_t getDataSize() const {
return support::endian::byte_swap<uint32_t, Endian>(DataSize);
}
// Return function lookup key. The value is consider opaque.
template <support::endianness Endian> IntPtrT getFuncNameRef() const {
return support::endian::byte_swap<IntPtrT, Endian>(NamePtr);
}
// Return the PGO name of the function.
// Return the PGO name of the function */
template <support::endianness Endian>
Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
IntPtrT NameRef = getFuncNameRef<Endian>();
@ -804,50 +720,31 @@ struct CovMapFunctionRecordV1
}
};
struct CovMapFunctionRecordV2
: accessors::FuncHashAndDataSize<CovMapFunctionRecordV2>,
accessors::HashedNameRef<CovMapFunctionRecordV2>,
accessors::OutOfLineCovMapping<CovMapFunctionRecordV2> {
#define COVMAP_V2
struct CovMapFunctionRecord {
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
#include "llvm/ProfileData/InstrProfData.inc"
#undef COVMAP_V2
CovMapFunctionRecordV2() = delete;
};
struct CovMapFunctionRecordV3
: accessors::FuncHashAndDataSize<CovMapFunctionRecordV3>,
accessors::HashedNameRef<CovMapFunctionRecordV3> {
#define COVMAP_V3
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
#include "llvm/ProfileData/InstrProfData.inc"
#undef COVMAP_V3
CovMapFunctionRecordV3() = delete;
// Get the filename set reference.
template <support::endianness Endian> uint64_t getFilenamesRef() const {
return support::endian::byte_swap<uint64_t, Endian>(FilenamesRef);
// Return the structural hash associated with the function.
template <support::endianness Endian> uint64_t getFuncHash() const {
return support::endian::byte_swap<uint64_t, Endian>(FuncHash);
}
// Read the inline coverage mapping. Ignore the out-of-line coverage mapping
// buffer.
template <support::endianness Endian>
StringRef getCoverageMapping(const char *) const {
return StringRef(&CoverageMapping, getDataSize<Endian>());
// Return the coverage map data size for the funciton.
template <support::endianness Endian> uint32_t getDataSize() const {
return support::endian::byte_swap<uint32_t, Endian>(DataSize);
}
// Advance to the next inline coverage mapping and its associated function
// record. Ignore the out-of-line coverage mapping buffer.
// Return function lookup key. The value is consider opaque.
template <support::endianness Endian> uint64_t getFuncNameRef() const {
return support::endian::byte_swap<uint64_t, Endian>(NameRef);
}
// Return the PGO name of the function */
template <support::endianness Endian>
std::pair<const char *, const CovMapFunctionRecordV3 *>
advanceByOne(const char *) const {
assert(isAddrAligned(Align(8), this) && "Function record not aligned");
const char *Next = ((const char *)this) + sizeof(CovMapFunctionRecordV3) -
sizeof(char) + getDataSize<Endian>();
// Each function record has an alignment of 8, so we need to adjust
// alignment before reading the next record.
Next += offsetToAlignedAddr(Next, Align(8));
return {nullptr, reinterpret_cast<const CovMapFunctionRecordV3 *>(Next)};
Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
uint64_t NameRef = getFuncNameRef<Endian>();
FuncName = ProfileNames.getFuncName(NameRef);
return Error::success();
}
};
@ -884,24 +781,12 @@ enum CovMapVersion {
// A new interpretation of the columnEnd field is added in order to mark
// regions as gap areas.
Version3 = 2,
// Function records are named, uniqued, and moved to a dedicated section.
Version4 = 3,
// The current version is Version4.
// The current version is Version3
CurrentVersion = INSTR_PROF_COVMAP_VERSION
};
template <int CovMapVersion, class IntPtrT> struct CovMapTraits {
using CovMapFuncRecordType = CovMapFunctionRecordV3;
using NameRefType = uint64_t;
};
template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version3, IntPtrT> {
using CovMapFuncRecordType = CovMapFunctionRecordV2;
using NameRefType = uint64_t;
};
template <class IntPtrT> struct CovMapTraits<CovMapVersion::Version2, IntPtrT> {
using CovMapFuncRecordType = CovMapFunctionRecordV2;
using CovMapFuncRecordType = CovMapFunctionRecord;
using NameRefType = uint64_t;
};

View File

@ -113,6 +113,20 @@ protected:
Error readString(StringRef &Result);
};
/// Reader for the raw coverage filenames.
class RawCoverageFilenamesReader : public RawCoverageReader {
std::vector<StringRef> &Filenames;
public:
RawCoverageFilenamesReader(StringRef Data, std::vector<StringRef> &Filenames)
: RawCoverageReader(Data), Filenames(Filenames) {}
RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete;
RawCoverageFilenamesReader &
operator=(const RawCoverageFilenamesReader &) = delete;
Error read();
};
/// Checks if the given coverage mapping data is exported for
/// an unused function.
class RawCoverageMappingDummyChecker : public RawCoverageReader {
@ -174,8 +188,6 @@ public:
FilenamesBegin(FilenamesBegin), FilenamesSize(FilenamesSize) {}
};
using DecompressedData = std::vector<std::unique_ptr<SmallVector<char, 0>>>;
private:
std::vector<StringRef> Filenames;
std::vector<ProfileMappingRecord> MappingRecords;
@ -185,10 +197,6 @@ private:
std::vector<CounterExpression> Expressions;
std::vector<CounterMappingRegion> MappingRegions;
// Used to tie the lifetimes of decompressed strings to the lifetime of this
// BinaryCoverageReader instance.
DecompressedData Decompressed;
BinaryCoverageReader() = default;
public:
@ -200,7 +208,7 @@ public:
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers);
static Expected<std::unique_ptr<BinaryCoverageReader>>
createCoverageReaderFromBuffer(StringRef Coverage, StringRef FuncRecords,
createCoverageReaderFromBuffer(StringRef Coverage,
InstrProfSymtab &&ProfileNames,
uint8_t BytesInAddress,
support::endianness Endian);
@ -208,24 +216,6 @@ public:
Error readNextRecord(CoverageMappingRecord &Record) override;
};
/// Reader for the raw coverage filenames.
class RawCoverageFilenamesReader : public RawCoverageReader {
std::vector<StringRef> &Filenames;
// Read an uncompressed sequence of filenames.
Error readUncompressed(uint64_t NumFilenames);
public:
RawCoverageFilenamesReader(StringRef Data, std::vector<StringRef> &Filenames)
: RawCoverageReader(Data), Filenames(Filenames) {}
RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete;
RawCoverageFilenamesReader &
operator=(const RawCoverageFilenamesReader &) = delete;
Error read(CovMapVersion Version,
BinaryCoverageReader::DecompressedData &Decompressed);
};
} // end namespace coverage
} // end namespace llvm

View File

@ -32,9 +32,8 @@ class CoverageFilenamesSectionWriter {
public:
CoverageFilenamesSectionWriter(ArrayRef<StringRef> Filenames);
/// Write encoded filenames to the given output stream. If \p Compress is
/// true, attempt to compress the filenames.
void write(raw_ostream &OS, bool Compress = true);
/// Write encoded filenames to the given output stream.
void write(raw_ostream &OS);
};
/// Writer for instrumentation based coverage mapping data.

View File

@ -23,7 +23,6 @@
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/InstrProfData.inc"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
@ -1140,9 +1139,5 @@ void createIRLevelProfileFlagVar(Module &M, bool IsCS);
// Create the variable for the profile file name.
void createProfileFileNameVar(Module &M, StringRef InstrProfileOutput);
// Whether to compress function names in profile records, and filenames in
// code coverage mappings. Used by the Instrumentation library and unit tests.
extern cl::opt<bool> DoInstrProfNameCompression;
} // end namespace llvm
#endif // LLVM_PROFILEDATA_INSTRPROF_H

View File

@ -198,14 +198,6 @@ VALUE_PROF_KIND(IPVK_Last, IPVK_MemOPSize, "last")
#undef VALUE_PROF_KIND
/* VALUE_PROF_KIND end */
#undef COVMAP_V2_OR_V3
#ifdef COVMAP_V2
#define COVMAP_V2_OR_V3
#endif
#ifdef COVMAP_V3
#define COVMAP_V2_OR_V3
#endif
/* COVMAP_FUNC_RECORD start */
/* Definition of member fields of the function record structure in coverage
* map.
@ -222,30 +214,16 @@ COVMAP_FUNC_RECORD(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), \
COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \
llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), \
NameValue.size()))
#endif
#ifdef COVMAP_V2_OR_V3
#else
COVMAP_FUNC_RECORD(const int64_t, llvm::Type::getInt64Ty(Ctx), NameRef, \
llvm::ConstantInt::get( \
llvm::Type::getInt64Ty(Ctx), NameHash))
llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \
llvm::IndexedInstrProf::ComputeHash(NameValue)))
#endif
COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), DataSize, \
llvm::ConstantInt::get( \
llvm::Type::getInt32Ty(Ctx), CoverageMapping.size()))
llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\
CoverageMapping.size()))
COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \
llvm::ConstantInt::get( \
llvm::Type::getInt64Ty(Ctx), FuncHash))
#ifdef COVMAP_V3
COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FilenamesRef, \
llvm::ConstantInt::get( \
llvm::Type::getInt64Ty(Ctx), FilenamesRef))
COVMAP_FUNC_RECORD(const char, \
llvm::ArrayType::get(llvm::Type::getInt8Ty(Ctx), \
CoverageMapping.size()), \
CoverageMapping,
llvm::ConstantDataArray::getRaw( \
CoverageMapping, CoverageMapping.size(), \
llvm::Type::getInt8Ty(Ctx)))
#endif
llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), FuncHash))
#undef COVMAP_FUNC_RECORD
/* COVMAP_FUNC_RECORD end. */
@ -258,7 +236,7 @@ COVMAP_FUNC_RECORD(const char, \
#define INSTR_PROF_DATA_DEFINED
#endif
COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \
llvm::ConstantInt::get(Int32Ty, NRecords))
llvm::ConstantInt::get(Int32Ty, FunctionRecords.size()))
COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \
llvm::ConstantInt::get(Int32Ty, FilenamesSize))
COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \
@ -289,9 +267,6 @@ INSTR_PROF_SECT_ENTRY(IPSK_vnodes, \
INSTR_PROF_SECT_ENTRY(IPSK_covmap, \
INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON), \
INSTR_PROF_COVMAP_COFF, "__LLVM_COV,")
INSTR_PROF_SECT_ENTRY(IPSK_covfun, \
INSTR_PROF_QUOTE(INSTR_PROF_COVFUN_COMMON), \
INSTR_PROF_COVFUN_COFF, "__LLVM_COV,")
INSTR_PROF_SECT_ENTRY(IPSK_orderfile, \
INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON), \
INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COFF), "__DATA,")
@ -658,8 +633,8 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
#define INSTR_PROF_RAW_VERSION 5
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 5
/* Coverage mapping format version (start from 0). */
#define INSTR_PROF_COVMAP_VERSION 3
/* Coverage mapping format vresion (start from 0). */
#define INSTR_PROF_COVMAP_VERSION 2
/* Profile version is always of type uint64_t. Reserve the upper 8 bits in the
* version for other variants of profile. We set the lowest bit of the upper 8
@ -686,7 +661,6 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
#define INSTR_PROF_VALS_COMMON __llvm_prf_vals
#define INSTR_PROF_VNODES_COMMON __llvm_prf_vnds
#define INSTR_PROF_COVMAP_COMMON __llvm_covmap
#define INSTR_PROF_COVFUN_COMMON __llvm_covfun
#define INSTR_PROF_ORDERFILE_COMMON __llvm_orderfile
/* Windows section names. Because these section names contain dollar characters,
* they must be quoted.
@ -697,7 +671,6 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
#define INSTR_PROF_VALS_COFF ".lprfv$M"
#define INSTR_PROF_VNODES_COFF ".lprfnd$M"
#define INSTR_PROF_COVMAP_COFF ".lcovmap$M"
#define INSTR_PROF_COVFUN_COFF ".lcovfun$M"
#define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M"
#ifdef _WIN32
@ -712,7 +685,6 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
/* Value profile nodes section. */
#define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_VNODES_COFF
#define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_COVMAP_COFF
#define INSTR_PROF_COVFUN_SECT_NAME INSTR_PROF_COVFUN_COFF
#define INSTR_PROF_ORDERFILE_SECT_NAME INSTR_PROF_ORDERFILE_COFF
#else
/* Runtime section names and name strings. */
@ -726,7 +698,6 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
/* Value profile nodes section. */
#define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_VNODES_COMMON)
#define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON)
#define INSTR_PROF_COVFUN_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVFUN_COMMON)
/* Order file instrumentation. */
#define INSTR_PROF_ORDERFILE_SECT_NAME \
INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON)
@ -781,5 +752,3 @@ typedef struct InstrProfValueData {
#else
#undef INSTR_PROF_DATA_DEFINED
#endif
#undef COVMAP_V2_OR_V3

View File

@ -420,8 +420,6 @@ static SectionKind getELFKindForNamedSection(StringRef Name, SectionKind K) {
// .section .eh_frame,"a",@progbits
if (Name == getInstrProfSectionName(IPSK_covmap, Triple::ELF,
/*AddSegmentInfo=*/false) ||
Name == getInstrProfSectionName(IPSK_covfun, Triple::ELF,
/*AddSegmentInfo=*/false))
return SectionKind::getMetadata();

View File

@ -805,8 +805,6 @@ static std::string getCoverageMapErrString(coveragemap_error Err) {
return "Truncated coverage data";
case coveragemap_error::malformed:
return "Malformed coverage data";
case coveragemap_error::decompression_failed:
return "Failed to decompress coverage data (zlib)";
}
llvm_unreachable("A value of coveragemap_error has no message.");
}

View File

@ -16,7 +16,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Object/Binary.h"
@ -26,7 +25,6 @@
#include "llvm/Object/COFF.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
@ -42,9 +40,6 @@ using namespace object;
#define DEBUG_TYPE "coverage-mapping"
STATISTIC(CovMapNumRecords, "The # of coverage function records");
STATISTIC(CovMapNumUsedRecords, "The # of used coverage function records");
void CoverageMappingIterator::increment() {
if (ReadErr != coveragemap_error::success)
return;
@ -97,60 +92,10 @@ Error RawCoverageReader::readString(StringRef &Result) {
return Error::success();
}
Error RawCoverageFilenamesReader::read(
CovMapVersion Version,
BinaryCoverageReader::DecompressedData &Decompressed) {
Error RawCoverageFilenamesReader::read() {
uint64_t NumFilenames;
if (auto Err = readSize(NumFilenames))
return Err;
if (!NumFilenames)
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Version < CovMapVersion::Version4)
return readUncompressed(NumFilenames);
// The uncompressed length may exceed the size of the encoded filenames.
// Skip size validation.
uint64_t UncompressedLen;
if (auto Err = readULEB128(UncompressedLen))
return Err;
uint64_t CompressedLen;
if (auto Err = readSize(CompressedLen))
return Err;
if (CompressedLen > 0) {
if (!zlib::isAvailable())
return make_error<CoverageMapError>(
coveragemap_error::decompression_failed);
// Allocate memory for the decompressed filenames. Transfer ownership of
// the memory to BinaryCoverageReader.
auto DecompressedStorage = std::make_unique<SmallVector<char, 0>>();
SmallVectorImpl<char> &StorageBuf = *DecompressedStorage.get();
Decompressed.push_back(std::move(DecompressedStorage));
// Read compressed filenames.
StringRef CompressedFilenames = Data.substr(0, CompressedLen);
Data = Data.substr(CompressedLen);
auto Err =
zlib::uncompress(CompressedFilenames, StorageBuf, UncompressedLen);
if (Err) {
consumeError(std::move(Err));
return make_error<CoverageMapError>(
coveragemap_error::decompression_failed);
}
StringRef UncompressedFilenames(StorageBuf.data(), StorageBuf.size());
RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames);
return Delegate.readUncompressed(NumFilenames);
}
return readUncompressed(NumFilenames);
}
Error RawCoverageFilenamesReader::readUncompressed(uint64_t NumFilenames) {
// Read uncompressed filenames.
for (size_t I = 0; I < NumFilenames; ++I) {
StringRef Filename;
if (auto Err = readString(Filename))
@ -435,51 +380,20 @@ static Expected<bool> isCoverageMappingDummy(uint64_t Hash, StringRef Mapping) {
return RawCoverageMappingDummyChecker(Mapping).isDummy();
}
/// A range of filename indices. Used to specify the location of a batch of
/// filenames in a vector-like container.
struct FilenameRange {
unsigned StartingIndex;
unsigned Length;
FilenameRange(unsigned StartingIndex, unsigned Length)
: StartingIndex(StartingIndex), Length(Length) {}
void markInvalid() { Length = 0; }
bool isInvalid() const { return Length == 0; }
};
namespace {
/// The interface to read coverage mapping function records for a module.
struct CovMapFuncRecordReader {
virtual ~CovMapFuncRecordReader() = default;
// Read a coverage header.
// The interface to read coverage mapping function records for a module.
//
// \p CovBuf points to the buffer containing the \c CovHeader of the coverage
// \p Buf points to the buffer containing the \c CovHeader of the coverage
// mapping data associated with the module.
//
// Returns a pointer to the next \c CovHeader if it exists, or to an address
// greater than \p CovEnd if not.
virtual Expected<const char *>
readCoverageHeader(const char *CovBuf, const char *CovBufEnd,
BinaryCoverageReader::DecompressedData &Decompressed) = 0;
// Read function records.
//
// \p FuncRecBuf points to the buffer containing a batch of function records.
// \p FuncRecBufEnd points past the end of the batch of records.
//
// Prior to Version4, \p OutOfLineFileRange points to a sequence of filenames
// associated with the function records. It is unused in Version4.
//
// Prior to Version4, \p OutOfLineMappingBuf points to a sequence of coverage
// mappings associated with the function records. It is unused in Version4.
virtual Error readFunctionRecords(const char *FuncRecBuf,
const char *FuncRecBufEnd,
Optional<FilenameRange> OutOfLineFileRange,
const char *OutOfLineMappingBuf,
const char *OutOfLineMappingBufEnd) = 0;
// Returns a pointer to the next \c CovHeader if it exists, or a pointer
// greater than \p End if not.
virtual Expected<const char *> readFunctionRecords(const char *Buf,
const char *End) = 0;
template <class IntPtrT, support::endianness Endian>
static Expected<std::unique_ptr<CovMapFuncRecordReader>>
@ -502,10 +416,6 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
std::vector<StringRef> &Filenames;
std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records;
// Maps a hash of the filenames in a TU to a \c FileRange. The range
// specifies the location of the hashed filenames in \c Filenames.
DenseMap<uint64_t, FilenameRange> FileRangeMap;
// Add the record to the collection if we don't already have a record that
// points to the same function name. This is useful to ignore the redundant
// records for the functions with ODR linkage.
@ -513,9 +423,7 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
// records, which were emitted for inline functions which were seen but
// not used in the corresponding translation unit.
Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR,
StringRef Mapping,
FilenameRange FileRange) {
++CovMapNumRecords;
StringRef Mapping, size_t FilenamesBegin) {
uint64_t FuncHash = CFR->template getFuncHash<Endian>();
NameRefType NameRef = CFR->template getFuncNameRef<Endian>();
auto InsertResult =
@ -526,9 +434,8 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
return Err;
if (FuncName.empty())
return make_error<InstrProfError>(instrprof_error::malformed);
++CovMapNumUsedRecords;
Records.emplace_back(Version, FuncName, FuncHash, Mapping,
FileRange.StartingIndex, FileRange.Length);
Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin,
Filenames.size() - FilenamesBegin);
return Error::success();
}
// Update the existing record if it's a dummy and the new record is real.
@ -547,11 +454,10 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
return Err;
if (*NewIsDummyExpected)
return Error::success();
++CovMapNumUsedRecords;
OldRecord.FunctionHash = FuncHash;
OldRecord.CoverageMapping = Mapping;
OldRecord.FilenamesBegin = FileRange.StartingIndex;
OldRecord.FilenamesSize = FileRange.Length;
OldRecord.FilenamesBegin = FilenamesBegin;
OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin;
return Error::success();
}
@ -564,134 +470,61 @@ public:
~VersionedCovMapFuncRecordReader() override = default;
Expected<const char *> readCoverageHeader(
const char *CovBuf, const char *CovBufEnd,
BinaryCoverageReader::DecompressedData &Decompressed) override {
Expected<const char *> readFunctionRecords(const char *Buf,
const char *End) override {
using namespace support;
if (CovBuf + sizeof(CovMapHeader) > CovBufEnd)
if (Buf + sizeof(CovMapHeader) > End)
return make_error<CoverageMapError>(coveragemap_error::malformed);
auto CovHeader = reinterpret_cast<const CovMapHeader *>(CovBuf);
auto CovHeader = reinterpret_cast<const CovMapHeader *>(Buf);
uint32_t NRecords = CovHeader->getNRecords<Endian>();
uint32_t FilenamesSize = CovHeader->getFilenamesSize<Endian>();
uint32_t CoverageSize = CovHeader->getCoverageSize<Endian>();
assert((CovMapVersion)CovHeader->getVersion<Endian>() == Version);
CovBuf = reinterpret_cast<const char *>(CovHeader + 1);
Buf = reinterpret_cast<const char *>(CovHeader + 1);
// Skip past the function records, saving the start and end for later.
// This is a no-op in Version4 (function records are read after all headers
// are read).
const char *FuncRecBuf = nullptr;
const char *FuncRecBufEnd = nullptr;
if (Version < CovMapVersion::Version4)
FuncRecBuf = CovBuf;
CovBuf += NRecords * sizeof(FuncRecordType);
if (Version < CovMapVersion::Version4)
FuncRecBufEnd = CovBuf;
const char *FunBuf = Buf;
Buf += NRecords * sizeof(FuncRecordType);
const char *FunEnd = Buf;
// Get the filenames.
if (CovBuf + FilenamesSize > CovBufEnd)
if (Buf + FilenamesSize > End)
return make_error<CoverageMapError>(coveragemap_error::malformed);
size_t FilenamesBegin = Filenames.size();
StringRef FilenameRegion(CovBuf, FilenamesSize);
RawCoverageFilenamesReader Reader(FilenameRegion, Filenames);
if (auto Err = Reader.read(Version, Decompressed))
RawCoverageFilenamesReader Reader(StringRef(Buf, FilenamesSize), Filenames);
if (auto Err = Reader.read())
return std::move(Err);
CovBuf += FilenamesSize;
FilenameRange FileRange(FilenamesBegin, Filenames.size() - FilenamesBegin);
if (Version == CovMapVersion::Version4) {
// Map a hash of the filenames region to the filename range associated
// with this coverage header.
int64_t FilenamesRef =
llvm::IndexedInstrProf::ComputeHash(FilenameRegion);
auto Insert =
FileRangeMap.insert(std::make_pair(FilenamesRef, FileRange));
if (!Insert.second) {
// The same filenames ref was encountered twice. It's possible that
// the associated filenames are the same.
auto It = Filenames.begin();
FilenameRange &OrigRange = Insert.first->getSecond();
if (std::equal(It + OrigRange.StartingIndex,
It + OrigRange.StartingIndex + OrigRange.Length,
It + FileRange.StartingIndex,
It + FileRange.StartingIndex + FileRange.Length))
// Map the new range to the original one.
FileRange = OrigRange;
else
// This is a hash collision. Mark the filenames ref invalid.
OrigRange.markInvalid();
}
}
Buf += FilenamesSize;
// We'll read the coverage mapping records in the loop below.
// This is a no-op in Version4 (coverage mappings are not affixed to the
// coverage header).
const char *MappingBuf = CovBuf;
if (Version == CovMapVersion::Version4 && CoverageSize != 0)
const char *CovBuf = Buf;
Buf += CoverageSize;
const char *CovEnd = Buf;
if (Buf > End)
return make_error<CoverageMapError>(coveragemap_error::malformed);
CovBuf += CoverageSize;
const char *MappingEnd = CovBuf;
if (CovBuf > CovBufEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Version < CovMapVersion::Version4) {
// Read each function record.
if (Error E = readFunctionRecords(FuncRecBuf, FuncRecBufEnd, FileRange,
MappingBuf, MappingEnd))
return std::move(E);
}
// Each coverage map has an alignment of 8, so we need to adjust alignment
// before reading the next map.
CovBuf += offsetToAlignedAddr(CovBuf, Align(8));
Buf += offsetToAlignedAddr(Buf, Align(8));
return CovBuf;
}
auto CFR = reinterpret_cast<const FuncRecordType *>(FunBuf);
while ((const char *)CFR < FunEnd) {
// Read the function information
uint32_t DataSize = CFR->template getDataSize<Endian>();
Error readFunctionRecords(const char *FuncRecBuf, const char *FuncRecBufEnd,
Optional<FilenameRange> OutOfLineFileRange,
const char *OutOfLineMappingBuf,
const char *OutOfLineMappingBufEnd) override {
auto CFR = reinterpret_cast<const FuncRecordType *>(FuncRecBuf);
while ((const char *)CFR < FuncRecBufEnd) {
// Validate the length of the coverage mapping for this function.
const char *NextMappingBuf;
const FuncRecordType *NextCFR;
std::tie(NextMappingBuf, NextCFR) =
CFR->template advanceByOne<Endian>(OutOfLineMappingBuf);
if (Version < CovMapVersion::Version4)
if (NextMappingBuf > OutOfLineMappingBufEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed);
// Now use that to read the coverage data.
if (CovBuf + DataSize > CovEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed);
auto Mapping = StringRef(CovBuf, DataSize);
CovBuf += DataSize;
// Look up the set of filenames associated with this function record.
Optional<FilenameRange> FileRange;
if (Version < CovMapVersion::Version4) {
FileRange = OutOfLineFileRange;
} else {
uint64_t FilenamesRef = CFR->template getFilenamesRef<Endian>();
auto It = FileRangeMap.find(FilenamesRef);
if (It == FileRangeMap.end())
return make_error<CoverageMapError>(coveragemap_error::malformed);
else
FileRange = It->getSecond();
}
// Now, read the coverage data.
if (FileRange && !FileRange->isInvalid()) {
StringRef Mapping =
CFR->template getCoverageMapping<Endian>(OutOfLineMappingBuf);
if (Version == CovMapVersion::Version4 &&
Mapping.data() + Mapping.size() > FuncRecBufEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange))
return Err;
}
std::tie(OutOfLineMappingBuf, CFR) = std::tie(NextMappingBuf, NextCFR);
if (Error Err =
insertFunctionRecordIfNeeded(CFR, Mapping, FilenamesBegin))
return std::move(Err);
CFR++;
}
return Error::success();
return Buf;
}
};
@ -710,34 +543,29 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F);
case CovMapVersion::Version2:
case CovMapVersion::Version3:
case CovMapVersion::Version4:
// Decompress the name data.
if (Error E = P.create(P.getNameData()))
return std::move(E);
if (Version == CovMapVersion::Version2)
return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F);
else if (Version == CovMapVersion::Version3)
else
return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version3, IntPtrT, Endian>>(P, R, F);
else if (Version == CovMapVersion::Version4)
return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version4, IntPtrT, Endian>>(P, R, F);
}
llvm_unreachable("Unsupported version");
}
template <typename T, support::endianness Endian>
static Error readCoverageMappingData(
InstrProfSymtab &ProfileNames, StringRef CovMap, StringRef FuncRecords,
InstrProfSymtab &ProfileNames, StringRef Data,
std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records,
std::vector<StringRef> &Filenames,
BinaryCoverageReader::DecompressedData &Decompressed) {
std::vector<StringRef> &Filenames) {
using namespace coverage;
// Read the records in the coverage data section.
auto CovHeader =
reinterpret_cast<const CovMapHeader *>(CovMap.data());
reinterpret_cast<const CovMapHeader *>(Data.data());
CovMapVersion Version = (CovMapVersion)CovHeader->getVersion<Endian>();
if (Version > CovMapVersion::CurrentVersion)
return make_error<CoverageMapError>(coveragemap_error::unsupported_version);
@ -747,28 +575,12 @@ static Error readCoverageMappingData(
if (Error E = ReaderExpected.takeError())
return E;
auto Reader = std::move(ReaderExpected.get());
const char *CovBuf = CovMap.data();
const char *CovBufEnd = CovBuf + CovMap.size();
const char *FuncRecBuf = FuncRecords.data();
const char *FuncRecBufEnd = FuncRecords.data() + FuncRecords.size();
while (CovBuf < CovBufEnd) {
// Read the current coverage header & filename data.
//
// Prior to Version4, this also reads all function records affixed to the
// header.
//
// Return a pointer to the next coverage header.
auto NextOrErr =
Reader->readCoverageHeader(CovBuf, CovBufEnd, Decompressed);
if (auto E = NextOrErr.takeError())
for (const char *Buf = Data.data(), *End = Buf + Data.size(); Buf < End;) {
auto NextHeaderOrErr = Reader->readFunctionRecords(Buf, End);
if (auto E = NextHeaderOrErr.takeError())
return E;
CovBuf = NextOrErr.get();
Buf = NextHeaderOrErr.get();
}
// In Version4, function records are not affixed to coverage headers. Read
// the records from their dedicated section.
if (Version == CovMapVersion::Version4)
return Reader->readFunctionRecords(FuncRecBuf, FuncRecBufEnd, None, nullptr,
nullptr);
return Error::success();
}
@ -776,33 +588,31 @@ static const char *TestingFormatMagic = "llvmcovmtestdata";
Expected<std::unique_ptr<BinaryCoverageReader>>
BinaryCoverageReader::createCoverageReaderFromBuffer(
StringRef Coverage, StringRef FuncRecords, InstrProfSymtab &&ProfileNames,
uint8_t BytesInAddress, support::endianness Endian) {
StringRef Coverage, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress,
support::endianness Endian) {
std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader());
Reader->ProfileNames = std::move(ProfileNames);
if (BytesInAddress == 4 && Endian == support::endianness::little) {
if (Error E =
readCoverageMappingData<uint32_t, support::endianness::little>(
Reader->ProfileNames, Coverage, FuncRecords,
Reader->MappingRecords, Reader->Filenames,
Reader->Decompressed))
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames))
return std::move(E);
} else if (BytesInAddress == 4 && Endian == support::endianness::big) {
if (Error E = readCoverageMappingData<uint32_t, support::endianness::big>(
Reader->ProfileNames, Coverage, FuncRecords, Reader->MappingRecords,
Reader->Filenames, Reader->Decompressed))
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames))
return std::move(E);
} else if (BytesInAddress == 8 && Endian == support::endianness::little) {
if (Error E =
readCoverageMappingData<uint64_t, support::endianness::little>(
Reader->ProfileNames, Coverage, FuncRecords,
Reader->MappingRecords, Reader->Filenames,
Reader->Decompressed))
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames))
return std::move(E);
} else if (BytesInAddress == 8 && Endian == support::endianness::big) {
if (Error E = readCoverageMappingData<uint64_t, support::endianness::big>(
Reader->ProfileNames, Coverage, FuncRecords, Reader->MappingRecords,
Reader->Filenames, Reader->Decompressed))
Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames))
return std::move(E);
} else
return make_error<CoverageMapError>(coveragemap_error::malformed);
@ -843,7 +653,7 @@ loadTestingFormat(StringRef Data) {
return make_error<CoverageMapError>(coveragemap_error::malformed);
CoverageMapping = CoverageMapping.substr(Pad);
return BinaryCoverageReader::createCoverageReaderFromBuffer(
CoverageMapping, "", std::move(ProfileNames), BytesInAddress, Endian);
CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian);
}
static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) {
@ -904,31 +714,18 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch) {
/*AddSegmentInfo=*/false));
if (auto E = CoverageSection.takeError())
return std::move(E);
// Get the contents of the given sections.
auto CoverageMappingOrErr = CoverageSection->getContents();
if (!CoverageMappingOrErr)
return CoverageMappingOrErr.takeError();
StringRef CoverageMapping = CoverageMappingOrErr.get();
InstrProfSymtab ProfileNames;
if (Error E = ProfileNames.create(*NamesSection))
return std::move(E);
// Look for the coverage records section (Version4 only).
StringRef FuncRecords;
auto CoverageRecordsSection =
lookupSection(*OF, getInstrProfSectionName(IPSK_covfun, ObjFormat,
/*AddSegmentInfo=*/false));
if (auto E = CoverageRecordsSection.takeError())
consumeError(std::move(E));
else {
auto CoverageRecordsOrErr = CoverageRecordsSection->getContents();
if (!CoverageRecordsOrErr)
return CoverageRecordsOrErr.takeError();
FuncRecords = CoverageRecordsOrErr.get();
}
return BinaryCoverageReader::createCoverageReaderFromBuffer(
CoverageMapping, FuncRecords, std::move(ProfileNames), BytesInAddress,
CoverageMappingOrErr.get(), std::move(ProfileNames), BytesInAddress,
Endian);
}

View File

@ -11,11 +11,9 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@ -36,34 +34,12 @@ CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter(
#endif
}
void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) {
std::string FilenamesStr;
{
raw_string_ostream FilenamesOS{FilenamesStr};
for (const auto &Filename : Filenames) {
encodeULEB128(Filename.size(), FilenamesOS);
FilenamesOS << Filename;
}
}
SmallString<128> CompressedStr;
bool doCompression =
Compress && zlib::isAvailable() && DoInstrProfNameCompression;
if (doCompression) {
auto E =
zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression);
if (E)
report_bad_alloc_error("Failed to zlib compress coverage data");
}
// ::= <num-filenames>
// <uncompressed-len>
// <compressed-len-or-zero>
// (<compressed-filenames> | <uncompressed-filenames>)
void CoverageFilenamesSectionWriter::write(raw_ostream &OS) {
encodeULEB128(Filenames.size(), OS);
encodeULEB128(FilenamesStr.size(), OS);
encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS);
OS << (doCompression ? StringRef(CompressedStr) : StringRef(FilenamesStr));
for (const auto &Filename : Filenames) {
encodeULEB128(Filename.size(), OS);
OS << Filename;
}
}
namespace {

View File

@ -162,10 +162,6 @@ const char *InstrProfSectNamePrefix[] = {
namespace llvm {
cl::opt<bool> DoInstrProfNameCompression(
"enable-name-compression",
cl::desc("Enable name/filename string compression"), cl::init(true));
std::string getInstrProfSectionName(InstrProfSectKind IPSK,
Triple::ObjectFormatType OF,
bool AddSegmentInfo) {

View File

@ -74,6 +74,10 @@ cl::opt<unsigned> MemOPSizeLarge(
namespace {
cl::opt<bool> DoNameCompression("enable-name-compression",
cl::desc("Enable name string compression"),
cl::init(true));
cl::opt<bool> DoHashBasedCounterSplit(
"hash-based-counter-split",
cl::desc("Rename counter variable of a comdat function based on cfg hash"),
@ -969,7 +973,7 @@ void InstrProfiling::emitNameData() {
std::string CompressedNameStr;
if (Error E = collectPGOFuncNameStrings(ReferencedNames, CompressedNameStr,
DoInstrProfNameCompression)) {
DoNameCompression)) {
report_fatal_error(toString(std::move(E)), false);
}

View File

@ -1,8 +1,6 @@
;; Ensure that SHF_ALLOC section flag is not set for the __llvm_covmap section on Linux.
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s
@covfun = linkonce_odr hidden constant i32 0, section "__llvm_covfun"
@__llvm_coverage_mapping = internal constant i32 0, section "__llvm_covmap"
; CHECK-DAG: .section __llvm_covfun,""
; CHECK-DAG: .section __llvm_covmap,""

View File

@ -7,6 +7,5 @@ int main(int argc, const char *argv[]) {}
// RUN: llvm-cov show %S/Inputs/binary-formats.macho32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov show %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov show %S/Inputs/binary-formats.macho32b -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov show %S/Inputs/binary-formats.v3.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json

View File

@ -895,29 +895,4 @@ INSTANTIATE_TEST_CASE_P(ParameterizedCovMapTest, CoverageMappingTest,
std::pair<bool, bool>({true, false}),
std::pair<bool, bool>({true, true})),);
TEST(CoverageMappingTest, filename_roundtrip) {
std::vector<StringRef> Paths({"a", "b", "c", "d", "e"});
for (bool Compress : {false, true}) {
std::string EncodedFilenames;
{
raw_string_ostream OS(EncodedFilenames);
CoverageFilenamesSectionWriter Writer(Paths);
Writer.write(OS, Compress);
}
std::vector<StringRef> ReadFilenames;
RawCoverageFilenamesReader Reader(EncodedFilenames, ReadFilenames);
BinaryCoverageReader::DecompressedData Decompressed;
EXPECT_THAT_ERROR(Reader.read(CovMapVersion::CurrentVersion, Decompressed),
Succeeded());
if (!Compress)
ASSERT_EQ(Decompressed.size(), 0U);
ASSERT_EQ(ReadFilenames.size(), Paths.size());
for (unsigned I = 0; I < Paths.size(); ++I)
ASSERT_TRUE(ReadFilenames[I] == Paths[I]);
}
}
} // end anonymous namespace