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

This reverts commit e18531595b.

On Windows, there is an error:

http://lab.llvm.org:8011/builders/sanitizer-windows/builds/54963/steps/stage%201%20check/logs/stdio

error: C:\b\slave\sanitizer-windows\build\stage1\projects\compiler-rt\test\profile\Profile-x86_64\Output\instrprof-merging.cpp.tmp.v1.o: Failed to load coverage: Malformed coverage data
This commit is contained in:
Vedant Kumar 2019-12-04 10:35:14 -08:00
parent 9b15873c92
commit f208b70fbc
24 changed files with 322 additions and 796 deletions

View File

@ -13,8 +13,6 @@
#include "CoverageMappingGen.h" #include "CoverageMappingGen.h"
#include "CodeGenFunction.h" #include "CodeGenFunction.h"
#include "clang/AST/StmtVisitor.h" #include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Lex/Lexer.h" #include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
@ -26,10 +24,6 @@
#include "llvm/Support/FileSystem.h" #include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
// This selects the coverage mapping format defined when `InstrProfData.inc`
// is textually included.
#define COVMAP_V3
using namespace clang; using namespace clang;
using namespace CodeGen; using namespace CodeGen;
using namespace llvm::coverage; using namespace llvm::coverage;
@ -1278,6 +1272,12 @@ struct CounterCoverageMappingBuilder
} }
}; };
std::string getCoverageSection(const CodeGenModule &CGM) {
return llvm::getInstrProfSectionName(
llvm::IPSK_covmap,
CGM.getContext().getTargetInfo().getTriple().getObjectFormat());
}
std::string normalizeFilename(StringRef Filename) { std::string normalizeFilename(StringRef Filename) {
llvm::SmallString<256> Path(Filename); llvm::SmallString<256> Path(Filename);
llvm::sys::fs::make_absolute(Path); llvm::sys::fs::make_absolute(Path);
@ -1317,71 +1317,30 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
} }
} }
static std::string getInstrProfSection(const CodeGenModule &CGM,
llvm::InstrProfSectKind SK) {
return llvm::getInstrProfSectionName(
SK, CGM.getContext().getTargetInfo().getTriple().getObjectFormat());
}
void CoverageMappingModuleGen::emitFunctionMappingRecord(
const FunctionInfo &Info, uint64_t FilenamesRef) {
llvm::LLVMContext &Ctx = CGM.getLLVMContext();
// Assign a name to the function record. This is used to merge duplicates.
std::string FuncRecordName = "__covrec_" + llvm::utohexstr(Info.NameHash);
// A dummy description for a function included-but-not-used in a TU can be
// replaced by full description provided by a different TU. The two kinds of
// descriptions play distinct roles: therefore, assign them different names
// to prevent `linkonce_odr` merging.
if (Info.IsUsed)
FuncRecordName += "u";
// Create the function record type.
const uint64_t NameHash = Info.NameHash;
const uint64_t FuncHash = Info.FuncHash;
const std::string &CoverageMapping = Info.CoverageMapping;
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
llvm::Type *FunctionRecordTypes[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
auto *FunctionRecordTy =
llvm::StructType::get(Ctx, makeArrayRef(FunctionRecordTypes),
/*isPacked=*/true);
// Create the function record constant.
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,
llvm::Constant *FunctionRecordVals[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
auto *FuncRecordConstant = llvm::ConstantStruct::get(
FunctionRecordTy, makeArrayRef(FunctionRecordVals));
// Create the function record global.
auto *FuncRecord = new llvm::GlobalVariable(
CGM.getModule(), FunctionRecordTy, /*isConstant=*/true,
llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,
FuncRecordName);
FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility);
FuncRecord->setSection(getInstrProfSection(CGM, llvm::IPSK_covfun));
FuncRecord->setAlignment(llvm::Align(8));
if (CGM.supportsCOMDAT())
FuncRecord->setComdat(CGM.getModule().getOrInsertComdat(FuncRecordName));
// Make sure the data doesn't get deleted.
CGM.addUsedGlobal(FuncRecord);
}
void CoverageMappingModuleGen::addFunctionMappingRecord( void CoverageMappingModuleGen::addFunctionMappingRecord(
llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash, llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash,
const std::string &CoverageMapping, bool IsUsed) { const std::string &CoverageMapping, bool IsUsed) {
llvm::LLVMContext &Ctx = CGM.getLLVMContext(); llvm::LLVMContext &Ctx = CGM.getLLVMContext();
const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue); if (!FunctionRecordTy) {
FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed}); #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
llvm::Type *FunctionRecordTypes[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
FunctionRecordTy =
llvm::StructType::get(Ctx, makeArrayRef(FunctionRecordTypes),
/*isPacked=*/true);
}
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,
llvm::Constant *FunctionRecordVals[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
FunctionRecords.push_back(llvm::ConstantStruct::get(
FunctionRecordTy, makeArrayRef(FunctionRecordVals)));
if (!IsUsed) if (!IsUsed)
FunctionNames.push_back( FunctionNames.push_back(
llvm::ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx))); llvm::ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx)));
CoverageMappings.push_back(CoverageMapping);
if (CGM.getCodeGenOpts().DumpCoverageMapping) { if (CGM.getCodeGenOpts().DumpCoverageMapping) {
// Dump the coverage mapping data for this function by decoding the // Dump the coverage mapping data for this function by decoding the
@ -1426,22 +1385,37 @@ void CoverageMappingModuleGen::emit() {
FilenameRefs[I] = FilenameStrs[I]; FilenameRefs[I] = FilenameStrs[I];
} }
std::string Filenames; std::string FilenamesAndCoverageMappings;
{ llvm::raw_string_ostream OS(FilenamesAndCoverageMappings);
llvm::raw_string_ostream OS(Filenames); CoverageFilenamesSectionWriter(FilenameRefs).write(OS);
CoverageFilenamesSectionWriter(FilenameRefs).write(OS);
// Stream the content of CoverageMappings to OS while keeping
// memory consumption under control.
size_t CoverageMappingSize = 0;
for (auto &S : CoverageMappings) {
CoverageMappingSize += S.size();
OS << S;
S.clear();
S.shrink_to_fit();
} }
auto *FilenamesVal = CoverageMappings.clear();
llvm::ConstantDataArray::getString(Ctx, Filenames, false); CoverageMappings.shrink_to_fit();
const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames);
// Emit the function records. size_t FilenamesSize = OS.str().size() - CoverageMappingSize;
for (const FunctionInfo &Info : FunctionRecords) // Append extra zeroes if necessary to ensure that the size of the filenames
emitFunctionMappingRecord(Info, FilenamesRef); // and coverage mappings is a multiple of 8.
if (size_t Rem = OS.str().size() % 8) {
CoverageMappingSize += 8 - Rem;
OS.write_zeros(8 - Rem);
}
auto *FilenamesAndMappingsVal =
llvm::ConstantDataArray::getString(Ctx, OS.str(), false);
// Create the deferred function records array
auto RecordsTy =
llvm::ArrayType::get(FunctionRecordTy, FunctionRecords.size());
auto RecordsVal = llvm::ConstantArray::get(RecordsTy, FunctionRecords);
const unsigned NRecords = 0;
const size_t FilenamesSize = Filenames.size();
const unsigned CoverageMappingSize = 0;
llvm::Type *CovDataHeaderTypes[] = { llvm::Type *CovDataHeaderTypes[] = {
#define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType, #define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType,
#include "llvm/ProfileData/InstrProfData.inc" #include "llvm/ProfileData/InstrProfData.inc"
@ -1456,16 +1430,18 @@ void CoverageMappingModuleGen::emit() {
CovDataHeaderTy, makeArrayRef(CovDataHeaderVals)); CovDataHeaderTy, makeArrayRef(CovDataHeaderVals));
// Create the coverage data record // Create the coverage data record
llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()}; llvm::Type *CovDataTypes[] = {CovDataHeaderTy, RecordsTy,
FilenamesAndMappingsVal->getType()};
auto CovDataTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataTypes)); auto CovDataTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataTypes));
llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal}; llvm::Constant *TUDataVals[] = {CovDataHeaderVal, RecordsVal,
FilenamesAndMappingsVal};
auto CovDataVal = auto CovDataVal =
llvm::ConstantStruct::get(CovDataTy, makeArrayRef(TUDataVals)); llvm::ConstantStruct::get(CovDataTy, makeArrayRef(TUDataVals));
auto CovData = new llvm::GlobalVariable( auto CovData = new llvm::GlobalVariable(
CGM.getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage, CGM.getModule(), CovDataTy, true, llvm::GlobalValue::InternalLinkage,
CovDataVal, llvm::getCoverageMappingVarName()); CovDataVal, llvm::getCoverageMappingVarName());
CovData->setSection(getInstrProfSection(CGM, llvm::IPSK_covmap)); CovData->setSection(getCoverageSection(CGM));
CovData->setAlignment(llvm::Align(8)); CovData->setAlignment(llvm::Align(8));
// Make sure the data doesn't get deleted. // Make sure the data doesn't get deleted.

