mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-19 03:25:54 -04:00
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:
parent
9b15873c92
commit
f208b70fbc
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
|
||||||
|
@ -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, let’s consider a C file and how it gets compiled to LLVM:
|
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;
|
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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,""
|
||||||
|
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.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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user