Index: clang/lib/CodeGen/CoverageMappingGen.h =================================================================== --- clang/lib/CodeGen/CoverageMappingGen.h +++ clang/lib/CodeGen/CoverageMappingGen.h @@ -47,17 +47,27 @@ /// Organizes the cross-function state that is used while generating /// code coverage mapping data. 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; CoverageSourceInfo &SourceInfo; llvm::SmallDenseMap FileEntries; - std::vector FunctionRecords; std::vector FunctionNames; - llvm::StructType *FunctionRecordTy; - std::vector CoverageMappings; + std::vector FunctionRecords; + + /// Emit a function record. + void emitFunctionMappingRecord(const FunctionInfo &Info, + uint64_t FilenamesRef); public: CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo) - : CGM(CGM), SourceInfo(SourceInfo), FunctionRecordTy(nullptr) {} + : CGM(CGM), SourceInfo(SourceInfo) {} CoverageSourceInfo &getSourceInfo() const { return SourceInfo; Index: clang/lib/CodeGen/CoverageMappingGen.cpp =================================================================== --- clang/lib/CodeGen/CoverageMappingGen.cpp +++ clang/lib/CodeGen/CoverageMappingGen.cpp @@ -13,6 +13,8 @@ #include "CoverageMappingGen.h" #include "CodeGenFunction.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" @@ -24,6 +26,10 @@ #include "llvm/Support/FileSystem.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 CodeGen; using namespace llvm::coverage; @@ -1272,12 +1278,6 @@ } }; -std::string getCoverageSection(const CodeGenModule &CGM) { - return llvm::getInstrProfSectionName( - llvm::IPSK_covmap, - CGM.getContext().getTargetInfo().getTriple().getObjectFormat()); -} - std::string normalizeFilename(StringRef Filename) { llvm::SmallString<256> Path(Filename); llvm::sys::fs::make_absolute(Path); @@ -1317,30 +1317,71 @@ } } +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( llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash, const std::string &CoverageMapping, bool IsUsed) { llvm::LLVMContext &Ctx = CGM.getLLVMContext(); - if (!FunctionRecordTy) { -#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); - } + const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue); + FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed}); - #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) FunctionNames.push_back( llvm::ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx))); - CoverageMappings.push_back(CoverageMapping); if (CGM.getCodeGenOpts().DumpCoverageMapping) { // Dump the coverage mapping data for this function by decoding the @@ -1385,37 +1426,22 @@ FilenameRefs[I] = FilenameStrs[I]; } - std::string FilenamesAndCoverageMappings; - llvm::raw_string_ostream OS(FilenamesAndCoverageMappings); - 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(); - } - CoverageMappings.clear(); - CoverageMappings.shrink_to_fit(); - - size_t FilenamesSize = OS.str().size() - CoverageMappingSize; - // Append extra zeroes if necessary to ensure that the size of the filenames - // and coverage mappings is a multiple of 8. - if (size_t Rem = OS.str().size() % 8) { - CoverageMappingSize += 8 - Rem; - OS.write_zeros(8 - Rem); + std::string Filenames; + { + llvm::raw_string_ostream OS(Filenames); + CoverageFilenamesSectionWriter(FilenameRefs).write(OS); } - auto *FilenamesAndMappingsVal = - llvm::ConstantDataArray::getString(Ctx, OS.str(), false); + auto *FilenamesVal = + llvm::ConstantDataArray::getString(Ctx, Filenames, false); + const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames); - // Create the deferred function records array - auto RecordsTy = - llvm::ArrayType::get(FunctionRecordTy, FunctionRecords.size()); - auto RecordsVal = llvm::ConstantArray::get(RecordsTy, FunctionRecords); + // Emit the function records. + for (const FunctionInfo &Info : FunctionRecords) + emitFunctionMappingRecord(Info, FilenamesRef); + const unsigned NRecords = 0; + const size_t FilenamesSize = Filenames.size(); + const unsigned CoverageMappingSize = 0; llvm::Type *CovDataHeaderTypes[] = { #define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType, #include "llvm/ProfileData/InstrProfData.inc" @@ -1430,18 +1456,16 @@ CovDataHeaderTy, makeArrayRef(CovDataHeaderVals)); // Create the coverage data record - llvm::Type *CovDataTypes[] = {CovDataHeaderTy, RecordsTy, - FilenamesAndMappingsVal->getType()}; + llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()}; auto CovDataTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataTypes)); - llvm::Constant *TUDataVals[] = {CovDataHeaderVal, RecordsVal, - FilenamesAndMappingsVal}; + llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal}; auto CovDataVal = llvm::ConstantStruct::get(CovDataTy, makeArrayRef(TUDataVals)); auto CovData = new llvm::GlobalVariable( - CGM.getModule(), CovDataTy, true, llvm::GlobalValue::InternalLinkage, + CGM.getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage, CovDataVal, llvm::getCoverageMappingVarName()); - CovData->setSection(getCoverageSection(CGM)); + CovData->setSection(getInstrProfSection(CGM, llvm::IPSK_covmap)); CovData->setAlignment(llvm::Align(8)); // Make sure the data doesn't get deleted. Index: clang/test/CoverageMapping/abspath.cpp =================================================================== --- clang/test/CoverageMapping/abspath.cpp +++ clang/test/CoverageMapping/abspath.cpp @@ -1,4 +1,4 @@ -// 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 +// 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 // RMDOTS: @__llvm_coverage_mapping = {{.*}}"\01 // RMDOTS-NOT: Inputs @@ -6,7 +6,7 @@ // RUN: mkdir -p %t/test && cd %t/test // RUN: echo "void f1() {}" > f1.c -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -main-file-name abspath.cpp ../test/f1.c -o - | FileCheck -check-prefix=RELPATH %s +// 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 // RELPATH: @__llvm_coverage_mapping = {{.*}}"\01 // RELPATH: {{[/\\].*(/|\\\\)test(/|\\\\)f1}}.c Index: clang/test/CoverageMapping/ir.c =================================================================== --- clang/test/CoverageMapping/ir.c +++ clang/test/CoverageMapping/ir.c @@ -1,12 +1,31 @@ // 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 | FileCheck %s +// 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--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) { foo(); return 0; } -// 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 }>] [<{{.*}}> <{{.*}}>, <{{.*}}> <{{.*}}>] +// Check the function records. Two of the record names should come in the 'used' +// 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 Index: clang/test/Profile/def-assignop.cpp =================================================================== --- clang/test/Profile/def-assignop.cpp +++ clang/test/Profile/def-assignop.cpp @@ -18,9 +18,12 @@ // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 // PGOGEN: store{{.*}}@__profc__ZN1AaSEOS_ - // Check that coverage mapping includes 6 function records including the + // Check that coverage mapping includes 3 function records including the // defaulted copy and move operators: A::operator= - // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [3 x <{{.*}}>], + // COVMAP: section "__llvm_covfun", comdat + // COVMAP: section "__llvm_covfun", comdat + // COVMAP: section "__llvm_covfun", comdat + // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 } B b; }; Index: clang/test/Profile/def-ctors.cpp =================================================================== --- clang/test/Profile/def-ctors.cpp +++ clang/test/Profile/def-ctors.cpp @@ -20,11 +20,15 @@ // PGOGEN-DAG: {{.*}}add{{.*}}%pgocount, 1 // PGOGEN-DAG: store{{.*}}@__profc__ZN7DerivedC2Ev - // Check that coverage mapping has 6 function records including + // Check that coverage mapping has 5 function records including // the defaulted Derived::Derived(const Derived), and Derived::Derived() // methds. - // 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: section "__llvm_covfun", comdat + // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 } }; Derived dd; Index: clang/test/Profile/def-dtors.cpp =================================================================== --- clang/test/Profile/def-dtors.cpp +++ clang/test/Profile/def-dtors.cpp @@ -16,10 +16,14 @@ // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 // PGOGEN: store{{.*}}@__profc__ZN7DerivedD2Ev - // Check that coverage mapping has 6 function records including + // Check that coverage mapping has 5 function records including // the default destructor in the derived class. - // 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: section "__llvm_covfun", comdat + // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 } }; int main() { Index: compiler-rt/include/profile/InstrProfData.inc =================================================================== --- compiler-rt/include/profile/InstrProfData.inc +++ compiler-rt/include/profile/InstrProfData.inc @@ -198,6 +198,14 @@ #undef VALUE_PROF_KIND /* VALUE_PROF_KIND end */ +#undef COVMAP_V2_OR_V3 +#ifdef COVMAP_V2 +#define COVMAP_V2_OR_V3 +#endif +#ifdef COVMAP_V3 +#define COVMAP_V2_OR_V3 +#endif + /* COVMAP_FUNC_RECORD start */ /* Definition of member fields of the function record structure in coverage * map. @@ -214,16 +222,30 @@ COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \ llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), \ NameValue.size())) -#else +#endif +#ifdef COVMAP_V2_OR_V3 COVMAP_FUNC_RECORD(const int64_t, llvm::Type::getInt64Ty(Ctx), NameRef, \ - llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \ - llvm::IndexedInstrProf::ComputeHash(NameValue))) + llvm::ConstantInt::get( \ + llvm::Type::getInt64Ty(Ctx), NameHash)) #endif COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), DataSize, \ - llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\ - CoverageMapping.size())) + llvm::ConstantInt::get( \ + llvm::Type::getInt32Ty(Ctx), CoverageMapping.size())) COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \ - llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), FuncHash)) + llvm::ConstantInt::get( \ + llvm::Type::getInt64Ty(Ctx), FuncHash)) +#ifdef COVMAP_V3 +COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FilenamesRef, \ + llvm::ConstantInt::get( \ + llvm::Type::getInt64Ty(Ctx), FilenamesRef)) +COVMAP_FUNC_RECORD(const char, \ + llvm::ArrayType::get(llvm::Type::getInt8Ty(Ctx), \ + CoverageMapping.size()), \ + CoverageMapping, + llvm::ConstantDataArray::getRaw( \ + CoverageMapping, CoverageMapping.size(), \ + llvm::Type::getInt8Ty(Ctx))) +#endif #undef COVMAP_FUNC_RECORD /* COVMAP_FUNC_RECORD end. */ @@ -236,7 +258,7 @@ #define INSTR_PROF_DATA_DEFINED #endif COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \ - llvm::ConstantInt::get(Int32Ty, FunctionRecords.size())) + llvm::ConstantInt::get(Int32Ty, NRecords)) COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \ llvm::ConstantInt::get(Int32Ty, FilenamesSize)) COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \ @@ -267,6 +289,9 @@ INSTR_PROF_SECT_ENTRY(IPSK_covmap, \ INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON), \ INSTR_PROF_COVMAP_COFF, "__LLVM_COV,") +INSTR_PROF_SECT_ENTRY(IPSK_covfun, \ + INSTR_PROF_QUOTE(INSTR_PROF_COVFUN_COMMON), \ + INSTR_PROF_COVFUN_COFF, "__LLVM_COV,") INSTR_PROF_SECT_ENTRY(IPSK_orderfile, \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON), \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COFF), "__DATA,") @@ -633,8 +658,8 @@ #define INSTR_PROF_RAW_VERSION 5 /* Indexed profile format version (start from 1). */ #define INSTR_PROF_INDEX_VERSION 5 -/* Coverage mapping format vresion (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 2 +/* Coverage mapping format version (start from 0). */ +#define INSTR_PROF_COVMAP_VERSION 3 /* 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 @@ -661,6 +686,7 @@ #define INSTR_PROF_VALS_COMMON __llvm_prf_vals #define INSTR_PROF_VNODES_COMMON __llvm_prf_vnds #define INSTR_PROF_COVMAP_COMMON __llvm_covmap +#define INSTR_PROF_COVFUN_COMMON __llvm_covfun #define INSTR_PROF_ORDERFILE_COMMON __llvm_orderfile /* Windows section names. Because these section names contain dollar characters, * they must be quoted. @@ -671,6 +697,7 @@ #define INSTR_PROF_VALS_COFF ".lprfv$M" #define INSTR_PROF_VNODES_COFF ".lprfnd$M" #define INSTR_PROF_COVMAP_COFF ".lcovmap$M" +#define INSTR_PROF_COVFUN_COFF ".lcovfun$M" #define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M" #ifdef _WIN32 @@ -685,6 +712,7 @@ /* Value profile nodes section. */ #define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_VNODES_COFF #define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_COVMAP_COFF +#define INSTR_PROF_COVFUN_SECT_NAME INSTR_PROF_COVFUN_COFF #define INSTR_PROF_ORDERFILE_SECT_NAME INSTR_PROF_ORDERFILE_COFF #else /* Runtime section names and name strings. */ @@ -698,6 +726,7 @@ /* Value profile nodes section. */ #define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_VNODES_COMMON) #define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON) +#define INSTR_PROF_COVFUN_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVFUN_COMMON) /* Order file instrumentation. */ #define INSTR_PROF_ORDERFILE_SECT_NAME \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON) @@ -752,3 +781,5 @@ #else #undef INSTR_PROF_DATA_DEFINED #endif + +#undef COVMAP_V2_OR_V3 Index: llvm/docs/CoverageMappingFormat.rst =================================================================== --- llvm/docs/CoverageMappingFormat.rst +++ llvm/docs/CoverageMappingFormat.rst @@ -15,46 +15,23 @@ analysis using LLVM's and Clang's instrumenation based profiling (Clang's ``-fprofile-instr-generate`` option). -This document is aimed at those who use LLVM's code coverage mapping to provide -code coverage analysis for their own programs, and for those who would like -to know how it works under the hood. A prior knowledge of how Clang's profile -guided optimization works is useful, but not required. +This document is aimed at those who would like to know how LLVM's code coverage +mapping works under the hood. A prior knowledge of how Clang's profile guided +optimization works is useful, but not required. For those interested in using +LLVM to provide code coverage analysis for their own programs, see the `Clang +documentation `. -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 +We start by briefly describing LLVM's code coverage mapping format and the way that Clang and LLVM's code coverage tool work with this format. After the basics are down, more advanced features of the coverage mapping format are discussed - such as the data structures, LLVM IR representation and the binary encoding. -Quick Start -=========== - -Here's a short story that describes how to generate code coverage overview -for a sample source file called *test.c*. - -* First, compile an instrumented version of your program using Clang's - ``-fprofile-instr-generate`` option with the additional ``-fcoverage-mapping`` - option: - - ``clang -o test -fprofile-instr-generate -fcoverage-mapping test.c`` -* Then, run the instrumented binary. The runtime will produce a file called - *default.profraw* containing the raw profile instrumentation data: - - ``./test`` -* After that, merge the profile data using the *llvm-profdata* tool: - - ``llvm-profdata merge -o test.profdata default.profraw`` -* Finally, run LLVM's code coverage tool (*llvm-cov*) to produce the code - coverage overview for the sample source file: - - ``llvm-cov show ./test -instr-profile=test.profdata test.c`` - High Level Overview =================== LLVM's code coverage mapping format is designed to be a self contained -data format, that can be embedded into the LLVM IR and object files. +data format that can be embedded into the LLVM IR and into object files. It's described in this document as a **mapping** format because its goal is to store the data that is required for a code coverage tool to map between the specific source ranges in a file and the execution counts obtained @@ -224,9 +201,9 @@ LLVM IR Representation ====================== -The coverage mapping data is stored in the LLVM IR using a single global -constant structure variable called *__llvm_coverage_mapping* -with the *__llvm_covmap* section specifier. +The coverage mapping data is stored in the LLVM IR using a global constant +structure variable called *__llvm_coverage_mapping* with the *IPSK_covmap* +section specifier (i.e. ".lcovmap$M" on Windows and "__llvm_covmap" elsewhere). For example, let’s consider a C file and how it gets compiled to LLVM: @@ -241,42 +218,45 @@ return 13; } -The coverage mapping variable generated by Clang has 3 fields: +The coverage mapping variable generated by Clang has 2 fields: * Coverage mapping header. -* An array of function records. +* An optionally compressed list of filenames present in the translation unit. -* Coverage mapping data which is an array of bytes. Zero paddings are added at the end to force 8 byte alignment. +The variable has 8-byte alignment because ld64 cannot always pack symbols from +different object files tightly (the word-level alignment assumption is baked in +too deeply). .. code-block:: llvm - @__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [2 x { i64, i32, i64 }], [40 x i8] } - { + @__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [32 x i8] } + { { i32, i32, i32, i32 } ; Coverage map header { - i32 2, ; The number of function records - i32 20, ; The length of the string that contains the encoded translation unit filenames - i32 20, ; The length of the string that contains the encoded coverage mapping data - i32 2, ; Coverage mapping format version + i32 0, ; Always 0. In prior versions, the number of affixed function records + i32 32, ; The length of the string that contains the encoded translation unit filenames + i32 0, ; Always 0. In prior versions, the length of the affixed string that contains the encoded coverage mapping data + i32 3, ; Coverage mapping format version }, - [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) + [32 x i8] c"..." ; Encoded data (dissected later) }, section "__llvm_covmap", align 8 -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. +The current version of the format is version 4. There are two differences from version 3: -The function record layout has evolved since version 1. In version 1, the function record for *foo* is defined as follows: +* Function records are now named symbols, and are marked *linkonce_odr*. This + allows linkers to merge duplicate function records. Merging of duplicate + *dummy* records (emitted for functions included-but-not-used in a translation + unit) reduces size bloat in the coverage mapping data. As part of this + change, region mapping information for a function is now included within the + function record, instead of being affixed to the coverage header. + +* The filename list for a translation unit may optionally be zlib-compressed. + +The only difference between versions 3 and 2 is that a special encoding for +column end locations was introduced to indicate gap regions. + +In version 1, the function record for *foo* was defined as follows: .. code-block:: llvm @@ -286,19 +266,27 @@ i64 0 ; Function's structural hash } +In version 2, the function record for *foo* was defined as follows: + +.. code-block:: llvm + + { i64, i32, i64 } { + i64 0x5cf8c24cdb18bdac, ; Function's name MD5 + i32 9, ; Function's encoded coverage mapping data string length + i64 0 ; Function's structural hash Coverage Mapping Header: ------------------------ The coverage mapping header has the following fields: -* The number of function records. +* The number of function records 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 translation unit filenames. -* The length of the string in the third field of *__llvm_coverage_mapping* that contains the encoded coverage mapping data. +* 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 format version. The current version is 3 (encoded as a 2). +* The format version. The current version is 4 (encoded as a 3). .. _function records: @@ -309,24 +297,11 @@ .. code-block:: llvm - { i64, i32, i64 } + { i64, i32, i64, i64, [? x i8] } -It contains function name's MD5, the length of the encoded mapping data for that function, and function's -structural hash value. - -Encoded data: -------------- - -The encoded data is stored in a single string that contains -the encoded filenames used by this translation unit and the encoded coverage -mapping data for each function in this translation unit. - -The encoded data has the following structure: - -``[filenames, coverageMappingDataForFunctionRecord0, coverageMappingDataForFunctionRecord1, ..., padding]`` - -If necessary, the encoded data is padded with zeroes so that the size -of the data string is rounded up to the nearest multiple of 8 bytes. +It contains the function name's MD5, the length of the encoded mapping data for +that function, the function's structural hash value, the hash of the filenames +in the function's translation unit, and the encoded mapping data. Dissecting the sample: ^^^^^^^^^^^^^^^^^^^^^^ @@ -339,32 +314,18 @@ .. code-block:: llvm - 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" + c"\01\15\1Dx\DA\13\D1\0F-N-*\D6/+\CE\D6/\C9-\D0O\CB\CF\D7K\06\00N+\07]" * The string contains values that are encoded in the LEB128 format, which is - used throughout for storing integers. It also contains a string value. + used throughout for storing integers. It also contains a compressed payload. -* The length of the substring that contains the encoded translation unit - filenames is the value of the second field in the *__llvm_coverage_mapping* - structure, which is 20, thus the filenames are encoded in this string: +* The first three LEB128-encoded numbers in the sample specify the number of + filenames, the length of the uncompressed filenames, and the length of the + compressed payload (or 0 if compression is disabled). In this sample, there + is 1 filename that is 21 bytes in length (uncompressed), and stored in 29 + bytes (compressed). - .. 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: +* The coverage mapping from the first function record is encoded in this string: .. code-block:: llvm Index: llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h =================================================================== --- llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -24,6 +24,7 @@ #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" @@ -54,7 +55,8 @@ no_data_found, unsupported_version, truncated, - malformed + malformed, + decompression_failed }; const std::error_category &coveragemap_category(); @@ -678,37 +680,119 @@ return make_range(Begin, End); } -// Profile coverage map has the following layout: -// [CoverageMapFileHeader] -// [ArrayStart] -// [CovMapFunctionRecord] -// [CovMapFunctionRecord] -// ... -// [ArrayEnd] -// [Encoded Region Mapping Data] +// Coverage mappping data (V2) has the following layout: +// IPSK_covmap: +// [CoverageMapFileHeader] +// [ArrayStart] +// [CovMapFunctionRecordV2] +// [CovMapFunctionRecordV2] +// ... +// [ArrayEnd] +// [Encoded Filenames and Region Mapping Data] +// +// Coverage mappping data (V3) has the following layout: +// IPSK_covmap: +// [CoverageMapFileHeader] +// [Encoded Filenames] +// IPSK_covfun: +// [ArrayStart] +// odr_name_1: [CovMapFunctionRecordV3] +// odr_name_2: [CovMapFunctionRecordV3] +// ... +// [ArrayEnd] +// +// Both versions of the coverage mapping format encode the same information, +// but the V3 format does so more compactly by taking advantage of linkonce_odr +// semantics (it allows exactly 1 function record per name reference). + +/// This namespace defines accessors (*not* data) shared by different versions +/// of coverage mapping records. The functionality is CRTP'd in. +namespace accessors { + +/// Accessors for a 64-bit function hash and a 64-bit coverage mapping data +/// size. +template struct FuncHashAndDataSize { + using ThisTy = const FuncRecordTy *; + + // Return the structural hash associated with the function. + template uint64_t getFuncHash() const { + return support::endian::byte_swap( + static_cast(this)->FuncHash); + } + + // Return the coverage map data size for the function. + template uint32_t getDataSize() const { + return support::endian::byte_swap( + static_cast(this)->DataSize); + } +}; + +/// Accessors for a hashed function name. +template struct HashedNameRef { + using ThisTy = const FuncRecordTy *; + + // Return the function lookup key. The value is considered opaque. + template uint64_t getFuncNameRef() const { + return support::endian::byte_swap( + static_cast(this)->NameRef); + } + + // Return the PGO name of the function. + template + Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { + uint64_t NameRef = getFuncNameRef(); + 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 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 uint64_t getFilenamesRef() const { + llvm_unreachable("Record does not contain a reference to filenames"); + } + + // Read coverage mapping out-of-line, from \p MappingBuf. + template + StringRef getCoverageMapping(const char *MappingBuf) const { + return {MappingBuf, + static_cast(this)->template getDataSize()}; + } + + // Advance to the next out-of-line coverage mapping and its associated + // function record. + template + std::pair + advanceByOne(const char *MappingBuf) const { + auto *CFR = static_cast(this); + return {MappingBuf + CFR->template getDataSize(), CFR + 1}; + } +}; +} // end namespace accessors + LLVM_PACKED_START -template struct CovMapFunctionRecordV1 { +template +struct CovMapFunctionRecordV1 + : accessors::FuncHashAndDataSize>, + accessors::OutOfLineCovMapping> { #define COVMAP_V1 #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; #include "llvm/ProfileData/InstrProfData.inc" #undef COVMAP_V1 - - // Return the structural hash associated with the function. - template uint64_t getFuncHash() const { - return support::endian::byte_swap(FuncHash); - } - - // Return the coverage map data size for the funciton. - template uint32_t getDataSize() const { - return support::endian::byte_swap(DataSize); - } + CovMapFunctionRecordV1() = delete; // Return function lookup key. The value is consider opaque. template IntPtrT getFuncNameRef() const { return support::endian::byte_swap(NamePtr); } - // Return the PGO name of the function */ + // Return the PGO name of the function. template Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { IntPtrT NameRef = getFuncNameRef(); @@ -720,31 +804,50 @@ } }; -struct CovMapFunctionRecord { +struct CovMapFunctionRecordV2 + : accessors::FuncHashAndDataSize, + accessors::HashedNameRef, + accessors::OutOfLineCovMapping { +#define COVMAP_V2 #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; #include "llvm/ProfileData/InstrProfData.inc" +#undef COVMAP_V2 + CovMapFunctionRecordV2() = delete; +}; - // Return the structural hash associated with the function. - template uint64_t getFuncHash() const { - return support::endian::byte_swap(FuncHash); - } +struct CovMapFunctionRecordV3 + : accessors::FuncHashAndDataSize, + accessors::HashedNameRef { +#define COVMAP_V3 +#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; +#include "llvm/ProfileData/InstrProfData.inc" +#undef COVMAP_V3 + CovMapFunctionRecordV3() = delete; - // Return the coverage map data size for the funciton. - template uint32_t getDataSize() const { - return support::endian::byte_swap(DataSize); + // Get the filename set reference. + template uint64_t getFilenamesRef() const { + return support::endian::byte_swap(FilenamesRef); } - // Return function lookup key. The value is consider opaque. - template uint64_t getFuncNameRef() const { - return support::endian::byte_swap(NameRef); + // Read the inline coverage mapping. Ignore the out-of-line coverage mapping + // buffer. + template + StringRef getCoverageMapping(const char *) const { + return StringRef(&CoverageMapping, getDataSize()); } - // Return the PGO name of the function */ + // Advance to the next inline coverage mapping and its associated function + // record. Ignore the out-of-line coverage mapping buffer. template - Error getFuncName(InstrProfSymtab &ProfileNames, StringRef &FuncName) const { - uint64_t NameRef = getFuncNameRef(); - FuncName = ProfileNames.getFuncName(NameRef); - return Error::success(); + std::pair + advanceByOne(const char *) const { + assert(isAddrAligned(Align(8), this) && "Function record not aligned"); + const char *Next = ((const char *)this) + sizeof(CovMapFunctionRecordV3) - + sizeof(char) + getDataSize(); + // 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(Next)}; } }; @@ -781,12 +884,24 @@ // A new interpretation of the columnEnd field is added in order to mark // regions as gap areas. Version3 = 2, - // The current version is Version3 + // Function records are named, uniqued, and moved to a dedicated section. + Version4 = 3, + // The current version is Version4. CurrentVersion = INSTR_PROF_COVMAP_VERSION }; template struct CovMapTraits { - using CovMapFuncRecordType = CovMapFunctionRecord; + using CovMapFuncRecordType = CovMapFunctionRecordV3; + using NameRefType = uint64_t; +}; + +template struct CovMapTraits { + using CovMapFuncRecordType = CovMapFunctionRecordV2; + using NameRefType = uint64_t; +}; + +template struct CovMapTraits { + using CovMapFuncRecordType = CovMapFunctionRecordV2; using NameRefType = uint64_t; }; Index: llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h =================================================================== --- llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h +++ llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h @@ -113,20 +113,6 @@ Error readString(StringRef &Result); }; -/// Reader for the raw coverage filenames. -class RawCoverageFilenamesReader : public RawCoverageReader { - std::vector &Filenames; - -public: - RawCoverageFilenamesReader(StringRef Data, std::vector &Filenames) - : RawCoverageReader(Data), Filenames(Filenames) {} - RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete; - RawCoverageFilenamesReader & - operator=(const RawCoverageFilenamesReader &) = delete; - - Error read(); -}; - /// Checks if the given coverage mapping data is exported for /// an unused function. class RawCoverageMappingDummyChecker : public RawCoverageReader { @@ -188,6 +174,8 @@ FilenamesBegin(FilenamesBegin), FilenamesSize(FilenamesSize) {} }; + using DecompressedData = std::vector>>; + private: std::vector Filenames; std::vector MappingRecords; @@ -197,6 +185,10 @@ std::vector Expressions; std::vector MappingRegions; + // Used to tie the lifetimes of decompressed strings to the lifetime of this + // BinaryCoverageReader instance. + DecompressedData Decompressed; + BinaryCoverageReader() = default; public: @@ -208,7 +200,7 @@ SmallVectorImpl> &ObjectFileBuffers); static Expected> - createCoverageReaderFromBuffer(StringRef Coverage, + createCoverageReaderFromBuffer(StringRef Coverage, StringRef FuncRecords, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, support::endianness Endian); @@ -216,6 +208,24 @@ Error readNextRecord(CoverageMappingRecord &Record) override; }; +/// Reader for the raw coverage filenames. +class RawCoverageFilenamesReader : public RawCoverageReader { + std::vector &Filenames; + + // Read an uncompressed sequence of filenames. + Error readUncompressed(uint64_t NumFilenames); + +public: + RawCoverageFilenamesReader(StringRef Data, std::vector &Filenames) + : RawCoverageReader(Data), Filenames(Filenames) {} + RawCoverageFilenamesReader(const RawCoverageFilenamesReader &) = delete; + RawCoverageFilenamesReader & + operator=(const RawCoverageFilenamesReader &) = delete; + + Error read(CovMapVersion Version, + BinaryCoverageReader::DecompressedData &Decompressed); +}; + } // end namespace coverage } // end namespace llvm Index: llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h =================================================================== --- llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h +++ llvm/include/llvm/ProfileData/Coverage/CoverageMappingWriter.h @@ -32,8 +32,9 @@ public: CoverageFilenamesSectionWriter(ArrayRef Filenames); - /// Write encoded filenames to the given output stream. - void write(raw_ostream &OS); + /// Write encoded filenames to the given output stream. If \p Compress is + /// true, attempt to compress the filenames. + void write(raw_ostream &OS, bool Compress = true); }; /// Writer for instrumentation based coverage mapping data. Index: llvm/include/llvm/ProfileData/InstrProf.h =================================================================== --- llvm/include/llvm/ProfileData/InstrProf.h +++ llvm/include/llvm/ProfileData/InstrProf.h @@ -23,6 +23,7 @@ #include "llvm/IR/GlobalValue.h" #include "llvm/IR/ProfileSummary.h" #include "llvm/ProfileData/InstrProfData.inc" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -1135,5 +1136,9 @@ // Create the variable for the profile file name. void createProfileFileNameVar(Module &M, StringRef InstrProfileOutput); +// Whether to compress function names in profile records, and filenames in +// code coverage mappings. Used by the Instrumentation library and unit tests. +extern cl::opt DoInstrProfNameCompression; + } // end namespace llvm #endif // LLVM_PROFILEDATA_INSTRPROF_H Index: llvm/include/llvm/ProfileData/InstrProfData.inc =================================================================== --- llvm/include/llvm/ProfileData/InstrProfData.inc +++ llvm/include/llvm/ProfileData/InstrProfData.inc @@ -198,6 +198,14 @@ #undef VALUE_PROF_KIND /* VALUE_PROF_KIND end */ +#undef COVMAP_V2_OR_V3 +#ifdef COVMAP_V2 +#define COVMAP_V2_OR_V3 +#endif +#ifdef COVMAP_V3 +#define COVMAP_V2_OR_V3 +#endif + /* COVMAP_FUNC_RECORD start */ /* Definition of member fields of the function record structure in coverage * map. @@ -214,16 +222,30 @@ COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \ llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), \ NameValue.size())) -#else +#endif +#ifdef COVMAP_V2_OR_V3 COVMAP_FUNC_RECORD(const int64_t, llvm::Type::getInt64Ty(Ctx), NameRef, \ - llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \ - llvm::IndexedInstrProf::ComputeHash(NameValue))) + llvm::ConstantInt::get( \ + llvm::Type::getInt64Ty(Ctx), NameHash)) #endif COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), DataSize, \ - llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\ - CoverageMapping.size())) + llvm::ConstantInt::get( \ + llvm::Type::getInt32Ty(Ctx), CoverageMapping.size())) COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \ - llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), FuncHash)) + llvm::ConstantInt::get( \ + llvm::Type::getInt64Ty(Ctx), FuncHash)) +#ifdef COVMAP_V3 +COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FilenamesRef, \ + llvm::ConstantInt::get( \ + llvm::Type::getInt64Ty(Ctx), FilenamesRef)) +COVMAP_FUNC_RECORD(const char, \ + llvm::ArrayType::get(llvm::Type::getInt8Ty(Ctx), \ + CoverageMapping.size()), \ + CoverageMapping, + llvm::ConstantDataArray::getRaw( \ + CoverageMapping, CoverageMapping.size(), \ + llvm::Type::getInt8Ty(Ctx))) +#endif #undef COVMAP_FUNC_RECORD /* COVMAP_FUNC_RECORD end. */ @@ -236,7 +258,7 @@ #define INSTR_PROF_DATA_DEFINED #endif COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \ - llvm::ConstantInt::get(Int32Ty, FunctionRecords.size())) + llvm::ConstantInt::get(Int32Ty, NRecords)) COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \ llvm::ConstantInt::get(Int32Ty, FilenamesSize)) COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \ @@ -267,6 +289,9 @@ INSTR_PROF_SECT_ENTRY(IPSK_covmap, \ INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON), \ INSTR_PROF_COVMAP_COFF, "__LLVM_COV,") +INSTR_PROF_SECT_ENTRY(IPSK_covfun, \ + INSTR_PROF_QUOTE(INSTR_PROF_COVFUN_COMMON), \ + INSTR_PROF_COVFUN_COFF, "__LLVM_COV,") INSTR_PROF_SECT_ENTRY(IPSK_orderfile, \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON), \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COFF), "__DATA,") @@ -633,8 +658,8 @@ #define INSTR_PROF_RAW_VERSION 5 /* Indexed profile format version (start from 1). */ #define INSTR_PROF_INDEX_VERSION 5 -/* Coverage mapping format vresion (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 2 +/* Coverage mapping format version (start from 0). */ +#define INSTR_PROF_COVMAP_VERSION 3 /* 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 @@ -661,6 +686,7 @@ #define INSTR_PROF_VALS_COMMON __llvm_prf_vals #define INSTR_PROF_VNODES_COMMON __llvm_prf_vnds #define INSTR_PROF_COVMAP_COMMON __llvm_covmap +#define INSTR_PROF_COVFUN_COMMON __llvm_covfun #define INSTR_PROF_ORDERFILE_COMMON __llvm_orderfile /* Windows section names. Because these section names contain dollar characters, * they must be quoted. @@ -671,6 +697,7 @@ #define INSTR_PROF_VALS_COFF ".lprfv$M" #define INSTR_PROF_VNODES_COFF ".lprfnd$M" #define INSTR_PROF_COVMAP_COFF ".lcovmap$M" +#define INSTR_PROF_COVFUN_COFF ".lcovfun$M" #define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M" #ifdef _WIN32 @@ -685,6 +712,7 @@ /* Value profile nodes section. */ #define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_VNODES_COFF #define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_COVMAP_COFF +#define INSTR_PROF_COVFUN_SECT_NAME INSTR_PROF_COVFUN_COFF #define INSTR_PROF_ORDERFILE_SECT_NAME INSTR_PROF_ORDERFILE_COFF #else /* Runtime section names and name strings. */ @@ -698,6 +726,7 @@ /* Value profile nodes section. */ #define INSTR_PROF_VNODES_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_VNODES_COMMON) #define INSTR_PROF_COVMAP_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_COMMON) +#define INSTR_PROF_COVFUN_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVFUN_COMMON) /* Order file instrumentation. */ #define INSTR_PROF_ORDERFILE_SECT_NAME \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON) @@ -752,3 +781,5 @@ #else #undef INSTR_PROF_DATA_DEFINED #endif + +#undef COVMAP_V2_OR_V3 Index: llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -420,6 +420,8 @@ // .section .eh_frame,"a",@progbits if (Name == getInstrProfSectionName(IPSK_covmap, Triple::ELF, + /*AddSegmentInfo=*/false) || + Name == getInstrProfSectionName(IPSK_covfun, Triple::ELF, /*AddSegmentInfo=*/false)) return SectionKind::getMetadata(); Index: llvm/lib/ProfileData/Coverage/CoverageMapping.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -804,6 +804,8 @@ return "Truncated coverage data"; case coveragemap_error::malformed: return "Malformed coverage data"; + case coveragemap_error::decompression_failed: + return "Failed to decompress coverage data (zlib)"; } llvm_unreachable("A value of coveragemap_error has no message."); } Index: llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/Binary.h" @@ -25,6 +26,7 @@ #include "llvm/Object/COFF.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -40,6 +42,9 @@ #define DEBUG_TYPE "coverage-mapping" +STATISTIC(CovMapNumRecords, "The # of coverage function records"); +STATISTIC(CovMapNumUsedRecords, "The # of used coverage function records"); + void CoverageMappingIterator::increment() { if (ReadErr != coveragemap_error::success) return; @@ -92,10 +97,60 @@ return Error::success(); } -Error RawCoverageFilenamesReader::read() { +Error RawCoverageFilenamesReader::read( + CovMapVersion Version, + BinaryCoverageReader::DecompressedData &Decompressed) { uint64_t NumFilenames; if (auto Err = readSize(NumFilenames)) return Err; + if (!NumFilenames) + return make_error(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( + coveragemap_error::decompression_failed); + + // Allocate memory for the decompressed filenames. Transfer ownership of + // the memory to BinaryCoverageReader. + auto DecompressedStorage = std::make_unique>(); + SmallVectorImpl &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( + coveragemap_error::decompression_failed); + } + + StringRef UncompressedFilenames(StorageBuf.data(), StorageBuf.size()); + RawCoverageFilenamesReader Delegate(UncompressedFilenames, Filenames); + return Delegate.readUncompressed(NumFilenames); + } + + return readUncompressed(NumFilenames); +} + +Error RawCoverageFilenamesReader::readUncompressed(uint64_t NumFilenames) { + // Read uncompressed filenames. for (size_t I = 0; I < NumFilenames; ++I) { StringRef Filename; if (auto Err = readString(Filename)) @@ -380,20 +435,51 @@ return RawCoverageMappingDummyChecker(Mapping).isDummy(); } +/// A range of filename indices. Used to specify the location of a batch of +/// filenames in a vector-like container. +struct FilenameRange { + unsigned StartingIndex; + unsigned Length; + + FilenameRange(unsigned StartingIndex, unsigned Length) + : StartingIndex(StartingIndex), Length(Length) {} + + void markInvalid() { Length = 0; } + bool isInvalid() const { return Length == 0; } +}; + namespace { +/// The interface to read coverage mapping function records for a module. struct CovMapFuncRecordReader { virtual ~CovMapFuncRecordReader() = default; - // The interface to read coverage mapping function records for a module. + // Read a coverage header. // - // \p Buf points to the buffer containing the \c CovHeader of the coverage + // \p CovBuf points to the buffer containing the \c CovHeader of the coverage // mapping data associated with the module. // - // Returns a pointer to the next \c CovHeader if it exists, or a pointer - // greater than \p End if not. - virtual Expected readFunctionRecords(const char *Buf, - const char *End) = 0; + // Returns a pointer to the next \c CovHeader if it exists, or to an address + // greater than \p CovEnd if not. + virtual Expected + readCoverageHeader(const char *CovBuf, const char *CovBufEnd, + BinaryCoverageReader::DecompressedData &Decompressed) = 0; + + // Read function records. + // + // \p FuncRecBuf points to the buffer containing a batch of function records. + // \p FuncRecBufEnd points past the end of the batch of records. + // + // Prior to Version4, \p OutOfLineFileRange points to a sequence of filenames + // associated with the function records. It is unused in Version4. + // + // Prior to Version4, \p OutOfLineMappingBuf points to a sequence of coverage + // mappings associated with the function records. It is unused in Version4. + virtual Error readFunctionRecords(const char *FuncRecBuf, + const char *FuncRecBufEnd, + Optional OutOfLineFileRange, + const char *OutOfLineMappingBuf, + const char *OutOfLineMappingBufEnd) = 0; template static Expected> @@ -416,6 +502,10 @@ std::vector &Filenames; std::vector &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 FileRangeMap; + // Add the record to the collection if we don't already have a record that // points to the same function name. This is useful to ignore the redundant // records for the functions with ODR linkage. @@ -423,7 +513,9 @@ // records, which were emitted for inline functions which were seen but // not used in the corresponding translation unit. Error insertFunctionRecordIfNeeded(const FuncRecordType *CFR, - StringRef Mapping, size_t FilenamesBegin) { + StringRef Mapping, + FilenameRange FileRange) { + ++CovMapNumRecords; uint64_t FuncHash = CFR->template getFuncHash(); NameRefType NameRef = CFR->template getFuncNameRef(); auto InsertResult = @@ -434,8 +526,9 @@ return Err; if (FuncName.empty()) return make_error(instrprof_error::malformed); - Records.emplace_back(Version, FuncName, FuncHash, Mapping, FilenamesBegin, - Filenames.size() - FilenamesBegin); + ++CovMapNumUsedRecords; + Records.emplace_back(Version, FuncName, FuncHash, Mapping, + FileRange.StartingIndex, FileRange.Length); return Error::success(); } // Update the existing record if it's a dummy and the new record is real. @@ -454,10 +547,11 @@ return Err; if (*NewIsDummyExpected) return Error::success(); + ++CovMapNumUsedRecords; OldRecord.FunctionHash = FuncHash; OldRecord.CoverageMapping = Mapping; - OldRecord.FilenamesBegin = FilenamesBegin; - OldRecord.FilenamesSize = Filenames.size() - FilenamesBegin; + OldRecord.FilenamesBegin = FileRange.StartingIndex; + OldRecord.FilenamesSize = FileRange.Length; return Error::success(); } @@ -470,61 +564,134 @@ ~VersionedCovMapFuncRecordReader() override = default; - Expected readFunctionRecords(const char *Buf, - const char *End) override { + Expected readCoverageHeader( + const char *CovBuf, const char *CovBufEnd, + BinaryCoverageReader::DecompressedData &Decompressed) override { using namespace support; - if (Buf + sizeof(CovMapHeader) > End) + if (CovBuf + sizeof(CovMapHeader) > CovBufEnd) return make_error(coveragemap_error::malformed); - auto CovHeader = reinterpret_cast(Buf); + auto CovHeader = reinterpret_cast(CovBuf); uint32_t NRecords = CovHeader->getNRecords(); uint32_t FilenamesSize = CovHeader->getFilenamesSize(); uint32_t CoverageSize = CovHeader->getCoverageSize(); assert((CovMapVersion)CovHeader->getVersion() == Version); - Buf = reinterpret_cast(CovHeader + 1); + CovBuf = reinterpret_cast(CovHeader + 1); // Skip past the function records, saving the start and end for later. - const char *FunBuf = Buf; - Buf += NRecords * sizeof(FuncRecordType); - const char *FunEnd = Buf; + // This is a no-op in Version4 (function records are read after all headers + // are read). + const char *FuncRecBuf = nullptr; + const char *FuncRecBufEnd = nullptr; + if (Version < CovMapVersion::Version4) + FuncRecBuf = CovBuf; + CovBuf += NRecords * sizeof(FuncRecordType); + if (Version < CovMapVersion::Version4) + FuncRecBufEnd = CovBuf; // Get the filenames. - if (Buf + FilenamesSize > End) + if (CovBuf + FilenamesSize > CovBufEnd) return make_error(coveragemap_error::malformed); size_t FilenamesBegin = Filenames.size(); - RawCoverageFilenamesReader Reader(StringRef(Buf, FilenamesSize), Filenames); - if (auto Err = Reader.read()) + StringRef FilenameRegion(CovBuf, FilenamesSize); + RawCoverageFilenamesReader Reader(FilenameRegion, Filenames); + if (auto Err = Reader.read(Version, Decompressed)) return std::move(Err); - Buf += FilenamesSize; + CovBuf += FilenamesSize; + FilenameRange FileRange(FilenamesBegin, Filenames.size() - FilenamesBegin); + + if (Version == CovMapVersion::Version4) { + // Map a hash of the filenames region to the filename range associated + // with this coverage header. + int64_t FilenamesRef = + llvm::IndexedInstrProf::ComputeHash(FilenameRegion); + auto Insert = + FileRangeMap.insert(std::make_pair(FilenamesRef, FileRange)); + if (!Insert.second) { + // The same filenames ref was encountered twice. It's possible that + // the associated filenames are the same. + auto It = Filenames.begin(); + FilenameRange &OrigRange = Insert.first->getSecond(); + if (std::equal(It + OrigRange.StartingIndex, + It + OrigRange.StartingIndex + OrigRange.Length, + It + FileRange.StartingIndex, + It + FileRange.StartingIndex + FileRange.Length)) + // Map the new range to the original one. + FileRange = OrigRange; + else + // This is a hash collision. Mark the filenames ref invalid. + OrigRange.markInvalid(); + } + } // We'll read the coverage mapping records in the loop below. - const char *CovBuf = Buf; - Buf += CoverageSize; - const char *CovEnd = Buf; + // This is a no-op in Version4 (coverage mappings are not affixed to the + // coverage header). + const char *MappingBuf = CovBuf; + if (Version == CovMapVersion::Version4 && CoverageSize != 0) + return make_error(coveragemap_error::malformed); + CovBuf += CoverageSize; + const char *MappingEnd = CovBuf; - if (Buf > End) + if (CovBuf > CovBufEnd) return make_error(coveragemap_error::malformed); + + if (Version < CovMapVersion::Version4) { + // Read each function record. + if (Error E = readFunctionRecords(FuncRecBuf, FuncRecBufEnd, FileRange, + MappingBuf, MappingEnd)) + return std::move(E); + } + // Each coverage map has an alignment of 8, so we need to adjust alignment // before reading the next map. - Buf += offsetToAlignedAddr(Buf, Align(8)); + CovBuf += offsetToAlignedAddr(CovBuf, Align(8)); - auto CFR = reinterpret_cast(FunBuf); - while ((const char *)CFR < FunEnd) { - // Read the function information - uint32_t DataSize = CFR->template getDataSize(); + return CovBuf; + } - // Now use that to read the coverage data. - if (CovBuf + DataSize > CovEnd) - return make_error(coveragemap_error::malformed); - auto Mapping = StringRef(CovBuf, DataSize); - CovBuf += DataSize; + Error readFunctionRecords(const char *FuncRecBuf, const char *FuncRecBufEnd, + Optional OutOfLineFileRange, + const char *OutOfLineMappingBuf, + const char *OutOfLineMappingBufEnd) override { + auto CFR = reinterpret_cast(FuncRecBuf); + while ((const char *)CFR < FuncRecBufEnd) { + // Validate the length of the coverage mapping for this function. + const char *NextMappingBuf; + const FuncRecordType *NextCFR; + std::tie(NextMappingBuf, NextCFR) = + CFR->template advanceByOne(OutOfLineMappingBuf); + if (Version < CovMapVersion::Version4) + if (NextMappingBuf > OutOfLineMappingBufEnd) + return make_error(coveragemap_error::malformed); - if (Error Err = - insertFunctionRecordIfNeeded(CFR, Mapping, FilenamesBegin)) - return std::move(Err); - CFR++; + // Look up the set of filenames associated with this function record. + Optional FileRange; + if (Version < CovMapVersion::Version4) { + FileRange = OutOfLineFileRange; + } else { + uint64_t FilenamesRef = CFR->template getFilenamesRef(); + auto It = FileRangeMap.find(FilenamesRef); + if (It == FileRangeMap.end()) + return make_error(coveragemap_error::malformed); + else + FileRange = It->getSecond(); + } + + // Now, read the coverage data. + if (FileRange && !FileRange->isInvalid()) { + StringRef Mapping = + CFR->template getCoverageMapping(OutOfLineMappingBuf); + if (Version == CovMapVersion::Version4 && + Mapping.data() + Mapping.size() > FuncRecBufEnd) + return make_error(coveragemap_error::malformed); + if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange)) + return Err; + } + + std::tie(OutOfLineMappingBuf, CFR) = std::tie(NextMappingBuf, NextCFR); } - return Buf; + return Error::success(); } }; @@ -543,29 +710,34 @@ CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F); case CovMapVersion::Version2: case CovMapVersion::Version3: + case CovMapVersion::Version4: // Decompress the name data. if (Error E = P.create(P.getNameData())) return std::move(E); if (Version == CovMapVersion::Version2) return std::make_unique>(P, R, F); - else + else if (Version == CovMapVersion::Version3) return std::make_unique>(P, R, F); + else if (Version == CovMapVersion::Version4) + return std::make_unique>(P, R, F); } llvm_unreachable("Unsupported version"); } template static Error readCoverageMappingData( - InstrProfSymtab &ProfileNames, StringRef Data, + InstrProfSymtab &ProfileNames, StringRef CovMap, StringRef FuncRecords, std::vector &Records, - std::vector &Filenames) { + std::vector &Filenames, + BinaryCoverageReader::DecompressedData &Decompressed) { using namespace coverage; // Read the records in the coverage data section. auto CovHeader = - reinterpret_cast(Data.data()); + reinterpret_cast(CovMap.data()); CovMapVersion Version = (CovMapVersion)CovHeader->getVersion(); if (Version > CovMapVersion::CurrentVersion) return make_error(coveragemap_error::unsupported_version); @@ -575,12 +747,28 @@ if (Error E = ReaderExpected.takeError()) return E; auto Reader = std::move(ReaderExpected.get()); - for (const char *Buf = Data.data(), *End = Buf + Data.size(); Buf < End;) { - auto NextHeaderOrErr = Reader->readFunctionRecords(Buf, End); - if (auto E = NextHeaderOrErr.takeError()) + const char *CovBuf = CovMap.data(); + const char *CovBufEnd = CovBuf + CovMap.size(); + const char *FuncRecBuf = FuncRecords.data(); + const char *FuncRecBufEnd = FuncRecords.data() + FuncRecords.size(); + while (CovBuf < CovBufEnd) { + // Read the current coverage header & filename data. + // + // Prior to Version4, this also reads all function records affixed to the + // header. + // + // Return a pointer to the next coverage header. + auto NextOrErr = + Reader->readCoverageHeader(CovBuf, CovBufEnd, Decompressed); + if (auto E = NextOrErr.takeError()) return E; - Buf = NextHeaderOrErr.get(); + CovBuf = NextOrErr.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(); } @@ -588,31 +776,33 @@ Expected> BinaryCoverageReader::createCoverageReaderFromBuffer( - StringRef Coverage, InstrProfSymtab &&ProfileNames, uint8_t BytesInAddress, - support::endianness Endian) { + StringRef Coverage, StringRef FuncRecords, InstrProfSymtab &&ProfileNames, + uint8_t BytesInAddress, support::endianness Endian) { std::unique_ptr Reader(new BinaryCoverageReader()); Reader->ProfileNames = std::move(ProfileNames); if (BytesInAddress == 4 && Endian == support::endianness::little) { if (Error E = readCoverageMappingData( - Reader->ProfileNames, Coverage, Reader->MappingRecords, - Reader->Filenames)) + Reader->ProfileNames, Coverage, FuncRecords, + Reader->MappingRecords, Reader->Filenames, + Reader->Decompressed)) return std::move(E); } else if (BytesInAddress == 4 && Endian == support::endianness::big) { if (Error E = readCoverageMappingData( - Reader->ProfileNames, Coverage, Reader->MappingRecords, - Reader->Filenames)) + Reader->ProfileNames, Coverage, FuncRecords, Reader->MappingRecords, + Reader->Filenames, Reader->Decompressed)) return std::move(E); } else if (BytesInAddress == 8 && Endian == support::endianness::little) { if (Error E = readCoverageMappingData( - Reader->ProfileNames, Coverage, Reader->MappingRecords, - Reader->Filenames)) + Reader->ProfileNames, Coverage, FuncRecords, + Reader->MappingRecords, Reader->Filenames, + Reader->Decompressed)) return std::move(E); } else if (BytesInAddress == 8 && Endian == support::endianness::big) { if (Error E = readCoverageMappingData( - Reader->ProfileNames, Coverage, Reader->MappingRecords, - Reader->Filenames)) + Reader->ProfileNames, Coverage, FuncRecords, Reader->MappingRecords, + Reader->Filenames, Reader->Decompressed)) return std::move(E); } else return make_error(coveragemap_error::malformed); @@ -653,7 +843,7 @@ return make_error(coveragemap_error::malformed); CoverageMapping = CoverageMapping.substr(Pad); return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMapping, std::move(ProfileNames), BytesInAddress, Endian); + CoverageMapping, "", std::move(ProfileNames), BytesInAddress, Endian); } static Expected lookupSection(ObjectFile &OF, StringRef Name) { @@ -714,18 +904,31 @@ /*AddSegmentInfo=*/false)); if (auto E = CoverageSection.takeError()) return std::move(E); - - // Get the contents of the given sections. auto CoverageMappingOrErr = CoverageSection->getContents(); if (!CoverageMappingOrErr) return CoverageMappingOrErr.takeError(); + StringRef CoverageMapping = CoverageMappingOrErr.get(); InstrProfSymtab ProfileNames; if (Error E = ProfileNames.create(*NamesSection)) return std::move(E); + // Look for the coverage records section (Version4 only). + StringRef FuncRecords; + auto CoverageRecordsSection = + lookupSection(*OF, getInstrProfSectionName(IPSK_covfun, ObjFormat, + /*AddSegmentInfo=*/false)); + if (auto E = CoverageRecordsSection.takeError()) + consumeError(std::move(E)); + else { + auto CoverageRecordsOrErr = CoverageRecordsSection->getContents(); + if (!CoverageRecordsOrErr) + return CoverageRecordsOrErr.takeError(); + FuncRecords = CoverageRecordsOrErr.get(); + } + return BinaryCoverageReader::createCoverageReaderFromBuffer( - CoverageMappingOrErr.get(), std::move(ProfileNames), BytesInAddress, + CoverageMapping, FuncRecords, std::move(ProfileNames), BytesInAddress, Endian); } Index: llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp @@ -11,9 +11,11 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" #include @@ -34,12 +36,34 @@ #endif } -void CoverageFilenamesSectionWriter::write(raw_ostream &OS) { +void CoverageFilenamesSectionWriter::write(raw_ostream &OS, bool Compress) { + std::string FilenamesStr; + { + raw_string_ostream FilenamesOS{FilenamesStr}; + for (const auto &Filename : Filenames) { + encodeULEB128(Filename.size(), FilenamesOS); + FilenamesOS << Filename; + } + } + + SmallString<128> CompressedStr; + bool doCompression = + Compress && zlib::isAvailable() && DoInstrProfNameCompression; + if (doCompression) { + Error E = + zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression); + if (E) + llvm_unreachable("Unexpected failure in zlib::compress"); + } + + // ::= + // + // + // ( | ) encodeULEB128(Filenames.size(), OS); - for (const auto &Filename : Filenames) { - encodeULEB128(Filename.size(), OS); - OS << Filename; - } + encodeULEB128(FilenamesStr.size(), OS); + encodeULEB128(doCompression ? CompressedStr.size() : 0U, OS); + OS << (doCompression ? StringRef(CompressedStr) : StringRef(FilenamesStr)); } namespace { Index: llvm/lib/ProfileData/InstrProf.cpp =================================================================== --- llvm/lib/ProfileData/InstrProf.cpp +++ llvm/lib/ProfileData/InstrProf.cpp @@ -162,6 +162,10 @@ namespace llvm { +cl::opt DoInstrProfNameCompression( + "enable-name-compression", + cl::desc("Enable name/filename string compression"), cl::init(true)); + std::string getInstrProfSectionName(InstrProfSectKind IPSK, Triple::ObjectFormatType OF, bool AddSegmentInfo) { Index: llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -74,10 +74,6 @@ namespace { -cl::opt DoNameCompression("enable-name-compression", - cl::desc("Enable name string compression"), - cl::init(true)); - cl::opt DoHashBasedCounterSplit( "hash-based-counter-split", cl::desc("Rename counter variable of a comdat function based on cfg hash"), @@ -916,7 +912,7 @@ std::string CompressedNameStr; if (Error E = collectPGOFuncNameStrings(ReferencedNames, CompressedNameStr, - DoNameCompression)) { + DoInstrProfNameCompression)) { report_fatal_error(toString(std::move(E)), false); } Index: llvm/test/Instrumentation/InstrProfiling/X86/alloc.ll =================================================================== --- llvm/test/Instrumentation/InstrProfiling/X86/alloc.ll +++ llvm/test/Instrumentation/InstrProfiling/X86/alloc.ll @@ -1,6 +1,8 @@ ;; Ensure that SHF_ALLOC section flag is not set for the __llvm_covmap section on Linux. ; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s +@covfun = linkonce_odr hidden constant i32 0, section "__llvm_covfun" @__llvm_coverage_mapping = internal constant i32 0, section "__llvm_covmap" +; CHECK-DAG: .section __llvm_covfun,"" ; CHECK-DAG: .section __llvm_covmap,"" Index: llvm/test/tools/llvm-cov/binary-formats.c =================================================================== --- llvm/test/tools/llvm-cov/binary-formats.c +++ llvm/test/tools/llvm-cov/binary-formats.c @@ -7,5 +7,6 @@ // RUN: llvm-cov show %S/Inputs/binary-formats.macho32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s // RUN: llvm-cov show %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s // RUN: llvm-cov show %S/Inputs/binary-formats.macho32b -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-cov show %S/Inputs/binary-formats.v3.macho64l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s // RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json Index: llvm/unittests/ProfileData/CoverageMappingTest.cpp =================================================================== --- llvm/unittests/ProfileData/CoverageMappingTest.cpp +++ llvm/unittests/ProfileData/CoverageMappingTest.cpp @@ -895,4 +895,29 @@ std::pair({true, false}), std::pair({true, true})),); +TEST(CoverageMappingTest, filename_roundtrip) { + std::vector 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 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