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 "fmt" 19 "path/filepath" 20 "strings" 21 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25 "android/soong/cc/config" 26) 27 28var ( 29 // Add flags to ignore warnings that profiles are old or missing for 30 // some functions. 31 profileUseOtherFlags = []string{ 32 "-Wno-backend-plugin", 33 } 34 35 globalPgoProfileProjects = []string{ 36 "toolchain/pgo-profiles", 37 "vendor/google_data/pgo_profile", 38 } 39) 40 41var pgoProfileProjectsConfigKey = android.NewOnceKey("PgoProfileProjects") 42 43const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp" 44const profileSamplingFlag = "-gmlt -fdebug-info-for-profiling" 45const profileUseInstrumentFormat = "-fprofile-use=%s" 46const profileUseSamplingFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s" 47 48func getPgoProfileProjects(config android.DeviceConfig) []string { 49 return config.OnceStringSlice(pgoProfileProjectsConfigKey, func() []string { 50 return append(globalPgoProfileProjects, config.PgoAdditionalProfileDirs()...) 51 }) 52} 53 54func recordMissingProfileFile(ctx BaseModuleContext, missing string) { 55 getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true) 56} 57 58type PgoProperties struct { 59 Pgo struct { 60 Instrumentation *bool 61 Sampling *bool 62 Profile_file *string `android:"arch_variant"` 63 Benchmarks []string 64 Enable_profile_use *bool `android:"arch_variant"` 65 // Additional compiler flags to use when building this module 66 // for profiling (either instrumentation or sampling). 67 Cflags []string `android:"arch_variant"` 68 } `android:"arch_variant"` 69 70 PgoPresent bool `blueprint:"mutated"` 71 ShouldProfileModule bool `blueprint:"mutated"` 72 PgoCompile bool `blueprint:"mutated"` 73} 74 75type pgo struct { 76 Properties PgoProperties 77} 78 79func (props *PgoProperties) isInstrumentation() bool { 80 return props.Pgo.Instrumentation != nil && *props.Pgo.Instrumentation == true 81} 82 83func (props *PgoProperties) isSampling() bool { 84 return props.Pgo.Sampling != nil && *props.Pgo.Sampling == true 85} 86 87func (pgo *pgo) props() []interface{} { 88 return []interface{}{&pgo.Properties} 89} 90 91func (props *PgoProperties) addInstrumentationProfileGatherFlags(ctx ModuleContext, flags Flags) Flags { 92 flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...) 93 94 flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag) 95 // The profile runtime is added below in deps(). Add the below 96 // flag, which is the only other link-time action performed by 97 // the Clang driver during link. 98 flags.Local.LdFlags = append(flags.Local.LdFlags, "-u__llvm_profile_runtime") 99 return flags 100} 101func (props *PgoProperties) addSamplingProfileGatherFlags(ctx ModuleContext, flags Flags) Flags { 102 flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...) 103 104 flags.Local.CFlags = append(flags.Local.CFlags, profileSamplingFlag) 105 flags.Local.LdFlags = append(flags.Local.LdFlags, profileSamplingFlag) 106 return flags 107} 108 109func (props *PgoProperties) getPgoProfileFile(ctx BaseModuleContext) android.OptionalPath { 110 profile_file := *props.Pgo.Profile_file 111 112 // Test if the profile_file is present in any of the PGO profile projects 113 for _, profileProject := range getPgoProfileProjects(ctx.DeviceConfig()) { 114 // Bug: http://b/74395273 If the profile_file is unavailable, 115 // use a versioned file named 116 // <profile_file>.<arbitrary-version> when available. This 117 // works around an issue where ccache serves stale cache 118 // entries when the profile file has changed. 119 globPattern := filepath.Join(profileProject, profile_file+".*") 120 versioned_profiles, err := ctx.GlobWithDeps(globPattern, nil) 121 if err != nil { 122 ctx.ModuleErrorf("glob: %s", err.Error()) 123 } 124 125 path := android.ExistentPathForSource(ctx, profileProject, profile_file) 126 if path.Valid() { 127 if len(versioned_profiles) != 0 { 128 ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+filepath.Join(profileProject, profile_file)+", "+strings.Join(versioned_profiles, ", ")) 129 } 130 return path 131 } 132 133 if len(versioned_profiles) > 1 { 134 ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+strings.Join(versioned_profiles, ", ")) 135 } else if len(versioned_profiles) == 1 { 136 return android.OptionalPathForPath(android.PathForSource(ctx, versioned_profiles[0])) 137 } 138 } 139 140 // Record that this module's profile file is absent 141 missing := *props.Pgo.Profile_file + ":" + ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName() 142 recordMissingProfileFile(ctx, missing) 143 144 return android.OptionalPathForPath(nil) 145} 146 147func (props *PgoProperties) profileUseFlag(ctx ModuleContext, file string) string { 148 if props.isInstrumentation() { 149 return fmt.Sprintf(profileUseInstrumentFormat, file) 150 } 151 if props.isSampling() { 152 return fmt.Sprintf(profileUseSamplingFormat, file) 153 } 154 return "" 155} 156 157func (props *PgoProperties) profileUseFlags(ctx ModuleContext, file string) []string { 158 flags := []string{props.profileUseFlag(ctx, file)} 159 flags = append(flags, profileUseOtherFlags...) 160 return flags 161} 162 163func (props *PgoProperties) addProfileUseFlags(ctx ModuleContext, flags Flags) Flags { 164 // Return if 'pgo' property is not present in this module. 165 if !props.PgoPresent { 166 return flags 167 } 168 169 if props.PgoCompile { 170 profileFile := props.getPgoProfileFile(ctx) 171 profileFilePath := profileFile.Path() 172 profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String()) 173 174 flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlags...) 175 flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlags...) 176 177 // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt 178 // if profileFile gets updated 179 flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath) 180 flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath) 181 182 if props.isSampling() { 183 flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true") 184 } 185 } 186 return flags 187} 188 189func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool { 190 isInstrumentation := props.isInstrumentation() 191 isSampling := props.isSampling() 192 193 profileKindPresent := isInstrumentation || isSampling 194 filePresent := props.Pgo.Profile_file != nil 195 benchmarksPresent := len(props.Pgo.Benchmarks) > 0 196 197 // If all three properties are absent, PGO is OFF for this module 198 if !profileKindPresent && !filePresent && !benchmarksPresent { 199 return false 200 } 201 202 // profileKindPresent and filePresent are mandatory properties. 203 if !profileKindPresent || !filePresent { 204 var missing []string 205 if !profileKindPresent { 206 missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)") 207 } 208 if !filePresent { 209 missing = append(missing, "profile_file property") 210 } 211 missingProps := strings.Join(missing, ", ") 212 ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps) 213 } 214 215 // Benchmark property is mandatory for instrumentation PGO. 216 if isInstrumentation && !benchmarksPresent { 217 ctx.ModuleErrorf("Instrumentation PGO specification is missing benchmark property") 218 } 219 220 if isSampling && isInstrumentation { 221 ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set") 222 } 223 224 return true 225} 226 227func (pgo *pgo) begin(ctx BaseModuleContext) { 228 // TODO Evaluate if we need to support PGO for host modules 229 if ctx.Host() { 230 return 231 } 232 233 // Check if PGO is needed for this module 234 pgo.Properties.PgoPresent = pgo.Properties.isPGO(ctx) 235 236 if !pgo.Properties.PgoPresent { 237 return 238 } 239 240 // This module should be instrumented if ANDROID_PGO_INSTRUMENT is set 241 // and includes 'all', 'ALL' or a benchmark listed for this module. 242 // 243 // TODO Validate that each benchmark instruments at least one module 244 pgo.Properties.ShouldProfileModule = false 245 pgoBenchmarks := ctx.Config().Getenv("ANDROID_PGO_INSTRUMENT") 246 pgoBenchmarksMap := make(map[string]bool) 247 for _, b := range strings.Split(pgoBenchmarks, ",") { 248 pgoBenchmarksMap[b] = true 249 } 250 251 if pgoBenchmarksMap["all"] == true || pgoBenchmarksMap["ALL"] == true { 252 pgo.Properties.ShouldProfileModule = true 253 } else { 254 for _, b := range pgo.Properties.Pgo.Benchmarks { 255 if pgoBenchmarksMap[b] == true { 256 pgo.Properties.ShouldProfileModule = true 257 break 258 } 259 } 260 } 261 262 // PGO profile use is not feasible for a Clang coverage build because 263 // -fprofile-use and -fprofile-instr-generate are incompatible. 264 if ctx.DeviceConfig().ClangCoverageEnabled() { 265 return 266 } 267 268 if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") && 269 proptools.BoolDefault(pgo.Properties.Pgo.Enable_profile_use, true) { 270 if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() { 271 pgo.Properties.PgoCompile = true 272 } 273 } 274} 275 276func (pgo *pgo) deps(ctx BaseModuleContext, deps Deps) Deps { 277 if pgo.Properties.ShouldProfileModule { 278 runtimeLibrary := config.ProfileRuntimeLibrary(ctx.toolchain()) 279 deps.LateStaticLibs = append(deps.LateStaticLibs, runtimeLibrary) 280 } 281 return deps 282} 283 284func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags { 285 if ctx.Host() { 286 return flags 287 } 288 289 props := pgo.Properties 290 291 // Add flags to profile this module based on its profile_kind 292 if props.ShouldProfileModule && props.isInstrumentation() { 293 props.addInstrumentationProfileGatherFlags(ctx, flags) 294 // Instrumentation PGO use and gather flags cannot coexist. 295 return flags 296 } else if props.ShouldProfileModule && props.isSampling() { 297 props.addSamplingProfileGatherFlags(ctx, flags) 298 } else if ctx.DeviceConfig().SamplingPGO() { 299 props.addSamplingProfileGatherFlags(ctx, flags) 300 } 301 302 if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") { 303 props.addProfileUseFlags(ctx, flags) 304 } 305 306 return flags 307} 308