View File

@ -47,27 +47,17 @@ class CodeGenModule;
/// Organizes the cross-function state that is used while generating /// Organizes the cross-function state that is used while generating
/// code coverage mapping data. /// code coverage mapping data.
class CoverageMappingModuleGen { class CoverageMappingModuleGen {
/// Information needed to emit a coverage record for a function.
struct FunctionInfo {
uint64_t NameHash;
uint64_t FuncHash;
std::string CoverageMapping;
bool IsUsed;
};
CodeGenModule &CGM; CodeGenModule &CGM;
CoverageSourceInfo &SourceInfo; CoverageSourceInfo &SourceInfo;
llvm::SmallDenseMap<const FileEntry *, unsigned, 8> FileEntries; llvm::SmallDenseMap<const FileEntry *, unsigned, 8> FileEntries;
std::vector<llvm::Constant *> FunctionRecords;
std::vector<llvm::Constant *> FunctionNames; std::vector<llvm::Constant *> FunctionNames;
std::vector<FunctionInfo> FunctionRecords; llvm::StructType *FunctionRecordTy;
std::vector<std::string> CoverageMappings;
/// Emit a function record.
void emitFunctionMappingRecord(const FunctionInfo &Info,
uint64_t FilenamesRef);
public: public:
CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo) CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo)
: CGM(CGM), SourceInfo(SourceInfo) {} : CGM(CGM), SourceInfo(SourceInfo), FunctionRecordTy(nullptr) {}
CoverageSourceInfo &getSourceInfo() const { CoverageSourceInfo &getSourceInfo() const {
return SourceInfo; return SourceInfo;

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp %S/Inputs/../abspath.cpp -o - | FileCheck -check-prefix=RMDOTS %s // RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -main-file-name abspath.cpp %S/Inputs/../abspath.cpp -o - | FileCheck -check-prefix=RMDOTS %s
// RMDOTS: @__llvm_coverage_mapping = {{.*}}"\01 // RMDOTS: @__llvm_coverage_mapping = {{.*}}"\01
// RMDOTS-NOT: Inputs // RMDOTS-NOT: Inputs
@ -6,7 +6,7 @@
// RUN: mkdir -p %t/test && cd %t/test // RUN: mkdir -p %t/test && cd %t/test
// RUN: echo "void f1() {}" > f1.c // RUN: echo "void f1() {}" > f1.c
// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name abspath.cpp ../test/f1.c -o - | FileCheck -check-prefix=RELPATH %s // RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -main-file-name abspath.cpp ../test/f1.c -o - | FileCheck -check-prefix=RELPATH %s
// RELPATH: @__llvm_coverage_mapping = {{.*}}"\01 // RELPATH: @__llvm_coverage_mapping = {{.*}}"\01
// RELPATH: {{[/\\].*(/|\\\\)test(/|\\\\)f1}}.c // RELPATH: {{[/\\].*(/|\\\\)test(/|\\\\)f1}}.c

View File

@ -1,31 +1,12 @@
// Check the data structures emitted by coverage mapping // Check the data structures emitted by coverage mapping
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false | FileCheck %s -check-prefixes=COMMON,DARWIN // RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck %s
// RUN: %clang_cc1 -triple x86_64--windows-msvc -main-file-name ir.c %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false | FileCheck %s -check-prefixes=COMMON,WINDOWS
static inline void unused() {}
void foo(void) {} void foo(void) { }
int main(void) { int main(void) {
foo(); foo();
return 0; return 0;
} }
// Check the function records. Two of the record names should come in the 'used' // CHECK: @__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [2 x <{ i64, i32, i64 }>], [{{[0-9]+}} x i8] } { { i32, i32, i32, i32 } { i32 2, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, [2 x <{ i64, i32, i64 }>] [<{{.*}}> <{{.*}}>, <{{.*}}> <{{.*}}>]
// flavor, and one should not.
// DARWIN: [[FuncRecord1:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8
// DARWIN: [[FuncRecord2:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8
// DARWIN: [[FuncRecord3:@__covrec_[0-9A-F]+]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8
// DARWIN: @__llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [{{.*}} x i8] } { {{.*}} }, section "__LLVM_COV,__llvm_covmap", align 8
// WINDOWS: [[FuncRecord1:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8
// WINDOWS: [[FuncRecord2:@__covrec_[0-9A-F]+u]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8
// WINDOWS: [[FuncRecord3:@__covrec_[0-9A-F]+]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8
// WINDOWS: @__llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [{{.*}} x i8] } { {{.*}} }, section ".lcovmap$M", align 8
// COMMON: @llvm.used = appending global [{{.*}} x i8*]
// COMMON-SAME: [[FuncRecord1]]
// COMMON-SAME: [[FuncRecord2]]
// COMMON-SAME: [[FuncRecord3]]
// COMMON-SAME: @__llvm_coverage_mapping

View File

@ -18,12 +18,9 @@ struct A {
// PGOGEN: {{.*}}add{{.*}}%pgocount, 1 // PGOGEN: {{.*}}add{{.*}}%pgocount, 1
// PGOGEN: store{{.*}}@__profc__ZN1AaSEOS_ // PGOGEN: store{{.*}}@__profc__ZN1AaSEOS_
// Check that coverage mapping includes 3 function records including the // Check that coverage mapping includes 6 function records including the
// defaulted copy and move operators: A::operator= // defaulted copy and move operators: A::operator=
// COVMAP: section "__llvm_covfun", comdat // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [3 x <{{.*}}>],
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }
B b; B b;
}; };

