From 389a64732076afed0462790836e024badf7bf333 Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Tue, 14 Dec 2021 18:54:06 +0000 Subject: [PATCH 1/2] Dexpreopt standalone system server jars. Standalone system server jars are dynamically loaded by system server using a `PathClassLoader` whose parent is `SYSTEMSERVERCLASSPATH`. They are listed in `PRODUCT_STANDALONE_SYSTEM_SERVER_JARS` and `PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS` in Makefile. We need to dexpreopt them to achieve better performance. Bug: 203198541 Test: m nothing Test: - 1. Add a standalone system server jar (e.g., by patching aosp/1906158) 2. Build a system image. 3. See the odex and vdex files generated in $ANDROID_PRODUCT_OUT/system/framework/oat/ 4. Flash the image to a device. 5. Run `atest art_standalone_dexpreopt_tests`. Change-Id: I358a62d34989c5c8eba12e18fe6167e0b72ff69d --- android/config.go | 2 +- dexpreopt/config.go | 42 +++++++++++++++++++ dexpreopt/dexpreopt.go | 49 ++++++++++++---------- dexpreopt/dexpreopt_test.go | 55 +++++++++++++++++++++++++ java/dexpreopt.go | 18 ++++---- java/dexpreopt_check.go | 5 ++- java/systemserver_classpath_fragment.go | 4 +- 7 files changed, 140 insertions(+), 35 deletions(-) diff --git a/android/config.go b/android/config.go index 5d90fd21d..93b32d1d9 100644 --- a/android/config.go +++ b/android/config.go @@ -1698,7 +1698,7 @@ func (l *ConfiguredJarList) Append(apex string, jar string) ConfiguredJarList { } // Append a list of (apex, jar) pairs to the list. -func (l *ConfiguredJarList) AppendList(other ConfiguredJarList) ConfiguredJarList { +func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList { apexes := make([]string, 0, l.Len()+other.Len()) jars := make([]string, 0, l.Len()+other.Len()) diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 6d6b41d4b..1f99a96c1 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -100,6 +100,48 @@ type GlobalConfig struct { RelaxUsesLibraryCheck bool } +var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars") + +// Returns all jars on the platform that system_server loads, including those on classpath and those +// loaded dynamically. +func (g *GlobalConfig) AllPlatformSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { + return ctx.Config().Once(allPlatformSystemServerJarsKey, func() interface{} { + res := g.SystemServerJars.AppendList(&g.StandaloneSystemServerJars) + return &res + }).(*android.ConfiguredJarList) +} + +var allApexSystemServerJarsKey = android.NewOnceKey("allApexSystemServerJars") + +// Returns all jars delivered via apex that system_server loads, including those on classpath and +// those loaded dynamically. +func (g *GlobalConfig) AllApexSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { + return ctx.Config().Once(allApexSystemServerJarsKey, func() interface{} { + res := g.ApexSystemServerJars.AppendList(&g.ApexStandaloneSystemServerJars) + return &res + }).(*android.ConfiguredJarList) +} + +var allSystemServerClasspathJarsKey = android.NewOnceKey("allSystemServerClasspathJars") + +// Returns all system_server classpath jars. +func (g *GlobalConfig) AllSystemServerClasspathJars(ctx android.PathContext) *android.ConfiguredJarList { + return ctx.Config().Once(allSystemServerClasspathJarsKey, func() interface{} { + res := g.SystemServerJars.AppendList(&g.ApexSystemServerJars) + return &res + }).(*android.ConfiguredJarList) +} + +var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars") + +// Returns all jars that system_server loads. +func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { + return ctx.Config().Once(allSystemServerJarsKey, func() interface{} { + res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx)) + return &res + }).(*android.ConfiguredJarList) +} + // GlobalSoongConfig contains the global config that is generated from Soong, // stored in dexpreopt_soong.config. type GlobalSoongConfig struct { diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index 3145315e3..de139c439 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -115,7 +115,7 @@ func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *Mo // /data. If we don't do this they will need to be extracted which is not favorable for RAM usage // or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options. if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) && - !AllSystemServerJars(ctx, global).ContainsJar(module.Name) && !module.PreoptExtractedApk { + !global.AllSystemServerJars(ctx).ContainsJar(module.Name) && !module.PreoptExtractedApk { return true } @@ -197,8 +197,8 @@ func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, } // Returns the dex location of a system server java library. -func GetSystemServerDexLocation(global *GlobalConfig, lib string) string { - if apex := global.ApexSystemServerJars.ApexOfJar(lib); apex != "" { +func GetSystemServerDexLocation(ctx android.PathContext, global *GlobalConfig, lib string) string { + if apex := global.AllApexSystemServerJars(ctx).ApexOfJar(lib); apex != "" { return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib) } return fmt.Sprintf("/system/framework/%s.jar", lib) @@ -240,7 +240,8 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g invocationPath := odexPath.ReplaceExtension(ctx, "invocation") - systemServerJars := AllSystemServerJars(ctx, global) + systemServerJars := global.AllSystemServerJars(ctx) + systemServerClasspathJars := global.AllSystemServerClasspathJars(ctx) rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String())) rule.Command().FlagWithOutput("rm -f ", odexPath) @@ -251,10 +252,15 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g var clcHost android.Paths var clcTarget []string - for i := 0; i < jarIndex; i++ { - lib := systemServerJars.Jar(i) + endIndex := systemServerClasspathJars.IndexOfJar(module.Name) + if endIndex < 0 { + // The jar is a standalone one. Use the full classpath as the class loader context. + endIndex = systemServerClasspathJars.Len() + } + for i := 0; i < endIndex; i++ { + lib := systemServerClasspathJars.Jar(i) clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib)) - clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib)) + clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib)) } if DexpreoptRunningInSoong { @@ -270,12 +276,22 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g // cannot see the rule in the generated dexpreopt.sh script). } - checkSystemServerOrder(ctx, jarIndex) + clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]" + clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]" + + if systemServerClasspathJars.ContainsJar(module.Name) { + checkSystemServerOrder(ctx, jarIndex) + } else { + // Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the + // parent. + clcHostString = "PCL[];" + clcHostString + clcTargetString = "PCL[];" + clcTargetString + } rule.Command(). - Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clcHost.Strings(), ":") + "]"). + Text(`class_loader_context_arg=--class-loader-context="` + clcHostString + `"`). Implicits(clcHost). - Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]") + Text(`stored_class_loader_context_arg=--stored-class-loader-context="` + clcTargetString + `"`) } else { // There are three categories of Java modules handled here: @@ -533,17 +549,6 @@ func makefileMatch(pattern, s string) bool { } } -var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars") - -// TODO: eliminate the superficial global config parameter by moving global config definition -// from java subpackage to dexpreopt. -func AllSystemServerJars(ctx android.PathContext, global *GlobalConfig) *android.ConfiguredJarList { - return ctx.Config().Once(allSystemServerJarsKey, func() interface{} { - allSystemServerJars := global.SystemServerJars.AppendList(global.ApexSystemServerJars) - return &allSystemServerJars - }).(*android.ConfiguredJarList) -} - // A predefined location for the system server dex jars. This is needed in order to generate // class loader context for dex2oat, as the path to the jar in the Soong module may be unknown // at that time (Soong processes the jars in dependency order, which may be different from the @@ -567,7 +572,7 @@ func checkSystemServerOrder(ctx android.PathContext, jarIndex int) { mctx, isModule := ctx.(android.ModuleContext) if isModule { config := GetGlobalConfig(ctx) - jars := AllSystemServerJars(ctx, config) + jars := config.AllSystemServerClasspathJars(ctx) mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { depIndex := jars.IndexOfJar(dep.Name()) if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars { diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go index 798d77604..07e4fad01 100644 --- a/dexpreopt/dexpreopt_test.go +++ b/dexpreopt/dexpreopt_test.go @@ -50,6 +50,15 @@ func testApexModuleConfig(ctx android.PathContext, name, apexName string) *Modul android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name))) } +func testPlatformSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig { + return createTestModuleConfig( + name, + fmt.Sprintf("/system/framework/%s.jar", name), + android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)), + android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)), + android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name))) +} + func createTestModuleConfig(name, dexLocation string, buildPath, dexPath, enforceUsesLibrariesStatusFile android.OutputPath) *ModuleConfig { return &ModuleConfig{ Name: name, @@ -181,6 +190,52 @@ func TestDexPreoptApexSystemServerJars(t *testing.T) { android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String()) } +func TestDexPreoptStandaloneSystemServerJars(t *testing.T) { + config := android.TestConfig("out", nil, "", nil) + ctx := android.BuilderContextForTesting(config) + globalSoong := globalSoongConfigForTests() + global := GlobalConfigForTests(ctx) + module := testPlatformSystemServerModuleConfig(ctx, "service-A") + + global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList( + []string{"platform:service-A"}) + + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module) + if err != nil { + t.Fatal(err) + } + + wantInstalls := android.RuleBuilderInstalls{ + {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system/framework/oat/arm/service-A.odex"}, + {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/service-A.vdex"}, + } + + android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String()) +} + +func TestDexPreoptApexStandaloneSystemServerJars(t *testing.T) { + config := android.TestConfig("out", nil, "", nil) + ctx := android.BuilderContextForTesting(config) + globalSoong := globalSoongConfigForTests() + global := GlobalConfigForTests(ctx) + module := testApexModuleConfig(ctx, "service-A", "com.android.apex1") + + global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList( + []string{"com.android.apex1:service-A"}) + + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module) + if err != nil { + t.Fatal(err) + } + + wantInstalls := android.RuleBuilderInstalls{ + {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"}, + {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"}, + } + + android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String()) +} + func TestDexPreoptProfile(t *testing.T) { config := android.TestConfig("out", nil, "", nil) ctx := android.BuilderContextForTesting(config) diff --git a/java/dexpreopt.go b/java/dexpreopt.go index f3a53eed9..3158450a8 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -152,15 +152,16 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { return true } + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) if isApexVariant(ctx) { // Don't preopt APEX variant module unless the module is an APEX system server jar and we are // building the entire system image. - if !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) || ctx.Config().UnbundledBuild() { + if !isApexSystemServerJar || ctx.Config().UnbundledBuild() { return true } } else { // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. - if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) { + if isApexSystemServerJar { return true } } @@ -191,8 +192,8 @@ func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath a func (d *dexpreopter) getInstallPath( ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath { global := dexpreopt.GetGlobalConfig(ctx) - if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) { - dexLocation := dexpreopt.GetSystemServerDexLocation(global, moduleName(ctx)) + if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) { + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx)) return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/")) } if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) && @@ -229,8 +230,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr return } - isSystemServerJar := global.SystemServerJars.ContainsJar(moduleName(ctx)) || - global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) + isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) bootImage := defaultBootImageConfig(ctx) if global.UseArtImage { @@ -336,6 +336,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr dexpreoptRule.Build("dexpreopt", "dexpreopt") + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + for _, install := range dexpreoptRule.Installs() { // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT. installDir := strings.TrimPrefix(filepath.Dir(install.To), "/") @@ -343,7 +345,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr arch := filepath.Base(installDir) installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) - if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) { + if isApexSystemServerJar { // APEX variants of java libraries are hidden from Make, so their dexpreopt // outputs need special handling. Currently, for APEX variants of java // libraries, only those in the system server classpath are handled here. @@ -362,7 +364,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr } } - if !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) { + if !isApexSystemServerJar { d.builtInstalled = dexpreoptRule.Installs().String() } } diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go index 565901d35..fade066eb 100644 --- a/java/dexpreopt_check.go +++ b/java/dexpreopt_check.go @@ -72,9 +72,10 @@ func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.Mod return } - systemServerJars := dexpreopt.AllSystemServerJars(ctx, global) + // TODO(b/203198541): Check all system server jars. + systemServerJars := global.AllSystemServerClasspathJars(ctx) for _, jar := range systemServerJars.CopyOfJars() { - dexLocation := dexpreopt.GetSystemServerDexLocation(global, jar) + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar) odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType) odexPath := getInstallPath(ctx, odexLocation) vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex")) diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 2ec33a422..fa61ea68c 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -60,7 +60,7 @@ func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx an classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType) standaloneConfiguredJars := p.standaloneConfiguredJars(ctx) standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS) - configuredJars = configuredJars.AppendList(standaloneConfiguredJars) + configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) classpathJars = append(classpathJars, standaloneClasspathJars...) p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } @@ -122,7 +122,7 @@ func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.Mo classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType) standaloneConfiguredJars := s.standaloneConfiguredJars(ctx) standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS) - configuredJars = configuredJars.AppendList(standaloneConfiguredJars) + configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) classpathJars = append(classpathJars, standaloneClasspathJars...) s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) From 28bc9a8a7eab2e7b6666df1079677a14e4899b78 Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Mon, 20 Dec 2021 15:08:57 +0000 Subject: [PATCH 2/2] Dexpreopt standalone system server jars from prebuilts. This change adds support for dexpreopting standalone system server jars from prebuilts. Bug: 203198541 Test: - 1. Add a standalone system server jar (e.g., by patching aosp/1906158) 2. Build and drop a module SDK and an APEX. 3. Build a system image from prebuilts. 4. See the odex and vdex files generated in $ANDROID_PRODUCT_OUT/system/framework/oat/ Change-Id: I8f4eaed10a1053cd560b8583efa12dc495f58db1 --- apex/apex_test.go | 2 +- java/androidmk.go | 9 ++++++++- java/dexpreopt.go | 9 ++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apex/apex_test.go b/apex/apex_test.go index a749ea161..e2262f7fa 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -8500,7 +8500,7 @@ func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) { java_import { name: "foo", jars: ["foo.jar"], - installable: true, + apex_available: ["myapex"], } `, dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"), diff --git a/java/androidmk.go b/java/androidmk.go index 272a4fd36..55242fe80 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -182,7 +182,14 @@ func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries { } func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { - if prebuilt.hideApexVariantFromMake || !prebuilt.ContainingSdk().Unversioned() { + if prebuilt.hideApexVariantFromMake { + // For a library imported from a prebuilt APEX, we don't need a Make module for itself, as we + // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it + // is preopted. + dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex() + return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true}) + } + if !prebuilt.ContainingSdk().Unversioned() { return []android.AndroidMkEntries{android.AndroidMkEntries{ Disabled: true, }} diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 3158450a8..edb69849f 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -115,6 +115,11 @@ func isApexVariant(ctx android.BaseModuleContext) bool { return !apexInfo.IsForPlatform() } +func forPrebuiltApex(ctx android.BaseModuleContext) bool { + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + return apexInfo.ForPrebuiltApex +} + func moduleName(ctx android.BaseModuleContext) string { // Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not // expected by dexpreopter. @@ -134,7 +139,9 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { return true } - if !ctx.Module().(DexpreopterInterface).IsInstallable() { + // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be + // dexpreopted. + if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) { return true }