• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 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	"strings"
20
21	"android/soong/android"
22
23	"github.com/google/blueprint"
24	"github.com/google/blueprint/proptools"
25)
26
27// TODO(b/267229066): Remove globalAfdoProfileProjects after implementing bp2build converter for fdo_profile
28var (
29	globalAfdoProfileProjects = []string{
30		"vendor/google_data/pgo_profile/sampling/",
31		"toolchain/pgo-profiles/sampling/",
32	}
33)
34
35var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
36
37const afdoCFlagsFormat = "-fprofile-sample-use=%s"
38
39func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
40	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
41}
42
43type afdoRdep struct {
44	VariationName *string
45	ProfilePath   *string
46}
47
48type AfdoProperties struct {
49	// Afdo allows developers self-service enroll for
50	// automatic feedback-directed optimization using profile data.
51	Afdo bool
52
53	FdoProfilePath *string `blueprint:"mutated"`
54
55	AfdoRDeps []afdoRdep `blueprint:"mutated"`
56}
57
58type afdo struct {
59	Properties AfdoProperties
60}
61
62func (afdo *afdo) props() []interface{} {
63	return []interface{}{&afdo.Properties}
64}
65
66// afdoEnabled returns true for binaries and shared libraries
67// that set afdo prop to True and there is a profile available
68func (afdo *afdo) afdoEnabled() bool {
69	return afdo != nil && afdo.Properties.Afdo
70}
71
72func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
73	if afdo.Properties.Afdo {
74		// We use `-funique-internal-linkage-names` to associate profiles to the right internal
75		// functions. This option should be used before generating a profile. Because a profile
76		// generated for a binary without unique names doesn't work well building a binary with
77		// unique names (they have different internal function names).
78		// To avoid a chicken-and-egg problem, we enable `-funique-internal-linkage-names` when
79		// `afdo=true`, whether a profile exists or not.
80		// The profile can take effect in three steps:
81		// 1. Add `afdo: true` in Android.bp, and build the binary.
82		// 2. Collect an AutoFDO profile for the binary.
83		// 3. Make the profile searchable by the build system. So it's used the next time the binary
84		//	  is built.
85		flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...)
86	}
87	if path := afdo.Properties.FdoProfilePath; path != nil {
88		// The flags are prepended to allow overriding.
89		profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path)
90		flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...)
91		flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...)
92
93		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
94		// if profileFile gets updated
95		pathForSrc := android.PathForSource(ctx, *path)
96		flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc)
97		flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc)
98	}
99
100	return flags
101}
102
103func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
104	if ctx.Host() {
105		return
106	}
107
108	if ctx.static() && !ctx.staticBinary() {
109		return
110	}
111
112	if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
113		if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil {
114			actx.AddFarVariationDependencies(
115				[]blueprint.Variation{
116					{Mutator: "arch", Variation: actx.Target().ArchVariation()},
117					{Mutator: "os", Variation: "android"},
118				},
119				FdoProfileTag,
120				[]string{*fdoProfileName}...,
121			)
122		}
123	}
124}
125
126// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag
127// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property
128func (c *Module) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
129	if !c.Enabled() {
130		return
131	}
132
133	ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
134		if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
135			info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
136			c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
137		}
138	})
139}
140
141var _ FdoProfileMutatorInterface = (*Module)(nil)
142
143// Propagate afdo requirements down from binaries and shared libraries
144func afdoDepsMutator(mctx android.TopDownMutatorContext) {
145	if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
146		path := m.afdo.Properties.FdoProfilePath
147		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
148			tag := mctx.OtherModuleDependencyTag(dep)
149			libTag, isLibTag := tag.(libraryDependencyTag)
150
151			// Do not recurse down non-static dependencies
152			if isLibTag {
153				if !libTag.static() {
154					return false
155				}
156			} else {
157				if tag != objDepTag && tag != reuseObjTag {
158					return false
159				}
160			}
161
162			if dep, ok := dep.(*Module); ok {
163				dep.afdo.Properties.AfdoRDeps = append(
164					dep.afdo.Properties.AfdoRDeps,
165					afdoRdep{
166						VariationName: proptools.StringPtr(encodeTarget(m.Name())),
167						ProfilePath:   path,
168					},
169				)
170			}
171
172			return true
173		})
174	}
175}
176
177// Create afdo variants for modules that need them
178func afdoMutator(mctx android.BottomUpMutatorContext) {
179	if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
180		if !m.static() && m.afdo.Properties.Afdo {
181			mctx.SetDependencyVariation(encodeTarget(m.Name()))
182			return
183		}
184
185		variationNames := []string{""}
186
187		variantNameToProfilePath := make(map[string]*string)
188
189		for _, afdoRDep := range m.afdo.Properties.AfdoRDeps {
190			variantName := *afdoRDep.VariationName
191			// An rdep can be set twice in AfdoRDeps because there can be
192			// more than one path from an afdo-enabled module to
193			// a static dep such as
194			// afdo_enabled_foo -> static_bar ----> static_baz
195			//                   \                      ^
196			//                    ----------------------|
197			// We only need to create one variant per unique rdep
198			if _, exists := variantNameToProfilePath[variantName]; !exists {
199				variationNames = append(variationNames, variantName)
200				variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
201			}
202		}
203
204		if len(variationNames) > 1 {
205			modules := mctx.CreateVariations(variationNames...)
206			for i, name := range variationNames {
207				if name == "" {
208					continue
209				}
210				variation := modules[i].(*Module)
211				variation.Properties.PreventInstall = true
212				variation.Properties.HideFromMake = true
213				variation.afdo.Properties.Afdo = true
214				variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
215			}
216		}
217	}
218}
219
220// Encode target name to variation name.
221func encodeTarget(target string) string {
222	if target == "" {
223		return ""
224	}
225	return "afdo-" + target
226}
227
228// Decode target name from variation name.
229func decodeTarget(variation string) string {
230	if variation == "" {
231		return ""
232	}
233	return strings.TrimPrefix(variation, "afdo-")
234}
235