• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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