Determine GC type based on BUILT_KERNEL_VERSION_FILE.

How it works:
1. build/make/core/Makefile generates a txt file with the kernel
   version, which is taken from an explicit BOARD_KERNEL_VERSION value,
   or extracted from the kernel image on the source tree, or extracted
   from the kernel image extracted from the prebuilt boot.img.
   The file is saved at
   $ANDROID_PRODUCT_OUT/obj/PACKAGING/check_vintf_all_intermediates/kernel_version.txt.
2. If PRODUCT_ENABLE_UFFD_GC is "default", meaning the GC type needs to
   be determined by the kernel version, build/make/core/Makefile copies
   kernel_version.txt to
   out/soong/dexpreopt/kernel_version_for_uffd_gc.txt.
3. build/soong/dexpreopt/config.go writes the the UFFD GC flag to
   out/soong/dexpreopt/uffd_gc_flag.txt. The flag is determined by an
   explicit PRODUCT_ENABLE_UFFD_GC value or by contruct_uffd_gc_flag.py,
   which reads kernel_version_for_uffd_gc.txt and determines the flag
   accordingly.
4. dex2oat takes the UFFD GC flag from uffd_gc_flag.txt.
5. post_process_props.py mangles ro.dalvik.vm.enable_uffd_gc based on
   the same logic.

Bug: 321751629
Bug: 319554951
Bug: 318763448
Bug: 319648491
Test: m --no-skip-soong-tests nothing
Test: atest uffd_gc_utils_test
Test: Build with `OVERRIDE_ENABLE_UFFD_GC=default m` for device with no
  UFFD support -
  1. Check the existence of `-Xgc:CMC` in
     out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.invocation
     (dex2oat invocation for a boot image)
  2. Check the existence of `-Xgc:CMC` in
     out/soong/.intermediates/packages/apps/Settings/Settings/android_common/dexpreopt/Settings/oat/arm64/package.invocation
     (dex2oat invocation for an app defined in .bp)
  3. Check the existence of `-Xgc:CMC` in
     $ANDROID_PRODUCT_OUT/obj/APPS/Dialer_intermediates/oat/arm64/package.invocation
     (dex2oat invocation for an app defined in .mk)
  4. Check the value of ro.dalvik.vm.enable_uffd_gc in
     $ANDROID_PRODUCT_OUT/product/etc/build.prop
Test: Build with `OVERRIDE_ENABLE_UFFD_GC=default m` for device with
  UFFD support, and do the steps above.
Test: Build with `OVERRIDE_ENABLE_UFFD_GC=true m`, and do the steps
  above.
Test: Build with `OVERRIDE_ENABLE_UFFD_GC=false m`, and do the steps
  above.

Change-Id: I035ad32233e49e2a30ce11f6c7c318a648511ef8
This commit is contained in:
Jiakai Zhang
2024-01-18 17:27:42 +00:00
parent c21f548fd4
commit 7d292228c1
11 changed files with 350 additions and 32 deletions

View File

