| // 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__", |
| } |
| 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.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") |
| } |
| } |
| } |
| |
| 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 |
| } |