From b55f97250d1c8796aa2154e829b51cd49d595de2 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sat, 20 May 2023 00:32:21 +0100 Subject: [PATCH] [IRGen] Update coverage mapping for version 6 The coverage format version we use is automatically set to whatever the latest version LLVM has. This resulting in us outputting version 6 as our format without having made the changes necessary to reflect the new format. Update the emission logic to take account for [the new changes in version 6][1] (we need to include the current working directory as the first element of the list of files we output). Additionally, add a `static_assert` so we don't get caught out by version bumps in the future. Note we can't pin our version, as it must stay in sync with the version Clang is using. [1]: https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation --- lib/IRGen/GenCoverage.cpp | 56 +++++++++++++------ test/Profiler/coverage_relative_path.swift | 6 +- .../coverage_relative_path_exec.swift | 25 +++++++++ 3 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 test/Profiler/coverage_relative_path_exec.swift diff --git a/lib/IRGen/GenCoverage.cpp b/lib/IRGen/GenCoverage.cpp index 5e9a5722c86..5d58ea9fb9f 100644 --- a/lib/IRGen/GenCoverage.cpp +++ b/lib/IRGen/GenCoverage.cpp @@ -27,16 +27,24 @@ #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/FileSystem.h" -// This selects the coverage mapping format defined when `InstrProfData.inc` -// is textually included. -#define COVMAP_V3 - using namespace swift; using namespace irgen; using llvm::coverage::CounterMappingRegion; using llvm::coverage::CovMapVersion; +// This affects the coverage mapping format defined when `InstrProfData.inc` +// is textually included. Note that it means 'version >= 3', not 'version == 3'. +#define COVMAP_V3 + +/// This assert is here to make sure we make all the necessary code generation +/// changes that are needed to support the new coverage mapping format. Note we +/// cannot pin our version, as it must remain in sync with the version Clang is +/// using. +/// Do not bump without at least filing a bug and pinging a coverage maintainer. +static_assert(CovMapVersion::CurrentVersion == CovMapVersion::Version6, + "Coverage mapping emission needs updating"); + static std::string getInstrProfSection(IRGenModule &IGM, llvm::InstrProfSectKind SK) { return llvm::getInstrProfSectionName(SK, IGM.Triple.getObjectFormat()); @@ -77,18 +85,26 @@ void IRGenModule::emitCoverageMaps(ArrayRef Mappings) { } std::vector Files; - for (const auto &M : Mappings) + for (const auto &M : Mappings) { if (std::find(Files.begin(), Files.end(), M->getFilename()) == Files.end()) Files.push_back(M->getFilename()); - - auto remapper = getOptions().CoveragePrefixMap; + } + const auto &Remapper = getOptions().CoveragePrefixMap; llvm::SmallVector FilenameStrs; - for (StringRef Name : Files) { - llvm::SmallString<256> Path(Name); - llvm::sys::fs::make_absolute(Path); - FilenameStrs.push_back(remapper.remapPath(Path)); - } + FilenameStrs.reserve(Files.size() + 1); + + // First element needs to be the current working directory. Note if this + // scheme ever changes, the FileID computation below will need updating. + SmallString<256> WorkingDirectory; + llvm::sys::fs::current_path(WorkingDirectory); + FilenameStrs.emplace_back(Remapper.remapPath(WorkingDirectory)); + + // Following elements are the filenames present. We use their relative path, + // which llvm-cov will turn back into absolute paths using the working + // directory element. + for (auto Name : Files) + FilenameStrs.emplace_back(Remapper.remapPath(Name)); // Encode the filenames. std::string Filenames; @@ -112,13 +128,21 @@ void IRGenModule::emitCoverageMaps(ArrayRef Mappings) { const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue); std::string FuncRecordName = "__covrec_" + llvm::utohexstr(NameHash); - unsigned FileID = - std::find(Files.begin(), Files.end(), M->getFilename()) - Files.begin(); + // The file ID needs to be bumped by 1 to account for the working directory + // as the first element. + unsigned FileID = 1 + + std::find(Files.begin(), Files.end(), M->getFilename()) - + Files.begin(); + assert(FileID < FilenameStrs.size()); + std::vector Regions; - for (const auto &MR : M->getMappedRegions()) + for (const auto &MR : M->getMappedRegions()) { + // The SubFileID here is 0, because it's an index into VirtualFileMapping, + // and we only ever have a single file associated for a function. Regions.emplace_back(CounterMappingRegion::makeRegion( - MR.Counter, /*FileID=*/0, MR.StartLine, MR.StartCol, MR.EndLine, + MR.Counter, /*SubFileID*/ 0, MR.StartLine, MR.StartCol, MR.EndLine, MR.EndCol)); + } // Append each function's regions into the encoded buffer. ArrayRef VirtualFileMapping(FileID); llvm::coverage::CoverageMappingWriter W(VirtualFileMapping, diff --git a/test/Profiler/coverage_relative_path.swift b/test/Profiler/coverage_relative_path.swift index 5dde002dc72..950970b4cc6 100644 --- a/test/Profiler/coverage_relative_path.swift +++ b/test/Profiler/coverage_relative_path.swift @@ -1,15 +1,15 @@ // To make sure this test is resilient to directory changes, we create nested directories inside of the // temporary test directory and assert those exist, or don't exist, in the emitted ir // -// RUN: rm -rf %t +// RUN: %empty-directory(%t) // RUN: mkdir -p %t/root/nested // RUN: echo "func coverage() {}" > %t/root/nested/coverage_relative_path.swift // RUN: cd %t/root // RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -Xllvm -enable-name-compression=false -emit-ir nested/coverage_relative_path.swift | %FileCheck -check-prefix=ABSOLUTE %s // -// ABSOLUTE: @__llvm_coverage_mapping = {{.*"\\01.*root.*nested.*coverage_relative_path\.swift}} +// ABSOLUTE: @__llvm_coverage_mapping = {{.*"\\02.*root[^/\\]*nested[/\\]*coverage_relative_path\.swift}} // RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -Xllvm -enable-name-compression=false -coverage-prefix-map %/t/root=. -emit-ir %/t/root/nested/coverage_relative_path.swift | %FileCheck -check-prefix=RELATIVE %s // -// RELATIVE: @__llvm_coverage_mapping = {{.*"\\01[^/]*}}.{{/|\\}}nested{{.*coverage_relative_path\.swift}} +// RELATIVE: @__llvm_coverage_mapping = {{.*"\\02.*\\01[^/]*\.[/\\]*nested[/\\]*coverage_relative_path\.swift}} diff --git a/test/Profiler/coverage_relative_path_exec.swift b/test/Profiler/coverage_relative_path_exec.swift new file mode 100644 index 00000000000..d6f733855f1 --- /dev/null +++ b/test/Profiler/coverage_relative_path_exec.swift @@ -0,0 +1,25 @@ +// REQUIRES: profile_runtime +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +// This test is to make sure llvm-cov can deal with a coverage-prefix-map. + +// To make sure this test is resilient to directory changes, we create nested directories inside of the +// temporary test directory and assert those exist, or don't exist, in the emitted ir +// +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/root/nested +// RUN: echo "func coverage() {}" > %t/root/nested/coverage_relative_path.swift +// RUN: cd %t/root + +// RUN: %target-build-swift -profile-generate -profile-coverage-mapping -Xfrontend -coverage-prefix-map -Xfrontend %/t/root=. -Xfrontend -disable-incremental-llvm-codegen -o %t/main %/t/root/nested/coverage_relative_path.swift + +// This unusual use of 'sh' allows the path of the profraw file to be +// substituted by %target-run. +// RUN: %target-codesign %t/main +// RUN: %target-run sh -c 'env LLVM_PROFILE_FILE=$1 $2' -- %t/default.profraw %t/main + +// RUN: %llvm-profdata merge %t/default.profraw -o %t/default.profdata +// RUN: %llvm-cov show %t/main -instr-profile=%t/default.profdata | %FileCheck %s + +// CHECK: func coverage