Implement fdo_profile module type

Introducing fdo_profile module type to reimplement the afdo support in cc moduels. This change allows the feature to be compatible with Bazel migration.

How it works:

PreDepsMutators:
  * BeginMutator: If non-static cc modules sets afdo prop, search and add corresponding fdo_profile module as a dep with fdoProfileTag
  * fdoProfileMutator:
    * If in fdo_profile module, set FdoProfileProvider with full path to profile
    * If in cc module, read FdoProfileProvider from dep with fdoProfileTag and set FdoProfileInfo.Path to FdoProfilePath field

PostDepsMutators:
  * afdoDepsMutator: If a module has FdoProfilePath set, walk to its static deps and set itself to the deps' AfdoRdeps
  * afdoMutator: If a static dep has AfdoRDeps set, create afdo variant.

Ignore-AOSP-First: Other CLs in the same topic are internal-only
Test: go test
Bug: b/267229065
Change-Id: I687d798a02d9743c92804fea36fb4ae3a7a0e5e3
This commit is contained in:
Vinh Tran
2023-03-09 22:07:19 -05:00
parent e2a125ffc8
commit 0ccc232691
7 changed files with 375 additions and 106 deletions

View File

@@ -1418,6 +1418,21 @@ func (c *deviceConfig) PgoAdditionalProfileDirs() []string {
return c.config.productVariables.PgoAdditionalProfileDirs return c.config.productVariables.PgoAdditionalProfileDirs
} }
// AfdoProfile returns fully qualified path associated to the given module name
func (c *deviceConfig) AfdoProfile(name string) (*string, error) {
for _, afdoProfile := range c.config.productVariables.AfdoProfiles {
split := strings.Split(afdoProfile, ":")
if len(split) != 3 {
return nil, fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+
"The expected format is <module>:<fully-qualified-path-to-fdo_profile>", afdoProfile)
}
if split[0] == name {
return proptools.StringPtr(strings.Join([]string{split[1], split[2]}, ":")), nil
}
}
return nil, nil
}
func (c *deviceConfig) VendorSepolicyDirs() []string { func (c *deviceConfig) VendorSepolicyDirs() []string {
return c.config.productVariables.BoardVendorSepolicyDirs return c.config.productVariables.BoardVendorSepolicyDirs
} }

View File

