1// Copyright 2017 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "strconv" 19 20 "github.com/google/blueprint" 21 22 "android/soong/android" 23) 24 25const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw" 26 27type CoverageProperties struct { 28 Native_coverage *bool 29 30 NeedCoverageVariant bool `blueprint:"mutated"` 31 NeedCoverageBuild bool `blueprint:"mutated"` 32 33 CoverageEnabled bool `blueprint:"mutated"` 34 IsCoverageVariant bool `blueprint:"mutated"` 35} 36 37type coverage struct { 38 Properties CoverageProperties 39 40 // Whether binaries containing this module need --coverage added to their ldflags 41 linkCoverage bool 42} 43 44func (cov *coverage) props() []interface{} { 45 return []interface{}{&cov.Properties} 46} 47 48func getGcovProfileLibraryName(ctx ModuleContextIntf) string { 49 // This function should only ever be called for a cc.Module, so the 50 // following statement should always succeed. 51 if ctx.useSdk() { 52 return "libprofile-extras_ndk" 53 } else { 54 return "libprofile-extras" 55 } 56} 57 58func getClangProfileLibraryName(ctx ModuleContextIntf) string { 59 if ctx.useSdk() { 60 return "libprofile-clang-extras_ndk" 61 } else if ctx.isCfiAssemblySupportEnabled() { 62 return "libprofile-clang-extras_cfi_support" 63 } else { 64 return "libprofile-clang-extras" 65 } 66} 67 68func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps { 69 if cov.Properties.NeedCoverageVariant { 70 ctx.AddVariationDependencies([]blueprint.Variation{ 71 {Mutator: "link", Variation: "static"}, 72 }, CoverageDepTag, getGcovProfileLibraryName(ctx)) 73 ctx.AddVariationDependencies([]blueprint.Variation{ 74 {Mutator: "link", Variation: "static"}, 75 }, CoverageDepTag, getClangProfileLibraryName(ctx)) 76 } 77 return deps 78} 79 80func EnableContinuousCoverage(ctx android.BaseModuleContext) bool { 81 return ctx.DeviceConfig().ClangCoverageContinuousMode() 82} 83 84func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { 85 clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled() 86 gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled() 87 88 if !gcovCoverage && !clangCoverage { 89 return flags, deps 90 } 91 92 if cov.Properties.CoverageEnabled { 93 cov.linkCoverage = true 94 95 if gcovCoverage { 96 flags.GcovCoverage = true 97 flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0") 98 99 // Override -Wframe-larger-than and non-default optimization 100 // flags that the module may use. 101 flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0") 102 } else if clangCoverage { 103 flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag, 104 "-fcoverage-mapping", "-Wno-pass-failed", "-D__ANDROID_CLANG_COVERAGE__") 105 // Override -Wframe-larger-than. We can expect frame size increase after 106 // coverage instrumentation. 107 flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=") 108 if EnableContinuousCoverage(ctx) { 109 flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation") 110 } 111 } 112 } 113 114 // Even if we don't have coverage enabled, if any of our object files were compiled 115 // with coverage, then we need to add --coverage to our ldflags. 116 if !cov.linkCoverage { 117 if ctx.static() && !ctx.staticBinary() { 118 // For static libraries, the only thing that changes our object files 119 // are included whole static libraries, so check to see if any of 120 // those have coverage enabled. 121 ctx.VisitDirectDeps(func(m android.Module) { 122 if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok { 123 if depTag.static() && depTag.wholeStatic { 124 if cc, ok := m.(*Module); ok && cc.coverage != nil { 125 if cc.coverage.linkCoverage { 126 cov.linkCoverage = true 127 } 128 } 129 } 130 } 131 }) 132 } else { 133 // For executables and shared libraries, we need to check all of 134 // our static dependencies. 135 ctx.VisitDirectDeps(func(m android.Module) { 136 cc, ok := m.(*Module) 137 if !ok || cc.coverage == nil { 138 return 139 } 140 141 if static, ok := cc.linker.(libraryInterface); !ok || !static.static() { 142 return 143 } 144 145 if cc.coverage.linkCoverage { 146 cov.linkCoverage = true 147 } 148 }) 149 } 150 } 151 152 if cov.linkCoverage { 153 if gcovCoverage { 154 flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage") 155 156 coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module) 157 deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path()) 158 159 flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv") 160 } else if clangCoverage { 161 flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag) 162 if EnableContinuousCoverage(ctx) { 163 flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation") 164 } 165 166 coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module) 167 deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path()) 168 flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open") 169 } 170 } 171 172 return flags, deps 173} 174 175func (cov *coverage) begin(ctx BaseModuleContext) { 176 if ctx.Host() { 177 // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a 178 // Just turn off for now. 179 } else { 180 cov.Properties = SetCoverageProperties(ctx, cov.Properties, ctx.nativeCoverage(), ctx.useSdk(), ctx.sdkVersion()) 181 } 182} 183 184func SetCoverageProperties(ctx android.BaseModuleContext, properties CoverageProperties, moduleTypeHasCoverage bool, 185 useSdk bool, sdkVersion string) CoverageProperties { 186 // Coverage is disabled globally 187 if !ctx.DeviceConfig().NativeCoverageEnabled() { 188 return properties 189 } 190 191 var needCoverageVariant bool 192 var needCoverageBuild bool 193 194 if moduleTypeHasCoverage { 195 // Check if Native_coverage is set to false. This property defaults to true. 196 needCoverageVariant = BoolDefault(properties.Native_coverage, true) 197 if useSdk && sdkVersion != "current" { 198 // Native coverage is not supported for SDK versions < 23 199 if fromApi, err := strconv.Atoi(sdkVersion); err == nil && fromApi < 23 { 200 needCoverageVariant = false 201 } 202 } 203 204 if needCoverageVariant { 205 // Coverage variant is actually built with coverage if enabled for its module path 206 needCoverageBuild = ctx.DeviceConfig().NativeCoverageEnabledForPath(ctx.ModuleDir()) 207 } 208 } 209 210 properties.NeedCoverageBuild = needCoverageBuild 211 properties.NeedCoverageVariant = needCoverageVariant 212 213 return properties 214} 215 216// Coverage is an interface for non-CC modules to implement to be mutated for coverage 217type Coverage interface { 218 android.Module 219 IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool 220 SetPreventInstall() 221 HideFromMake() 222 MarkAsCoverageVariant(bool) 223 EnableCoverageIfNeeded() 224} 225 226func coverageMutator(mctx android.BottomUpMutatorContext) { 227 if c, ok := mctx.Module().(*Module); ok && c.coverage != nil { 228 needCoverageVariant := c.coverage.Properties.NeedCoverageVariant 229 needCoverageBuild := c.coverage.Properties.NeedCoverageBuild 230 if needCoverageVariant { 231 m := mctx.CreateVariations("", "cov") 232 233 // Setup the non-coverage version and set HideFromMake and 234 // PreventInstall to true. 235 m[0].(*Module).coverage.Properties.CoverageEnabled = false 236 m[0].(*Module).coverage.Properties.IsCoverageVariant = false 237 m[0].(*Module).Properties.HideFromMake = true 238 m[0].(*Module).Properties.PreventInstall = true 239 240 // The coverage-enabled version inherits HideFromMake, 241 // PreventInstall from the original module. 242 m[1].(*Module).coverage.Properties.CoverageEnabled = needCoverageBuild 243 m[1].(*Module).coverage.Properties.IsCoverageVariant = true 244 } 245 } else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) { 246 // APEX and Rust modules fall here 247 248 // Note: variant "" is also created because an APEX can be depended on by another 249 // module which are split into "" and "cov" variants. e.g. when cc_test refers 250 // to an APEX via 'data' property. 251 m := mctx.CreateVariations("", "cov") 252 m[0].(Coverage).MarkAsCoverageVariant(false) 253 m[0].(Coverage).SetPreventInstall() 254 m[0].(Coverage).HideFromMake() 255 256 m[1].(Coverage).MarkAsCoverageVariant(true) 257 m[1].(Coverage).EnableCoverageIfNeeded() 258 } 259} 260 261func parseSymbolFileForAPICoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath { 262 apiLevelsJson := android.GetApiLevelsJson(ctx) 263 symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) 264 outputFile := ctx.baseModuleName() + ".xml" 265 parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFile) 266 rule := android.NewRuleBuilder(pctx, ctx) 267 rule.Command(). 268 BuiltTool("ndk_api_coverage_parser"). 269 Input(symbolFilePath). 270 Output(parsedApiCoveragePath). 271 Implicit(apiLevelsJson). 272 FlagWithArg("--api-map ", apiLevelsJson.String()) 273 rule.Build("native_library_api_list", "Generate native API list based on symbol files for coverage measurement") 274 return parsedApiCoveragePath 275} 276