@@ -164,6 +164,7 @@ func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) {
"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
"out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof", "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof",
"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
"out/soong/dexpreopt/uffd_gc_flag.txt",
} }
expectedOutputs := []string{ expectedOutputs := []string{
@@ -201,6 +202,7 @@ func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) {
"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
"out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof", "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
"out/soong/dexpreopt/uffd_gc_flag.txt",
} }
expectedOutputs := []string{ expectedOutputs := []string{

View File

@@ -98,7 +98,9 @@ type GlobalConfig struct {
// measure, as it masks real errors and affects performance. // measure, as it masks real errors and affects performance.
RelaxUsesLibraryCheck bool RelaxUsesLibraryCheck bool
EnableUffdGc bool // preopt with the assumption that userfaultfd GC will be used on device. // "true" to force preopt with CMC GC (a.k.a., UFFD GC); "false" to force preopt with CC GC;
// "default" to determine the GC type based on the kernel version file.
EnableUffdGc string
} }
var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars") var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars")
@@ -154,6 +156,7 @@ type GlobalSoongConfig struct {
Zip2zip android.Path Zip2zip android.Path
ManifestCheck android.Path ManifestCheck android.Path
ConstructContext android.Path ConstructContext android.Path
UffdGcFlag android.WritablePath
} }
type ModuleConfig struct { type ModuleConfig struct {
@@ -537,6 +540,7 @@ func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"), Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"),
ManifestCheck: ctx.Config().HostToolPath(ctx, "manifest_check"), ManifestCheck: ctx.Config().HostToolPath(ctx, "manifest_check"),
ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"), ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"),
UffdGcFlag: getUffdGcFlagPath(ctx),
} }
} }
@@ -588,6 +592,7 @@ type globalJsonSoongConfig struct {
Zip2zip string Zip2zip string
ManifestCheck string ManifestCheck string
ConstructContext string ConstructContext string
UffdGcFlag string
} }
// ParseGlobalSoongConfig parses the given data assumed to be read from the // ParseGlobalSoongConfig parses the given data assumed to be read from the
@@ -609,6 +614,7 @@ func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongC
Zip2zip: constructPath(ctx, jc.Zip2zip), Zip2zip: constructPath(ctx, jc.Zip2zip),
ManifestCheck: constructPath(ctx, jc.ManifestCheck), ManifestCheck: constructPath(ctx, jc.ManifestCheck),
ConstructContext: constructPath(ctx, jc.ConstructContext), ConstructContext: constructPath(ctx, jc.ConstructContext),
UffdGcFlag: constructWritablePath(ctx, jc.UffdGcFlag),
} }
return config, nil return config, nil
@@ -633,12 +639,15 @@ func checkBootJarsConfigConsistency(ctx android.SingletonContext, dexpreoptConfi
} }
func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
checkBootJarsConfigConsistency(ctx, GetGlobalConfig(ctx), ctx.Config()) global := GetGlobalConfig(ctx)
checkBootJarsConfigConsistency(ctx, global, ctx.Config())
if GetGlobalConfig(ctx).DisablePreopt { if global.DisablePreopt {
return return
} }
buildUffdGcFlag(ctx, global)
config := GetCachedGlobalSoongConfig(ctx) config := GetCachedGlobalSoongConfig(ctx)
if config == nil { if config == nil {
// No module has enabled dexpreopting, so we assume there will be no calls // No module has enabled dexpreopting, so we assume there will be no calls
@@ -654,6 +663,7 @@ func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonC
Zip2zip: config.Zip2zip.String(), Zip2zip: config.Zip2zip.String(),
ManifestCheck: config.ManifestCheck.String(), ManifestCheck: config.ManifestCheck.String(),
ConstructContext: config.ConstructContext.String(), ConstructContext: config.ConstructContext.String(),
UffdGcFlag: config.UffdGcFlag.String(),
} }
data, err := json.Marshal(jc) data, err := json.Marshal(jc)
@@ -684,9 +694,32 @@ func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
config.Zip2zip.String(), config.Zip2zip.String(),
config.ManifestCheck.String(), config.ManifestCheck.String(),
config.ConstructContext.String(), config.ConstructContext.String(),
config.UffdGcFlag.String(),
}, " ")) }, " "))
} }
func buildUffdGcFlag(ctx android.BuilderContext, global *GlobalConfig) {
uffdGcFlag := getUffdGcFlagPath(ctx)
if global.EnableUffdGc == "true" {
android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "--runtime-arg -Xgc:CMC")
} else if global.EnableUffdGc == "false" {
android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "")
} else if global.EnableUffdGc == "default" {
// Generated by `build/make/core/Makefile`.
kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
// Determine the UFFD GC flag by the kernel version file.
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(ctx.Config().HostToolPath(ctx, "construct_uffd_gc_flag")).
Input(kernelVersionFile).
Output(uffdGcFlag)
rule.Restat().Build("dexpreopt_uffd_gc_flag", "dexpreopt_uffd_gc_flag")
} else {
panic(fmt.Sprintf("Unknown value of PRODUCT_ENABLE_UFFD_GC: %s", global.EnableUffdGc))
}
}
func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig { func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
return &GlobalConfig{ return &GlobalConfig{
DisablePreopt: false, DisablePreopt: false,
@@ -731,7 +764,7 @@ func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
} }
} }
func globalSoongConfigForTests() *GlobalSoongConfig { func globalSoongConfigForTests(ctx android.BuilderContext) *GlobalSoongConfig {
return &GlobalSoongConfig{ return &GlobalSoongConfig{
Profman: android.PathForTesting("profman"), Profman: android.PathForTesting("profman"),
Dex2oat: android.PathForTesting("dex2oat"), Dex2oat: android.PathForTesting("dex2oat"),
@@ -740,5 +773,19 @@ func globalSoongConfigForTests() *GlobalSoongConfig {
Zip2zip: android.PathForTesting("zip2zip"), Zip2zip: android.PathForTesting("zip2zip"),
ManifestCheck: android.PathForTesting("manifest_check"), ManifestCheck: android.PathForTesting("manifest_check"),
ConstructContext: android.PathForTesting("construct_context"), ConstructContext: android.PathForTesting("construct_context"),
UffdGcFlag: android.PathForOutput(ctx, "dexpreopt_test", "uffd_gc_flag.txt"),
} }
} }
func GetDexpreoptDirName(ctx android.PathContext) string {
prefix := "dexpreopt_"
targets := ctx.Config().Targets[android.Android]
if len(targets) > 0 {
return prefix + targets[0].Arch.ArchType.String()
}
return prefix + "unknown_target"
}
func getUffdGcFlagPath(ctx android.PathContext) android.WritablePath {
return android.PathForOutput(ctx, "dexpreopt/uffd_gc_flag.txt")
}

View File

@@ -390,7 +390,8 @@ func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig
Flag("--generate-build-id"). Flag("--generate-build-id").
Flag("--abort-on-hard-verifier-error"). Flag("--abort-on-hard-verifier-error").
Flag("--force-determinism"). Flag("--force-determinism").
FlagWithArg("--no-inline-from=", "core-oj.jar") FlagWithArg("--no-inline-from=", "core-oj.jar").
Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
var preoptFlags []string var preoptFlags []string
if len(module.PreoptFlags) > 0 { if len(module.PreoptFlags) > 0 {
@@ -506,10 +507,6 @@ func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig
cmd.FlagWithInput("--profile-file=", profile) cmd.FlagWithInput("--profile-file=", profile)
} }
if global.EnableUffdGc {
cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
}
rule.Install(odexPath, odexInstallPath) rule.Install(odexPath, odexInstallPath)
rule.Install(vdexPath, vdexInstallPath) rule.Install(vdexPath, vdexInstallPath)
} }

