Skip to content

[5.9] [Profiler] A couple of coverage fixes #66058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/Basic/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ class SourceManager {
/// Set the generated source information associated with a given buffer.
void setGeneratedSourceInfo(unsigned bufferID, GeneratedSourceInfo);

/// Checks whether the given buffer has generated source information.
bool hasGeneratedSourceInfo(unsigned bufferID);

/// Retrieve the generated source information for the given buffer.
Optional<GeneratedSourceInfo> getGeneratedSourceInfo(unsigned bufferID) const;

Expand Down
4 changes: 4 additions & 0 deletions lib/Basic/SourceLoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ void SourceManager::setGeneratedSourceInfo(
}
}

bool SourceManager::hasGeneratedSourceInfo(unsigned bufferID) {
return GeneratedSourceInfos.count(bufferID);
}

Optional<GeneratedSourceInfo> SourceManager::getGeneratedSourceInfo(
unsigned bufferID
) const {
Expand Down
68 changes: 49 additions & 19 deletions lib/IRGen/GenCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -76,19 +84,31 @@ void IRGenModule::emitCoverageMaps(ArrayRef<const SILCoverageMap *> Mappings) {
llvm::getCoverageUnusedNamesVarName());
}

std::vector<StringRef> Files;
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;
llvm::DenseMap<StringRef, unsigned> RawFileIndices;
llvm::SmallVector<StringRef, 8> RawFiles;
for (const auto &M : Mappings) {
auto Filename = M->getFilename();
auto Inserted = RawFileIndices.insert({Filename, RawFiles.size()}).second;
if (!Inserted)
continue;
RawFiles.push_back(Filename);
}
const auto &Remapper = getOptions().CoveragePrefixMap;

llvm::SmallVector<std::string, 8> FilenameStrs;
for (StringRef Name : Files) {
llvm::SmallString<256> Path(Name);
llvm::sys::fs::make_absolute(Path);
FilenameStrs.push_back(remapper.remapPath(Path));
}
FilenameStrs.reserve(RawFiles.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 : RawFiles)
FilenameStrs.emplace_back(Remapper.remapPath(Name));

// Encode the filenames.
std::string Filenames;
Expand All @@ -112,13 +132,23 @@ void IRGenModule::emitCoverageMaps(ArrayRef<const SILCoverageMap *> 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 = [&]() {
auto Result = RawFileIndices.find(M->getFilename());
assert(Result != RawFileIndices.end());
return Result->second + 1;
}();
assert(FileID < FilenameStrs.size());

std::vector<CounterMappingRegion> 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<unsigned> VirtualFileMapping(FileID);
llvm::coverage::CoverageMappingWriter W(VirtualFileMapping,
Expand Down
21 changes: 19 additions & 2 deletions lib/SIL/IR/SILProfiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,35 @@ static NodeToProfile getNodeToProfile(SILDeclRef Constant) {
/// Check whether we should profile a given SILDeclRef.
static bool shouldProfile(SILDeclRef Constant) {
auto Root = getNodeToProfile(Constant);
auto *DC = Constant.getInnermostDeclContext();

// Do not profile AST nodes with invalid source locations.
if (auto N = Root.getAsNode()) {
// Do not profile AST nodes with invalid source locations.
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
LLVM_DEBUG(llvm::dbgs()
<< "Skipping ASTNode: invalid start/end locations\n");
return false;
}

// Do not profile generated code. This includes macro expansions, which we
// otherwise consider to be "written by the user", because they wrote the
// macro attribute or expr. We may want to revist this in the future. We'll
// need to figure out how we'll be writing out the macro expansions though,
// such that they can be referenced by llvm-cov.
// Note we check `getSourceFileContainingLocation` instead of
// `getParentSourceFile` to make sure initializer exprs are correctly
// handled.
auto *M = DC->getParentModule();
if (auto *SF = M->getSourceFileContainingLocation(N.getStartLoc())) {
auto &SM = M->getASTContext().SourceMgr;
if (SM.hasGeneratedSourceInfo(*SF->getBufferID())) {
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: generated code\n");
return false;
}
}
}

// Do not profile AST nodes in unavailable contexts.
auto *DC = Constant.getInnermostDeclContext();
if (auto *D = DC->getInnermostDeclarationDeclContext()) {
if (D->getSemanticUnavailableAttr()) {
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: unavailable context\n");
Expand Down
49 changes: 49 additions & 0 deletions test/Profiler/coverage_macros.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// REQUIRES: swift_swift_parser

// RUN: %empty-directory(%t)

// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/../Macros/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath

// RUN: %target-swift-frontend -emit-sil -emit-sorted-sil -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -module-name coverage_macros %s -load-plugin-library %t/%target-library-name(MacroDefinition) | %FileCheck %s --implicit-check-not sil_coverage_map
// RUN: %target-swift-frontend -emit-ir -profile-generate -profile-coverage-mapping -load-plugin-library %t/%target-library-name(MacroDefinition) %s

@attached(accessor)
macro accessViaStorage() = #externalMacro(module: "MacroDefinition", type: "AccessViaStorageMacro")

@attached(
member,
names: named(`init`), named(Storage), named(storage), named(getStorage()), named(method)
)
macro addMembers() = #externalMacro(module: "MacroDefinition", type: "AddMembers")

@freestanding(expression)
macro nestedDeclInExpr() -> () -> Void = #externalMacro(module: "MacroDefinition", type: "NestedDeclInExprMacro")

// Note we use implicit-check-not, so this test ensures we don't emit
// coverage maps for the macro expansions.

struct S1 {
// CHECK: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.x
var x: Int = 0

// CHECK: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S1.y
var y: Int = 0
}

@addMembers
struct S2 {
// CHECK: sil_coverage_map{{.*}}variable initialization expression of coverage_macros.S2.(_storage
private var _storage = S1()

@accessViaStorage
var x: Int

// No coverage map for the initializer, as it gets subsumed.
@accessViaStorage
var y: Int = 17
}

// CHECK: sil_coverage_map{{.*}}s15coverage_macros3fooyyF
func foo() {
_ = #nestedDeclInExpr()
}
8 changes: 4 additions & 4 deletions test/Profiler/coverage_relative_path.swift
Original file line number Diff line number Diff line change
@@ -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
// 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}}
27 changes: 27 additions & 0 deletions test/Profiler/coverage_relative_path_exec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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.
//
// 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 --check-prefix SHOW %s
// RUN: %llvm-cov report %t/main -instr-profile=%t/default.profdata | %FileCheck --check-prefix REPORT %s

// SHOW: func coverage
// REPORT: coverage_relative_path.swift