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