Add PGO support to soong
Bug: http://b/63768402 Bug: http://b/65598278 Add support for the 'pgo' property to specify how a module is processed under PGO. A sample property is below: pgo: { instrumentation: true, // could be "sampling: true" when supported profile_file: "pgo_simple.profdata", benchmarks: ["pgo_simple"], } 1. Runtime profiles can be gathered using "sampling" or "instrumentation". Sampling is not supported initially. 2. If 'toolchain/pgo-profiles' project is found, 'toolchain/pgo-profiles/${profile_file}' is passed to the compiler and linker when building this module. 3. If ANDROID_PGO_INSTRUMENT environment variable is set, and includes a benchmark in the 'benchmarks' list, appropriate flags (for e.g. -fprofile-generate for instrumentation) are passed to the compiler and linker when building this module. Test: Add example modules that specify the pgo property and verify appropriate flags and dependencies in the Ninja file. Some tests/examples are in https://android-review.googlesource.com/474805 Change-Id: I6242e0c904497a115e367dea6927ba1c4b906355
This commit is contained in:
parent
d685385c6b
commit
ada83ec0a6
|
@ -119,6 +119,7 @@ bootstrap_go_package {
|
|||
"cc/gen.go",
|
||||
"cc/lto.go",
|
||||
"cc/makevars.go",
|
||||
"cc/pgo.go",
|
||||
"cc/prebuilt.go",
|
||||
"cc/proto.go",
|
||||
"cc/relocation_packer.go",
|
||||
|
|
|
@ -325,6 +325,7 @@ func (binary *binaryDecorator) link(ctx ModuleContext,
|
|||
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
|
||||
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
|
||||
linkerDeps = append(linkerDeps, objs.tidyFiles...)
|
||||
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
|
||||
|
||||
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
|
||||
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
|
||||
|
|
15
cc/cc.go
15
cc/cc.go
|
@ -134,7 +134,8 @@ type Flags struct {
|
|||
RequiredInstructionSet string
|
||||
DynamicLinker string
|
||||
|
||||
CFlagsDeps android.Paths // Files depended on by compiler flags
|
||||
CFlagsDeps android.Paths // Files depended on by compiler flags
|
||||
LdFlagsDeps android.Paths // Files depended on by linker flags
|
||||
|
||||
GroupStaticLibs bool
|
||||
}
|
||||
|
@ -308,6 +309,7 @@ type Module struct {
|
|||
sabi *sabi
|
||||
vndkdep *vndkdep
|
||||
lto *lto
|
||||
pgo *pgo
|
||||
|
||||
androidMkSharedLibDeps []string
|
||||
|
||||
|
@ -350,6 +352,9 @@ func (c *Module) Init() android.Module {
|
|||
if c.lto != nil {
|
||||
c.AddProperties(c.lto.props()...)
|
||||
}
|
||||
if c.pgo != nil {
|
||||
c.AddProperties(c.pgo.props()...)
|
||||
}
|
||||
for _, feature := range c.features {
|
||||
c.AddProperties(feature.props()...)
|
||||
}
|
||||
|
@ -506,6 +511,7 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo
|
|||
module.sabi = &sabi{}
|
||||
module.vndkdep = &vndkdep{}
|
||||
module.lto = <o{}
|
||||
module.pgo = &pgo{}
|
||||
return module
|
||||
}
|
||||
|
||||
|
@ -557,6 +563,9 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
|
|||
if c.lto != nil {
|
||||
flags = c.lto.flags(ctx, flags)
|
||||
}
|
||||
if c.pgo != nil {
|
||||
flags = c.pgo.flags(ctx, flags)
|
||||
}
|
||||
for _, feature := range c.features {
|
||||
flags = feature.flags(ctx, flags)
|
||||
}
|
||||
|
@ -643,6 +652,9 @@ func (c *Module) begin(ctx BaseModuleContext) {
|
|||
if c.lto != nil {
|
||||
c.lto.begin(ctx)
|
||||
}
|
||||
if c.pgo != nil {
|
||||
c.pgo.begin(ctx)
|
||||
}
|
||||
for _, feature := range c.features {
|
||||
feature.begin(ctx)
|
||||
}
|
||||
|
@ -1250,6 +1262,7 @@ func DefaultsFactory(props ...interface{}) android.Module {
|
|||
&SAbiProperties{},
|
||||
&VndkProperties{},
|
||||
<OProperties{},
|
||||
&PgoProperties{},
|
||||
)
|
||||
|
||||
android.InitDefaultsModule(module)
|
||||
|
|
|
@ -484,6 +484,7 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext,
|
|||
flags Flags, deps PathDeps, objs Objects) android.Path {
|
||||
|
||||
var linkerDeps android.Paths
|
||||
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
|
||||
|
||||
versionScript := android.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
|
||||
unexportedSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Unexported_symbols_list)
|
||||
|
@ -628,7 +629,6 @@ func (library *libraryDecorator) link(ctx ModuleContext,
|
|||
flags Flags, deps PathDeps, objs Objects) android.Path {
|
||||
|
||||
objs = objs.Append(deps.Objs)
|
||||
|
||||
var out android.Path
|
||||
if library.static() || library.header() {
|
||||
out = library.linkStatic(ctx, flags, deps, objs)
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
package cc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"android/soong/android"
|
||||
)
|
||||
|
||||
var (
|
||||
// Add flags to ignore warnings that profiles are old or missing for
|
||||
// some functions
|
||||
profileUseOtherFlags = []string{}
|
||||
)
|
||||
|
||||
const pgoProfileProject = "toolchain/pgo-profiles"
|
||||
|
||||
const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
|
||||
const profileSamplingFlag = "-gline-tables-only"
|
||||
const profileUseInstrumentFormat = "-fprofile-use=%s"
|
||||
const profileUseSamplingFormat = "-fprofile-sample-use=%s"
|
||||
|
||||
type PgoProperties struct {
|
||||
Pgo struct {
|
||||
Instrumentation *bool
|
||||
Sampling *bool
|
||||
Profile_file *string `android:"arch_variant"`
|
||||
Benchmarks []string
|
||||
} `android:"arch_variant"`
|
||||
|
||||
PgoPresent bool `blueprint:"mutated"`
|
||||
ShouldProfileModule bool `blueprint:"mutated"`
|
||||
}
|
||||
|
||||
type pgo struct {
|
||||
Properties PgoProperties
|
||||
}
|
||||
|
||||
func (pgo *pgo) props() []interface{} {
|
||||
return []interface{}{&pgo.Properties}
|
||||
}
|
||||
|
||||
func (pgo *pgo) profileGatherFlags(ctx ModuleContext) string {
|
||||
if *pgo.Properties.Pgo.Instrumentation {
|
||||
return profileInstrumentFlag
|
||||
}
|
||||
if *pgo.Properties.Pgo.Sampling {
|
||||
return profileSamplingFlag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (pgo *pgo) profileUseFlag(ctx ModuleContext, file string) string {
|
||||
if *pgo.Properties.Pgo.Instrumentation {
|
||||
return fmt.Sprintf(profileUseInstrumentFormat, file)
|
||||
}
|
||||
if *pgo.Properties.Pgo.Sampling {
|
||||
return fmt.Sprintf(profileUseSamplingFormat, file)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (pgo *pgo) profileUseFlags(ctx ModuleContext, file string) []string {
|
||||
flags := []string{pgo.profileUseFlag(ctx, file)}
|
||||
flags = append(flags, profileUseOtherFlags...)
|
||||
return flags
|
||||
}
|
||||
|
||||
func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
|
||||
isInstrumentation := props.Pgo.Instrumentation != nil
|
||||
isSampling := props.Pgo.Sampling != nil
|
||||
|
||||
profileKindPresent := isInstrumentation || isSampling
|
||||
filePresent := props.Pgo.Profile_file != nil
|
||||
benchmarksPresent := len(props.Pgo.Benchmarks) > 0
|
||||
|
||||
// If all three properties are absent, PGO is OFF for this module
|
||||
if !profileKindPresent && !filePresent && !benchmarksPresent {
|
||||
return false
|
||||
}
|
||||
|
||||
// If at least one property exists, validate that all properties exist
|
||||
if !profileKindPresent || !filePresent || !benchmarksPresent {
|
||||
var missing []string
|
||||
if !profileKindPresent {
|
||||
missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
|
||||
}
|
||||
if !filePresent {
|
||||
missing = append(missing, "profile_file property")
|
||||
}
|
||||
if !benchmarksPresent {
|
||||
missing = append(missing, "non-empty benchmarks property")
|
||||
}
|
||||
missingProps := strings.Join(missing, ", ")
|
||||
ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
|
||||
}
|
||||
|
||||
// Sampling not supported yet
|
||||
//
|
||||
// TODO When sampling support is turned on, check that instrumentation and
|
||||
// sampling are not simultaneously specified
|
||||
if isSampling {
|
||||
ctx.PropertyErrorf("pgo.sampling", "\"sampling\" is not supported yet)")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getPgoProfilesDir(ctx ModuleContext) android.OptionalPath {
|
||||
return android.ExistentPathForSource(ctx, "", pgoProfileProject)
|
||||
}
|
||||
|
||||
func (pgo *pgo) begin(ctx BaseModuleContext) {
|
||||
// TODO Evaluate if we need to support PGO for host modules
|
||||
if ctx.Host() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if PGO is needed for this module
|
||||
pgo.Properties.PgoPresent = pgo.Properties.isPGO(ctx)
|
||||
|
||||
if !pgo.Properties.PgoPresent {
|
||||
return
|
||||
}
|
||||
|
||||
// This module should be instrumented if ANDROID_PGO_INSTRUMENT is set
|
||||
// and includes a benchmark listed for this module
|
||||
//
|
||||
// TODO Validate that each benchmark instruments at least one module
|
||||
pgo.Properties.ShouldProfileModule = false
|
||||
pgoBenchmarks := ctx.AConfig().Getenv("ANDROID_PGO_INSTRUMENT")
|
||||
pgoBenchmarksMap := make(map[string]bool)
|
||||
for _, b := range strings.Split(pgoBenchmarks, ",") {
|
||||
pgoBenchmarksMap[b] = true
|
||||
}
|
||||
|
||||
for _, b := range pgo.Properties.Pgo.Benchmarks {
|
||||
if pgoBenchmarksMap[b] == true {
|
||||
pgo.Properties.ShouldProfileModule = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
|
||||
if ctx.Host() {
|
||||
return flags
|
||||
}
|
||||
|
||||
props := pgo.Properties
|
||||
|
||||
// Add flags to profile this module based on its profile_kind
|
||||
if props.ShouldProfileModule {
|
||||
profileGatherFlags := pgo.profileGatherFlags(ctx)
|
||||
flags.LdFlags = append(flags.LdFlags, profileGatherFlags)
|
||||
flags.CFlags = append(flags.CFlags, profileGatherFlags)
|
||||
return flags
|
||||
}
|
||||
|
||||
// If the PGO profiles project is found, and this module has PGO
|
||||
// enabled, add flags to use the profile
|
||||
if profilesDir := getPgoProfilesDir(ctx); props.PgoPresent && profilesDir.Valid() {
|
||||
profileFile := android.PathForSource(ctx, profilesDir.String(), *(props.Pgo.Profile_file))
|
||||
profileUseFlags := pgo.profileUseFlags(ctx, profileFile.String())
|
||||
|
||||
flags.CFlags = append(flags.CFlags, profileUseFlags...)
|
||||
flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
|
||||
|
||||
// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
|
||||
// if profileFile gets updated
|
||||
flags.CFlagsDeps = append(flags.CFlagsDeps, profileFile)
|
||||
flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFile)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
Loading…
Reference in New Issue