View File

@@ -96,7 +96,7 @@ func createTestModuleConfig(name, dexLocation string, buildPath, dexPath, enforc
func TestDexPreopt(t *testing.T) { func TestDexPreopt(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test") module := testSystemModuleConfig(ctx, "test")
productPackages := android.PathForTesting("product_packages.txt") productPackages := android.PathForTesting("product_packages.txt")
@@ -114,12 +114,15 @@ func TestDexPreopt(t *testing.T) {
if rule.Installs().String() != wantInstalls.String() { if rule.Installs().String() != wantInstalls.String() {
t.Errorf("\nwant installs:\n %v\ngot:\n %v", wantInstalls, rule.Installs()) t.Errorf("\nwant installs:\n %v\ngot:\n %v", wantInstalls, rule.Installs())
} }
android.AssertStringListContains(t, "", rule.Inputs().RelativeToTop().Strings(),
"out/soong/dexpreopt_test/uffd_gc_flag.txt")
} }
func TestDexPreoptSystemOther(t *testing.T) { func TestDexPreoptSystemOther(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest") systemModule := testSystemModuleConfig(ctx, "Stest")
systemProductModule := testSystemProductModuleConfig(ctx, "SPtest") systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -180,7 +183,7 @@ func TestDexPreoptSystemOther(t *testing.T) {
func TestDexPreoptApexSystemServerJars(t *testing.T) { func TestDexPreoptApexSystemServerJars(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
module := testApexModuleConfig(ctx, "service-A", "com.android.apex1") module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
productPackages := android.PathForTesting("product_packages.txt") productPackages := android.PathForTesting("product_packages.txt")
@@ -204,7 +207,7 @@ func TestDexPreoptApexSystemServerJars(t *testing.T) {
func TestDexPreoptStandaloneSystemServerJars(t *testing.T) { func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
module := testPlatformSystemServerModuleConfig(ctx, "service-A") module := testPlatformSystemServerModuleConfig(ctx, "service-A")
productPackages := android.PathForTesting("product_packages.txt") productPackages := android.PathForTesting("product_packages.txt")
@@ -228,7 +231,7 @@ func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
func TestDexPreoptSystemExtSystemServerJars(t *testing.T) { func TestDexPreoptSystemExtSystemServerJars(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
module := testSystemExtSystemServerModuleConfig(ctx, "service-A") module := testSystemExtSystemServerModuleConfig(ctx, "service-A")
productPackages := android.PathForTesting("product_packages.txt") productPackages := android.PathForTesting("product_packages.txt")
@@ -252,7 +255,7 @@ func TestDexPreoptSystemExtSystemServerJars(t *testing.T) {
func TestDexPreoptApexStandaloneSystemServerJars(t *testing.T) { func TestDexPreoptApexStandaloneSystemServerJars(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
module := testApexModuleConfig(ctx, "service-A", "com.android.apex1") module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
productPackages := android.PathForTesting("product_packages.txt") productPackages := android.PathForTesting("product_packages.txt")
@@ -276,7 +279,7 @@ func TestDexPreoptApexStandaloneSystemServerJars(t *testing.T) {
func TestDexPreoptProfile(t *testing.T) { func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil) config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config) ctx := android.BuilderContextForTesting(config)
globalSoong := globalSoongConfigForTests() globalSoong := globalSoongConfigForTests(ctx)
global := GlobalConfigForTests(ctx) global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test") module := testSystemModuleConfig(ctx, "test")
productPackages := android.PathForTesting("product_packages.txt") productPackages := android.PathForTesting("product_packages.txt")
@@ -316,3 +319,55 @@ func TestDexPreoptConfigToJson(t *testing.T) {
after := fmt.Sprintf("%v", parsed) after := fmt.Sprintf("%v", parsed)
android.AssertStringEquals(t, "The result must be the same as the original after marshalling and unmarshalling it.", before, after) android.AssertStringEquals(t, "The result must be the same as the original after marshalling and unmarshalling it.", before, after)
} }
func TestUffdGcFlagForce(t *testing.T) {
for _, enableUffdGc := range []string{"true", "false"} {
t.Run(enableUffdGc, func(t *testing.T) {
preparers := android.GroupFixturePreparers(
PrepareForTestWithFakeDex2oatd,
PrepareForTestWithDexpreoptConfig,
FixtureSetEnableUffdGc(enableUffdGc),
)
result := preparers.RunTest(t)
ctx := result.TestContext
ctx.SingletonForTests("dexpreopt-soong-config").Output("out/soong/dexpreopt/uffd_gc_flag.txt")
})
}
}
func TestUffdGcFlagDefault(t *testing.T) {
preparers := android.GroupFixturePreparers(
PrepareForTestWithFakeDex2oatd,
PrepareForTestWithDexpreoptConfig,
FixtureSetEnableUffdGc("default"),
)
result := preparers.RunTest(t)
ctx := result.TestContext
config := ctx.Config()
rule := ctx.SingletonForTests("dexpreopt-soong-config").Rule("dexpreopt_uffd_gc_flag")
android.AssertStringDoesContain(t, "", rule.RuleParams.Command, "construct_uffd_gc_flag")
android.AssertStringPathsRelativeToTopEquals(t, "", config, []string{
"out/soong/dexpreopt/uffd_gc_flag.txt",
}, rule.AllOutputs())
android.AssertPathsRelativeToTopEquals(t, "", []string{
"out/soong/dexpreopt/kernel_version_for_uffd_gc.txt",
}, rule.Implicits)
}
func TestUffdGcFlagBogus(t *testing.T) {
preparers := android.GroupFixturePreparers(
PrepareForTestWithFakeDex2oatd,
PrepareForTestWithDexpreoptConfig,
FixtureSetEnableUffdGc("bogus"),
)
preparers.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
"Unknown value of PRODUCT_ENABLE_UFFD_GC: bogus")).
RunTest(t)
}

View File

@@ -88,6 +88,15 @@ var PrepareForTestByEnablingDexpreopt = android.GroupFixturePreparers(
FixtureModifyGlobalConfig(func(android.PathContext, *GlobalConfig) {}), FixtureModifyGlobalConfig(func(android.PathContext, *GlobalConfig) {}),
) )
var PrepareForTestWithDexpreoptConfig = android.GroupFixturePreparers(
android.PrepareForTestWithAndroidBuildComponents,
android.FixtureModifyContext(func(ctx *android.TestContext) {
ctx.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
return &globalSoongConfigSingleton{}
})
}),
)
// FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the // FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
// configuration. // configuration.
func FixtureModifyGlobalConfig(configModifier func(ctx android.PathContext, dexpreoptConfig *GlobalConfig)) android.FixturePreparer { func FixtureModifyGlobalConfig(configModifier func(ctx android.PathContext, dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
@@ -195,3 +204,10 @@ func FixtureDisableDexpreopt(disable bool) android.FixturePreparer {
dexpreoptConfig.DisablePreopt = disable dexpreoptConfig.DisablePreopt = disable
}) })
} }
// FixtureSetEnableUffdGc sets the EnableUffdGc property in the global config.
func FixtureSetEnableUffdGc(value string) android.FixturePreparer {
return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
dexpreoptConfig.EnableUffdGc = value
})
}

View File

@@ -617,7 +617,8 @@ func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContex
// GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make. // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) { func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
d.dexpreoptConfigForMake = android.PathForOutput(ctx, getDexpreoptDirName(ctx), "dexpreopt.config") d.dexpreoptConfigForMake =
android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx), "dexpreopt.config")
writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake) writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
} }
@@ -1066,8 +1067,8 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p
cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]) cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
} }
if global.EnableUffdGc && image.target.Os == android.Android { if image.target.Os == android.Android {
cmd.Flag("--runtime-arg").Flag("-Xgc:CMC") cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
} }
if global.BootFlags != "" { if global.BootFlags != "" {
@@ -1235,7 +1236,7 @@ func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig)
func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
var allPhonies android.Paths var allPhonies android.Paths
name := image.name name := image.name
global := dexpreopt.GetGlobalConfig(ctx) globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
for _, image := range image.variants { for _, image := range image.variants {
arch := image.target.Arch.ArchType arch := image.target.Arch.ArchType
suffix := arch.String() suffix := arch.String()
@@ -1247,6 +1248,7 @@ func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
output := android.PathForOutput(ctx, name+"."+suffix+".oatdump.txt") output := android.PathForOutput(ctx, name+"."+suffix+".oatdump.txt")
rule := android.NewRuleBuilder(pctx, ctx) rule := android.NewRuleBuilder(pctx, ctx)
imageLocationsOnHost, _ := image.imageLocations() imageLocationsOnHost, _ := image.imageLocations()
cmd := rule.Command(). cmd := rule.Command().
BuiltTool("oatdump"). BuiltTool("oatdump").
FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
@@ -1254,8 +1256,8 @@ func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()). FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()).
FlagWithOutput("--output=", output). FlagWithOutput("--output=", output).
FlagWithArg("--instruction-set=", arch.String()) FlagWithArg("--instruction-set=", arch.String())
if global.EnableUffdGc && image.target.Os == android.Android { if image.target.Os == android.Android {
cmd.Flag("--runtime-arg").Flag("-Xgc:CMC") cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
} }
rule.Build("dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String()) rule.Build("dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String())

View File

@@ -115,7 +115,7 @@ func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig
func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
return ctx.Config().Once(bootImageConfigKey, func() interface{} { return ctx.Config().Once(bootImageConfigKey, func() interface{} {
targets := dexpreoptTargets(ctx) targets := dexpreoptTargets(ctx)
deviceDir := android.PathForOutput(ctx, getDexpreoptDirName(ctx)) deviceDir := android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx))
configs := genBootImageConfigRaw(ctx) configs := genBootImageConfigRaw(ctx)
@@ -234,12 +234,3 @@ func init() {
func dexpreoptConfigMakevars(ctx android.MakeVarsContext) { func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":")) ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
} }
func getDexpreoptDirName(ctx android.PathContext) string {
prefix := "dexpreopt_"
targets := ctx.Config().Targets[android.Android]
if len(targets) > 0 {
return prefix + targets[0].Arch.ArchType.String()
}
return prefix + "unknown_target"
}

View File

@@ -142,6 +142,39 @@ python_test_host {
test_suites: ["general-tests"], test_suites: ["general-tests"],
} }
python_library_host {
name: "uffd_gc_utils",
srcs: [
"uffd_gc_utils.py",
],
visibility: [
"//build/make/tools:__subpackages__",
],
}
python_test_host {
name: "uffd_gc_utils_test",
main: "uffd_gc_utils_test.py",
srcs: [
"uffd_gc_utils_test.py",
],
libs: [
"uffd_gc_utils",
],
test_suites: ["general-tests"],
}
python_binary_host {
name: "construct_uffd_gc_flag",
main: "construct_uffd_gc_flag.py",
srcs: [
"construct_uffd_gc_flag.py",
],
libs: [
"uffd_gc_utils",
],
}
python_library_host { python_library_host {
name: "ninja_rsp", name: "ninja_rsp",
srcs: ["ninja_rsp.py"], srcs: ["ninja_rsp.py"],

View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python
#
# Copyright (C) 2024 The Android Open Source Project
#
# 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.
#
"""A tool for constructing UFFD GC flag."""
import argparse
import os
from uffd_gc_utils import should_enable_uffd_gc
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('kernel_version_file')
parser.add_argument('output')
return parser.parse_args()
def main():
args = parse_args()
enable_uffd_gc = should_enable_uffd_gc(args.kernel_version_file)
flag = '--runtime-arg -Xgc:CMC' if enable_uffd_gc else ''
# Prevent the file's mtime from being changed if the contents don't change.
# This avoids unnecessary dexpreopt reruns.
if os.path.isfile(args.output):
with open(args.output, 'r') as f:
if f.read() == flag:
return
with open(args.output, 'w') as f:
f.write(flag)
if __name__ == '__main__':
main()

68
scripts/uffd_gc_utils.py Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python
#
# Copyright (C) 2024 The Android Open Source Project
#
# 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.
#
"""Utils to determine whether to enable UFFD GC."""
import re
import sys
def should_enable_uffd_gc(kernel_version_file):
with open(kernel_version_file, 'r') as f:
kernel_version = f.read().strip()
return should_enable_uffd_gc_impl(kernel_version)
def should_enable_uffd_gc_impl(kernel_version):
# See https://source.android.com/docs/core/architecture/kernel/gki-versioning#determine-release
p = r"^(?P<w>\d+)[.](?P<x>\d+)[.](?P<y>\d+)(-android(?P<z>\d+)-(?P<k>\d+).*$)?"
m = re.match(p, kernel_version)
if m is not None:
if m.group('z') is not None:
android_release = int(m.group('z'))
# No need to check w, x, y because all Android 12 kernels have backports.
return android_release >= 12
else:
# Old kernel or non-GKI kernel.
version = int(m.group('w'))
patch_level = int(m.group('x'))
if version < 5:
# Old kernel.
return False
elif (version == 5 and patch_level >= 7) or version >= 6:
# New non-GKI kernel. 5.7 supports MREMAP_DONTUNMAP without the need for
# backports.
return True
else:
# Non-GKI kernel between 5 and 5.6. It may have backports.
raise exit_with_error(kernel_version)
elif kernel_version == '<unknown-kernel>':
# The kernel information isn't available to the build system, probably
# because PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS is set to false. We
# assume that the kernel supports UFFD GC because it is the case for most of
# the products today and it is the future.
return True
else:
# Unrecognizable non-GKI kernel.
raise exit_with_error(kernel_version)
def exit_with_error(kernel_version):
sys.exit(f"""
Unable to determine UFFD GC flag for kernel version "{kernel_version}".
You can fix this by explicitly setting PRODUCT_ENABLE_UFFD_GC to "true" or
"false" based on the kernel version.
1. Set PRODUCT_ENABLE_UFFD_GC to "true" if the kernel supports userfaultfd(2)
and MREMAP_DONTUNMAP.
2. Set PRODUCT_ENABLE_UFFD_GC to "false" otherwise.""")

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python
#
# Copyright (C) 2024 The Android Open Source Project
#
# 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.
#
"""Unit tests for uffd_gc_utils.py."""
import unittest
from uffd_gc_utils import should_enable_uffd_gc_impl
class UffdGcUtilsTest(unittest.TestCase):
def test_should_enable_uffd_gc_impl(self):
# GKI kernels in new format.
self.assertTrue(should_enable_uffd_gc_impl(
"6.1.25-android14-11-g34fde9ec08a3-ab10675345"))
self.assertTrue(should_enable_uffd_gc_impl(
"5.4.42-android12-0-something"))
self.assertFalse(should_enable_uffd_gc_impl(
"5.4.42-android11-0-something"))
# GKI kernels in old format.
self.assertFalse(should_enable_uffd_gc_impl(
"4.19.282-g4b749a433956-ab10893502"))
# Non GKI kernels.
self.assertTrue(should_enable_uffd_gc_impl(
"6.1.25-foo"))
self.assertTrue(should_enable_uffd_gc_impl(
"6.1.25"))
self.assertTrue(should_enable_uffd_gc_impl(
"5.10.19-foo"))
self.assertTrue(should_enable_uffd_gc_impl(
"5.10.19"))
with self.assertRaises(SystemExit):
should_enable_uffd_gc_impl("5.4.42-foo")
with self.assertRaises(SystemExit):
should_enable_uffd_gc_impl("5.4.42")
self.assertFalse(should_enable_uffd_gc_impl(
"4.19.282-foo"))
self.assertFalse(should_enable_uffd_gc_impl(
"4.19.282"))
with self.assertRaises(SystemExit):
should_enable_uffd_gc_impl("foo")
# No kernel.
self.assertTrue(should_enable_uffd_gc_impl(
"<unknown-kernel>"))
if __name__ == '__main__':
unittest.main(verbosity=2)