Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -256,6 +256,9 @@ "as the %select{aliasee|resolver}2">, InGroup; +def err_code_coverage_mapping_failed : Error< + "cannot create code coverage mapping: %0">; + let CategoryName = "Instrumentation Issue" in { def warn_profile_data_out_of_date : Warning< "profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1" Index: clang/lib/CodeGen/CoverageMappingGen.h =================================================================== --- clang/lib/CodeGen/CoverageMappingGen.h +++ clang/lib/CodeGen/CoverageMappingGen.h @@ -47,19 +47,39 @@ /// 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; + + FunctionInfo(uint64_t NameHash, uint64_t FuncHash, + const std::string &CoverageMapping, bool IsUsed) + : NameHash(NameHash), FuncHash(FuncHash), + CoverageMapping(CoverageMapping), IsUsed(IsUsed) {} + + FunctionInfo(const FunctionInfo &) = delete; + FunctionInfo &operator=(const FunctionInfo &) = delete; + FunctionInfo(FunctionInfo &&) = default; + FunctionInfo &operator=(FunctionInfo &&) = default; + }; + CodeGenModule &CGM; CoverageSourceInfo &SourceInfo; llvm::SmallDenseMap FileEntries; - std::vector FunctionRecords; std::vector FunctionNames; - llvm::StructType *FunctionRecordTy; - std::vector CoverageMappings; + std::vector FunctionRecords; SmallString<256> CWD; /// Make the filename absolute, remove dots, and normalize slashes to local /// path style. std::string normalizeFilename(StringRef Filename); + /// Emit a function record. + void emitFunctionMappingRecord(const FunctionInfo &Info, + uint64_t FilenamesRef); + public: CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &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()); -} - } // end anonymous namespace static void dump(llvm::raw_ostream &OS, StringRef FunctionName, @@ -1312,7 +1312,7 @@ CoverageMappingModuleGen::CoverageMappingModuleGen( CodeGenModule &CGM, CoverageSourceInfo &SourceInfo) - : CGM(CGM), SourceInfo(SourceInfo), FunctionRecordTy(nullptr) { + : CGM(CGM), SourceInfo(SourceInfo) { // Honor -fdebug-compilation-dir in paths in coverage data. Otherwise, use the // regular working directory when normalizing paths. if (!CGM.getCodeGenOpts().DebugCompilationDir.empty()) @@ -1328,30 +1328,71 @@ return Path.str().str(); } +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 = 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.emplace_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 @@ -1396,37 +1437,27 @@ 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); + auto E = CoverageFilenamesSectionWriter(FilenameRefs).write(OS); + if (E) { + CGM.getDiags().Report(diag::err_code_coverage_mapping_failed) + << toString(std::move(E)); + return; + } } - 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; + 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" @@ -1441,18 +1472,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/debug-dir.cpp =================================================================== --- clang/test/CoverageMapping/debug-dir.cpp +++ clang/test/CoverageMapping/debug-dir.cpp @@ -5,11 +5,11 @@ // RUN: cp %s %t/foo/bar/baz/debug-dir.cpp // RUN: cd %t/foo/bar -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -main-file-name debug-dir.cpp baz/debug-dir.cpp -o - | FileCheck -check-prefix=ABSOLUTE %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name debug-dir.cpp baz/debug-dir.cpp -o - | FileCheck -check-prefix=ABSOLUTE %s // // ABSOLUTE: @__llvm_coverage_mapping = {{.*"\\01.*foo.*bar.*baz.*debug-dir\.cpp}} -// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -main-file-name debug-dir.cpp baz/debug-dir.cpp -fdebug-compilation-dir . -o - | FileCheck -check-prefix=RELATIVE %s +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -mllvm -enable-name-compression=false -emit-llvm -main-file-name debug-dir.cpp baz/debug-dir.cpp -fdebug-compilation-dir . -o - | FileCheck -check-prefix=RELATIVE %s // // RELATIVE: @__llvm_coverage_mapping = {{.*"\\01[^/]*baz.*debug-dir\.cpp}} 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:@"?[0-9A-F]+u"?]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8 +// DARWIN: [[FuncRecord2:@"?[0-9A-F]+u"?]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section "__LLVM_COV,__llvm_covfun", align 8 +// DARWIN: [[FuncRecord3:@["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:@"?[0-9A-F]+u"?]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8 +// WINDOWS: [[FuncRecord2:@"?[0-9A-F]+u"?]] = linkonce_odr hidden constant <{ i64, i32, i64, i64, [{{.*}} x i8] }> <{ {{.*}} }>, section ".lcovfun$M", comdat, align 8 +// WINDOWS: [[FuncRecord3:@["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/lib/profile/InstrProfData.inc =================================================================== --- compiler-rt/lib/profile/InstrProfData.inc +++ compiler-rt/lib/profile/InstrProfData.inc @@ -196,6 +196,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. @@ -212,16 +220,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. */ @@ -234,7 +256,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, \ @@ -265,6 +287,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,") @@ -631,8 +656,8 @@ #define INSTR_PROF_RAW_VERSION 4 /* 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 @@ -659,6 +684,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. @@ -669,6 +695,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 @@ -683,6 +710,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. */ @@ -696,6 +724,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) @@ -742,7 +771,7 @@ #endif /* INSTR_PROF_DATA_INC */ #ifndef INSTR_ORDER_FILE_INC -/* The maximal # of functions: 128*1024 (the buffer size will be 128*4 KB). */ +// The maximal # of functions: 128*1024 (the buffer size will be 128*4 KB). #define INSTR_ORDER_FILE_BUFFER_SIZE 131072 #define INSTR_ORDER_FILE_BUFFER_BITS 17 #define INSTR_ORDER_FILE_BUFFER_MASK 0x1ffff @@ -750,3 +779,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,10 +15,11 @@ 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 @@ -27,34 +28,11 @@ 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 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 +202,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. For example, let’s consider a C file and how it gets compiled to LLVM: @@ -241,42 +219,37 @@ 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 marked *linkonce_odr*. This allows linkers to merge duplicate *dummy* records (emitted for functions included-but-not-used in a translation unit). 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 +259,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 +290,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 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 +307,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,9 @@ no_data_found, unsupported_version, truncated, - malformed + malformed, + compression_failed, + decompression_failed }; const std::error_category &coveragemap_category(); @@ -678,37 +681,104 @@ 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 { +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); + } +}; +template struct HashedNameRef { + using ThisTy = const FuncRecordTy *; + // Return function lookup key. The value is consider 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(); + } +}; +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 +790,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)}; } }; @@ -769,7 +858,6 @@ return support::endian::byte_swap(Version); } }; - LLVM_PACKED_END enum CovMapVersion { @@ -781,12 +869,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. + Error write(raw_ostream &OS, bool Compress = true); }; /// Writer for instrumentation based coverage mapping data. Index: llvm/include/llvm/ProfileData/InstrProfData.inc =================================================================== --- llvm/include/llvm/ProfileData/InstrProfData.inc +++ llvm/include/llvm/ProfileData/InstrProfData.inc @@ -196,6 +196,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. @@ -212,16 +220,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. */ @@ -234,7 +256,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, \ @@ -265,6 +287,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,") @@ -631,8 +656,8 @@ #define INSTR_PROF_RAW_VERSION 4 /* 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 @@ -659,6 +684,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. @@ -669,6 +695,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 @@ -683,6 +710,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. */ @@ -696,6 +724,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) @@ -750,3 +779,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,10 @@ return "Truncated coverage data"; case coveragemap_error::malformed: return "Malformed coverage data"; + case coveragemap_error::compression_failed: + return "Failed to compress coverage data (zlib)"; + 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. + Insert.first->getSecond().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 @@ -14,6 +14,8 @@ #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" #include @@ -24,6 +26,8 @@ using namespace llvm; using namespace coverage; +extern cl::opt DoNameCompression; + CoverageFilenamesSectionWriter::CoverageFilenamesSectionWriter( ArrayRef Filenames) : Filenames(Filenames) { @@ -34,12 +38,37 @@ #endif } -void CoverageFilenamesSectionWriter::write(raw_ostream &OS) { +Error 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() && DoNameCompression; + if (doCompression) { + Error E = + zlib::compress(FilenamesStr, CompressedStr, zlib::BestSizeCompression); + if (E) { + consumeError(std::move(E)); + return make_error( + coveragemap_error::compression_failed); + } + } + + // ::= + // + // + // ( | ) 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)); + return Error::success(); } namespace { Index: llvm/lib/ProfileData/InstrProf.cpp =================================================================== --- llvm/lib/ProfileData/InstrProf.cpp +++ llvm/lib/ProfileData/InstrProf.cpp @@ -55,6 +55,13 @@ using namespace llvm; +// Whether to compress function names in profile records, and filenames in +// code coverage mappings. Used by the Instrumentation library and unit tests. +cl::opt + DoNameCompression("enable-name-compression", + cl::desc("Enable name/filename string compression"), + cl::init(true)); + static cl::opt StaticFuncFullModulePrefix( "static-func-full-module-prefix", cl::init(true), cl::Hidden, cl::desc("Use full module build paths in the profile counter names for " Index: llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -71,12 +71,10 @@ "Value of 0 disables the large value profiling."), cl::init(8192)); +extern cl::opt DoNameCompression; + 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"), 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); + EXPECT_THAT_ERROR(Writer.write(OS, Compress), Succeeded()); + } + + 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