1// Copyright 2023 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//
15// Note: If you want to know how to use orderfile for your binary or shared
16// library, you can go look at the README in toolchains/pgo-profiles/orderfiles
17
18package cc
19
20import (
21	"fmt"
22
23	"github.com/google/blueprint"
24
25	"android/soong/android"
26)
27
28// Order files are text files containing symbols representing functions names.
29// Linkers (lld) uses order files to layout functions in a specific order.
30// These binaries with ordered symbols will reduce page faults and improve a program's launch time
31// due to the efficient loading of symbols during a program’s cold-start.
32var (
33	// Add flags to ignore warnings about symbols not be found
34	// or not allowed to be ordered
35	orderfileOtherFlags = []string{
36		"-Wl,--no-warn-symbol-ordering",
37	}
38
39	// Add folder projects for orderfiles
40	globalOrderfileProjects = []string{
41		"toolchain/pgo-profiles/orderfiles",
42		"vendor/google_data/pgo_profile/orderfiles",
43	}
44)
45
46var orderfileProjectsConfigKey = android.NewOnceKey("OrderfileProjects")
47
48const orderfileProfileFlag = "-forder-file-instrumentation"
49const orderfileUseFormat = "-Wl,--symbol-ordering-file=%s"
50
51func getOrderfileProjects(config android.DeviceConfig) []string {
52	return config.OnceStringSlice(orderfileProjectsConfigKey, func() []string {
53		return globalOrderfileProjects
54	})
55}
56
57func recordMissingOrderfile(ctx BaseModuleContext, missing string) {
58	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
59}
60
61type OrderfileProperties struct {
62	Orderfile struct {
63		Instrumentation *bool
64		Order_file_path *string `android:"arch_variant"`
65		Load_order_file *bool   `android:"arch_variant"`
66		// Additional compiler flags to use when building this module
67		// for orderfile profiling.
68		Cflags []string `android:"arch_variant"`
69	} `android:"arch_variant"`
70
71	ShouldProfileModule bool `blueprint:"mutated"`
72	OrderfileLoad       bool `blueprint:"mutated"`
73	OrderfileInstrLink  bool `blueprint:"mutated"`
74}
75
76type orderfile struct {
77	Properties OrderfileProperties
78}
79
80func (props *OrderfileProperties) shouldInstrument() bool {
81	return Bool(props.Orderfile.Instrumentation)
82}
83
84// ShouldLoadOrderfile returns true if we need to load the order file rather than
85// profile the binary or shared library
86func (props *OrderfileProperties) shouldLoadOrderfile() bool {
87	return Bool(props.Orderfile.Load_order_file) && props.Orderfile.Order_file_path != nil
88}
89
90// orderfileEnabled returns true for binaries and shared libraries
91// if instrument flag is set to true
92func (orderfile *orderfile) orderfileEnabled() bool {
93	return orderfile != nil && orderfile.Properties.shouldInstrument()
94}
95
96// orderfileLinkEnabled returns true for binaries and shared libraries
97// if you should instrument dependencies
98func (orderfile *orderfile) orderfileLinkEnabled() bool {
99	return orderfile != nil && orderfile.Properties.OrderfileInstrLink
100}
101
102func (orderfile *orderfile) props() []interface{} {
103	return []interface{}{&orderfile.Properties}
104}
105
106// Get the path to the order file by checking it is valid and not empty
107func (props *OrderfileProperties) getOrderfile(ctx BaseModuleContext) android.OptionalPath {
108	orderFile := *props.Orderfile.Order_file_path
109
110	// Test if the order file is present in any of the Orderfile projects
111	for _, profileProject := range getOrderfileProjects(ctx.DeviceConfig()) {
112		path := android.ExistentPathForSource(ctx, profileProject, orderFile)
113		if path.Valid() {
114			return path
115		}
116	}
117
118	// Record that this module's order file is absent
119	missing := *props.Orderfile.Order_file_path + ":" + ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
120	recordMissingOrderfile(ctx, missing)
121
122	return android.OptionalPath{}
123}
124
125func (props *OrderfileProperties) addInstrumentationProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
126	flags.Local.CFlags = append(flags.Local.CFlags, orderfileProfileFlag)
127	flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm -enable-order-file-instrumentation")
128	flags.Local.CFlags = append(flags.Local.CFlags, props.Orderfile.Cflags...)
129	flags.Local.LdFlags = append(flags.Local.LdFlags, orderfileProfileFlag)
130	return flags
131}
132
133func (props *OrderfileProperties) loadOrderfileFlags(ctx ModuleContext, file string) []string {
134	flags := []string{fmt.Sprintf(orderfileUseFormat, file)}
135	flags = append(flags, orderfileOtherFlags...)
136	return flags
137}
138
139func (props *OrderfileProperties) addLoadFlags(ctx ModuleContext, flags Flags) Flags {
140	orderFile := props.getOrderfile(ctx)
141	orderFilePath := orderFile.Path()
142	loadFlags := props.loadOrderfileFlags(ctx, orderFilePath.String())
143
144	flags.Local.LdFlags = append(flags.Local.LdFlags, loadFlags...)
145
146	// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
147	// if orderfile gets updated
148	flags.CFlagsDeps = append(flags.CFlagsDeps, orderFilePath)
149	flags.LdFlagsDeps = append(flags.LdFlagsDeps, orderFilePath)
150	return flags
151}
152
153func (orderfile *orderfile) begin(ctx BaseModuleContext) {
154	// Currently, we are not enabling orderfiles for host
155	if ctx.Host() {
156		return
157	}
158
159	// Currently, we are not enabling orderfiles to begin from static libraries
160	if ctx.static() && !ctx.staticBinary() {
161		return
162	}
163
164	if ctx.DeviceConfig().ClangCoverageEnabled() {
165		return
166	}
167
168	// Checking if orderfile is enabled for this module
169	if !orderfile.orderfileEnabled() {
170		return
171	}
172
173	orderfile.Properties.OrderfileLoad = orderfile.Properties.shouldLoadOrderfile()
174	orderfile.Properties.ShouldProfileModule = !orderfile.Properties.shouldLoadOrderfile()
175	orderfile.Properties.OrderfileInstrLink = orderfile.orderfileEnabled() && !orderfile.Properties.shouldLoadOrderfile()
176}
177
178func (orderfile *orderfile) flags(ctx ModuleContext, flags Flags) Flags {
179	props := orderfile.Properties
180	// Add flags to load the orderfile using the path in its Android.bp
181	if orderfile.Properties.OrderfileLoad {
182		flags = props.addLoadFlags(ctx, flags)
183		return flags
184	}
185
186	// Add flags to profile this module
187	if props.ShouldProfileModule {
188		flags = props.addInstrumentationProfileGatherFlags(ctx, flags)
189		return flags
190	}
191
192	return flags
193}
194
195func orderfilePropagateViaDepTag(tag blueprint.DependencyTag) bool {
196	libTag, isLibTag := tag.(libraryDependencyTag)
197	// Do not recurse down non-static dependencies
198	if isLibTag {
199		return libTag.static()
200	} else {
201		return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag
202	}
203}
204
205// orderfileTransitionMutator creates orderfile variants of cc modules.
206type orderfileTransitionMutator struct{}
207
208const ORDERFILE_VARIATION = "orderfile"
209
210func (o *orderfileTransitionMutator) Split(ctx android.BaseModuleContext) []string {
211	return []string{""}
212}
213
214func (o *orderfileTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
215	if m, ok := ctx.Module().(*Module); ok && m.orderfile != nil {
216		if !orderfilePropagateViaDepTag(ctx.DepTag()) {
217			return ""
218		}
219
220		if sourceVariation != "" {
221			return sourceVariation
222		}
223
224		// Propagate profile orderfile flags down from binaries and shared libraries
225		if m.orderfile.orderfileLinkEnabled() {
226			return ORDERFILE_VARIATION
227		}
228	}
229	return ""
230}
231
232func (o *orderfileTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
233	if m, ok := ctx.Module().(*Module); ok && m.orderfile != nil {
234		return incomingVariation
235	}
236	return ""
237}
238
239func (o *orderfileTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
240	if variation == "" {
241		return
242	}
243
244	if m, ok := ctx.Module().(*Module); ok && m.orderfile != nil {
245		m.Properties.PreventInstall = true
246		m.Properties.HideFromMake = true
247		m.orderfile.Properties.ShouldProfileModule = true
248		// We do not allow propagation for load flags because the orderfile is specific
249		// to the module (binary / shared library)
250		m.orderfile.Properties.OrderfileLoad = false
251	}
252}
253