@@ -464,6 +464,8 @@ type productVariables struct {
IncludeTags []string `json:",omitempty"` IncludeTags []string `json:",omitempty"`
SourceRootDirs []string `json:",omitempty"` SourceRootDirs []string `json:",omitempty"`
AfdoProfiles []string `json:",omitempty"`
} }
func boolPtr(v bool) *bool { func boolPtr(v bool) *bool {

View File

@@ -21,6 +21,8 @@ bootstrap_go_package {
], ],
srcs: [ srcs: [
"afdo.go", "afdo.go",
"fdo_profile.go",
"androidmk.go", "androidmk.go",
"api_level.go", "api_level.go",
"bp2build.go", "bp2build.go",

View File

@@ -18,9 +18,10 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/google/blueprint/proptools"
"android/soong/android" "android/soong/android"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
) )
var ( var (
@@ -34,6 +35,7 @@ var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s" const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s"
// TODO(b/267229065): Remove getAfdoProfileProjects after reimplementing afdo support for rust
func getAfdoProfileProjects(config android.DeviceConfig) []string { func getAfdoProfileProjects(config android.DeviceConfig) []string {
return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string { return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
return globalAfdoProfileProjects return globalAfdoProfileProjects
@@ -44,13 +46,19 @@ func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string)
getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true) getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
} }
type afdoRdep struct {
VariationName *string
ProfilePath *string
}
type AfdoProperties struct { type AfdoProperties struct {
// Afdo allows developers self-service enroll for // Afdo allows developers self-service enroll for
// automatic feedback-directed optimization using profile data. // automatic feedback-directed optimization using profile data.
Afdo bool Afdo bool
AfdoTarget *string `blueprint:"mutated"` FdoProfilePath *string `blueprint:"mutated"`
AfdoDeps []string `blueprint:"mutated"`
AfdoRDeps []afdoRdep `blueprint:"mutated"`
} }
type afdo struct { type afdo struct {
@@ -61,8 +69,10 @@ func (afdo *afdo) props() []interface{} {
return []interface{}{&afdo.Properties} return []interface{}{&afdo.Properties}
} }
func (afdo *afdo) AfdoEnabled() bool { // afdoEnabled returns true for binaries and shared libraries
return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil // that set afdo prop to True and there is a profile available
func (afdo *afdo) afdoEnabled() bool {
return afdo != nil && afdo.Properties.Afdo && afdo.Properties.FdoProfilePath != nil
} }
// Get list of profile file names, ordered by level of specialisation. For example: // Get list of profile file names, ordered by level of specialisation. For example:
@@ -70,6 +80,7 @@ func (afdo *afdo) AfdoEnabled() bool {
// 2. libfoo.afdo // 2. libfoo.afdo
// //
// Add more specialisation as needed. // Add more specialisation as needed.
// TODO(b/267229065): Remove getProfileFiles after reimplementing afdo support for rust
func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string { func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string {
var files []string var files []string
files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo") files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo")
@@ -77,6 +88,7 @@ func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string
return files return files
} }
// TODO(b/267229065): Remove GetAfdoProfileFile after reimplementing afdo support for rust
func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath { func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath {
// Test if the profile_file is present in any of the Afdo profile projects // Test if the profile_file is present in any of the Afdo profile projects
for _, profileFile := range getProfileFiles(ctx, module) { for _, profileFile := range getProfileFiles(ctx, module) {
@@ -95,82 +107,125 @@ func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, m
return android.OptionalPathForPath(nil) return android.OptionalPathForPath(nil)
} }
func (afdo *afdo) begin(ctx BaseModuleContext) {
if ctx.Host() {
return
}
if ctx.static() && !ctx.staticBinary() {
return
}
if afdo.Properties.Afdo {
module := ctx.ModuleName()
if afdo.Properties.GetAfdoProfileFile(ctx, module).Valid() {
afdo.Properties.AfdoTarget = proptools.StringPtr(module)
}
}
}
func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
if profile := afdo.Properties.AfdoTarget; profile != nil { if path := afdo.Properties.FdoProfilePath; path != nil {
if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, *profile); profileFile.Valid() { profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path)
profileFilePath := profileFile.Path() flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag)
flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag)
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile) // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag) // if profileFile gets updated
flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag) pathForSrc := android.PathForSource(ctx, *path)
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true") flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc)
flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc)
// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
// if profileFile gets updated
flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
}
} }
return flags return flags
} }
// Propagate afdo requirements down from binaries func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
if ctx.Host() {
return
}
if ctx.static() && !ctx.staticBinary() {
return
}
if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil {
actx.AddFarVariationDependencies(
[]blueprint.Variation{
{Mutator: "arch", Variation: actx.Target().ArchVariation()},
{Mutator: "os", Variation: "android"},
},
FdoProfileTag,
[]string{*fdoProfileName}...,
)
}
}
}
// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag
// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property
func (c *Module) FdoProfileMutator(ctx android.BottomUpMutatorContext) {
if !c.Enabled() {
return
}
ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
}
})
}
var _ FdoProfileMutatorInterface = (*Module)(nil)
// Propagate afdo requirements down from binaries and shared libraries
func afdoDepsMutator(mctx android.TopDownMutatorContext) { func afdoDepsMutator(mctx android.TopDownMutatorContext) {
if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() { if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
afdoTarget := *m.afdo.Properties.AfdoTarget if path := m.afdo.Properties.FdoProfilePath; path != nil {
mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
tag := mctx.OtherModuleDependencyTag(dep) tag := mctx.OtherModuleDependencyTag(dep)
libTag, isLibTag := tag.(libraryDependencyTag) libTag, isLibTag := tag.(libraryDependencyTag)
// Do not recurse down non-static dependencies // Do not recurse down non-static dependencies
if isLibTag { if isLibTag {
if !libTag.static() { if !libTag.static() {
return false return false
}
} else {
if tag != objDepTag && tag != reuseObjTag {
return false
}
} }
} else {
if tag != objDepTag && tag != reuseObjTag { if dep, ok := dep.(*Module); ok {
return false dep.afdo.Properties.AfdoRDeps = append(
dep.afdo.Properties.AfdoRDeps,
afdoRdep{
VariationName: proptools.StringPtr(encodeTarget(m.Name())),
ProfilePath: path,
},
)
} }
}
if dep, ok := dep.(*Module); ok { return true
dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget) })
} }
return true
})
} }
} }
// Create afdo variants for modules that need them // Create afdo variants for modules that need them
func afdoMutator(mctx android.BottomUpMutatorContext) { func afdoMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok && m.afdo != nil { if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
if m.afdo.AfdoEnabled() && !m.static() { if !m.static() && m.afdo.Properties.Afdo && m.afdo.Properties.FdoProfilePath != nil {
afdoTarget := *m.afdo.Properties.AfdoTarget mctx.SetDependencyVariation(encodeTarget(m.Name()))
mctx.SetDependencyVariation(encodeTarget(afdoTarget)) return
} }
variationNames := []string{""} variationNames := []string{""}
afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps)
for _, dep := range afdoDeps { variantNameToProfilePath := make(map[string]*string)
variationNames = append(variationNames, encodeTarget(dep))
for _, afdoRDep := range m.afdo.Properties.AfdoRDeps {
variantName := *afdoRDep.VariationName
// An rdep can be set twice in AfdoRDeps because there can be
// more than one path from an afdo-enabled module to
// a static dep such as
// afdo_enabled_foo -> static_bar ----> static_baz
// \ ^
// ----------------------|
// We only need to create one variant per unique rdep
if variantNameToProfilePath[variantName] == nil {
variationNames = append(variationNames, variantName)
variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
}
} }
if len(variationNames) > 1 { if len(variationNames) > 1 {
modules := mctx.CreateVariations(variationNames...) modules := mctx.CreateVariations(variationNames...)
for i, name := range variationNames { for i, name := range variationNames {
@@ -180,7 +235,7 @@ func afdoMutator(mctx android.BottomUpMutatorContext) {
variation := modules[i].(*Module) variation := modules[i].(*Module)
variation.Properties.PreventInstall = true variation.Properties.PreventInstall = true
variation.Properties.HideFromMake = true variation.Properties.HideFromMake = true
variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name)) variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
} }
} }
} }

