platform_build_soong/cc/coverage.go

362 lines
12 KiB
Go
Raw Normal View History

// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cc
import (
"strconv"
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
"github.com/google/blueprint"
"android/soong/android"
)
var (
clangCoverageHostLdFlags = []string{
"-Wl,--no-as-needed",
"-Wl,--wrap,open",
}
clangContinuousCoverageFlags = []string{
"-mllvm",
"-runtime-counter-relocation",
}
clangCoverageCFlags = []string{
"-Wno-frame-larger-than=",
}
clangCoverageCommonFlags = []string{
"-fcoverage-mapping",
"-Wno-pass-failed",
"-D__ANDROID_CLANG_COVERAGE__",
}
clangCoverageHWASanFlags = []string{
"-mllvm",
"-hwasan-globals=0",
}
)
const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
type CoverageProperties struct {
Native_coverage *bool
NeedCoverageVariant bool `blueprint:"mutated"`
NeedCoverageBuild bool `blueprint:"mutated"`
CoverageEnabled bool `blueprint:"mutated"`
IsCoverageVariant bool `blueprint:"mutated"`
}
type coverage struct {
Properties CoverageProperties
// Whether binaries containing this module need --coverage added to their ldflags
linkCoverage bool
}
func (cov *coverage) props() []interface{} {
return []interface{}{&cov.Properties}
}
func getGcovProfileLibraryName(ctx ModuleContextIntf) string {
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
// This function should only ever be called for a cc.Module, so the
// following statement should always succeed.
// LINT.IfChange
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
if ctx.useSdk() {
return "libprofile-extras_ndk"
} else {
return "libprofile-extras"
}
}
func getClangProfileLibraryName(ctx ModuleContextIntf) string {
if ctx.useSdk() {
return "libprofile-clang-extras_ndk"
} else if ctx.isCfiAssemblySupportEnabled() {
return "libprofile-clang-extras_cfi_support"
} else {
return "libprofile-clang-extras"
}
// LINT.ThenChange(library.go)
}
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
if cov.Properties.NeedCoverageVariant && ctx.Device() {
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, CoverageDepTag, getGcovProfileLibraryName(ctx))
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, CoverageDepTag, getClangProfileLibraryName(ctx))
}
return deps
}
func EnableContinuousCoverage(ctx android.BaseModuleContext) bool {
return ctx.DeviceConfig().ClangCoverageContinuousMode()
}
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled()
if !gcovCoverage && !clangCoverage {
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
return flags, deps
}
if cov.Properties.CoverageEnabled {
cov.linkCoverage = true
if gcovCoverage {
flags.GcovCoverage = true
flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
// Override -Wframe-larger-than and non-default optimization
// flags that the module may use.
flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
} else if clangCoverage {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag)
flags.Local.CommonFlags = append(flags.Local.CommonFlags, clangCoverageCommonFlags...)
// Override -Wframe-larger-than. We can expect frame size increase after
// coverage instrumentation.
flags.Local.CFlags = append(flags.Local.CFlags, clangCoverageCFlags...)
if EnableContinuousCoverage(ctx) {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, clangContinuousCoverageFlags...)
}
// http://b/248022906, http://b/247941801 enabling coverage and hwasan-globals
// instrumentation together causes duplicate-symbol errors for __llvm_profile_filename.
if c, ok := ctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(Hwasan) {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, clangCoverageHWASanFlags...)
}
}
}
// Even if we don't have coverage enabled, if any of our object files were compiled
// with coverage, then we need to add --coverage to our ldflags.
if !cov.linkCoverage {
if ctx.static() && !ctx.staticBinary() {
// For static libraries, the only thing that changes our object files
// are included whole static libraries, so check to see if any of
// those have coverage enabled.
ctx.VisitDirectDeps(func(m android.Module) {
if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
if depTag.static() && depTag.wholeStatic {
if cc, ok := m.(*Module); ok && cc.coverage != nil {
if cc.coverage.linkCoverage {
cov.linkCoverage = true
}
}
}
}
})
} else {
// For executables and shared libraries, we need to check all of
// our static dependencies.
ctx.VisitDirectDeps(func(m android.Module) {
cc, ok := m.(*Module)
if !ok || cc.coverage == nil {
return
}
if static, ok := cc.linker.(libraryInterface); !ok || !static.static() {
return
}
if cc.coverage.linkCoverage {
cov.linkCoverage = true
}
})
}
}
if cov.linkCoverage {
if gcovCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
if ctx.Device() {
coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
}
} else if clangCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
if EnableContinuousCoverage(ctx) {
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
}
if ctx.Device() {
coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open")
}
}
}
Refactor libprofile-extras to be added as a whole static library Bug: http://b/134177005 Bug: http://b/116873221 Previously, the libprofile-extras dependency was added as a LateStaticLib and the constructor in this library was included during linking with the '-uinit_profile_extras' linker flag. This was done because at the deps() stage, the exact binaries that need coverage are not known (in fact the coverage-enabled variants are not created yet). This meant that for a link command, if one of the shared libraries already exported the constructor, the output of the link command did not load/link libprofile-extras. For other reasons, we now want to add more symbols to this library that need to be linked into all libraries and executables. To accomplish that, refactor the dependency handling so libprofile-extras can be added as a 'WholeStaticLib'. This is done by creating a new dependency type (with a coverageDepTag dependency tag) to add libprofile-extras as a dependency for all modules that can potentially link with coverage. During the flags() call, this dependency is moved as a WholeStaticLib dependency iff coverage is enabled in this link step. There are a few NFC changes as well: - deps() takes a DepsContext parameter. - flags() has an extra PathDeps parameter and return value. - add useSdk() helper to cc.Module. Test: Build with coverage and check that we can generate coverage using SIGUSR1 and the debug.coverage.flush sysprop. Change-Id: I7e7d8201956a150febbda5bb1794f8ece016db8b
2019-07-02 23:55:35 +02:00
return flags, deps
}
func (cov *coverage) begin(ctx BaseModuleContext) {
if ctx.Host() && !ctx.Os().Linux() {
// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
// Just turn off for now.
} else {
cov.Properties = SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), ctx.useSdk(), ctx.sdkVersion())
}
}
func SetCoverageProperties(ctx android.BaseModuleContext, properties CoverageProperties, moduleTypeHasCoverage bool,
useSdk bool, sdkVersion string) CoverageProperties {
// Coverage is disabled globally
if !ctx.DeviceConfig().NativeCoverageEnabled() {
return properties
}
var needCoverageVariant bool
var needCoverageBuild bool
if moduleTypeHasCoverage {
// Check if Native_coverage is set to false. This property defaults to true.
needCoverageVariant = BoolDefault(properties.Native_coverage, true)
if useSdk && sdkVersion != "current" {
// Native coverage is not supported for SDK versions < 23
if fromApi, err := strconv.Atoi(sdkVersion); err == nil && fromApi < 23 {
needCoverageVariant = false
}
}
if needCoverageVariant {
// Coverage variant is actually built with coverage if enabled for its module path
needCoverageBuild = ctx.DeviceConfig().NativeCoverageEnabledForPath(ctx.ModuleDir())
}
}
properties.NeedCoverageBuild = needCoverageBuild
properties.NeedCoverageVariant = needCoverageVariant
return properties
}
type UseCoverage interface {
android.Module
IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool
}
// Coverage is an interface for non-CC modules to implement to be mutated for coverage
type Coverage interface {
UseCoverage
SetPreventInstall()
HideFromMake()
MarkAsCoverageVariant(bool)
EnableCoverageIfNeeded()
}
type coverageTransitionMutator struct{}
var _ android.TransitionMutator = (*coverageTransitionMutator)(nil)
func (c coverageTransitionMutator) Split(ctx android.BaseModuleContext) []string {
if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
if c.coverage.Properties.NeedCoverageVariant {
return []string{"", "cov"}
}
} else if cov, ok := ctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(ctx) {
// APEX and Rust modules fall here
// Note: variant "" is also created because an APEX can be depended on by another
// module which are split into "" and "cov" variants. e.g. when cc_test refers
// to an APEX via 'data' property.
return []string{"", "cov"}
} else if cov, ok := ctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(ctx) {
// Module itself doesn't have to have "cov" variant, but it should use "cov" variants of
// deps.
return []string{"cov"}
}
return []string{""}
}
func (c coverageTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
return sourceVariation
}
func (c coverageTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
if !c.coverage.Properties.NeedCoverageVariant {
return ""
}
} else if cov, ok := ctx.Module().(Coverage); ok {
if !cov.IsNativeCoverageNeeded(ctx) {
return ""
}
} else if cov, ok := ctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(ctx) {
// Module only has a "cov" variation, so all incoming variations should use "cov".
return "cov"
} else {
return ""
}
return incomingVariation
}
func (c coverageTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
if variation == "" && c.coverage.Properties.NeedCoverageVariant {
// Setup the non-coverage version and set HideFromMake and
// PreventInstall to true.
c.coverage.Properties.CoverageEnabled = false
c.coverage.Properties.IsCoverageVariant = false
c.Properties.HideFromMake = true
c.Properties.PreventInstall = true
} else if variation == "cov" {
// The coverage-enabled version inherits HideFromMake,
// PreventInstall from the original module.
c.coverage.Properties.CoverageEnabled = c.coverage.Properties.NeedCoverageBuild
c.coverage.Properties.IsCoverageVariant = true
}
} else if cov, ok := ctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(ctx) {
// APEX and Rust modules fall here
// Note: variant "" is also created because an APEX can be depended on by another
// module which are split into "" and "cov" variants. e.g. when cc_test refers
// to an APEX via 'data' property.
if variation == "" {
cov.MarkAsCoverageVariant(false)
cov.SetPreventInstall()
cov.HideFromMake()
} else if variation == "cov" {
cov.MarkAsCoverageVariant(true)
cov.EnableCoverageIfNeeded()
}
} else if cov, ok := ctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(ctx) {
// Module itself doesn't have to have "cov" variant, but it should use "cov" variants of
// deps.
}
}
func parseSymbolFileForAPICoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
apiLevelsJson := android.GetApiLevelsJson(ctx)
symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
outputFile := ctx.baseModuleName() + ".xml"
parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFile)
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
BuiltTool("ndk_api_coverage_parser").
Input(symbolFilePath).
Output(parsedApiCoveragePath).
Implicit(apiLevelsJson).
FlagWithArg("--api-map ", apiLevelsJson.String())
rule.Build("native_library_api_list", "Generate native API list based on symbol files for coverage measurement")
return parsedApiCoveragePath
}