| // 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" |
| |
| "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__", |
| |
| // Bug: http://b/408093589, http://b/396515430: LLVM change 4089763883 to |
| // global merge regressed code coverage, marking previously covered lines |
| // as uncovered. Disable global merge until the regression is fixed. |
| // -Wunused-command-line-argument needs to be disabled because |
| // -mno-global-merge is reported as unused in LTO mode. |
| "-mno-global-merge", |
| "-Wno-unused-command-line-argument", |
| } |
| 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 { |
| // This function should only ever be called for a cc.Module, so the |
| // following statement should always succeed. |
| // LINT.IfChange |
| 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) |
| } |
| |
| func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps { |
| if cov.Properties.NeedCoverageVariant && ctx.Device() { |
| 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() |
| } |
| |
| func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { |
| clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled() |
| gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled() |
| |
| if !gcovCoverage && !clangCoverage { |
| 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.staticLibrary() { |
| // 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.VisitDirectDepsProxy(func(m android.ModuleProxy) { |
| if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok { |
| if depTag.static() && depTag.wholeStatic { |
| if info, ok := android.OtherModuleProvider(ctx, m, LinkableInfoProvider); ok { |
| if info.LinkCoverage { |
| cov.linkCoverage = true |
| } |
| } |
| } |
| } |
| }) |
| } else { |
| // For executables and shared libraries, we need to check all of |
| // our static dependencies. |
| ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) { |
| if _, ok := android.OtherModuleProvider(ctx, m, StaticLibraryInfoProvider); ok { |
| if info, ok := android.OtherModuleProvider(ctx, m, LinkableInfoProvider); ok { |
| if info.LinkCoverage { |
| cov.linkCoverage = true |
| } |
| } |
| } |
| }) |
| } |
| } |
| |
| if cov.linkCoverage { |
| if gcovCoverage { |
| flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage") |
| |
| if ctx.Device() { |
| coverage := ctx.GetDirectDepProxyWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag) |
| deps.WholeStaticLibs = append(deps.WholeStaticLibs, android.OutputFileForModule(ctx, coverage, "")) |
| 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.GetDirectDepProxyWithTag(getClangProfileLibraryName(ctx), CoverageDepTag) |
| deps.WholeStaticLibs = append(deps.WholeStaticLibs, android.OutputFileForModule(ctx, coverage, "")) |
| flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open") |
| } |
| } |
| } |
| |
| return flags, deps |
| } |
| |
| func (cov *coverage) begin(ctx BaseModuleContext, binary bool, test bool) { |
| if IsCoverageEnabled(ctx, binary, test) { |
| cov.Properties = SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), ctx.useSdk(), ctx.sdkVersion()) |
| } |
| } |
| |
| func IsCoverageEnabled(ctx android.BaseModuleContext, binary bool, test bool) bool { |
| if ctx.Host() { |
| // Only allow coverage for host modules if they are explicitly marked as tests |
| // or if they are libraries (which might be dependencies of tests). |
| // Build tools (binaries that are NOT tests) should be excluded. |
| if binary && !test { |
| return false |
| } |
| // Host coverage is only supported on Linux 64-bit binaries |
| if !ctx.Os().Linux() { |
| return false |
| } |
| if ctx.Arch().ArchType.Multilib == "lib32" { |
| return false |
| } |
| } |
| return true |
| } |
| |
| 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 |
| } |
| } |
| |
| // Coverage is still untested with LFI |
| if ctx.Target().LFI { |
| 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 IsNativeCoverageNeededContext interface { |
| Config() android.Config |
| DeviceConfig() android.DeviceConfig |
| Device() bool |
| } |
| |
| var _ IsNativeCoverageNeededContext = android.IncomingTransitionContext(nil) |
| var _ IsNativeCoverageNeededContext = android.BaseModuleContext(nil) |
| var _ IsNativeCoverageNeededContext = android.BottomUpMutatorContext(nil) |
| |
| type UseCoverage interface { |
| android.Module |
| IsNativeCoverageNeeded(ctx IsNativeCoverageNeededContext) 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 UseCoverageDeptag interface { |
| IsNativeCoverageNeededDepTag(ctx IsNativeCoverageNeededContext) bool |
| } |
| |
| type coverageTransitionMutator struct{} |
| |
| 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) Split(ctx android.BaseModuleContext) []string { |
| allSplits := c.split(ctx) |
| if ctx.Config().GetBuildFlagBool("RELEASE_SOONG_COV_VARIANT_ON_DEMAND") { |
| return allSplits[0:1] |
| } else { |
| return allSplits |
| } |
| } |
| |
| func (c coverageTransitionMutator) SplitOnDemand(ctx android.BaseModuleContext) []string { |
| allSplits := c.split(ctx) |
| if len(allSplits) <= 1 || !ctx.Config().GetBuildFlagBool("RELEASE_SOONG_COV_VARIANT_ON_DEMAND") { |
| return nil |
| } else { |
| return allSplits[1:] |
| } |
| } |
| |
| 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 "" |
| } |
| |
| // non-coverage variants have PreventInstall = true, so we need certain deptags that are |
| // used for installation to use the coverage variant so that filesystem modules will package |
| // them correctly. |
| if x, ok := ctx.DepTag().(UseCoverageDeptag); (ok && x.IsNativeCoverageNeededDepTag(ctx)) || ctx.DepTag() == android.RequiredDepTag { |
| return "cov" |
| } |
| |
| 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 android.ModuleContext, symbolFile string) android.ModuleOutPath { |
| apiLevelsJson := android.GetApiLevelsJson(ctx) |
| symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) |
| outputFile := ctx.Module().(LinkableInterface).BaseModuleName() + ".xml" |
| parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFile) |
| rule := android.NewRuleBuilder(pctx, ctx).SandboxDisabled() |
| 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 |
| } |