View File

@@ -23,6 +23,11 @@ import (
"github.com/google/blueprint" "github.com/google/blueprint"
) )
var prepareForTestWithFdoProfile = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("soong_namespace", android.NamespaceFactory)
ctx.RegisterModuleType("fdo_profile", fdoProfileFactory)
})
type visitDirectDepsInterface interface { type visitDirectDepsInterface interface {
VisitDirectDeps(blueprint.Module, func(dep blueprint.Module)) VisitDirectDeps(blueprint.Module, func(dep blueprint.Module))
} }
@@ -58,38 +63,77 @@ func TestAfdoDeps(t *testing.T) {
srcs: ["bar.c"], srcs: ["bar.c"],
} }
` `
prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST")
result := android.GroupFixturePreparers( result := android.GroupFixturePreparers(
prepareForTestWithFdoProfile,
prepareForCcTest, prepareForCcTest,
prepareForAfdoTest, android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.AfdoProfiles = []string{
"libTest://afdo_profiles_package:libTest_afdo",
}
}),
android.MockFS{
"afdo_profiles_package/Android.bp": []byte(`
fdo_profile {
name: "libTest_afdo",
profile: "libTest.afdo",
}
`),
}.AddToFixture(),
).RunTestWithBp(t, bp) ).RunTestWithBp(t, bp)
libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
if !hasDirectDep(result, libTest.Module(), libFoo.Module()) { libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
cFlags := libTest.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlag) {
t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
}
cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlag) {
t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
}
cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlag) {
t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
}
// Check dependency edge from afdo-enabled module to static deps
if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) {
t.Errorf("libTest missing dependency on afdo variant of libFoo") t.Errorf("libTest missing dependency on afdo variant of libFoo")
} }
if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) {
t.Errorf("libTest missing dependency on afdo variant of libBar") t.Errorf("libTest missing dependency on afdo variant of libBar")
} }
cFlags := libTest.Rule("cc").Args["cFlags"] // Verify non-afdo variant exists and doesn't contain afdo
if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", w, cFlags) libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
}
cFlags = libFoo.Rule("cc").Args["cFlags"] cFlags = libFoo.Rule("cc").Args["cFlags"]
if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { if strings.Contains(cFlags, expectedCFlag) {
t.Errorf("Expected 'libFoo' to enable afdo, but did not find %q in cflags %q", w, cFlags) t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
}
cFlags = libBar.Rule("cc").Args["cFlags"]
if strings.Contains(cFlags, expectedCFlag) {
t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
} }
cFlags = libBar.Rule("cc").Args["cFlags"] // Check dependency edges of static deps
if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { if hasDirectDep(result, libTest.Module(), libFoo.Module()) {
t.Errorf("Expected 'libBar' to enable afdo, but did not find %q in cflags %q", w, cFlags) t.Errorf("libTest should not depend on non-afdo variant of libFoo")
}
if !hasDirectDep(result, libFoo.Module(), libBar.Module()) {
t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
} }
} }
@@ -113,11 +157,21 @@ func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) {
name: "libBar", name: "libBar",
} }
` `
prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", "TEST")
result := android.GroupFixturePreparers( result := android.GroupFixturePreparers(
prepareForCcTest, prepareForCcTest,
prepareForAfdoTest, prepareForTestWithFdoProfile,
android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", ""),
android.MockFS{
"afdo_profiles_package/Android.bp": []byte(`
soong_namespace {
}
fdo_profile {
name: "libFoo_afdo",
profile: "libFoo.afdo",
}
`),
}.AddToFixture(),
).RunTestWithBp(t, bp) ).RunTestWithBp(t, bp)
libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module() libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module()
@@ -150,7 +204,6 @@ func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) {
t.Errorf("Expected no afdo variant of 'bar', got %q", v) t.Errorf("Expected no afdo variant of 'bar', got %q", v)
} }
} }
} }
func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) {
@@ -166,11 +219,24 @@ func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) {
name: "libFoo", name: "libFoo",
} }
` `
prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST")
result := android.GroupFixturePreparers( result := android.GroupFixturePreparers(
prepareForCcTest, prepareForCcTest,
prepareForAfdoTest, prepareForTestWithFdoProfile,
android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.AfdoProfiles = []string{
"libTest://afdo_profiles_package:libTest_afdo",
}
}),
android.MockFS{
"afdo_profiles_package/Android.bp": []byte(`
fdo_profile {
name: "libTest_afdo",
profile: "libTest.afdo",
}
`),
}.AddToFixture(),
).RunTestWithBp(t, bp) ).RunTestWithBp(t, bp)
libFooVariants := result.ModuleVariantsForTests("libFoo") libFooVariants := result.ModuleVariantsForTests("libFoo")
@@ -182,7 +248,6 @@ func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) {
} }
func TestAfdoEnabledWithMultiArchs(t *testing.T) { func TestAfdoEnabledWithMultiArchs(t *testing.T) {
t.Parallel()
bp := ` bp := `
cc_library_shared { cc_library_shared {
name: "foo", name: "foo",
@@ -192,20 +257,43 @@ func TestAfdoEnabledWithMultiArchs(t *testing.T) {
} }
` `
result := android.GroupFixturePreparers( result := android.GroupFixturePreparers(
prepareForTestWithFdoProfile,
prepareForCcTest, prepareForCcTest,
android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm.afdo", "TEST"), android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""),
android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm64.afdo", "TEST"), android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.AfdoProfiles = []string{
"foo://afdo_profiles_package:foo_afdo",
}
}),
android.MockFS{
"afdo_profiles_package/Android.bp": []byte(`
soong_namespace {
}
fdo_profile {
name: "foo_afdo",
arch: {
arm: {
profile: "foo_arm.afdo",
},
arm64: {
profile: "foo_arm64.afdo",
}
}
}
`),
}.AddToFixture(),
).RunTestWithBp(t, bp) ).RunTestWithBp(t, bp)
fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared") fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared")
fooArmCFlags := fooArm.Rule("cc").Args["cFlags"] fooArmCFlags := fooArm.Rule("cc").Args["cFlags"]
if w := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) { if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) {
t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags) t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags)
} }
fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared") fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared")
fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"] fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"]
if w := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) { if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) {
t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags) t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags)
} }
} }
@@ -234,46 +322,65 @@ func TestMultipleAfdoRDeps(t *testing.T) {
` `
result := android.GroupFixturePreparers( result := android.GroupFixturePreparers(
prepareForTestWithFdoProfile,
prepareForCcTest, prepareForCcTest,
android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST"), android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""),
android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libBar.afdo", "TEST"), android.FixtureAddTextFile("afdo_profiles_package/libBar.afdo", ""),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.AfdoProfiles = []string{
"libTest://afdo_profiles_package:libTest_afdo",
"libBar://afdo_profiles_package:libBar_afdo",
}
}),
android.MockFS{
"afdo_profiles_package/Android.bp": []byte(`
fdo_profile {
name: "libTest_afdo",
profile: "libTest.afdo",
}
fdo_profile {
name: "libBar_afdo",
profile: "libBar.afdo",
}
`),
}.AddToFixture(),
).RunTestWithBp(t, bp) ).RunTestWithBp(t, bp)
expectedCFlagLibTest := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/libTest.afdo" expectedCFlagLibTest := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
expectedCFlagLibBar := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/libBar.afdo" expectedCFlagLibBar := "-fprofile-sample-use=afdo_profiles_package/libBar.afdo"
libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
libTestAfdoVariantOfLibFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") libFooAfdoVariantWithLibTest := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared") libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared")
libBarAfdoVariantOfLibFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar") libFooAfdoVariantWithLibBar := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar")
// Check cFlags of afdo-enabled modules and the afdo-variant of their static deps // Check cFlags of afdo-enabled module and the afdo-variant of its static deps
cFlags := libTest.Rule("cc").Args["cFlags"] cFlags := libTest.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlagLibTest) { if !strings.Contains(cFlags, expectedCFlagLibTest) {
t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags)
} }
cFlags = libBar.Rule("cc").Args["cFlags"] cFlags = libBar.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlagLibBar) { if !strings.Contains(cFlags, expectedCFlagLibBar) {
t.Errorf("Expected 'libBar' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags)
} }
cFlags = libTestAfdoVariantOfLibFoo.Rule("cc").Args["cFlags"] cFlags = libFooAfdoVariantWithLibTest.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlagLibTest) { if !strings.Contains(cFlags, expectedCFlagLibTest) {
t.Errorf("Expected 'libTestAfdoVariantOfLibFoo' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) t.Errorf("Expected 'libFooAfdoVariantWithLibTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags)
} }
cFlags = libBarAfdoVariantOfLibFoo.Rule("cc").Args["cFlags"] cFlags = libFooAfdoVariantWithLibBar.Rule("cc").Args["cFlags"]
if !strings.Contains(cFlags, expectedCFlagLibBar) { if !strings.Contains(cFlags, expectedCFlagLibBar) {
t.Errorf("Expected 'libBarAfdoVariantOfLibFoo' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags)
} }
// Check dependency edges of static deps // Check dependency edges of static deps
if !hasDirectDep(result, libTest.Module(), libTestAfdoVariantOfLibFoo.Module()) { if !hasDirectDep(result, libTest.Module(), libFooAfdoVariantWithLibTest.Module()) {
t.Errorf("libTest missing dependency on afdo variant of libFoo") t.Errorf("libTest missing dependency on afdo variant of libFoo")
} }
if !hasDirectDep(result, libBar.Module(), libBarAfdoVariantOfLibFoo.Module()) { if !hasDirectDep(result, libBar.Module(), libFooAfdoVariantWithLibBar.Module()) {
t.Errorf("libBar missing dependency on afdo variant of libFoo") t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
} }
} }

