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/gen.go",
|
||||||
"cc/lto.go",
|
"cc/lto.go",
|
||||||
"cc/makevars.go",
|
"cc/makevars.go",
|
||||||
|
"cc/pgo.go",
|
||||||
"cc/prebuilt.go",
|
"cc/prebuilt.go",
|
||||||
"cc/proto.go",
|
"cc/proto.go",
|
||||||
"cc/relocation_packer.go",
|
"cc/relocation_packer.go",
|
||||||
|
|
|
@ -325,6 +325,7 @@ func (binary *binaryDecorator) link(ctx ModuleContext,
|
||||||
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
|
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
|
||||||
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
|
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
|
||||||
linkerDeps = append(linkerDeps, objs.tidyFiles...)
|
linkerDeps = append(linkerDeps, objs.tidyFiles...)
|
||||||
|
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
|
||||||
|
|
||||||
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
|
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
|
||||||
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
|
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
|
RequiredInstructionSet string
|
||||||
DynamicLinker 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
|
GroupStaticLibs bool
|
||||||
}
|
}
|
||||||
|
@ -308,6 +309,7 @@ type Module struct {
|
||||||
sabi *sabi
|
sabi *sabi
|
||||||
vndkdep *vndkdep
|
vndkdep *vndkdep
|
||||||
lto *lto
|
lto *lto
|
||||||
|
pgo *pgo
|
||||||
|
|
||||||
androidMkSharedLibDeps []string
|
androidMkSharedLibDeps []string
|
||||||
|
|
||||||
|
@ -350,6 +352,9 @@ func (c *Module) Init() android.Module {
|
||||||
if c.lto != nil {
|
if c.lto != nil {
|
||||||
c.AddProperties(c.lto.props()...)
|
c.AddProperties(c.lto.props()...)
|
||||||
}
|
}
|
||||||
|
if c.pgo != nil {
|
||||||
|
c.AddProperties(c.pgo.props()...)
|
||||||
|
}
|
||||||
for _, feature := range c.features {
|
for _, feature := range c.features {
|
||||||
c.AddProperties(feature.props()...)
|
c.AddProperties(feature.props()...)
|
||||||
}
|
}
|
||||||
|
@ -506,6 +511,7 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo
|
||||||
module.sabi = &sabi{}
|
module.sabi = &sabi{}
|
||||||
module.vndkdep = &vndkdep{}
|
module.vndkdep = &vndkdep{}
|
||||||
module.lto = <o{}
|
module.lto = <o{}
|
||||||
|
module.pgo = &pgo{}
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +563,9 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
|
||||||
if c.lto != nil {
|
if c.lto != nil {
|
||||||
flags = c.lto.flags(ctx, flags)
|
flags = c.lto.flags(ctx, flags)
|
||||||
}
|
}
|
||||||
|
if c.pgo != nil {
|
||||||
|
flags = c.pgo.flags(ctx, flags)
|
||||||
|
}
|
||||||
for _, feature := range c.features {
|
for _, feature := range c.features {
|
||||||
flags = feature.flags(ctx, flags)
|
flags = feature.flags(ctx, flags)
|
||||||
}
|
}
|
||||||
|
@ -643,6 +652,9 @@ func (c *Module) begin(ctx BaseModuleContext) {
|
||||||
if c.lto != nil {
|
if c.lto != nil {
|
||||||
c.lto.begin(ctx)
|
c.lto.begin(ctx)
|
||||||
}
|
}
|
||||||
|
if c.pgo != nil {
|
||||||
|
c.pgo.begin(ctx)
|
||||||
|
}
|
||||||
for _, feature := range c.features {
|
for _, feature := range c.features {
|
||||||
feature.begin(ctx)
|
feature.begin(ctx)
|
||||||
}
|
}
|
||||||
|
@ -1250,6 +1262,7 @@ func DefaultsFactory(props ...interface{}) android.Module {
|
||||||
&SAbiProperties{},
|
&SAbiProperties{},
|
||||||
&VndkProperties{},
|
&VndkProperties{},
|
||||||
<OProperties{},
|
<OProperties{},
|
||||||
|
&PgoProperties{},
|
||||||
)
|
)
|
||||||
|
|
||||||
android.InitDefaultsModule(module)
|
android.InitDefaultsModule(module)
|
||||||
|
|
|
@ -484,6 +484,7 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext,
|
||||||
flags Flags, deps PathDeps, objs Objects) android.Path {
|
flags Flags, deps PathDeps, objs Objects) android.Path {
|
||||||
|
|
||||||
var linkerDeps android.Paths
|
var linkerDeps android.Paths
|
||||||
|
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
|
||||||
|
|
||||||
versionScript := android.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
|
versionScript := android.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
|
||||||
unexportedSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Unexported_symbols_list)
|
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 {
|
flags Flags, deps PathDeps, objs Objects) android.Path {
|
||||||
|
|
||||||
objs = objs.Append(deps.Objs)
|
objs = objs.Append(deps.Objs)
|
||||||
|
|
||||||
var out android.Path
|
var out android.Path
|
||||||
if library.static() || library.header() {
|
if library.static() || library.header() {
|
||||||
out = library.linkStatic(ctx, flags, deps, objs)
|
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