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:
parent
ddbbf4cb94
commit
52738a45b0
@ -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, let’s consider a C file and how it gets compiled to LLVM:
|
||||
|
||||
@ -218,45 +241,42 @@ For example, let’s 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
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,""
|
||||
|
Binary file not shown.
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user