View File

@ -20,15 +20,11 @@ struct Derived : public Base {
// PGOGEN-DAG: {{.*}}add{{.*}}%pgocount, 1 // PGOGEN-DAG: {{.*}}add{{.*}}%pgocount, 1
// PGOGEN-DAG: store{{.*}}@__profc__ZN7DerivedC2Ev // PGOGEN-DAG: store{{.*}}@__profc__ZN7DerivedC2Ev
// Check that coverage mapping has 5 function records including // Check that coverage mapping has 6 function records including
// the defaulted Derived::Derived(const Derived), and Derived::Derived() // the defaulted Derived::Derived(const Derived), and Derived::Derived()
// methds. // methds.
// COVMAP: section "__llvm_covfun", comdat // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [5 x
// COVMAP: section "__llvm_covfun", comdat // <{{.*}}>],
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }
}; };
Derived dd; Derived dd;

View File

@ -16,14 +16,10 @@ struct Derived : public Base {
// PGOGEN: {{.*}}add{{.*}}%pgocount, 1 // PGOGEN: {{.*}}add{{.*}}%pgocount, 1
// PGOGEN: store{{.*}}@__profc__ZN7DerivedD2Ev // PGOGEN: store{{.*}}@__profc__ZN7DerivedD2Ev
// Check that coverage mapping has 5 function records including // Check that coverage mapping has 6 function records including
// the default destructor in the derived class. // the default destructor in the derived class.
// COVMAP: section "__llvm_covfun", comdat // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [5 x
// COVMAP: section "__llvm_covfun", comdat // <{{.*}}>],
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: section "__llvm_covfun", comdat
// COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }
}; };
int main() { int main() {

View File

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

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 instrumenation based profiling analysis using LLVM's and Clang's instrumenation based profiling
(Clang's ``-fprofile-instr-generate`` option). (Clang's ``-fprofile-instr-generate`` option).
This document is aimed at those who would like to know how LLVM's code coverage This document is aimed at those who use LLVM's code coverage mapping to provide
mapping works under the hood. A prior knowledge of how Clang's profile guided code coverage analysis for their own programs, and for those who would like
optimization works is useful, but not required. For those interested in using to know how it works under the hood. A prior knowledge of how Clang's profile
LLVM to provide code coverage analysis for their own programs, see the `Clang guided optimization works is useful, but not required.
documentation <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html>`.
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 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 the basics are down, more advanced features of the coverage mapping format
are discussed - such as the data structures, LLVM IR representation and are discussed - such as the data structures, LLVM IR representation and
the binary encoding. 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 High Level Overview
=================== ===================
LLVM's code coverage mapping format is designed to be a self contained 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 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 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 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 LLVM IR Representation
====================== ======================
The coverage mapping data is stored in the LLVM IR using a global constant The coverage mapping data is stored in the LLVM IR using a single global
structure variable called *__llvm_coverage_mapping* with the *IPSK_covmap* constant structure variable called *__llvm_coverage_mapping*
section specifier (i.e. ".lcovmap$M" on Windows and "__llvm_covmap" elsewhere). with the *__llvm_covmap* section specifier.
For example, lets consider a C file and how it gets compiled to LLVM: 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; 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. * 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 * Coverage mapping data which is an array of bytes. Zero paddings are added at the end to force 8 byte alignment.
different object files tightly (the word-level alignment assumption is baked in
too deeply).
.. code-block:: llvm .. 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, i32, i32, i32 } ; Coverage map header
{ {
i32 0, ; Always 0. In prior versions, the number of affixed function records i32 2, ; The number of function records
i32 32, ; The length of the string that contains the encoded translation unit filenames i32 20, ; 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 20, ; The length of the string that contains the encoded coverage mapping data
i32 3, ; Coverage mapping format version 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 }, 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 The function record layout has evolved since version 1. In version 1, the function record for *foo* is defined as follows:
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:
.. code-block:: llvm .. 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 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: Coverage Mapping Header:
------------------------ ------------------------
The coverage mapping header has the following fields: 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 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: .. _function records:
@ -297,11 +309,24 @@ A function record is a structure of the following type:
.. code-block:: llvm .. 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 It contains function name's MD5, the length of the encoded mapping data for that function, and function's
that function, the function's structural hash value, the hash of the filenames structural hash value.
in the function's translation unit, and the encoded mapping data.
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: Dissecting the sample:
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
@ -314,18 +339,32 @@ IR for the `coverage mapping sample`_ that was shown earlier:
.. code-block:: llvm .. 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 * 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 * The length of the substring that contains the encoded translation unit
filenames, the length of the uncompressed filenames, and the length of the filenames is the value of the second field in the *__llvm_coverage_mapping*
compressed payload (or 0 if compression is disabled). In this sample, there structure, which is 20, thus the filenames are encoded in this string:
is 1 filename that is 21 bytes in length (uncompressed), and stored in 29
bytes (compressed).
* 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 .. code-block:: llvm

View File

@ -24,7 +24,6 @@
#include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h" #include "llvm/ADT/iterator_range.h"
#include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Compiler.h" #include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h" #include "llvm/Support/Endian.h"
@ -55,8 +54,7 @@ enum class coveragemap_error {
no_data_found, no_data_found,
unsupported_version, unsupported_version,
truncated, truncated,
malformed, malformed
decompression_failed
}; };
const std::error_category &coveragemap_category(); const std::error_category &coveragemap_category();
@ -680,119 +678,37 @@ getLineCoverageStats(const coverage::CoverageData &CD) {
return make_range(Begin, End); return make_range(Begin, End);
} }
// Coverage mappping data (V2) has the following layout: // Profile coverage map has the following layout:
// IPSK_covmap: // [CoverageMapFileHeader]
// [CoverageMapFileHeader] // [ArrayStart]
// [ArrayStart] // [CovMapFunctionRecord]
// [CovMapFunctionRecordV2] // [CovMapFunctionRecord]
// [CovMapFunctionRecordV2] // ...
// ... // [ArrayEnd]
// [ArrayEnd] // [Encoded Region Mapping Data]
// [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
LLVM_PACKED_START LLVM_PACKED_START
template <class IntPtrT> template <class IntPtrT> struct CovMapFunctionRecordV1 {
struct CovMapFunctionRecordV1
: accessors::FuncHashAndDataSize<CovMapFunctionRecordV1<IntPtrT>>,
accessors::OutOfLineCovMapping<CovMapFunctionRecordV1<IntPtrT>> {
#define COVMAP_V1 #define COVMAP_V1
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
#include "llvm/ProfileData/InstrProfData.inc" #include "llvm/ProfileData/InstrProfData.inc"
#undef COVMAP_V1 #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. // Return function lookup key. The value is consider opaque.
template <support::endianness Endian> IntPtrT getFuncNameRef() const { template <support::endianness Endian> IntPtrT getFuncNameRef() const {
return support::endian::byte_swap<IntPtrT, Endian>(NamePtr); 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> template <support::endianness Endian>
Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
IntPtrT NameRef = getFuncNameRef<Endian>(); IntPtrT NameRef = getFuncNameRef<Endian>();
@ -804,50 +720,31 @@ struct CovMapFunctionRecordV1
} }
}; };
struct CovMapFunctionRecordV2 struct CovMapFunctionRecord {
: accessors::FuncHashAndDataSize<CovMapFunctionRecordV2>,
accessors::HashedNameRef<CovMapFunctionRecordV2>,
accessors::OutOfLineCovMapping<CovMapFunctionRecordV2> {
#define COVMAP_V2
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name;
#include "llvm/ProfileData/InstrProfData.inc" #include "llvm/ProfileData/InstrProfData.inc"
#undef COVMAP_V2
CovMapFunctionRecordV2() = delete;
};
struct CovMapFunctionRecordV3 // Return the structural hash associated with the function.
: accessors::FuncHashAndDataSize<CovMapFunctionRecordV3>, template <support::endianness Endian> uint64_t getFuncHash() const {
accessors::HashedNameRef<CovMapFunctionRecordV3> { return support::endian::byte_swap<uint64_t, Endian>(FuncHash);
#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);
} }
// Read the inline coverage mapping. Ignore the out-of-line coverage mapping // Return the coverage map data size for the funciton.
// buffer. template <support::endianness Endian> uint32_t getDataSize() const {
template <support::endianness Endian> return support::endian::byte_swap<uint32_t, Endian>(DataSize);
StringRef getCoverageMapping(const char *) const {
return StringRef(&CoverageMapping, getDataSize<Endian>());
} }
// Advance to the next inline coverage mapping and its associated function // Return function lookup key. The value is consider opaque.
// record. Ignore the out-of-line coverage mapping buffer. 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> template <support::endianness Endian>
std::pair<const char *, const CovMapFunctionRecordV3 *> Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const {
advanceByOne(const char *) const { uint64_t NameRef = getFuncNameRef<Endian>();
assert(isAddrAligned(Align(8), this) && "Function record not aligned"); FuncName = ProfileNames.getFuncName(NameRef);
const char *Next = ((const char *)this) + sizeof(CovMapFunctionRecordV3) - return Error::success();
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)};
} }
}; };
@ -884,24 +781,12 @@ enum CovMapVersion {
// A new interpretation of the columnEnd field is added in order to mark // A new interpretation of the columnEnd field is added in order to mark
// regions as gap areas. // regions as gap areas.
Version3 = 2, Version3 = 2,
// Function records are named, uniqued, and moved to a dedicated section. // The current version is Version3
Version4 = 3,
// The current version is Version4.
CurrentVersion = INSTR_PROF_COVMAP_VERSION CurrentVersion = INSTR_PROF_COVMAP_VERSION
}; };
template <int CovMapVersion, class IntPtrT> struct CovMapTraits { template <int CovMapVersion, class IntPtrT> struct CovMapTraits {
using CovMapFuncRecordType = CovMapFunctionRecordV3; using CovMapFuncRecordType = CovMapFunctionRecord;
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 NameRefType = uint64_t; using NameRefType = uint64_t;
}; };

