// Copyright 2023 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Note: If you want to know how to use orderfile for your binary or shared // library, you can go look at the README in toolchains/pgo-profiles/orderfiles package cc import ( "fmt" "android/soong/android" ) // Order files are text files containing symbols representing functions names. // Linkers (lld) uses order files to layout functions in a specific order. // These binaries with ordered symbols will reduce page faults and improve a program's launch time // due to the efficient loading of symbols during a program’s cold-start. var ( // Add flags to ignore warnings about symbols not be found // or not allowed to be ordered orderfileOtherFlags = []string{ "-Wl,--no-warn-symbol-ordering", } // Add folder projects for orderfiles globalOrderfileProjects = []string{ "toolchain/pgo-profiles/orderfiles", "vendor/google_data/pgo_profile/orderfiles", } ) var orderfileProjectsConfigKey = android.NewOnceKey("OrderfileProjects") const orderfileProfileFlag = "-forder-file-instrumentation" const orderfileUseFormat = "-Wl,--symbol-ordering-file=%s" func getOrderfileProjects(config android.DeviceConfig) []string { return config.OnceStringSlice(orderfileProjectsConfigKey, func() []string { return globalOrderfileProjects }) } func recordMissingOrderfile(ctx BaseModuleContext, missing string) { getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true) } type OrderfileProperties struct { Orderfile struct { Instrumentation *bool Order_file_path *string `android:"arch_variant"` Load_order_file *bool `android:"arch_variant"` // Additional compiler flags to use when building this module // for orderfile profiling. Cflags []string `android:"arch_variant"` } `android:"arch_variant"` ShouldProfileModule bool `blueprint:"mutated"` OrderfileLoad bool `blueprint:"mutated"` OrderfileInstrLink bool `blueprint:"mutated"` } type orderfile struct { Properties OrderfileProperties } func (props *OrderfileProperties) shouldInstrument() bool { return Bool(props.Orderfile.Instrumentation) } // ShouldLoadOrderfile returns true if we need to load the order file rather than // profile the binary or shared library func (props *OrderfileProperties) shouldLoadOrderfile() bool { return Bool(props.Orderfile.Load_order_file) && props.Orderfile.Order_file_path != nil } // orderfileEnabled returns true for binaries and shared libraries // if instrument flag is set to true func (orderfile *orderfile) orderfileEnabled() bool { return orderfile != nil && orderfile.Properties.shouldInstrument() } // orderfileLinkEnabled returns true for binaries and shared libraries // if you should instrument dependencies func (orderfile *orderfile) orderfileLinkEnabled() bool { return orderfile != nil && orderfile.Properties.OrderfileInstrLink } func (orderfile *orderfile) props() []interface{} { return []interface{}{&orderfile.Properties} } // Get the path to the order file by checking it is valid and not empty func (props *OrderfileProperties) getOrderfile(ctx BaseModuleContext) android.OptionalPath { orderFile := *props.Orderfile.Order_file_path // Test if the order file is present in any of the Orderfile projects for _, profileProject := range getOrderfileProjects(ctx.DeviceConfig()) { path := android.ExistentPathForSource(ctx, profileProject, orderFile) if path.Valid() { return path } } // Record that this module's order file is absent missing := *props.Orderfile.Order_file_path + ":" + ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName() recordMissingOrderfile(ctx, missing) return android.OptionalPath{} } func (props *OrderfileProperties) addInstrumentationProfileGatherFlags(ctx ModuleContext, flags Flags) Flags { flags.Local.CFlags = append(flags.Local.CFlags, orderfileProfileFlag) flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm -enable-order-file-instrumentation") flags.Local.CFlags = append(flags.Local.CFlags, props.Orderfile.Cflags...) flags.Local.LdFlags = append(flags.Local.LdFlags, orderfileProfileFlag) return flags } func (props *OrderfileProperties) loadOrderfileFlags(ctx ModuleContext, file string) []string { flags := []string{fmt.Sprintf(orderfileUseFormat, file)} flags = append(flags, orderfileOtherFlags...) return flags } func (props *OrderfileProperties) addLoadFlags(ctx ModuleContext, flags Flags) Flags { orderFile := props.getOrderfile(ctx) orderFilePath := orderFile.Path() loadFlags := props.loadOrderfileFlags(ctx, orderFilePath.String()) flags.Local.LdFlags = append(flags.Local.LdFlags, loadFlags...) // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt // if orderfile gets updated flags.CFlagsDeps = append(flags.CFlagsDeps, orderFilePath) flags.LdFlagsDeps = append(flags.LdFlagsDeps, orderFilePath) return flags } func (orderfile *orderfile) begin(ctx BaseModuleContext) { // Currently, we are not enabling orderfiles for host if ctx.Host() { return } // Currently, we are not enabling orderfiles to begin from static libraries if ctx.static() && !ctx.staticBinary() { return } if ctx.DeviceConfig().ClangCoverageEnabled() { return } // Checking if orderfile is enabled for this module if !orderfile.orderfileEnabled() { return } orderfile.Properties.OrderfileLoad = orderfile.Properties.shouldLoadOrderfile() orderfile.Properties.ShouldProfileModule = !orderfile.Properties.shouldLoadOrderfile() orderfile.Properties.OrderfileInstrLink = orderfile.orderfileEnabled() && !orderfile.Properties.shouldLoadOrderfile() } func (orderfile *orderfile) flags(ctx ModuleContext, flags Flags) Flags { props := orderfile.Properties // Add flags to load the orderfile using the path in its Android.bp if orderfile.Properties.OrderfileLoad { flags = props.addLoadFlags(ctx, flags) return flags } // Add flags to profile this module if props.ShouldProfileModule { flags = props.addInstrumentationProfileGatherFlags(ctx, flags) return flags } return flags } // Propagate profile orderfile flags down from binaries and shared libraries // We do not allow propagation for load flags because the orderfile is specific // to the module (binary / shared library) func orderfileDepsMutator(mctx android.TopDownMutatorContext) { if m, ok := mctx.Module().(*Module); ok { if !m.orderfile.orderfileLinkEnabled() { return } mctx.WalkDeps(func(dep android. Module, parent android.Module) bool { tag := mctx.OtherModuleDependencyTag(dep) libTag, isLibTag := tag.(libraryDependencyTag) // Do not recurse down non-static dependencies if isLibTag { if !libTag.static() { return false } } else { if tag != objDepTag && tag != reuseObjTag { return false } } if dep, ok := dep.(*Module); ok { if m.orderfile.Properties.OrderfileInstrLink { dep.orderfile.Properties.OrderfileInstrLink = true; } } return true }) } } // Create orderfile variants for modules that need them func orderfileMutator(mctx android.BottomUpMutatorContext) { if m, ok := mctx.Module().(*Module); ok && m.orderfile != nil { if !m.static() && m.orderfile.orderfileEnabled() { mctx.SetDependencyVariation("orderfile") return } variationNames := []string{""} if m.orderfile.Properties.OrderfileInstrLink { variationNames = append(variationNames, "orderfile") } if len(variationNames) > 1 { modules := mctx.CreateVariations(variationNames...) for i, name := range variationNames { if name == "" { continue } variation := modules[i].(*Module) variation.Properties.PreventInstall = true variation.Properties.HideFromMake = true variation.orderfile.Properties.ShouldProfileModule = true variation.orderfile.Properties.OrderfileLoad = false } } } }