pgo.go 9.5 KB

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