View File

@@ -52,6 +52,7 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) {
ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel() ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
ctx.BottomUp("version", versionMutator).Parallel() ctx.BottomUp("version", versionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel()
ctx.BottomUp("fdo_profile", fdoProfileMutator)
}) })
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -763,6 +764,7 @@ var (
testPerSrcDepTag = dependencyTag{name: "test_per_src"} testPerSrcDepTag = dependencyTag{name: "test_per_src"}
stubImplDepTag = dependencyTag{name: "stub_impl"} stubImplDepTag = dependencyTag{name: "stub_impl"}
JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"} JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"}
FdoProfileTag = dependencyTag{name: "fdo_profile"}
) )
func IsSharedDepTag(depTag blueprint.DependencyTag) bool { func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -1336,7 +1338,7 @@ func (c *Module) IsVndk() bool {
func (c *Module) isAfdoCompile() bool { func (c *Module) isAfdoCompile() bool {
if afdo := c.afdo; afdo != nil { if afdo := c.afdo; afdo != nil {
return afdo.Properties.AfdoTarget != nil return afdo.Properties.FdoProfilePath != nil
} }
return false return false
} }
@@ -2162,9 +2164,6 @@ func (c *Module) begin(ctx BaseModuleContext) {
if c.lto != nil { if c.lto != nil {
c.lto.begin(ctx) c.lto.begin(ctx)
} }
if c.afdo != nil {
c.afdo.begin(ctx)
}
if c.pgo != nil { if c.pgo != nil {
c.pgo.begin(ctx) c.pgo.begin(ctx)
} }
@@ -2239,6 +2238,10 @@ func (c *Module) beginMutator(actx android.BottomUpMutatorContext) {
} }
ctx.ctx = ctx ctx.ctx = ctx
if !actx.Host() || !ctx.static() || ctx.staticBinary() {
c.afdo.addDep(ctx, actx)
}
c.begin(ctx) c.begin(ctx)
} }

