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