github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/cc/pgo.go (about)

     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  
    15  package cc
    16  
    17  import (
    18  	"fmt"
    19  	"path/filepath"
    20  	"strings"
    21  
    22  	"android/soong/android"
    23  	"android/soong/cc/config"
    24  )
    25  
    26  var (
    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  
    37  const pgoProfileProjectsConfigKey = "PgoProfileProjects"
    38  const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
    39  const profileSamplingFlag = "-gline-tables-only"
    40  const profileUseInstrumentFormat = "-fprofile-use=%s"
    41  const profileUseSamplingFormat = "-fprofile-sample-use=%s"
    42  
    43  func getPgoProfileProjects(config android.DeviceConfig) []string {
    44  	return config.OnceStringSlice(pgoProfileProjectsConfigKey, func() []string {
    45  		return append(globalPgoProfileProjects, config.PgoAdditionalProfileDirs()...)
    46  	})
    47  }
    48  
    49  func recordMissingProfileFile(ctx BaseModuleContext, missing string) {
    50  	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFile).Store(missing, true)
    51  }
    52  
    53  type 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  
    70  type pgo struct {
    71  	Properties PgoProperties
    72  }
    73  
    74  func (props *PgoProperties) isInstrumentation() bool {
    75  	return props.Pgo.Instrumentation != nil && *props.Pgo.Instrumentation == true
    76  }
    77  
    78  func (props *PgoProperties) isSampling() bool {
    79  	return props.Pgo.Sampling != nil && *props.Pgo.Sampling == true
    80  }
    81  
    82  func (pgo *pgo) props() []interface{} {
    83  	return []interface{}{&pgo.Properties}
    84  }
    85  
    86  func (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  
   103  func (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  
   141  func (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  
   151  func (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  
   157  func (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  
   184  func (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  
   225  func (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  
   267  func (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  
   275  func (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  }