85
cc/fdo_profile.go Normal file
View File

@@ -0,0 +1,85 @@
// 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.
package cc
import (
"android/soong/android"
"github.com/google/blueprint"
)
func init() {
RegisterFdoProfileBuildComponents(android.InitRegistrationContext)
}
func RegisterFdoProfileBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("fdo_profile", fdoProfileFactory)
}
type fdoProfile struct {
android.ModuleBase
properties fdoProfileProperties
}
type fdoProfileProperties struct {
Profile *string `android:"arch_variant"`
}
// FdoProfileInfo is provided by FdoProfileProvider
type FdoProfileInfo struct {
Path android.Path
}
// FdoProfileProvider is used to provide path to an fdo profile
var FdoProfileProvider = blueprint.NewMutatorProvider(FdoProfileInfo{}, "fdo_profile")
// FdoProfileMutatorInterface is the interface implemented by fdo_profile module type
// module types that can depend on an fdo_profile module
type FdoProfileMutatorInterface interface {
// FdoProfileMutator eithers set or get FdoProfileProvider
FdoProfileMutator(ctx android.BottomUpMutatorContext)
}
var _ FdoProfileMutatorInterface = (*fdoProfile)(nil)
// GenerateAndroidBuildActions of fdo_profile does not have any build actions
func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
// FdoProfileMutator sets FdoProfileProvider to fdo_profile module
// or sets afdo.Properties.FdoProfilePath to path in FdoProfileProvider of the depended fdo_profile
func (fp *fdoProfile) FdoProfileMutator(ctx android.BottomUpMutatorContext) {
if fp.properties.Profile != nil {
path := android.PathForModuleSrc(ctx, *fp.properties.Profile)
ctx.SetProvider(FdoProfileProvider, FdoProfileInfo{
Path: path,
})
}
}
// fdoProfileMutator calls the generic FdoProfileMutator function of FdoProfileMutator
// which is implemented by cc and cc.FdoProfile
func fdoProfileMutator(ctx android.BottomUpMutatorContext) {
if f, ok := ctx.Module().(FdoProfileMutatorInterface); ok {
f.FdoProfileMutator(ctx)
}
}
func fdoProfileFactory() android.Module {
m := &fdoProfile{}
m.AddProperties(&m.properties)
android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibBoth)
return m
}