View File

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

View File

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

View File

@ -23,7 +23,6 @@
#include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalValue.h"
#include "llvm/IR/ProfileSummary.h" #include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/InstrProfData.inc" #include "llvm/ProfileData/InstrProfData.inc"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h" #include "llvm/Support/Compiler.h"
#include "llvm/Support/Endian.h" #include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h" #include "llvm/Support/Error.h"
@ -1136,9 +1135,5 @@ void createIRLevelProfileFlagVar(Module &M, bool IsCS);
// Create the variable for the profile file name. // Create the variable for the profile file name.
void createProfileFileNameVar(Module &M, StringRef InstrProfileOutput); 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 } // end namespace llvm
#endif // LLVM_PROFILEDATA_INSTRPROF_H #endif // LLVM_PROFILEDATA_INSTRPROF_H

View File

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

View File

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

View File

@ -804,8 +804,6 @@ static std::string getCoverageMapErrString(coveragemap_error Err) {
return "Truncated coverage data"; return "Truncated coverage data";
case coveragemap_error::malformed: case coveragemap_error::malformed:
return "Malformed coverage data"; 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."); llvm_unreachable("A value of coveragemap_error has no message.");
} }

View File

@ -16,7 +16,6 @@
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h" #include "llvm/ADT/Triple.h"
#include "llvm/Object/Binary.h" #include "llvm/Object/Binary.h"
@ -26,7 +25,6 @@
#include "llvm/Object/COFF.h" #include "llvm/Object/COFF.h"
#include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h" #include "llvm/Support/Casting.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h" #include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h" #include "llvm/Support/Error.h"
@ -42,9 +40,6 @@ using namespace object;
#define DEBUG_TYPE "coverage-mapping" #define DEBUG_TYPE "coverage-mapping"
STATISTIC(CovMapNumRecords, "The # of coverage function records");
STATISTIC(CovMapNumUsedRecords, "The # of used coverage function records");
void CoverageMappingIterator::increment() { void CoverageMappingIterator::increment() {
if (ReadErr != coveragemap_error::success) if (ReadErr != coveragemap_error::success)
return; return;
@ -97,60 +92,10 @@ Error RawCoverageReader::readString(StringRef &Result) {
return Error::success(); return Error::success();
} }
Error RawCoverageFilenamesReader::read( Error RawCoverageFilenamesReader::read() {
CovMapVersion Version,
BinaryCoverageReader::DecompressedData &Decompressed) {
uint64_t NumFilenames; uint64_t NumFilenames;
if (auto Err = readSize(NumFilenames)) if (auto Err = readSize(NumFilenames))
return Err; 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) { for (size_t I = 0; I < NumFilenames; ++I) {
StringRef Filename; StringRef Filename;
if (auto Err = readString(Filename)) if (auto Err = readString(Filename))
@ -435,51 +380,20 @@ static Expected<bool> isCoverageMappingDummy(uint64_t Hash, StringRef Mapping) {
return RawCoverageMappingDummyChecker(Mapping).isDummy(); 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 { namespace {
/// The interface to read coverage mapping function records for a module.
struct CovMapFuncRecordReader { struct CovMapFuncRecordReader {
virtual ~CovMapFuncRecordReader() = default; 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. // mapping data associated with the module.
// //
// Returns a pointer to the next \c CovHeader if it exists, or to an address // Returns a pointer to the next \c CovHeader if it exists, or a pointer
// greater than \p CovEnd if not. // greater than \p End if not.
virtual Expected<const char *> virtual Expected<const char *> readFunctionRecords(const char *Buf,
readCoverageHeader(const char *CovBuf, const char *CovBufEnd, const char *End) = 0;
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;
template <class IntPtrT, support::endianness Endian> template <class IntPtrT, support::endianness Endian>
static Expected<std::unique_ptr<CovMapFuncRecordReader>> static Expected<std::unique_ptr<CovMapFuncRecordReader>>
@ -502,10 +416,6 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
std::vector<StringRef> &Filenames; std::vector<StringRef> &Filenames;
std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records; 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 // 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 // points to the same function name. This is useful to ignore the redundant
// records for the functions with ODR linkage. // 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 // records, which were emitted for inline functions which were seen but
// not used in the corresponding translation unit. // not used in the corresponding translation unit.
Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR, Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR,
StringRef Mapping, StringRef Mapping, size_t FilenamesBegin) {
FilenameRange FileRange) {
++CovMapNumRecords;
uint64_t FuncHash = CFR->template getFuncHash<Endian>(); uint64_t FuncHash = CFR->template getFuncHash<Endian>();
NameRefType NameRef = CFR->template getFuncNameRef<Endian>(); NameRefType NameRef = CFR->template getFuncNameRef<Endian>();
auto InsertResult = auto InsertResult =
@ -526,9 +434,8 @@ class VersionedCovMapFuncRecordReader : public CovMapFuncRecordReader {
return Err; return Err;
if (FuncName.empty()) if (FuncName.empty())
return make_error<InstrProfError>(instrprof_error::malformed); return make_error<InstrProfError>(instrprof_error::malformed);
++CovMapNumUsedRecords; Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin,
Records.emplace_back(Version, FuncName, FuncHash, Mapping, Filenames.size() - FilenamesBegin);
FileRange.StartingIndex, FileRange.Length);
return Error::success(); return Error::success();
} }
// Update the existing record if it's a dummy and the new record is real. // 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; return Err;
if (*NewIsDummyExpected) if (*NewIsDummyExpected)
return Error::success(); return Error::success();
++CovMapNumUsedRecords;
OldRecord.FunctionHash = FuncHash; OldRecord.FunctionHash = FuncHash;
OldRecord.CoverageMapping = Mapping; OldRecord.CoverageMapping = Mapping;
OldRecord.FilenamesBegin = FileRange.StartingIndex; OldRecord.FilenamesBegin = FilenamesBegin;
OldRecord.FilenamesSize = FileRange.Length; OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin;
return Error::success(); return Error::success();
} }
@ -564,134 +470,61 @@ public:
~VersionedCovMapFuncRecordReader() override = default; ~VersionedCovMapFuncRecordReader() override = default;
Expected<const char *> readCoverageHeader( Expected<const char *> readFunctionRecords(const char *Buf,
const char *CovBuf, const char *CovBufEnd, const char *End) override {
BinaryCoverageReader::DecompressedData &Decompressed) override {
using namespace support; using namespace support;
if (CovBuf + sizeof(CovMapHeader) > CovBufEnd) if (Buf + sizeof(CovMapHeader) > End)
return make_error<CoverageMapError>(coveragemap_error::malformed); 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 NRecords = CovHeader->getNRecords<Endian>();
uint32_t FilenamesSize = CovHeader->getFilenamesSize<Endian>(); uint32_t FilenamesSize = CovHeader->getFilenamesSize<Endian>();
uint32_t CoverageSize = CovHeader->getCoverageSize<Endian>(); uint32_t CoverageSize = CovHeader->getCoverageSize<Endian>();
assert((CovMapVersion)CovHeader->getVersion<Endian>() == Version); 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. // 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 const char *FunBuf = Buf;
// are read). Buf += NRecords * sizeof(FuncRecordType);
const char *FuncRecBuf = nullptr; const char *FunEnd = Buf;
const char *FuncRecBufEnd = nullptr;
if (Version < CovMapVersion::Version4)
FuncRecBuf = CovBuf;
CovBuf += NRecords * sizeof(FuncRecordType);
if (Version < CovMapVersion::Version4)
FuncRecBufEnd = CovBuf;
// Get the filenames. // Get the filenames.
if (CovBuf + FilenamesSize > CovBufEnd) if (Buf + FilenamesSize > End)
return make_error<CoverageMapError>(coveragemap_error::malformed); return make_error<CoverageMapError>(coveragemap_error::malformed);
size_t FilenamesBegin = Filenames.size(); size_t FilenamesBegin = Filenames.size();
StringRef FilenameRegion(CovBuf, FilenamesSize); RawCoverageFilenamesReader Reader(StringRef(Buf, FilenamesSize), Filenames);
RawCoverageFilenamesReader Reader(FilenameRegion, Filenames); if (auto Err = Reader.read())
if (auto Err = Reader.read(Version, Decompressed))
return std::move(Err); return std::move(Err);
CovBuf += FilenamesSize; Buf += 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();
}
}
// We'll read the coverage mapping records in the loop below. // 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 const char *CovBuf = Buf;
// coverage header). Buf += CoverageSize;
const char *MappingBuf = CovBuf; const char *CovEnd = Buf;
if (Version == CovMapVersion::Version4 && CoverageSize != 0)
if (Buf > End)
return make_error<CoverageMapError>(coveragemap_error::malformed); 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 // Each coverage map has an alignment of 8, so we need to adjust alignment
// before reading the next map. // 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, // Now use that to read the coverage data.
Optional<FilenameRange> OutOfLineFileRange, if (CovBuf + DataSize > CovEnd)
const char *OutOfLineMappingBuf, return make_error<CoverageMapError>(coveragemap_error::malformed);
const char *OutOfLineMappingBufEnd) override { auto Mapping = StringRef(CovBuf, DataSize);
auto CFR = reinterpret_cast<const FuncRecordType *>(FuncRecBuf); CovBuf += DataSize;
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);
// Look up the set of filenames associated with this function record. if (Error Err =
Optional<FilenameRange> FileRange; insertFunctionRecordIfNeeded(CFR, Mapping, FilenamesBegin))
if (Version < CovMapVersion::Version4) { return std::move(Err);
FileRange = OutOfLineFileRange; CFR++;
} 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);
} }
return Error::success(); return Buf;
} }
}; };
@ -710,34 +543,29 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F); CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F);
case CovMapVersion::Version2: case CovMapVersion::Version2:
case CovMapVersion::Version3: case CovMapVersion::Version3:
case CovMapVersion::Version4:
// Decompress the name data. // Decompress the name data.
if (Error E = P.create(P.getNameData())) if (Error E = P.create(P.getNameData()))
return std::move(E); return std::move(E);
if (Version == CovMapVersion::Version2) if (Version == CovMapVersion::Version2)
return std::make_unique<VersionedCovMapFuncRecordReader< return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F); CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F);
else if (Version == CovMapVersion::Version3) else
return std::make_unique<VersionedCovMapFuncRecordReader< return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version3, IntPtrT, Endian>>(P, R, F); 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"); llvm_unreachable("Unsupported version");
} }
template <typename T, support::endianness Endian> template <typename T, support::endianness Endian>
static Error readCoverageMappingData( static Error readCoverageMappingData(
InstrProfSymtab &ProfileNames, StringRef CovMap, StringRef FuncRecords, InstrProfSymtab &ProfileNames, StringRef Data,
std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records, std::vector<BinaryCoverageReader::ProfileMappingRecord> &Records,
std::vector<StringRef> &Filenames, std::vector<StringRef> &Filenames) {
BinaryCoverageReader::DecompressedData &Decompressed) {
using namespace coverage; using namespace coverage;
// Read the records in the coverage data section. // Read the records in the coverage data section.
auto CovHeader = auto CovHeader =
reinterpret_cast<const CovMapHeader *>(CovMap.data()); reinterpret_cast<const CovMapHeader *>(Data.data());
CovMapVersion Version = (CovMapVersion)CovHeader->getVersion<Endian>(); CovMapVersion Version = (CovMapVersion)CovHeader->getVersion<Endian>();
if (Version > CovMapVersion::CurrentVersion) if (Version > CovMapVersion::CurrentVersion)
return make_error<CoverageMapError>(coveragemap_error::unsupported_version); return make_error<CoverageMapError>(coveragemap_error::unsupported_version);
@ -747,28 +575,12 @@ static Error readCoverageMappingData(
if (Error E = ReaderExpected.takeError()) if (Error E = ReaderExpected.takeError())
return E; return E;
auto Reader = std::move(ReaderExpected.get()); auto Reader = std::move(ReaderExpected.get());
const char *CovBuf = CovMap.data(); for (const char *Buf = Data.data(), *End = Buf + Data.size(); Buf < End;) {
const char *CovBufEnd = CovBuf + CovMap.size(); auto NextHeaderOrErr = Reader->readFunctionRecords(Buf, End);
const char *FuncRecBuf = FuncRecords.data(); if (auto E = NextHeaderOrErr.takeError())
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())
return E; 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(); return Error::success();
} }
@ -776,33 +588,31 @@ static const char *TestingFormatMagic = "llvmcovmtestdata";
Expected<std::unique_ptr<BinaryCoverageReader>> Expected<std::unique_ptr<BinaryCoverageReader>>
BinaryCoverageReader::createCoverageReaderFromBuffer( BinaryCoverageReader::createCoverageReaderFromBuffer(
StringRef Coverage, StringRef FuncRecords, InstrProfSymtab &&ProfileNames, StringRef Coverage, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress,
uint8_t BytesInAddress, support::endianness Endian) { support::endianness Endian) {
std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader()); std::unique_ptr<BinaryCoverageReader> Reader(new BinaryCoverageReader());
Reader->ProfileNames = std::move(ProfileNames); Reader->ProfileNames = std::move(ProfileNames);
if (BytesInAddress == 4 && Endian == support::endianness::little) { if (BytesInAddress == 4 && Endian == support::endianness::little) {
if (Error E = if (Error E =
readCoverageMappingData<uint32_t, support::endianness::little>( readCoverageMappingData<uint32_t, support::endianness::little>(
Reader->ProfileNames, Coverage, FuncRecords, Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->MappingRecords, Reader->Filenames, Reader->Filenames))
Reader->Decompressed))
return std::move(E); return std::move(E);
} else if (BytesInAddress == 4 && Endian == support::endianness::big) { } else if (BytesInAddress == 4 && Endian == support::endianness::big) {
if (Error E = readCoverageMappingData<uint32_t, support::endianness::big>( if (Error E = readCoverageMappingData<uint32_t, support::endianness::big>(
Reader->ProfileNames, Coverage, FuncRecords, Reader->MappingRecords, Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames, Reader->Decompressed)) Reader->Filenames))
return std::move(E); return std::move(E);
} else if (BytesInAddress == 8 && Endian == support::endianness::little) { } else if (BytesInAddress == 8 && Endian == support::endianness::little) {
if (Error E = if (Error E =
readCoverageMappingData<uint64_t, support::endianness::little>( readCoverageMappingData<uint64_t, support::endianness::little>(
Reader->ProfileNames, Coverage, FuncRecords, Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->MappingRecords, Reader->Filenames, Reader->Filenames))
Reader->Decompressed))
return std::move(E); return std::move(E);
} else if (BytesInAddress == 8 && Endian == support::endianness::big) { } else if (BytesInAddress == 8 && Endian == support::endianness::big) {
if (Error E = readCoverageMappingData<uint64_t, support::endianness::big>( if (Error E = readCoverageMappingData<uint64_t, support::endianness::big>(
Reader->ProfileNames, Coverage, FuncRecords, Reader->MappingRecords, Reader->ProfileNames, Coverage, Reader->MappingRecords,
Reader->Filenames, Reader->Decompressed)) Reader->Filenames))
return std::move(E); return std::move(E);
} else } else
return make_error<CoverageMapError>(coveragemap_error::malformed); return make_error<CoverageMapError>(coveragemap_error::malformed);
@ -843,7 +653,7 @@ loadTestingFormat(StringRef Data) {
return make_error<CoverageMapError>(coveragemap_error::malformed); return make_error<CoverageMapError>(coveragemap_error::malformed);
CoverageMapping = CoverageMapping.substr(Pad); CoverageMapping = CoverageMapping.substr(Pad);
return BinaryCoverageReader::createCoverageReaderFromBuffer( return BinaryCoverageReader::createCoverageReaderFromBuffer(
CoverageMapping, "", std::move(ProfileNames), BytesInAddress, Endian); CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian);
} }
static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) { static Expected<SectionRef> lookupSection(ObjectFile &OF, StringRef Name) {
@ -904,31 +714,18 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch) {
/*AddSegmentInfo=*/false)); /*AddSegmentInfo=*/false));
if (auto E = CoverageSection.takeError()) if (auto E = CoverageSection.takeError())
return std::move(E); return std::move(E);
// Get the contents of the given sections.
auto CoverageMappingOrErr = CoverageSection->getContents(); auto CoverageMappingOrErr = CoverageSection->getContents();
if (!CoverageMappingOrErr) if (!CoverageMappingOrErr)
return CoverageMappingOrErr.takeError(); return CoverageMappingOrErr.takeError();
StringRef CoverageMapping = CoverageMappingOrErr.get();
InstrProfSymtab ProfileNames; InstrProfSymtab ProfileNames;
if (Error E = ProfileNames.create(*NamesSection)) if (Error E = ProfileNames.create(*NamesSection))
return std::move(E); 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( return BinaryCoverageReader::createCoverageReaderFromBuffer(
CoverageMapping, FuncRecords, std::move(ProfileNames), BytesInAddress, CoverageMappingOrErr.get(), std::move(ProfileNames), BytesInAddress,
Endian); Endian);
} }

