Created an Orderfile go file for the build system.
In addition, I added a test file to check if flags are added and propagated correctly.
Test: mma build/soong
Output: #### build completed successfully (07:24 (mm:ss)) ####
For testing with an actual binary or shared library, steps are in this
README:
https://android.googlesource.com/toolchain/pgo-profiles/+/refs/heads/main/orderfiles/README.md
Change-Id: Idcf169156ef691bcacb8adc92828ef09450085f8
diff --git a/cc/orderfile.go b/cc/orderfile.go
new file mode 100644
index 0000000..cc1ab29
--- /dev/null
+++ b/cc/orderfile.go
@@ -0,0 +1,257 @@
+// 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, props.Orderfile.Cflags...)
+ flags.Local.CFlags = append(flags.Local.CFlags, orderfileProfileFlag)
+ flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm -enable-order-file-instrumentation")
+ 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.CFlags = append(flags.Local.CFlags, loadFlags...)
+ 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
+ }
+ }
+ }
+}