View File

@ -11,11 +11,9 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/LEB128.h" #include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include <algorithm> #include <algorithm>
@ -36,31 +34,12 @@ CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter(
#endif #endif
} }
void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) { void CoverageFilenamesSectionWriter::write(raw_ostream &OS) {
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)
cantFail(
zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression));
// ::= <num-filenames>
// <uncompressed-len>
// <compressed-len-or-zero>
// (<compressed-filenames> | <uncompressed-filenames>)
encodeULEB128(Filenames.size(), OS); encodeULEB128(Filenames.size(), OS);
encodeULEB128(FilenamesStr.size(), OS); for (const auto &Filename : Filenames) {
encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS); encodeULEB128(Filename.size(), OS);
OS << (doCompression ? StringRef(CompressedStr) : StringRef(FilenamesStr)); OS << Filename;
}
} }
namespace { namespace {

View File

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

View File

@ -74,6 +74,10 @@ cl::opt<unsigned> MemOPSizeLarge(
namespace { namespace {
cl::opt<bool> DoNameCompression("enable-name-compression",
cl::desc("Enable name string compression"),
cl::init(true));
cl::opt<bool> DoHashBasedCounterSplit( cl::opt<bool> DoHashBasedCounterSplit(
"hash-based-counter-split", "hash-based-counter-split",
cl::desc("Rename counter variable of a comdat function based on cfg hash"), cl::desc("Rename counter variable of a comdat function based on cfg hash"),
@ -912,7 +916,7 @@ void InstrProfiling::emitNameData() {
std::string CompressedNameStr; std::string CompressedNameStr;
if (Error E = collectPGOFuncNameStrings(ReferencedNames, CompressedNameStr, if (Error E = collectPGOFuncNameStrings(ReferencedNames, CompressedNameStr,
DoInstrProfNameCompression)) { DoNameCompression)) {
report_fatal_error(toString(std::move(E)), false); 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. ;; 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 ; 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" @__llvm_coverage_mapping = internal constant i32 0, section "__llvm_covmap"
; CHECK-DAG: .section __llvm_covfun,""
; CHECK-DAG: .section __llvm_covmap,"" ; 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.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.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.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 // 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, false}),
std::pair<bool, bool>({true, true})),); 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 } // end anonymous namespace