summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-09-23 09:56:13 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-09-23 09:56:13 +0000
commitce4af10bdb762077988f60dc79e4a251d4ea97a0 (patch)
tree7dbe9760b6a3ba46caa424f67bf724725dffdfec
parent532d4af422fc93d99b55d3887e1c3efe9e4f3325 (diff)
parent7d360b3bc2e2ed19e5d7f64fe2e2b20e2dbcdee9 (diff)
downloadart-android13-mainline-scheduling-release.tar.gz
Snap for 9098257 from 7d360b3bc2e2ed19e5d7f64fe2e2b20e2dbcdee9 to mainline-scheduling-releaseaml_sch_331113000aml_sch_331111000android13-mainline-scheduling-release
Change-Id: Ia1cc4457c297fae722221ade419cf17045cc299a
-rw-r--r--TEST_MAPPING6
-rwxr-xr-xbuild/apex/art_apex_test.py2
-rw-r--r--build/apex/manifest-art.json6
-rw-r--r--build/sdk/Android.bp15
-rw-r--r--compiler/jit/jit_compiler.cc2
-rw-r--r--dex2oat/Android.bp2
-rw-r--r--dex2oat/art_standalone_dex2oat_tests.xml2
-rw-r--r--dex2oat/dex2oat.cc9
-rw-r--r--dex2oat/driver/compiler_driver.cc1
-rw-r--r--dex2oat/linker/image_test.cc32
-rw-r--r--dex2oat/linker/image_test.h16
-rw-r--r--dex2oat/verifier_deps_test.cc7
-rw-r--r--libartbase/Android.bp5
-rw-r--r--libartbase/base/flags.h6
-rw-r--r--libartbase/base/metrics/metrics.h94
-rw-r--r--libartbase/base/metrics/metrics_common.cc148
-rw-r--r--libartbase/base/metrics/metrics_test.cc263
-rw-r--r--libartservice/Android.bp47
-rw-r--r--libartservice/api/current.txt8
-rw-r--r--libartservice/jarjar-rules.txt2
-rw-r--r--libartservice/service/java/com/android/server/art/ArtManagerLocal.java11
-rw-r--r--libnativebridge/tests/Android.bp8
-rw-r--r--libnativeloader/library_namespaces.cpp130
-rw-r--r--libnativeloader/native_loader_test.cpp15
-rw-r--r--libnativeloader/test/Android.bp146
-rw-r--r--libnativeloader/test/Android.mk72
-rw-r--r--libnativeloader/test/libnativeloader_e2e_tests.xml28
-rw-r--r--libnativeloader/test/library_container_app_manifest.xml (renamed from libnativeloader/test/system/AndroidManifest.xml)15
-rw-r--r--libnativeloader/test/loadlibrarytest_data_app_manifest.xml33
-rw-r--r--libnativeloader/test/loadlibrarytest_product_app_manifest.xml33
-rw-r--r--libnativeloader/test/loadlibrarytest_system_app_manifest.xml (renamed from libnativeloader/test/vendor/AndroidManifest.xml)18
-rw-r--r--libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml31
-rw-r--r--libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml31
-rw-r--r--libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml33
-rw-r--r--libnativeloader/test/public.libraries-oem1.txt2
-rw-r--r--libnativeloader/test/public.libraries-oem2.txt2
-rw-r--r--libnativeloader/test/public.libraries-product1.txt2
-rwxr-xr-xlibnativeloader/test/runtest.sh11
-rw-r--r--libnativeloader/test/src/android/test/app/DataAppTest.java64
-rw-r--r--libnativeloader/test/src/android/test/app/ProductAppTest.java64
-rw-r--r--libnativeloader/test/src/android/test/app/SystemAppTest.java64
-rw-r--r--libnativeloader/test/src/android/test/app/TestActivity.java44
-rw-r--r--libnativeloader/test/src/android/test/app/VendorAppTest.java67
-rw-r--r--libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java332
-rw-r--r--libnativeloader/test/src/android/test/lib/TestUtils.java35
-rw-r--r--libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java21
-rw-r--r--libnativeloader/test/src/android/test/systemsharedlib/SystemSharedLib.java (renamed from libnativeloader/test/test.cpp)10
-rw-r--r--odrefresh/Android.bp1
-rw-r--r--odrefresh/odr_metrics.cc25
-rw-r--r--odrefresh/odr_metrics.h12
-rw-r--r--odrefresh/odr_metrics_record.cc129
-rw-r--r--odrefresh/odr_metrics_record.h39
-rw-r--r--odrefresh/odr_metrics_record_test.cc124
-rw-r--r--odrefresh/odr_metrics_test.cc34
-rw-r--r--odrefresh/odr_statslog_android.cc39
-rw-r--r--openjdkjvmti/deopt_manager.cc10
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/app_info.cc13
-rw-r--r--runtime/app_info.h7
-rw-r--r--runtime/app_info_test.cc14
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64.cc12
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64.h4
-rw-r--r--runtime/arch/instruction_set_features.cc27
-rw-r--r--runtime/arch/instruction_set_features.h6
-rw-r--r--runtime/class_linker.cc15
-rw-r--r--runtime/gc/collector/concurrent_copying.cc6
-rw-r--r--runtime/gc/collector/garbage_collector.cc16
-rw-r--r--runtime/gc/collector/garbage_collector.h3
-rw-r--r--runtime/gc/space/dlmalloc_space.cc20
-rw-r--r--runtime/gc/space/image_space.cc29
-rw-r--r--runtime/instrumentation.cc17
-rw-r--r--runtime/instrumentation.h7
-rw-r--r--runtime/jit/jit.cc7
-rw-r--r--runtime/metrics/reporter.cc12
-rw-r--r--runtime/metrics/reporter.h3
-rw-r--r--runtime/metrics/statsd.cc24
-rw-r--r--runtime/oat_file.cc11
-rw-r--r--runtime/oat_file_manager.cc23
-rw-r--r--runtime/runtime.cc17
-rw-r--r--runtime/thread.cc6
-rw-r--r--test/1000-non-moving-space-stress/src-art/Main.java34
-rw-r--r--test/719-varhandle-concurrency/src/Main.java3
-rw-r--r--test/Android.bp20
-rw-r--r--test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali18
-rw-r--r--test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali17
-rw-r--r--test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali28
-rw-r--r--test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali26
-rw-r--r--test/SuperWithAccessChecks/ImplementsClass.smali18
-rw-r--r--test/SuperWithAccessChecks/Itf.smali23
-rw-r--r--test/SuperWithAccessChecks/SubClass.smali17
-rw-r--r--test/SuperWithAccessChecks/SuperClass.smali24
-rwxr-xr-xtest/etc/run-test-jar6
-rw-r--r--test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java3
-rwxr-xr-xtest/utils/regen-test-files19
94 files changed, 2356 insertions, 608 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 300526faa9..ec814ef614 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1283,6 +1283,9 @@
"name": "art_standalone_sigchain_tests[com.google.android.art.apex]"
},
{
+ "name": "libnativeloader_e2e_tests[com.google.android.art.apex]"
+ },
+ {
"name": "libnativeloader_test[com.google.android.art.apex]"
}
],
@@ -2584,6 +2587,9 @@
"name": "art_standalone_sigchain_tests"
},
{
+ "name": "libnativeloader_e2e_tests"
+ },
+ {
"name": "libnativeloader_test"
}
]
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 428baf4ab9..fa9ca5a137 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -698,6 +698,7 @@ class TestingTargetChecker:
# Check ART jar files which are needed for gtests.
self._checker.check_art_test_data('art-gtest-jars-AbstractMethod.jar')
+ self._checker.check_art_test_data('art-gtest-jars-ArrayClassWithUnresolvedComponent.dex')
self._checker.check_art_test_data('art-gtest-jars-MyClassNatives.jar')
self._checker.check_art_test_data('art-gtest-jars-Main.jar')
self._checker.check_art_test_data('art-gtest-jars-ProtoCompare.jar')
@@ -749,6 +750,7 @@ class TestingTargetChecker:
self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressed.jar')
self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexTestDex.jar')
self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexPublicSdkDex.dex')
+ self._checker.check_art_test_data('art-gtest-jars-SuperWithAccessChecks.dex')
class NoSuperfluousBinariesChecker:
diff --git a/build/apex/manifest-art.json b/build/apex/manifest-art.json
index bf45076b0d..4f20be67d2 100644
--- a/build/apex/manifest-art.json
+++ b/build/apex/manifest-art.json
@@ -1,6 +1,10 @@
{
"name": "com.android.art",
- "version": 339990000,
+
+ // Placeholder module version to be replaced during build.
+ // Do not change!
+ "version": 0,
+
"provideNativeLibs": [
"libjdwp.so"
],
diff --git a/build/sdk/Android.bp b/build/sdk/Android.bp
index a9ee6754a1..b47bdc2fee 100644
--- a/build/sdk/Android.bp
+++ b/build/sdk/Android.bp
@@ -93,17 +93,10 @@ art_module_sdk {
},
android: {
- bootclasspath_fragments: [
- // Adds the fragment and its contents to the sdk.
- "art-bootclasspath-fragment",
- ],
-
- systemserverclasspath_fragments: [
- "art-systemserverclasspath-fragment",
- ],
-
- compat_configs: [
- "libcore-platform-compat-config",
+ apexes: [
+ // Adds exportable dependencies of the API to the sdk,
+ // e.g. *classpath_fragments.
+ "com.android.art",
],
java_header_libs: [
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 7002636d4e..e578d3bf7f 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -85,7 +85,7 @@ void JitCompiler::ParseCompilerOptions() {
if (StartsWith(option, "--instruction-set-variant=")) {
const char* str = option.c_str() + strlen("--instruction-set-variant=");
VLOG(compiler) << "JIT instruction set variant " << str;
- instruction_set_features = InstructionSetFeatures::FromVariant(
+ instruction_set_features = InstructionSetFeatures::FromVariantAndHwcap(
instruction_set, str, &error_msg);
if (instruction_set_features == nullptr) {
LOG(WARNING) << "Error parsing " << option << " message=" << error_msg;
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 26cbd51459..941b9e0e95 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -459,6 +459,7 @@ art_cc_defaults {
name: "art_dex2oat_tests_defaults",
data: [
":art-gtest-jars-AbstractMethod",
+ ":art-gtest-jars-ArrayClassWithUnresolvedComponent",
":art-gtest-jars-DefaultMethods",
":art-gtest-jars-Dex2oatVdexPublicSdkDex",
":art-gtest-jars-Dex2oatVdexTestDex",
@@ -479,6 +480,7 @@ art_cc_defaults {
":art-gtest-jars-StaticLeafMethods",
":art-gtest-jars-Statics",
":art-gtest-jars-StringLiterals",
+ ":art-gtest-jars-SuperWithAccessChecks",
":art-gtest-jars-VerifierDeps",
":art-gtest-jars-VerifierDepsMulti",
":art-gtest-jars-VerifySoftFailDuringClinit",
diff --git a/dex2oat/art_standalone_dex2oat_tests.xml b/dex2oat/art_standalone_dex2oat_tests.xml
index 32346f2f23..8bd0fb1fac 100644
--- a/dex2oat/art_standalone_dex2oat_tests.xml
+++ b/dex2oat/art_standalone_dex2oat_tests.xml
@@ -23,6 +23,8 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art-gtest-jars-AbstractMethod.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-AbstractMethod.jar" />
+ <option name="push" value="art-gtest-jars-ArrayClassWithUnresolvedComponent.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ArrayClassWithUnresolvedComponent.dex" />
+ <option name="push" value="art-gtest-jars-SuperWithAccessChecks.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-SuperWithAccessChecks.dex" />
<option name="push" value="art-gtest-jars-DefaultMethods.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-DefaultMethods.jar" />
<option name="push" value="art-gtest-jars-Dex2oatVdexPublicSdkDex.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Dex2oatVdexPublicSdkDex.dex" />
<option name="push" value="art-gtest-jars-Dex2oatVdexTestDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Dex2oatVdexTestDex.jar" />
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 9e6103b424..27bae654c3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -607,8 +607,13 @@ class Dex2Oat final {
}
void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) {
- compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
- compiler_options_->instruction_set_, option, &parser_options->error_msg);
+ if (kIsTargetBuild) {
+ compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariantAndHwcap(
+ compiler_options_->instruction_set_, option, &parser_options->error_msg);
+ } else {
+ compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
+ compiler_options_->instruction_set_, option, &parser_options->error_msg);
+ }
if (compiler_options_->instruction_set_features_ == nullptr) {
Usage("%s", parser_options->error_msg.c_str());
}
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index d9509b0c53..5a7217897d 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -1109,6 +1109,7 @@ static void MaybeAddToImageClasses(Thread* self,
HashSet<std::string>* image_classes)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(self, Thread::Current());
+ DCHECK(klass->IsResolved());
Runtime* runtime = Runtime::Current();
gc::Heap* heap = runtime->GetHeap();
if (heap->ObjectIsInBootImageSpace(klass)) {
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index 33d122bdbe..82c87f504d 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -176,5 +176,37 @@ TEST_F(ImageTest, TestSoftVerificationFailureDuringClassInitialization) {
/*image_classes_failing_aot_clinit=*/ {"LClassToInitialize;"});
}
+TEST_F(ImageTest, TestImageClassWithArrayClassWithUnresolvedComponent) {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed,
+ /*max_image_block_size=*/std::numeric_limits<uint32_t>::max(),
+ helper,
+ "ArrayClassWithUnresolvedComponent",
+ /*image_classes=*/ {"LClassWithStatic;",
+ "LClassWithStaticConst;",
+ "[LClassWithMissingInterface;",
+ "[[LClassWithMissingInterface;",
+ "[LClassWithMissingSuper",
+ "[[LClassWithMissingSuper"},
+ /*image_classes_failing_aot_clinit=*/ {
+ "LClassWithStatic;",
+ "LClassWithStaticConst;"},
+ /*image_classes_failing_resolution=*/ {
+ "[LClassWithMissingInterface;",
+ "[[LClassWithMissingInterface;",
+ "[LClassWithMissingSuper",
+ "[[LClassWithMissingSuper"});
+}
+
+TEST_F(ImageTest, TestSuperWithAccessChecks) {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed,
+ /*max_image_block_size=*/std::numeric_limits<uint32_t>::max(),
+ helper,
+ "SuperWithAccessChecks",
+ /*image_classes=*/ {"LSubClass;", "LImplementsClass;"},
+ /*image_classes_failing_aot_clinit=*/ {"LSubClass;", "LImplementsClass;"});
+}
+
} // namespace linker
} // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 5c2d84cc5e..b570d99e64 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -86,7 +86,8 @@ class ImageTest : public CommonCompilerDriverTest {
/*out*/ CompilationHelper& out_helper,
const std::string& extra_dex = "",
const std::initializer_list<std::string>& image_classes = {},
- const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {});
+ const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {},
+ const std::initializer_list<std::string>& image_classes_failing_resolution = {});
void SetUpRuntimeOptions(RuntimeOptions* options) override {
CommonCompilerTest::SetUpRuntimeOptions(options);
@@ -352,7 +353,8 @@ inline void ImageTest::Compile(
CompilationHelper& helper,
const std::string& extra_dex,
const std::initializer_list<std::string>& image_classes,
- const std::initializer_list<std::string>& image_classes_failing_aot_clinit) {
+ const std::initializer_list<std::string>& image_classes_failing_aot_clinit,
+ const std::initializer_list<std::string>& image_classes_failing_resolution) {
for (const std::string& image_class : image_classes_failing_aot_clinit) {
ASSERT_TRUE(ContainsElement(image_classes, image_class));
}
@@ -375,12 +377,14 @@ inline void ImageTest::Compile(
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
for (const std::string& image_class : image_classes) {
ObjPtr<mirror::Class> klass =
- class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
- EXPECT_TRUE(klass != nullptr);
- EXPECT_TRUE(klass->IsResolved());
- if (ContainsElement(image_classes_failing_aot_clinit, image_class)) {
+ class_linker->LookupClass(Thread::Current(), image_class.c_str(), nullptr);
+ if (ContainsElement(image_classes_failing_resolution, image_class)) {
+ EXPECT_TRUE(klass == nullptr || klass->IsErroneousUnresolved());
+ } else if (ContainsElement(image_classes_failing_aot_clinit, image_class)) {
+ ASSERT_TRUE(klass != nullptr) << image_class;
EXPECT_FALSE(klass->IsInitialized());
} else {
+ ASSERT_TRUE(klass != nullptr) << image_class;
EXPECT_TRUE(klass->IsInitialized());
}
}
diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc
index 708af04390..27eadec3fc 100644
--- a/dex2oat/verifier_deps_test.cc
+++ b/dex2oat/verifier_deps_test.cc
@@ -630,12 +630,5 @@ TEST_F(VerifierDepsTest, MultiDexVerification) {
ASSERT_FALSE(buffer.empty());
}
-TEST_F(VerifierDepsTest, Assignable_Arrays) {
- ASSERT_TRUE(TestAssignabilityRecording(/* dst= */ "[LIface;",
- /* src= */ "[LMyClassExtendingInterface;"));
- ASSERT_FALSE(HasAssignable(
- "LIface;", "LMyClassExtendingInterface;"));
-}
-
} // namespace verifier
} // namespace art
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index f9375239e4..80c63c6cfb 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -70,6 +70,7 @@ cc_defaults {
// ZipArchive support, the order matters here to get all symbols.
"libziparchive",
],
+ whole_static_libs: ["libtinyxml2"],
shared_libs: [
"libz",
"liblog",
@@ -88,6 +89,7 @@ cc_defaults {
static: {
cflags: ["-DART_STATIC_LIBARTBASE"],
},
+ whole_static_libs: ["libtinyxml2"],
shared_libs: [
"libziparchive",
"libz",
@@ -112,6 +114,7 @@ cc_defaults {
// For common macros.
"libbase",
],
+ whole_static_libs: ["libtinyxml2"],
export_static_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h.
cflags: ["-Wno-thread-safety"],
@@ -391,6 +394,8 @@ cc_library_headers {
shared_libs: ["libbase"],
export_shared_lib_headers: ["libbase"],
+ whole_static_libs: ["libtinyxml2"],
+
apex_available: [
"com.android.art",
"com.android.art.debug",
diff --git a/libartbase/base/flags.h b/libartbase/base/flags.h
index d1e1ca6243..fc568b2be2 100644
--- a/libartbase/base/flags.h
+++ b/libartbase/base/flags.h
@@ -304,6 +304,12 @@ struct Flags {
// Note that the actual write is still controlled by
// MetricsReportingMods and MetricsReportingNumMods.
Flag<std::string> MetricsWriteToFile{"metrics.write-to-file", "", FlagType::kCmdlineOnly};
+
+ // The output format for metrics. This is only used
+ // when writing metrics to a file; metrics written
+ // to logcat will be in human-readable text format.
+ // Supported values are "text" and "xml".
+ Flag<std::string> MetricsFormat{"metrics.format", "text", FlagType::kCmdlineOnly};
};
// This is the actual instance of all the flags.
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index d6f2463bb6..ebc44a9394 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -30,6 +30,7 @@
#include "android-base/logging.h"
#include "base/bit_utils.h"
#include "base/time_utils.h"
+#include "tinyxml2.h"
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wconversion"
@@ -55,7 +56,15 @@
METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000) \
METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000) \
METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
- METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000)
+ METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(GcWorldStopTime, MetricsCounter) \
+ METRIC(GcWorldStopCount, MetricsCounter) \
+ METRIC(YoungGcScannedBytes, MetricsCounter) \
+ METRIC(YoungGcFreedBytes, MetricsCounter) \
+ METRIC(YoungGcDuration, MetricsCounter) \
+ METRIC(FullGcScannedBytes, MetricsCounter) \
+ METRIC(FullGcFreedBytes, MetricsCounter) \
+ METRIC(FullGcDuration, MetricsCounter)
// A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
// and ART_HISTOGRAMS. This means metrics.h and metrics.cc are very #define-heavy, which can be
@@ -433,12 +442,80 @@ class MetricsAccumulator final : MetricsBase<T> {
friend class ArtMetrics;
};
-// A backend that writes metrics in a human-readable format to a string.
+// Base class for formatting metrics into different formats
+// (human-readable text, JSON, etc.)
+class MetricsFormatter {
+ public:
+ virtual ~MetricsFormatter() = default;
+
+ virtual void FormatBeginReport(uint64_t timestamp_since_start_ms,
+ const std::optional<SessionData>& session_data) = 0;
+ virtual void FormatEndReport() = 0;
+ virtual void FormatReportCounter(DatumId counter_type, uint64_t value) = 0;
+ virtual void FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) = 0;
+ virtual std::string GetAndResetBuffer() = 0;
+
+ protected:
+ const std::string version = "1.0";
+};
+
+// Formatter outputting metrics in human-readable text format
+class TextFormatter : public MetricsFormatter {
+ public:
+ TextFormatter() = default;
+
+ void FormatBeginReport(uint64_t timestamp_millis,
+ const std::optional<SessionData>& session_data) override;
+
+ void FormatReportCounter(DatumId counter_type, uint64_t value) override;
+
+ void FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) override;
+
+ void FormatEndReport() override;
+
+ std::string GetAndResetBuffer() override;
+
+ private:
+ std::ostringstream os_;
+};
+
+// Formatter outputting metrics in XML format
+class XmlFormatter : public MetricsFormatter {
+ public:
+ XmlFormatter() = default;
+
+ void FormatBeginReport(uint64_t timestamp_millis,
+ const std::optional<SessionData>& session_data) override;
+
+ void FormatReportCounter(DatumId counter_type, uint64_t value) override;
+
+ void FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) override;
+
+ void FormatEndReport() override;
+
+ std::string GetAndResetBuffer() override;
+
+ private:
+ tinyxml2::XMLDocument document_;
+};
+
+// A backend that writes metrics to a string.
+// The format of the metrics' output is delegated
+// to the MetricsFormatter class.
//
// This is used as a base for LogBackend and FileBackend.
class StringBackend : public MetricsBackend {
public:
- StringBackend();
+ explicit StringBackend(std::unique_ptr<MetricsFormatter> formatter);
void BeginOrUpdateSession(const SessionData& session_data) override;
@@ -456,14 +533,15 @@ class StringBackend : public MetricsBackend {
std::string GetAndResetBuffer();
private:
- std::ostringstream os_;
+ std::unique_ptr<MetricsFormatter> formatter_;
std::optional<SessionData> session_data_;
};
// A backend that writes metrics in human-readable format to the log (i.e. logcat).
class LogBackend : public StringBackend {
public:
- explicit LogBackend(android::base::LogSeverity level);
+ explicit LogBackend(std::unique_ptr<MetricsFormatter> formatter,
+ android::base::LogSeverity level);
void BeginReport(uint64_t timestamp_millis) override;
void EndReport() override;
@@ -473,12 +551,10 @@ class LogBackend : public StringBackend {
};
// A backend that writes metrics to a file.
-//
-// These are currently written in the same human-readable format used by StringBackend and
-// LogBackend, but we will probably want a more machine-readable format in the future.
class FileBackend : public StringBackend {
public:
- explicit FileBackend(const std::string& filename);
+ explicit FileBackend(std::unique_ptr<MetricsFormatter> formatter,
+ const std::string& filename);
void BeginReport(uint64_t timestamp_millis) override;
void EndReport() override;
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index f09987ba8e..025f5eb79e 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -76,7 +76,7 @@ void ArtMetrics::ReportAllMetrics(MetricsBackend* backend) const {
}
void ArtMetrics::DumpForSigQuit(std::ostream& os) const {
- StringBackend backend;
+ StringBackend backend(std::make_unique<TextFormatter>());
ReportAllMetrics(&backend);
os << backend.GetAndResetBuffer();
}
@@ -88,13 +88,12 @@ void ArtMetrics::Reset() {
#undef ART_METRIC
}
-StringBackend::StringBackend() {}
+StringBackend::StringBackend(std::unique_ptr<MetricsFormatter> formatter)
+ : formatter_(std::move(formatter))
+{}
std::string StringBackend::GetAndResetBuffer() {
- std::string result = os_.str();
- os_.clear();
- os_.str("");
- return result;
+ return formatter_->GetAndResetBuffer();
}
void StringBackend::BeginOrUpdateSession(const SessionData& session_data) {
@@ -102,32 +101,51 @@ void StringBackend::BeginOrUpdateSession(const SessionData& session_data) {
}
void StringBackend::BeginReport(uint64_t timestamp_since_start_ms) {
+ formatter_->FormatBeginReport(timestamp_since_start_ms, session_data_);
+}
+
+void StringBackend::EndReport() {
+ formatter_->FormatEndReport();
+}
+
+void StringBackend::ReportCounter(DatumId counter_type, uint64_t value) {
+ formatter_->FormatReportCounter(counter_type, value);
+}
+
+void StringBackend::ReportHistogram(DatumId histogram_type,
+ int64_t minimum_value_,
+ int64_t maximum_value_,
+ const std::vector<uint32_t>& buckets) {
+ formatter_->FormatReportHistogram(histogram_type, minimum_value_, maximum_value_, buckets);
+}
+
+void TextFormatter::FormatBeginReport(uint64_t timestamp_since_start_ms,
+ const std::optional<SessionData>& session_data) {
os_ << "\n*** ART internal metrics ***\n";
os_ << " Metadata:\n";
os_ << " timestamp_since_start_ms: " << timestamp_since_start_ms << "\n";
- if (session_data_.has_value()) {
- os_ << " session_id: " << session_data_->session_id << "\n";
- os_ << " uid: " << session_data_->uid << "\n";
- os_ << " compilation_reason: " << CompilationReasonName(session_data_->compilation_reason)
+ if (session_data.has_value()) {
+ os_ << " session_id: " << session_data->session_id << "\n";
+ os_ << " uid: " << session_data->uid << "\n";
+ os_ << " compilation_reason: " << CompilationReasonName(session_data->compilation_reason)
<< "\n";
- os_ << " compiler_filter: " << CompilerFilterReportingName(session_data_->compiler_filter)
+ os_ << " compiler_filter: " << CompilerFilterReportingName(session_data->compiler_filter)
<< "\n";
}
os_ << " Metrics:\n";
}
-void StringBackend::EndReport() { os_ << "*** Done dumping ART internal metrics ***\n"; }
-
-void StringBackend::ReportCounter(DatumId counter_type, uint64_t value) {
+void TextFormatter::FormatReportCounter(DatumId counter_type, uint64_t value) {
os_ << " " << DatumName(counter_type) << ": count = " << value << "\n";
}
-void StringBackend::ReportHistogram(DatumId histogram_type,
- int64_t minimum_value_,
- int64_t maximum_value_,
- const std::vector<uint32_t>& buckets) {
- os_ << " " << DatumName(histogram_type) << ": range = " << minimum_value_ << "..." << maximum_value_;
- if (buckets.size() > 0) {
+void TextFormatter::FormatReportHistogram(DatumId histogram_type,
+ int64_t minimum_value_,
+ int64_t maximum_value_,
+ const std::vector<uint32_t>& buckets) {
+ os_ << " " << DatumName(histogram_type) << ": range = "
+ << minimum_value_ << "..." << maximum_value_;
+ if (!buckets.empty()) {
os_ << ", buckets: ";
bool first = true;
for (const auto& count : buckets) {
@@ -143,22 +161,100 @@ void StringBackend::ReportHistogram(DatumId histogram_type,
}
}
-LogBackend::LogBackend(android::base::LogSeverity level) : level_{level} {}
+void TextFormatter::FormatEndReport() {
+ os_ << "*** Done dumping ART internal metrics ***\n";
+}
+
+std::string TextFormatter::GetAndResetBuffer() {
+ std::string result = os_.str();
+ os_.clear();
+ os_.str("");
+ return result;
+}
+
+void XmlFormatter::FormatBeginReport(uint64_t timestamp_millis,
+ const std::optional<SessionData>& session_data) {
+ tinyxml2::XMLElement* art_runtime_metrics = document_.NewElement("art_runtime_metrics");
+ document_.InsertEndChild(art_runtime_metrics);
+
+ art_runtime_metrics->InsertNewChildElement("version")->SetText(version.data());
+
+ tinyxml2::XMLElement* metadata = art_runtime_metrics->InsertNewChildElement("metadata");
+ metadata->InsertNewChildElement("timestamp_since_start_ms")->SetText(timestamp_millis);
+
+ if (session_data.has_value()) {
+ metadata->InsertNewChildElement("session_id")->SetText(session_data->session_id);
+ metadata->InsertNewChildElement("uid")->SetText(session_data->uid);
+ metadata
+ ->InsertNewChildElement("compilation_reason")
+ ->SetText(CompilationReasonName(session_data->compilation_reason));
+ metadata
+ ->InsertNewChildElement("compiler_filter")
+ ->SetText(CompilerFilterReportingName(session_data->compiler_filter));
+ }
+
+ art_runtime_metrics->InsertNewChildElement("metrics");
+}
+
+void XmlFormatter::FormatReportCounter(DatumId counter_type, uint64_t value) {
+ tinyxml2::XMLElement* metrics = document_.RootElement()->FirstChildElement("metrics");
+
+ tinyxml2::XMLElement* counter = metrics->InsertNewChildElement(DatumName(counter_type).data());
+ counter->InsertNewChildElement("counter_type")->SetText("count");
+ counter->InsertNewChildElement("value")->SetText(value);
+}
+
+void XmlFormatter::FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) {
+ tinyxml2::XMLElement* metrics = document_.RootElement()->FirstChildElement("metrics");
+
+ tinyxml2::XMLElement* histogram =
+ metrics->InsertNewChildElement(DatumName(histogram_type).data());
+ histogram->InsertNewChildElement("counter_type")->SetText("histogram");
+ histogram->InsertNewChildElement("minimum_value")->SetText(low_value);
+ histogram->InsertNewChildElement("maximum_value")->SetText(high_value);
+
+ tinyxml2::XMLElement* buckets_element = histogram->InsertNewChildElement("buckets");
+ for (const auto& count : buckets) {
+ buckets_element->InsertNewChildElement("bucket")->SetText(count);
+ }
+}
+
+void XmlFormatter::FormatEndReport() {}
+
+std::string XmlFormatter::GetAndResetBuffer() {
+ tinyxml2::XMLPrinter printer(/*file=*/nullptr, /*compact=*/true);
+ document_.Print(&printer);
+ std::string result = printer.CStr();
+ document_.Clear();
+
+ return result;
+}
+
+LogBackend::LogBackend(std::unique_ptr<MetricsFormatter> formatter,
+ android::base::LogSeverity level)
+ : StringBackend{std::move(formatter)}, level_{level}
+{}
void LogBackend::BeginReport(uint64_t timestamp_since_start_ms) {
- GetAndResetBuffer();
+ StringBackend::GetAndResetBuffer();
StringBackend::BeginReport(timestamp_since_start_ms);
}
void LogBackend::EndReport() {
StringBackend::EndReport();
- LOG_STREAM(level_) << GetAndResetBuffer();
+ LOG_STREAM(level_) << StringBackend::GetAndResetBuffer();
}
-FileBackend::FileBackend(const std::string& filename) : filename_{filename} {}
+FileBackend::FileBackend(std::unique_ptr<MetricsFormatter> formatter,
+ const std::string& filename)
+ : StringBackend{std::move(formatter)}, filename_{filename}
+{}
void FileBackend::BeginReport(uint64_t timestamp_since_start_ms) {
- GetAndResetBuffer();
+ StringBackend::GetAndResetBuffer();
StringBackend::BeginReport(timestamp_since_start_ms);
}
@@ -170,7 +266,7 @@ void FileBackend::EndReport() {
if (file.get() == nullptr) {
LOG(WARNING) << "Could open metrics file '" << filename_ << "': " << error_message;
} else {
- if (!android::base::WriteStringToFd(GetAndResetBuffer(), file.get()->Fd())) {
+ if (!android::base::WriteStringToFd(StringBackend::GetAndResetBuffer(), file.get()->Fd())) {
PLOG(WARNING) << "Error writing metrics to file";
}
}
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index ed6f88d709..282029012b 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -16,6 +16,7 @@
#include "metrics.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "metrics_test.h"
@@ -248,7 +249,7 @@ TEST_F(MetricsTest, HistogramTimer) {
// Makes sure all defined metrics are included when dumping through StreamBackend.
TEST_F(MetricsTest, StreamBackendDumpAllMetrics) {
ArtMetrics metrics;
- StringBackend backend;
+ StringBackend backend(std::make_unique<TextFormatter>());
metrics.ReportAllMetrics(&backend);
@@ -305,6 +306,266 @@ TEST_F(MetricsTest, ResetMetrics) {
metrics.ReportAllMetrics(&zero_backend);
}
+TEST(TextFormatterTest, ReportMetrics_WithBuckets) {
+ TextFormatter text_formatter;
+ SessionData session_data {
+ .session_id = 1000,
+ .uid = 50,
+ .compilation_reason = CompilationReason::kInstall,
+ .compiler_filter = CompilerFilterReporting::kSpeed,
+ };
+
+ text_formatter.FormatBeginReport(200, session_data);
+ text_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u);
+ text_formatter.FormatReportHistogram(DatumId::kFullGcCollectionTime,
+ 50,
+ 200,
+ {2, 4, 7, 1});
+ text_formatter.FormatEndReport();
+
+ const std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 200\n"
+ " session_id: 1000\n"
+ " uid: 50\n"
+ " compilation_reason: install\n"
+ " compiler_filter: speed\n"
+ " Metrics:\n"
+ " FullGcCount: count = 1\n"
+ " FullGcCollectionTime: range = 50...200, buckets: 2,4,7,1\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(TextFormatterTest, ReportMetrics_NoBuckets) {
+ TextFormatter text_formatter;
+ SessionData session_data {
+ .session_id = 500,
+ .uid = 15,
+ .compilation_reason = CompilationReason::kCmdLine,
+ .compiler_filter = CompilerFilterReporting::kExtract,
+ };
+
+ text_formatter.FormatBeginReport(400, session_data);
+ text_formatter.FormatReportHistogram(DatumId::kFullGcCollectionTime, 10, 20, {});
+ text_formatter.FormatEndReport();
+
+ std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 400\n"
+ " session_id: 500\n"
+ " uid: 15\n"
+ " compilation_reason: cmdline\n"
+ " compiler_filter: extract\n"
+ " Metrics:\n"
+ " FullGcCollectionTime: range = 10...20, no buckets\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(TextFormatterTest, BeginReport_NoSessionData) {
+ TextFormatter text_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ text_formatter.FormatBeginReport(100, empty_session_data);
+ text_formatter.FormatEndReport();
+
+ std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 100\n"
+ " Metrics:\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(TextFormatterTest, GetAndResetBuffer_ActuallyResetsBuffer) {
+ TextFormatter text_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ text_formatter.FormatBeginReport(200, empty_session_data);
+ text_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u);
+ text_formatter.FormatEndReport();
+
+ std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 200\n"
+ " Metrics:\n"
+ " FullGcCount: count = 1\n"
+ "*** Done dumping ART internal metrics ***\n");
+
+ text_formatter.FormatBeginReport(300, empty_session_data);
+ text_formatter.FormatReportCounter(DatumId::kFullGcCount, 5u);
+ text_formatter.FormatEndReport();
+
+ result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 300\n"
+ " Metrics:\n"
+ " FullGcCount: count = 5\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(XmlFormatterTest, ReportMetrics_WithBuckets) {
+ XmlFormatter xml_formatter;
+ SessionData session_data {
+ .session_id = 123,
+ .uid = 456,
+ .compilation_reason = CompilationReason::kFirstBoot,
+ .compiler_filter = CompilerFilterReporting::kSpace,
+ };
+
+ xml_formatter.FormatBeginReport(250, session_data);
+ xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 3u);
+ xml_formatter.FormatReportHistogram(DatumId::kYoungGcCollectionTime,
+ 300,
+ 600,
+ {1, 5, 3});
+ xml_formatter.FormatEndReport();
+
+ const std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>250</timestamp_since_start_ms>"
+ "<session_id>123</session_id>"
+ "<uid>456</uid>"
+ "<compilation_reason>first-boot</compilation_reason>"
+ "<compiler_filter>space</compiler_filter>"
+ "</metadata>"
+ "<metrics>"
+ "<YoungGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>3</value>"
+ "</YoungGcCount>"
+ "<YoungGcCollectionTime>"
+ "<counter_type>histogram</counter_type>"
+ "<minimum_value>300</minimum_value>"
+ "<maximum_value>600</maximum_value>"
+ "<buckets>"
+ "<bucket>1</bucket>"
+ "<bucket>5</bucket>"
+ "<bucket>3</bucket>"
+ "</buckets>"
+ "</YoungGcCollectionTime>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
+TEST(XmlFormatterTest, ReportMetrics_NoBuckets) {
+ XmlFormatter xml_formatter;
+ SessionData session_data {
+ .session_id = 234,
+ .uid = 345,
+ .compilation_reason = CompilationReason::kFirstBoot,
+ .compiler_filter = CompilerFilterReporting::kSpace,
+ };
+
+ xml_formatter.FormatBeginReport(160, session_data);
+ xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 4u);
+ xml_formatter.FormatReportHistogram(DatumId::kYoungGcCollectionTime, 20, 40, {});
+ xml_formatter.FormatEndReport();
+
+ const std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>160</timestamp_since_start_ms>"
+ "<session_id>234</session_id>"
+ "<uid>345</uid>"
+ "<compilation_reason>first-boot</compilation_reason>"
+ "<compiler_filter>space</compiler_filter>"
+ "</metadata>"
+ "<metrics>"
+ "<YoungGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>4</value>"
+ "</YoungGcCount>"
+ "<YoungGcCollectionTime>"
+ "<counter_type>histogram</counter_type>"
+ "<minimum_value>20</minimum_value>"
+ "<maximum_value>40</maximum_value>"
+ "<buckets/>"
+ "</YoungGcCollectionTime>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
+TEST(XmlFormatterTest, BeginReport_NoSessionData) {
+ XmlFormatter xml_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ xml_formatter.FormatBeginReport(100, empty_session_data);
+ xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 3u);
+ xml_formatter.FormatEndReport();
+
+ std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>100</timestamp_since_start_ms>"
+ "</metadata>"
+ "<metrics>"
+ "<YoungGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>3</value>"
+ "</YoungGcCount>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
+TEST(XmlFormatterTest, GetAndResetBuffer_ActuallyResetsBuffer) {
+ XmlFormatter xml_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ xml_formatter.FormatBeginReport(200, empty_session_data);
+ xml_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u);
+ xml_formatter.FormatEndReport();
+
+ std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>200</timestamp_since_start_ms>"
+ "</metadata>"
+ "<metrics>"
+ "<FullGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>1</value>"
+ "</FullGcCount>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+
+ xml_formatter.FormatBeginReport(300, empty_session_data);
+ xml_formatter.FormatReportCounter(DatumId::kFullGcCount, 5u);
+ xml_formatter.FormatEndReport();
+
+ result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>300</timestamp_since_start_ms>"
+ "</metadata>"
+ "<metrics>"
+ "<FullGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>5</value>"
+ "</FullGcCount>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
TEST(CompilerFilterReportingTest, FromName) {
ASSERT_EQ(CompilerFilterReportingFromName("error"),
CompilerFilterReporting::kError);
diff --git a/libartservice/Android.bp b/libartservice/Android.bp
index 985a2eb956..9fac53b67c 100644
--- a/libartservice/Android.bp
+++ b/libartservice/Android.bp
@@ -49,66 +49,27 @@ java_sdk_library {
// for JAR files in the System Server.
name: "service-art",
defaults: ["framework-system-server-module-defaults"],
-
permitted_packages: ["com.android.server.art"],
-
visibility: [
"//art:__subpackages__",
"//frameworks/base/services/core",
],
-
impl_library_visibility: [
"//art/libartservice/tests",
],
-
- stubs_library_visibility: ["//visibility:public"],
- stubs_source_visibility: ["//visibility:private"],
-
apex_available: [
"com.android.art",
"com.android.art.debug",
],
- sdk_version: "core_platform",
+ sdk_version: "system_server_current",
min_sdk_version: "31",
-
- public: {
- // Override the setting of "module_current" from the defaults as that is
- // not available in all manifests where this needs to be built.
- sdk_version: "core_current",
- },
-
- system_server: {
- // Override the setting of "module_current" from the defaults as that is
- // not available in all manifests where this needs to be built.
- sdk_version: "core_current",
- },
-
- // The API elements are the ones annotated with
- // libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE)
- droiddoc_options: [
- "--show-single-annotation libcore.api.CorePlatformApi\\(status=libcore.api.CorePlatformApi.Status.STABLE\\)",
- ],
-
- // Temporarily disable compatibility with previous released APIs.
- // TODO - remove once prototype has stabilized
- // running "m update-api" will give instructions on what to do next
- unsafe_ignore_missing_latest_api: true,
-
- // This cannot be accessed by apps using <uses-library> in their manifest.
- shared_library: false,
- // TODO(b/188773212): force dex compilation for inclusion in bootclasspath_fragment.
- compile_dex: true,
-
srcs: [
- "service/java/com/android/server/art/ArtManagerLocal.java",
+ "service/java/**/*.java",
],
-
- libs: [
- "art.module.api.annotations.for.system.modules",
+ static_libs: [
],
-
plugins: ["java_api_finder"],
- dist_group: "android",
+ jarjar_rules: "jarjar-rules.txt",
}
art_cc_defaults {
diff --git a/libartservice/api/current.txt b/libartservice/api/current.txt
index c7844e0780..d802177e24 100644
--- a/libartservice/api/current.txt
+++ b/libartservice/api/current.txt
@@ -1,9 +1 @@
// Signature format: 2.0
-package com.android.server.art {
-
- public final class ArtManagerLocal {
- ctor public ArtManagerLocal();
- }
-
-}
-
diff --git a/libartservice/jarjar-rules.txt b/libartservice/jarjar-rules.txt
new file mode 100644
index 0000000000..c7d39e68c9
--- /dev/null
+++ b/libartservice/jarjar-rules.txt
@@ -0,0 +1,2 @@
+# Repackages static libraries to make them private to ART Services.
+rule com.android.modules.utils.** com.android.server.art.jarjar.@0
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index aac4b2508d..64aec7bf6b 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -16,15 +16,16 @@
package com.android.server.art;
-import libcore.api.CorePlatformApi;
+import android.annotation.SystemApi;
/**
- * This class provides a system API for functionality provided by the ART
- * module.
+ * This class provides a system API for functionality provided by the ART module.
+ *
+ * @hide
*/
-@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public final class ArtManagerLocal {
- static final String LOG_TAG = "ArtService";
+ private static final String TAG = "ArtService";
public ArtManagerLocal() {}
}
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index b787fbabb6..57d26c0316 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -110,6 +110,14 @@ cc_test {
// unloading, so each test needs to be its own process.
test_per_src: true,
+ // Disable pre-submit host unit-testing for this test module, as
+ // it is not compatible with TradeFed (because of the use of the
+ // `test_per_src` feature above) and meant to be executed with the
+ // `runtests.sh` script instead.
+ test_options: {
+ unit_test: false,
+ },
+
srcs: [
"NativeBridgeApi.c",
"CodeCacheCreate_test.cpp",
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index f31c4302a4..f3c93a0ecc 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -81,16 +81,22 @@ constexpr const char* kSharedNamespaceSuffix = "-shared";
constexpr const char* kAlwaysPermittedDirectories = "/data:/mnt/expand";
constexpr const char* kVendorLibPath = "/vendor/" LIB;
+// TODO(mast): It's unlikely that both paths are necessary for kProductLibPath
+// below, because they can't be two separate directories - either one has to be
+// a symlink to the other.
constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
+constexpr const char* kSystemLibPath = "/system/" LIB ":/system_ext/" LIB;
-const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kVendorDexPathRegex("(^|:)(/system)?/vendor/");
const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+const std::regex kSystemDexPathRegex("(^|:)/system(_ext)?/"); // MUST be tested last.
-// Define origin of APK if it is from vendor partition or product partition
+// Define origin partition of APK
using ApkOrigin = enum {
APK_ORIGIN_DEFAULT = 0,
- APK_ORIGIN_VENDOR = 1,
- APK_ORIGIN_PRODUCT = 2,
+ APK_ORIGIN_VENDOR = 1, // Includes both /vendor and /system/vendor
+ APK_ORIGIN_PRODUCT = 2, // Includes both /product and /system/product
+ APK_ORIGIN_SYSTEM = 3, // Includes both /system and /system_ext but not /system/{vendor,product}
};
jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
@@ -113,6 +119,9 @@ ApkOrigin GetApkOriginFromDexPath(const std::string& dex_path) {
apk_origin = APK_ORIGIN_PRODUCT;
}
+ if (apk_origin == APK_ORIGIN_DEFAULT && std::regex_search(dex_path, kSystemDexPathRegex)) {
+ apk_origin = APK_ORIGIN_SYSTEM;
+ }
return apk_origin;
}
@@ -231,51 +240,49 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t
std::string system_exposed_libraries = default_public_libraries();
std::string namespace_name = kClassloaderNamespaceName;
ApkOrigin unbundled_app_origin = APK_ORIGIN_DEFAULT;
- if ((apk_origin == APK_ORIGIN_VENDOR ||
- (apk_origin == APK_ORIGIN_PRODUCT &&
- is_product_vndk_version_defined())) &&
- !is_shared) {
- unbundled_app_origin = apk_origin;
- // For vendor / product apks, give access to the vendor / product lib even though
- // they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor / product partition.
- const char* origin_partition;
- const char* origin_lib_path;
- const char* llndk_libraries;
-
- switch (apk_origin) {
- case APK_ORIGIN_VENDOR:
- origin_partition = "vendor";
- origin_lib_path = kVendorLibPath;
- llndk_libraries = llndk_libraries_vendor().c_str();
- break;
- case APK_ORIGIN_PRODUCT:
- origin_partition = "product";
- origin_lib_path = kProductLibPath;
- llndk_libraries = llndk_libraries_product().c_str();
- break;
- default:
- origin_partition = "unknown";
- origin_lib_path = "";
- llndk_libraries = "";
- }
- library_path = library_path + ":" + origin_lib_path;
- permitted_path = permitted_path + ":" + origin_lib_path;
-
- // Also give access to LLNDK libraries since they are available to vendor or product
- system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries;
-
- // Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
- origin_partition, library_path.c_str());
- } else {
- auto libs = filter_public_libraries(target_sdk_version, uses_libraries,
- extended_public_libraries());
- // extended public libraries are NOT available to vendor apks, otherwise it
- // would be system->vendor violation.
- if (!libs.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + libs;
+ const char* apk_origin_msg = "other apk"; // Only for debug logging.
+
+ if (!is_shared) {
+ if (apk_origin == APK_ORIGIN_SYSTEM) {
+ // System apps commonly get shared namespaces and hence don't need this.
+ // In practice it's necessary for shared system libraries (i.e. JARs
+ // rather than actual APKs) that are loaded by ordinary apps which don't
+ // get shared namespaces.
+ apk_origin_msg = "system apk";
+
+ // Give access to all libraries in the system and system_ext partitions
+ // (they can freely access each other's private APIs).
+ library_path = library_path + ":" + kSystemLibPath;
+ permitted_path = permitted_path + ":" + kSystemLibPath;
+ } else if (apk_origin == APK_ORIGIN_VENDOR) {
+ unbundled_app_origin = APK_ORIGIN_VENDOR;
+ apk_origin_msg = "unbundled vendor apk";
+
+ // For vendor apks, give access to the vendor libs even though they are
+ // treated as unbundled; the libs and apks are still bundled together in the
+ // vendor partition.
+ library_path = library_path + ':' + kVendorLibPath;
+ permitted_path = permitted_path + ':' + kVendorLibPath;
+
+ // Also give access to LLNDK libraries since they are available to vendor.
+ system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_vendor();
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
+ } else if (apk_origin == APK_ORIGIN_PRODUCT && is_product_vndk_version_defined()) {
+ unbundled_app_origin = APK_ORIGIN_PRODUCT;
+ apk_origin_msg = "unbundled product apk";
+
+ // Like for vendor apks, give access to the product libs since they are
+ // bundled together in the same partition.
+ library_path = library_path + ':' + kProductLibPath;
+ permitted_path = permitted_path + ':' + kProductLibPath;
+
+ // Also give access to LLNDK libraries since they are available to product.
+ system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_product();
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
}
}
@@ -285,6 +292,31 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t
namespace_name = namespace_name + kSharedNamespaceSuffix;
}
+ ALOGD(
+ "Configuring %s for %s %s. target_sdk_version=%u, uses_libraries=%s, library_path=%s, "
+ "permitted_path=%s",
+ namespace_name.c_str(),
+ apk_origin_msg,
+ dex_path.c_str(),
+ static_cast<unsigned>(target_sdk_version),
+ android::base::Join(uses_libraries, ':').c_str(),
+ library_path.c_str(),
+ permitted_path.c_str());
+
+ if (unbundled_app_origin != APK_ORIGIN_VENDOR) {
+ // Extended public libraries are NOT available to unbundled vendor apks, but
+ // they are to other apps, including those in system, system_ext, and
+ // product partitions. The reason is that when GSI is used, the system
+ // partition may get replaced, and then vendor apps may fail. It's fine for
+ // product apps, because that partition isn't mounted in GSI tests.
+ auto libs =
+ filter_public_libraries(target_sdk_version, uses_libraries, extended_public_libraries());
+ if (!libs.empty()) {
+ ALOGD("Extending system_exposed_libraries: %s", libs.c_str());
+ system_exposed_libraries = system_exposed_libraries + ':' + libs;
+ }
+ }
+
// Create the app namespace
NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
// Heuristic: the first classloader with non-empty library_path is assumed to
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index 9648713e70..080b4d6e46 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -167,13 +167,16 @@ INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
/////////////////////////////////////////////////////////////////
-std::string default_public_and_extended_libraries() {
- std::string public_libs = default_public_libraries();
- std::string ext_libs = extended_public_libraries();
+std::string append_extended_libraries(const std::string& libs) {
+ const std::string& ext_libs = extended_public_libraries();
if (!ext_libs.empty()) {
- public_libs = public_libs + ":" + ext_libs;
+ return libs + ":" + ext_libs;
}
- return public_libs;
+ return libs;
+}
+
+std::string default_public_and_extended_libraries() {
+ return append_extended_libraries(default_public_libraries());
}
class NativeLoaderTest_Create : public NativeLoaderTest {
@@ -380,7 +383,7 @@ TEST_P(NativeLoaderTest_Create, UnbundledProductApp) {
expected_permitted_path =
expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
expected_shared_libs_to_platform_ns =
- default_public_libraries() + ":" + llndk_libraries_product();
+ append_extended_libraries(default_public_libraries() + ":" + llndk_libraries_product());
expected_link_with_vndk_product_ns = true;
}
SetExpectations();
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index fb9ae0d9d3..1d3a07a5f3 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -24,57 +24,125 @@ package {
}
cc_library {
- name: "libfoo.oem1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
- shared_libs: [
- "libbase",
- ],
+ name: "libnativeloader_testlib",
+ srcs: [],
+ stl: "none",
}
-cc_library {
- name: "libbar.oem1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
- shared_libs: [
- "libbase",
- ],
+// This app is just an intermediate container to be able to include the .so
+// library in the host test. It's not actually installed or started.
+android_test_helper_app {
+ name: "library_container_app",
+ defaults: ["art_module_source_build_java_defaults"],
+ manifest: "library_container_app_manifest.xml",
+ compile_multilib: "both",
+ jni_libs: ["libnativeloader_testlib"],
}
-cc_library {
- name: "libfoo.oem2",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
- shared_libs: [
- "libbase",
+java_library {
+ name: "loadlibrarytest_test_utils",
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
],
+ srcs: ["src/android/test/lib/TestUtils.java"],
}
-cc_library {
- name: "libbar.oem2",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
- shared_libs: [
- "libbase",
- ],
+// Test fixture that represents a shared library in /system/framework.
+java_library {
+ name: "libnativeloader_system_shared_lib",
+ installable: true,
+ srcs: ["src/android/test/systemsharedlib/SystemSharedLib.java"],
}
-cc_library {
- name: "libfoo.product1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
- product_specific: true,
- shared_libs: [
- "libbase",
+// Test fixture that represents a shared library in /system_ext/framework.
+java_library {
+ name: "libnativeloader_system_ext_shared_lib",
+ installable: true,
+ srcs: ["src/android/test/systemextsharedlib/SystemExtSharedLib.java"],
+}
+
+java_defaults {
+ name: "loadlibrarytest_app_defaults",
+ defaults: ["art_module_source_build_java_defaults"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "loadlibrarytest_test_utils",
+ ],
+ libs: [
+ "libnativeloader_system_shared_lib",
+ "libnativeloader_system_ext_shared_lib",
],
}
-cc_library {
- name: "libbar.product1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+android_test_helper_app {
+ name: "loadlibrarytest_system_priv_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ manifest: "loadlibrarytest_system_priv_app_manifest.xml",
+ // /system/priv-app currently reuses the same test as /system/app.
+ srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_system_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ manifest: "loadlibrarytest_system_app_manifest.xml",
+ srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_system_ext_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ system_ext_specific: true,
+ manifest: "loadlibrarytest_system_ext_app_manifest.xml",
+ // /system_ext should behave the same as /system, so use the same test class there.
+ srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_product_app",
+ defaults: ["loadlibrarytest_app_defaults"],
product_specific: true,
- shared_libs: [
- "libbase",
+ manifest: "loadlibrarytest_product_app_manifest.xml",
+ srcs: ["src/android/test/app/ProductAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_vendor_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ vendor: true,
+ manifest: "loadlibrarytest_vendor_app_manifest.xml",
+ srcs: ["src/android/test/app/VendorAppTest.java"],
+}
+
+// A normal app installed in /data.
+android_test_helper_app {
+ name: "loadlibrarytest_data_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ manifest: "loadlibrarytest_data_app_manifest.xml",
+ srcs: ["src/android/test/app/DataAppTest.java"],
+}
+
+java_test_host {
+ name: "libnativeloader_e2e_tests",
+ defaults: ["art_module_source_build_java_defaults"],
+ srcs: ["src/android/test/hostside/*.java"],
+ libs: [
+ "compatibility-tradefed",
+ "tradefed",
+ ],
+ data: [
+ ":library_container_app",
+ ":libnativeloader_system_shared_lib",
+ ":libnativeloader_system_ext_shared_lib",
+ ":loadlibrarytest_system_priv_app",
+ ":loadlibrarytest_system_app",
+ ":loadlibrarytest_system_ext_app",
+ ":loadlibrarytest_product_app",
+ ":loadlibrarytest_vendor_app",
+ ":loadlibrarytest_data_app",
],
+ test_config: "libnativeloader_e2e_tests.xml",
+ test_suites: ["general-tests"],
}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
deleted file mode 100644
index 95fa68af68..0000000000
--- a/libnativeloader/test/Android.mk
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem1.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem2.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-product1.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-system
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)
diff --git a/libnativeloader/test/libnativeloader_e2e_tests.xml b/libnativeloader/test/libnativeloader_e2e_tests.xml
new file mode 100644
index 0000000000..b1333b0765
--- /dev/null
+++ b/libnativeloader/test/libnativeloader_e2e_tests.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<configuration description="Config for libnativeloader e2e test cases">
+ <option name="test-suite-tag" value="libnativeloader_e2e_tests" />
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="libnativeloader_e2e_tests.jar" />
+ </test>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
+</configuration>
diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/library_container_app_manifest.xml
index c3048891a8..20030de7eb 100644
--- a/libnativeloader/test/system/AndroidManifest.xml
+++ b/libnativeloader/test/library_container_app_manifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -16,16 +16,5 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.app.system">
-
- <application>
- <activity android:name="android.test.app.TestActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
+ package="android.test.app.container">
</manifest>
-
diff --git a/libnativeloader/test/loadlibrarytest_data_app_manifest.xml b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
new file mode 100644
index 0000000000..2af0af417e
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.data">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.data" />
+ <application>
+ <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:name="android.test.systemextsharedlib" />
+ <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
+ <uses-native-library android:required="false" android:name="libbar.oem1.so" />
+ <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
+ <!--libbar.oem2.so is left out -->
+ <uses-native-library android:required="false" android:name="libfoo.product1.so" />
+ <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
new file mode 100644
index 0000000000..614f33ffc9
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.product">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.product" />
+ <application>
+ <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:name="android.test.systemextsharedlib" />
+ <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
+ <uses-native-library android:required="false" android:name="libbar.oem1.so" />
+ <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
+ <!--libbar.oem2.so is left out -->
+ <uses-native-library android:required="false" android:name="libfoo.product1.so" />
+ <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
index c4c1a9c210..5711f651cf 100644
--- a/libnativeloader/test/vendor/AndroidManifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
@@ -16,16 +16,16 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.app.vendor">
-
+ package="android.test.app.system">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.system" />
<application>
- <activity android:name="android.test.app.TestActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
+ <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:name="android.test.systemextsharedlib" />
+ <!-- System apps get a shared classloader namespace, so they don't need
+ uses-native-library entries for anything in /system. -->
+ <uses-native-library android:required="false" android:name="libfoo.product1.so" />
+ <uses-native-library android:required="false" android:name="libbar.product1.so" />
</application>
-
</manifest>
diff --git a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
new file mode 100644
index 0000000000..8aa3fa9f1f
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.system_ext">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.system_ext" />
+ <application>
+ <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:name="android.test.systemextsharedlib" />
+ <!-- System apps get a shared classloader namespace, so they don't need
+ uses-native-library entries for anything in /system. -->
+ <uses-native-library android:required="false" android:name="libfoo.product1.so" />
+ <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
new file mode 100644
index 0000000000..126453c86f
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.system_priv">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.system_priv" />
+ <application>
+ <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:name="android.test.systemextsharedlib" />
+ <!-- System apps get a shared classloader namespace, so they don't need
+ uses-native-library entries for anything in /system. -->
+ <uses-native-library android:required="false" android:name="libfoo.product1.so" />
+ <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
new file mode 100644
index 0000000000..a2a9f648f4
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.vendor">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.vendor" />
+ <application>
+ <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:name="android.test.systemextsharedlib" />
+ <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
+ <uses-native-library android:required="false" android:name="libbar.oem1.so" />
+ <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
+ <!--libbar.oem2.so is left out -->
+ <uses-native-library android:required="false" android:name="libfoo.product1.so" />
+ <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
deleted file mode 100644
index f9433e2a04..0000000000
--- a/libnativeloader/test/public.libraries-oem1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem1.so
-libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
deleted file mode 100644
index de6bdb08e0..0000000000
--- a/libnativeloader/test/public.libraries-oem2.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem2.so
-libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
deleted file mode 100644
index 358154c626..0000000000
--- a/libnativeloader/test/public.libraries-product1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.product1.so
-libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
deleted file mode 100755
index 40beb5b4d5..0000000000
--- a/libnativeloader/test/runtest.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-adb root
-adb remount
-adb sync
-adb shell stop
-adb shell start
-sleep 5 # wait until device reboots
-adb logcat -c;
-adb shell am start -n android.test.app.system/android.test.app.TestActivity
-adb shell am start -n android.test.app.vendor/android.test.app.TestActivity
-adb logcat | grep android.test.app
diff --git a/libnativeloader/test/src/android/test/app/DataAppTest.java b/libnativeloader/test/src/android/test/app/DataAppTest.java
new file mode 100644
index 0000000000..767a7b19ff
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/DataAppTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DataAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ System.loadLibrary("foo.oem1");
+ System.loadLibrary("bar.oem1");
+ System.loadLibrary("foo.oem2");
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.loadLibrary("bar.oem2")); // Missing <uses-native-library>.
+ System.loadLibrary("foo.product1");
+ System.loadLibrary("bar.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
new file mode 100644
index 0000000000..1f36798515
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProductAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ System.loadLibrary("foo.oem1");
+ System.loadLibrary("bar.oem1");
+ System.loadLibrary("foo.oem2");
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.loadLibrary("bar.oem2")); // Missing <uses-native-library>.
+ System.loadLibrary("foo.product1");
+ System.loadLibrary("bar.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
+ System.loadLibrary("product_private1");
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/app/SystemAppTest.java b/libnativeloader/test/src/android/test/app/SystemAppTest.java
new file mode 100644
index 0000000000..197a40cf0e
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/SystemAppTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// These tests are run from /system/app, /system/priv-app, and /system_ext/app.
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ System.loadLibrary("foo.oem1");
+ System.loadLibrary("bar.oem1");
+ System.loadLibrary("foo.oem2");
+ System.loadLibrary("bar.oem2");
+ System.loadLibrary("foo.product1");
+ System.loadLibrary("bar.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ System.loadLibrary("system_private1");
+ System.loadLibrary("systemext_private1");
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
deleted file mode 100644
index a7a455d33d..0000000000
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-package android.test.app;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestActivity extends Activity {
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- tryLoadingLib("foo.oem1");
- tryLoadingLib("bar.oem1");
- tryLoadingLib("foo.oem2");
- tryLoadingLib("bar.oem2");
- tryLoadingLib("foo.product1");
- tryLoadingLib("bar.product1");
- }
-
- private void tryLoadingLib(String name) {
- try {
- System.loadLibrary(name);
- Log.d(getPackageName(), "library " + name + " is successfully loaded");
- } catch (UnsatisfiedLinkError e) {
- Log.d(getPackageName(), "failed to load libarary " + name, e);
- }
- }
-}
diff --git a/libnativeloader/test/src/android/test/app/VendorAppTest.java b/libnativeloader/test/src/android/test/app/VendorAppTest.java
new file mode 100644
index 0000000000..c9ce8db57a
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/VendorAppTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VendorAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("foo.oem1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("bar.oem1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("foo.oem2"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("bar.oem2"));
+ System.loadLibrary("foo.product1");
+ System.loadLibrary("bar.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
+ // TODO(mast): The vendor app fails to load a private vendor library because it gets
+ // classified as untrusted_app in SELinux, which doesn't have access to vendor_file. Even an
+ // app in /vendor/priv-app, which gets classified as priv_app, still doesn't have access to
+ // vendor_file. Check that the test setup is correct and if this is WAI.
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
new file mode 100644
index 0000000000..c9290377ee
--- /dev/null
+++ b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.hostside;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.util.CommandResult;
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test libnativeloader behavior for apps and libs in various partitions by overlaying them over
+ * the system partitions. Requires root.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class LibnativeloaderTest extends BaseHostJUnit4Test {
+ private static final String TAG = "LibnativeloaderTest";
+ private static final String CLEANUP_PATHS_KEY = TAG + ":CLEANUP_PATHS";
+ private static final String LOG_FILE_NAME = "TestActivity.log";
+
+ @BeforeClassWithInfo
+ public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
+ DeviceContext ctx = new DeviceContext(
+ testInfo.getContext(), testInfo.getDevice(), testInfo.getBuildInfo());
+
+ // A soft reboot is slow, so do setup for all tests and reboot once.
+
+ ctx.mDevice.remountSystemWritable();
+
+ File libContainerApk = ctx.mBuildHelper.getTestFile("library_container_app.apk");
+ try (ZipFile libApk = new ZipFile(libContainerApk)) {
+ ctx.pushExtendedPublicSystemOemLibs(libApk);
+ ctx.pushExtendedPublicProductLibs(libApk);
+ ctx.pushPrivateLibs(libApk);
+ }
+ ctx.pushSystemSharedLib("/system/framework", "android.test.systemsharedlib",
+ "libnativeloader_system_shared_lib.jar");
+ ctx.pushSystemSharedLib("/system_ext/framework", "android.test.systemextsharedlib",
+ "libnativeloader_system_ext_shared_lib.jar");
+
+ // "Install" apps in various partitions through plain adb push followed by a soft reboot. We
+ // need them in these locations to test library loading restrictions, so for all except
+ // loadlibrarytest_data_app we cannot use ITestDevice.installPackage for it since it only
+ // installs in /data.
+
+ // For testSystemPrivApp
+ ctx.pushApk("loadlibrarytest_system_priv_app", "/system/priv-app");
+
+ // For testSystemApp
+ ctx.pushApk("loadlibrarytest_system_app", "/system/app");
+
+ // For testSystemExtApp
+ ctx.pushApk("loadlibrarytest_system_ext_app", "/system_ext/app");
+
+ // For testProductApp
+ ctx.pushApk("loadlibrarytest_product_app", "/product/app");
+
+ // For testVendorApp
+ ctx.pushApk("loadlibrarytest_vendor_app", "/vendor/app");
+
+ ctx.softReboot();
+
+ // For testDataApp. Install this the normal way after the system server restart.
+ ctx.installPackage("loadlibrarytest_data_app");
+
+ testInfo.properties().put(CLEANUP_PATHS_KEY, ctx.mCleanup.getPathList());
+ }
+
+ @AfterClassWithInfo
+ public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
+ ITestDevice device = testInfo.getDevice();
+
+ // Uninstall loadlibrarytest_data_app.
+ device.uninstallPackage("android.test.app.data");
+
+ String cleanupPathList = testInfo.properties().get(CLEANUP_PATHS_KEY);
+ CleanupPaths cleanup = new CleanupPaths(device, cleanupPathList);
+ cleanup.cleanup();
+ }
+
+ @Test
+ public void testSystemPrivApp() throws Exception {
+ // There's currently no difference in the tests between /system/priv-app and /system/app, so
+ // let's reuse the same one.
+ runDeviceTests("android.test.app.system_priv", "android.test.app.SystemAppTest");
+ }
+
+ @Test
+ public void testSystemApp() throws Exception {
+ runDeviceTests("android.test.app.system", "android.test.app.SystemAppTest");
+ }
+
+ @Test
+ public void testSystemExtApp() throws Exception {
+ // /system_ext should behave the same as /system, so run the same test class there.
+ runDeviceTests("android.test.app.system_ext", "android.test.app.SystemAppTest");
+ }
+
+ @Test
+ public void testProductApp() throws Exception {
+ runDeviceTests("android.test.app.product", "android.test.app.ProductAppTest");
+ }
+
+ @Test
+ public void testVendorApp() throws Exception {
+ runDeviceTests("android.test.app.vendor", "android.test.app.VendorAppTest");
+ }
+
+ @Test
+ public void testDataApp() throws Exception {
+ runDeviceTests("android.test.app.data", "android.test.app.DataAppTest");
+ }
+
+ // Utility class that keeps track of a set of paths the need to be deleted after testing.
+ private static class CleanupPaths {
+ private ITestDevice mDevice;
+ private List<String> mCleanupPaths;
+
+ CleanupPaths(ITestDevice device) {
+ mDevice = device;
+ mCleanupPaths = new ArrayList<String>();
+ }
+
+ CleanupPaths(ITestDevice device, String pathList) {
+ mDevice = device;
+ mCleanupPaths = Arrays.asList(pathList.split(":"));
+ }
+
+ String getPathList() { return String.join(":", mCleanupPaths); }
+
+ // Adds the given path, or its topmost nonexisting parent directory, to the list of paths to
+ // clean up.
+ void addPath(String devicePath) throws DeviceNotAvailableException {
+ File path = new File(devicePath);
+ while (true) {
+ File parentPath = path.getParentFile();
+ if (parentPath == null || mDevice.doesFileExist(parentPath.toString())) {
+ break;
+ }
+ path = parentPath;
+ }
+ String nonExistingPath = path.toString();
+ if (!mCleanupPaths.contains(nonExistingPath)) {
+ mCleanupPaths.add(nonExistingPath);
+ }
+ }
+
+ void cleanup() throws DeviceNotAvailableException {
+ // Clean up in reverse order in case several pushed files were in the same nonexisting
+ // directory.
+ for (int i = mCleanupPaths.size() - 1; i >= 0; --i) {
+ mDevice.deleteFile(mCleanupPaths.get(i));
+ }
+ }
+ }
+
+ // Class for code that needs an ITestDevice. It is instantiated both in tests and in
+ // (Before|After)ClassWithInfo.
+ private static class DeviceContext implements AutoCloseable {
+ IInvocationContext mContext;
+ ITestDevice mDevice;
+ CompatibilityBuildHelper mBuildHelper;
+ CleanupPaths mCleanup;
+ private String mTestArch;
+
+ DeviceContext(IInvocationContext context, ITestDevice device, IBuildInfo buildInfo) {
+ mContext = context;
+ mDevice = device;
+ mBuildHelper = new CompatibilityBuildHelper(buildInfo);
+ mCleanup = new CleanupPaths(mDevice);
+ }
+
+ public void close() throws DeviceNotAvailableException { mCleanup.cleanup(); }
+
+ void pushExtendedPublicSystemOemLibs(ZipFile libApk) throws Exception {
+ pushNativeTestLib(libApk, "/system/${LIB}/libfoo.oem1.so");
+ pushNativeTestLib(libApk, "/system/${LIB}/libbar.oem1.so");
+ pushString("libfoo.oem1.so\n"
+ + "libbar.oem1.so\n",
+ "/system/etc/public.libraries-oem1.txt");
+
+ pushNativeTestLib(libApk, "/system/${LIB}/libfoo.oem2.so");
+ pushNativeTestLib(libApk, "/system/${LIB}/libbar.oem2.so");
+ pushString("libfoo.oem2.so\n"
+ + "libbar.oem2.so\n",
+ "/system/etc/public.libraries-oem2.txt");
+ }
+
+ void pushExtendedPublicProductLibs(ZipFile libApk) throws Exception {
+ pushNativeTestLib(libApk, "/product/${LIB}/libfoo.product1.so");
+ pushNativeTestLib(libApk, "/product/${LIB}/libbar.product1.so");
+ pushString("libfoo.product1.so\n"
+ + "libbar.product1.so\n",
+ "/product/etc/public.libraries-product1.txt");
+ }
+
+ void pushPrivateLibs(ZipFile libApk) throws Exception {
+ // Push the libraries once for each test. Since we cannot unload them, we need a fresh
+ // never-before-loaded library in each loadLibrary call.
+ for (int i = 1; i <= 3; ++i) {
+ pushNativeTestLib(libApk, "/system/${LIB}/libsystem_private" + i + ".so");
+ pushNativeTestLib(libApk, "/system_ext/${LIB}/libsystemext_private" + i + ".so");
+ pushNativeTestLib(libApk, "/product/${LIB}/libproduct_private" + i + ".so");
+ pushNativeTestLib(libApk, "/vendor/${LIB}/libvendor_private" + i + ".so");
+ }
+ }
+
+ void pushSystemSharedLib(String packageDir, String packageName, String buildJarName)
+ throws Exception {
+ String path = packageDir + "/" + packageName + ".jar";
+ pushFile(buildJarName, path);
+ pushString("<permissions>\n"
+ + "<library name=\"" + packageName + "\" file=\"" + path + "\" />\n"
+ + "</permissions>\n",
+ "system/etc/permissions/" + packageName + ".xml");
+ }
+
+ void softReboot() throws DeviceNotAvailableException {
+ assertCommandSucceeds("setprop dev.bootcomplete 0");
+ assertCommandSucceeds("stop");
+ assertCommandSucceeds("start");
+ mDevice.waitForDeviceAvailable();
+ }
+
+ String getTestArch() throws DeviceNotAvailableException {
+ if (mTestArch == null) {
+ IAbi abi = mContext.getConfigurationDescriptor().getAbi();
+ mTestArch = abi != null ? abi.getName()
+ : assertCommandSucceeds("getprop ro.bionic.arch");
+ }
+ return mTestArch;
+ }
+
+ // Pushes the given file contents to the device at the given destination path. destPath is
+ // assumed to have no risk of overlapping with existing files, and is deleted in tearDown(),
+ // along with any directory levels that had to be created.
+ void pushString(String fileContents, String destPath) throws DeviceNotAvailableException {
+ mCleanup.addPath(destPath);
+ assertThat(mDevice.pushString(fileContents, destPath)).isTrue();
+ }
+
+ // Like pushString, but pushes a data file included in the host test.
+ void pushFile(String fileName, String destPath) throws Exception {
+ mCleanup.addPath(destPath);
+ assertThat(mDevice.pushFile(mBuildHelper.getTestFile(fileName), destPath)).isTrue();
+ }
+
+ void pushApk(String apkBaseName, String destPath) throws Exception {
+ pushFile(apkBaseName + ".apk",
+ destPath + "/" + apkBaseName + "/" + apkBaseName + ".apk");
+ }
+
+ // Like pushString, but extracts libnativeloader_testlib.so from the library_container_app
+ // APK and pushes it to destPath. "${LIB}" is replaced with "lib" or "lib64" as appropriate.
+ void pushNativeTestLib(ZipFile libApk, String destPath) throws Exception {
+ String libApkPath = "lib/" + getTestArch() + "/libnativeloader_testlib.so";
+ ZipEntry entry = libApk.getEntry(libApkPath);
+ assertWithMessage("Failed to find " + libApkPath + " in library_container_app.apk")
+ .that(entry)
+ .isNotNull();
+
+ File libraryTempFile;
+ try (InputStream inStream = libApk.getInputStream(entry)) {
+ libraryTempFile = writeStreamToTempFile("libnativeloader_testlib.so", inStream);
+ }
+
+ String libDir = getTestArch().contains("64") ? "lib64" : "lib";
+ destPath = destPath.replace("${LIB}", libDir);
+
+ mCleanup.addPath(destPath);
+ assertThat(mDevice.pushFile(libraryTempFile, destPath)).isTrue();
+ }
+
+ void installPackage(String apkBaseName) throws Exception {
+ assertThat(mDevice.installPackage(mBuildHelper.getTestFile(apkBaseName + ".apk"),
+ false /* reinstall */))
+ .isNull();
+ }
+
+ String assertCommandSucceeds(String command) throws DeviceNotAvailableException {
+ CommandResult result = mDevice.executeShellV2Command(command);
+ assertWithMessage(result.toString()).that(result.getExitCode()).isEqualTo(0);
+ // Remove trailing \n's.
+ return result.getStdout().trim();
+ }
+ }
+
+ static private File writeStreamToTempFile(String tempFileBaseName, InputStream inStream)
+ throws Exception {
+ File hostTempFile = File.createTempFile(tempFileBaseName, null);
+ try (FileOutputStream outStream = new FileOutputStream(hostTempFile)) {
+ ByteStreams.copy(inStream, outStream);
+ }
+ return hostTempFile;
+ }
+}
diff --git a/libnativeloader/test/src/android/test/lib/TestUtils.java b/libnativeloader/test/src/android/test/lib/TestUtils.java
new file mode 100644
index 0000000000..eda9993a7f
--- /dev/null
+++ b/libnativeloader/test/src/android/test/lib/TestUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.function.ThrowingRunnable;
+
+public final class TestUtils {
+ public static void assertLibraryNotFound(ThrowingRunnable loadLibrary) {
+ Throwable t = assertThrows(UnsatisfiedLinkError.class, loadLibrary);
+ assertThat(t.getMessage()).containsMatch("dlopen failed: library .* not found");
+ }
+
+ public static void assertLinkerNamespaceError(ThrowingRunnable loadLibrary) {
+ Throwable t = assertThrows(UnsatisfiedLinkError.class, loadLibrary);
+ assertThat(t.getMessage())
+ .containsMatch("dlopen failed: .* is not accessible for the namespace");
+ }
+}
diff --git a/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
new file mode 100644
index 0000000000..1240e12e55
--- /dev/null
+++ b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.test.systemextsharedlib;
+
+public final class SystemExtSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/src/android/test/systemsharedlib/SystemSharedLib.java
index b166928f0e..8e2af9f79c 100644
--- a/libnativeloader/test/test.cpp
+++ b/libnativeloader/test/src/android/test/systemsharedlib/SystemSharedLib.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "oemlib"
-#include <android-base/logging.h>
-static __attribute__((constructor)) void test_lib_init() {
- LOG(DEBUG) << LIBNAME << " loaded";
+package android.test.systemsharedlib;
+
+public final class SystemSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
}
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index 8fee83a38a..55a1fdea2c 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -47,7 +47,6 @@ cc_defaults {
],
static_libs: [
"libc++fs",
- "libtinyxml2",
],
tidy: true,
tidy_disabled_srcs: [":art-apex-cache-info"],
diff --git a/odrefresh/odr_metrics.cc b/odrefresh/odr_metrics.cc
index 4bddb17bd8..a2ce51f65b 100644
--- a/odrefresh/odr_metrics.cc
+++ b/odrefresh/odr_metrics.cc
@@ -64,16 +64,16 @@ OdrMetrics::~OdrMetrics() {
}
}
-void OdrMetrics::SetCompilationTime(int32_t seconds) {
+void OdrMetrics::SetCompilationTime(int32_t millis) {
switch (stage_) {
case Stage::kPrimaryBootClasspath:
- primary_bcp_compilation_seconds_ = seconds;
+ primary_bcp_compilation_millis_ = millis;
break;
case Stage::kSecondaryBootClasspath:
- secondary_bcp_compilation_seconds_ = seconds;
+ secondary_bcp_compilation_millis_ = millis;
break;
case Stage::kSystemServerClasspath:
- system_server_compilation_seconds_ = seconds;
+ system_server_compilation_millis_ = millis;
break;
case Stage::kCheck:
case Stage::kComplete:
@@ -119,28 +119,31 @@ bool OdrMetrics::ToRecord(/*out*/OdrMetricsRecord* record) const {
if (!trigger_.has_value()) {
return false;
}
+ record->odrefresh_metrics_version = kOdrefreshMetricsVersion;
record->art_apex_version = art_apex_version_;
record->trigger = static_cast<uint32_t>(trigger_.value());
record->stage_reached = static_cast<uint32_t>(stage_);
record->status = static_cast<uint32_t>(status_);
- record->primary_bcp_compilation_seconds = primary_bcp_compilation_seconds_;
- record->secondary_bcp_compilation_seconds = secondary_bcp_compilation_seconds_;
- record->system_server_compilation_seconds = system_server_compilation_seconds_;
record->cache_space_free_start_mib = cache_space_free_start_mib_;
record->cache_space_free_end_mib = cache_space_free_end_mib_;
+ record->primary_bcp_compilation_millis = primary_bcp_compilation_millis_;
+ record->secondary_bcp_compilation_millis = secondary_bcp_compilation_millis_;
+ record->system_server_compilation_millis = system_server_compilation_millis_;
return true;
}
void OdrMetrics::WriteToFile(const std::string& path, const OdrMetrics* metrics) {
- OdrMetricsRecord record;
+ OdrMetricsRecord record{};
if (!metrics->ToRecord(&record)) {
LOG(ERROR) << "Attempting to report metrics without a compilation trigger.";
return;
}
- // Preserve order from frameworks/proto_logging/stats/atoms.proto in metrics file written.
- std::ofstream ofs(path);
- ofs << record;
+ const android::base::Result<void>& result = record.WriteToFile(path);
+ if (!result.ok()) {
+ LOG(ERROR) << "Failed to report metrics to file: " << path
+ << ", error: " << result.error().message();
+ }
}
} // namespace odrefresh
diff --git a/odrefresh/odr_metrics.h b/odrefresh/odr_metrics.h
index cd80beffc5..7ebc148684 100644
--- a/odrefresh/odr_metrics.h
+++ b/odrefresh/odr_metrics.h
@@ -126,7 +126,7 @@ class OdrMetrics final {
static int32_t GetFreeSpaceMiB(const std::string& path);
static void WriteToFile(const std::string& path, const OdrMetrics* metrics);
- void SetCompilationTime(int32_t seconds);
+ void SetCompilationTime(int32_t millis);
const std::string cache_directory_;
const std::string metrics_file_;
@@ -137,11 +137,11 @@ class OdrMetrics final {
Stage stage_ = Stage::kUnknown;
Status status_ = Status::kUnknown;
- int32_t primary_bcp_compilation_seconds_ = 0;
- int32_t secondary_bcp_compilation_seconds_ = 0;
- int32_t system_server_compilation_seconds_ = 0;
int32_t cache_space_free_start_mib_ = 0;
int32_t cache_space_free_end_mib_ = 0;
+ int32_t primary_bcp_compilation_millis_ = 0;
+ int32_t secondary_bcp_compilation_millis_ = 0;
+ int32_t system_server_compilation_millis_ = 0;
friend class ScopedOdrCompilationTimer;
};
@@ -155,8 +155,8 @@ class ScopedOdrCompilationTimer final {
~ScopedOdrCompilationTimer() {
auto elapsed_time = std::chrono::steady_clock::now() - start_;
- auto elapsed_seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed_time);
- metrics_.SetCompilationTime(static_cast<int32_t>(elapsed_seconds.count()));
+ auto elapsed_millis = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
+ metrics_.SetCompilationTime(static_cast<int32_t>(elapsed_millis.count()));
}
private:
diff --git a/odrefresh/odr_metrics_record.cc b/odrefresh/odr_metrics_record.cc
index fc135d3399..b8c2f80fea 100644
--- a/odrefresh/odr_metrics_record.cc
+++ b/odrefresh/odr_metrics_record.cc
@@ -14,59 +14,110 @@
* limitations under the License.
*/
+#include "android-base/logging.h"
#include "odr_metrics_record.h"
+#include "tinyxml2.h"
#include <iosfwd>
-#include <istream>
-#include <ostream>
-#include <streambuf>
#include <string>
namespace art {
namespace odrefresh {
-std::istream& operator>>(std::istream& is, OdrMetricsRecord& record) {
- // Block I/O related exceptions
- auto saved_exceptions = is.exceptions();
- is.exceptions(std::ios_base::iostate {});
+namespace {
+android::base::Result<int64_t> ReadInt64(tinyxml2::XMLElement* parent, const char* name) {
+ tinyxml2::XMLElement* element = parent->FirstChildElement(name);
+ if (element == nullptr) {
+ return Errorf("Expected Odrefresh metric {} not found", name);
+ }
- // The order here matches the field order of MetricsRecord.
- is >> record.art_apex_version >> std::ws;
- is >> record.trigger >> std::ws;
- is >> record.stage_reached >> std::ws;
- is >> record.status >> std::ws;
- is >> record.primary_bcp_compilation_seconds >> std::ws;
- is >> record.secondary_bcp_compilation_seconds >> std::ws;
- is >> record.system_server_compilation_seconds >> std::ws;
- is >> record.cache_space_free_start_mib >> std::ws;
- is >> record.cache_space_free_end_mib >> std::ws;
-
- // Restore I/O related exceptions
- is.exceptions(saved_exceptions);
- return is;
+ int64_t metric;
+ tinyxml2::XMLError result = element->QueryInt64Text(&metric);
+ if (result == tinyxml2::XML_SUCCESS) {
+ return metric;
+ } else {
+ return Errorf("Odrefresh metric {} is not an int64", name);
+ }
+}
+
+android::base::Result<int32_t> ReadInt32(tinyxml2::XMLElement* parent, const char* name) {
+ tinyxml2::XMLElement* element = parent->FirstChildElement(name);
+ if (element == nullptr) {
+ return Errorf("Expected Odrefresh metric {} not found", name);
+ }
+
+ int32_t metric;
+ tinyxml2::XMLError result = element->QueryIntText(&metric);
+ if (result == tinyxml2::XML_SUCCESS) {
+ return metric;
+ } else {
+ return Errorf("Odrefresh metric {} is not an int32", name);
+ }
+}
+
+template <typename T>
+void AddMetric(tinyxml2::XMLElement* parent, const char* name, const T& value) {
+ parent->InsertNewChildElement(name)->SetText(value);
}
+} // namespace
-std::ostream& operator<<(std::ostream& os, const OdrMetricsRecord& record) {
- static const char kSpace = ' ';
+android::base::Result<void> OdrMetricsRecord::ReadFromFile(const std::string& filename) {
+ tinyxml2::XMLDocument xml_document;
+ tinyxml2::XMLError result = xml_document.LoadFile(filename.data());
+ if (result != tinyxml2::XML_SUCCESS) {
+ return android::base::Error() << xml_document.ErrorStr();
+ }
- // Block I/O related exceptions
- auto saved_exceptions = os.exceptions();
- os.exceptions(std::ios_base::iostate {});
+ tinyxml2::XMLElement* metrics = xml_document.FirstChildElement("odrefresh_metrics");
+ if (metrics == nullptr) {
+ return Errorf("odrefresh_metrics element not found in {}", filename);
+ }
+
+ odrefresh_metrics_version = OR_RETURN(ReadInt32(metrics, "odrefresh_metrics_version"));
+ if (odrefresh_metrics_version != kOdrefreshMetricsVersion) {
+ return Errorf("odrefresh_metrics_version {} is different than expected ({})",
+ odrefresh_metrics_version,
+ kOdrefreshMetricsVersion);
+ }
+
+ art_apex_version = OR_RETURN(ReadInt64(metrics, "art_apex_version"));
+ trigger = OR_RETURN(ReadInt32(metrics, "trigger"));
+ stage_reached = OR_RETURN(ReadInt32(metrics, "stage_reached"));
+ status = OR_RETURN(ReadInt32(metrics, "status"));
+ cache_space_free_start_mib = OR_RETURN(ReadInt32(metrics, "cache_space_free_start_mib"));
+ cache_space_free_end_mib = OR_RETURN(ReadInt32(metrics, "cache_space_free_end_mib"));
+ primary_bcp_compilation_millis = OR_RETURN(
+ ReadInt32(metrics, "primary_bcp_compilation_millis"));
+ secondary_bcp_compilation_millis = OR_RETURN(
+ ReadInt32(metrics, "secondary_bcp_compilation_millis"));
+ system_server_compilation_millis = OR_RETURN(
+ ReadInt32(metrics, "system_server_compilation_millis"));
+ return {};
+}
+
+android::base::Result<void> OdrMetricsRecord::WriteToFile(const std::string& filename) const {
+ tinyxml2::XMLDocument xml_document;
+ tinyxml2::XMLElement* metrics = xml_document.NewElement("odrefresh_metrics");
+ xml_document.InsertEndChild(metrics);
// The order here matches the field order of MetricsRecord.
- os << record.art_apex_version << kSpace;
- os << record.trigger << kSpace;
- os << record.stage_reached << kSpace;
- os << record.status << kSpace;
- os << record.primary_bcp_compilation_seconds << kSpace;
- os << record.secondary_bcp_compilation_seconds << kSpace;
- os << record.system_server_compilation_seconds << kSpace;
- os << record.cache_space_free_start_mib << kSpace;
- os << record.cache_space_free_end_mib << std::endl;
-
- // Restore I/O related exceptions
- os.exceptions(saved_exceptions);
- return os;
+ AddMetric(metrics, "odrefresh_metrics_version", odrefresh_metrics_version);
+ AddMetric(metrics, "art_apex_version", art_apex_version);
+ AddMetric(metrics, "trigger", trigger);
+ AddMetric(metrics, "stage_reached", stage_reached);
+ AddMetric(metrics, "status", status);
+ AddMetric(metrics, "cache_space_free_start_mib", cache_space_free_start_mib);
+ AddMetric(metrics, "cache_space_free_end_mib", cache_space_free_end_mib);
+ AddMetric(metrics, "primary_bcp_compilation_millis", primary_bcp_compilation_millis);
+ AddMetric(metrics, "secondary_bcp_compilation_millis", secondary_bcp_compilation_millis);
+ AddMetric(metrics, "system_server_compilation_millis", system_server_compilation_millis);
+
+ tinyxml2::XMLError result = xml_document.SaveFile(filename.data(), /*compact=*/true);
+ if (result == tinyxml2::XML_SUCCESS) {
+ return {};
+ } else {
+ return android::base::Error() << xml_document.ErrorStr();
+ }
}
} // namespace odrefresh
diff --git a/odrefresh/odr_metrics_record.h b/odrefresh/odr_metrics_record.h
index 9dd51a63cc..fa953374e4 100644
--- a/odrefresh/odr_metrics_record.h
+++ b/odrefresh/odr_metrics_record.h
@@ -20,42 +20,41 @@
#include <cstdint>
#include <iosfwd> // For forward-declaration of std::string.
+#include "android-base/result.h"
+#include "tinyxml2.h"
+
namespace art {
namespace odrefresh {
// Default location for storing metrics from odrefresh.
-constexpr const char* kOdrefreshMetricsFile = "/data/misc/odrefresh/odrefresh-metrics.txt";
+constexpr const char* kOdrefreshMetricsFile = "/data/misc/odrefresh/odrefresh-metrics.xml";
+
+// Initial OdrefreshMetrics version
+static constexpr int32_t kOdrefreshMetricsVersion = 2;
// MetricsRecord is a simpler container for Odrefresh metric values reported to statsd. The order
// and types of fields here mirror definition of `OdrefreshReported` in
// frameworks/proto_logging/stats/atoms.proto.
struct OdrMetricsRecord {
+ int32_t odrefresh_metrics_version;
int64_t art_apex_version;
int32_t trigger;
int32_t stage_reached;
int32_t status;
- int32_t primary_bcp_compilation_seconds;
- int32_t secondary_bcp_compilation_seconds;
- int32_t system_server_compilation_seconds;
int32_t cache_space_free_start_mib;
int32_t cache_space_free_end_mib;
-};
+ int32_t primary_bcp_compilation_millis;
+ int32_t secondary_bcp_compilation_millis;
+ int32_t system_server_compilation_millis;
-// Read a `MetricsRecord` from an `istream`.
-//
-// This method blocks istream related exceptions, the caller should check `is.fail()` is false after
-// calling.
-//
-// Returns `is`.
-std::istream& operator>>(std::istream& is, OdrMetricsRecord& record);
-
-// Write a `MetricsRecord` to an `ostream`.
-//
-// This method blocks ostream related exceptions, the caller should check `os.fail()` is false after
-// calling.
-//
-// Returns `os`
-std::ostream& operator<<(std::ostream& os, const OdrMetricsRecord& record);
+ // Reads a `MetricsRecord` from an XML file.
+ // Returns an error if the XML document was not found or parsed correctly.
+ android::base::Result<void> ReadFromFile(const std::string& filename);
+
+ // Writes a `MetricsRecord` to an XML file.
+ // Returns an error if the XML document was not saved correctly.
+ android::base::Result<void> WriteToFile(const std::string& filename) const;
+};
} // namespace odrefresh
} // namespace art
diff --git a/odrefresh/odr_metrics_record_test.cc b/odrefresh/odr_metrics_record_test.cc
index dd739d68f2..fbb5b99063 100644
--- a/odrefresh/odr_metrics_record_test.cc
+++ b/odrefresh/odr_metrics_record_test.cc
@@ -20,6 +20,8 @@
#include <fstream>
+#include "android-base/result-gmock.h"
+#include "android-base/stringprintf.h"
#include "base/common_art_test.h"
namespace art {
@@ -27,86 +29,124 @@ namespace odrefresh {
class OdrMetricsRecordTest : public CommonArtTest {};
+using android::base::testing::Ok;
+using android::base::testing::HasError;
+using android::base::testing::WithMessage;
+
TEST_F(OdrMetricsRecordTest, HappyPath) {
- const OdrMetricsRecord expected {
+ const OdrMetricsRecord expected{
+ .odrefresh_metrics_version = art::odrefresh::kOdrefreshMetricsVersion,
.art_apex_version = 0x01233456'789abcde,
.trigger = 0x01020304,
.stage_reached = 0x11121314,
.status = 0x21222324,
- .primary_bcp_compilation_seconds = 0x31323334,
- .secondary_bcp_compilation_seconds = 0x41424344,
- .system_server_compilation_seconds = 0x51525354,
.cache_space_free_start_mib = 0x61626364,
- .cache_space_free_end_mib = 0x71727374
+ .cache_space_free_end_mib = 0x71727374,
+ .primary_bcp_compilation_millis = 0x31323334,
+ .secondary_bcp_compilation_millis = 0x41424344,
+ .system_server_compilation_millis = 0x51525354
};
ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
-
- {
- std::ofstream ofs(file_path);
- ofs << expected;
- ASSERT_FALSE(ofs.fail());
- ofs.close();
- }
+ std::string file_path = dir.GetPath() + "/metrics-record.xml";
+ ASSERT_THAT(expected.WriteToFile(file_path), Ok());
OdrMetricsRecord actual {};
- {
- std::ifstream ifs(file_path);
- ifs >> actual;
- ASSERT_TRUE(ifs.eof());
- }
+ ASSERT_THAT(actual.ReadFromFile(file_path), Ok());
+ ASSERT_EQ(expected.odrefresh_metrics_version, actual.odrefresh_metrics_version);
ASSERT_EQ(expected.art_apex_version, actual.art_apex_version);
ASSERT_EQ(expected.trigger, actual.trigger);
ASSERT_EQ(expected.stage_reached, actual.stage_reached);
ASSERT_EQ(expected.status, actual.status);
- ASSERT_EQ(expected.primary_bcp_compilation_seconds, actual.primary_bcp_compilation_seconds);
- ASSERT_EQ(expected.secondary_bcp_compilation_seconds, actual.secondary_bcp_compilation_seconds);
- ASSERT_EQ(expected.system_server_compilation_seconds, actual.system_server_compilation_seconds);
ASSERT_EQ(expected.cache_space_free_start_mib, actual.cache_space_free_start_mib);
ASSERT_EQ(expected.cache_space_free_end_mib, actual.cache_space_free_end_mib);
+ ASSERT_EQ(expected.primary_bcp_compilation_millis, actual.primary_bcp_compilation_millis);
+ ASSERT_EQ(expected.secondary_bcp_compilation_millis, actual.secondary_bcp_compilation_millis);
+ ASSERT_EQ(expected.system_server_compilation_millis, actual.system_server_compilation_millis);
ASSERT_EQ(0, memcmp(&expected, &actual, sizeof(expected)));
}
TEST_F(OdrMetricsRecordTest, EmptyInput) {
ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
-
- std::ifstream ifs(file_path);
- OdrMetricsRecord record;
- ifs >> record;
+ std::string file_path = dir.GetPath() + "/metrics-record.xml";
- ASSERT_TRUE(ifs.fail());
- ASSERT_TRUE(!ifs);
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path), testing::Not(Ok()));
}
-TEST_F(OdrMetricsRecordTest, ClosedInput) {
+TEST_F(OdrMetricsRecordTest, UnexpectedInput) {
ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
+ std::string file_path = dir.GetPath() + "/metrics-record.xml";
- std::ifstream ifs(file_path);
- ifs.close();
+ std::ofstream ofs(file_path);
+ ofs << "<not_odrefresh_metrics></not_odrefresh_metrics>";
+ ofs.close();
- OdrMetricsRecord record;
- ifs >> record;
+ OdrMetricsRecord record{};
+ ASSERT_THAT(
+ record.ReadFromFile(file_path),
+ HasError(WithMessage("odrefresh_metrics element not found in " + file_path)));
+}
+
+TEST_F(OdrMetricsRecordTest, ExpectedElementNotFound) {
+ ScratchDir dir(/*keep_files=*/false);
+ std::string file_path = dir.GetPath() + "/metrics-record.xml";
- ASSERT_TRUE(ifs.fail());
- ASSERT_TRUE(!ifs);
+ std::ofstream ofs(file_path);
+ ofs << "<odrefresh_metrics>";
+ ofs << "<not_valid_metric>25</not_valid_metric>";
+ ofs << "</odrefresh_metrics>";
+ ofs.close();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(
+ record.ReadFromFile(file_path),
+ HasError(WithMessage("Expected Odrefresh metric odrefresh_metrics_version not found")));
}
-TEST_F(OdrMetricsRecordTest, ClosedOutput) {
+TEST_F(OdrMetricsRecordTest, UnexpectedOdrefreshMetricsVersion) {
ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
+ std::string file_path = dir.GetPath() + "/metrics-record.xml";
std::ofstream ofs(file_path);
+ ofs << "<odrefresh_metrics>";
+ ofs << "<odrefresh_metrics_version>0</odrefresh_metrics_version>";
+ ofs << "</odrefresh_metrics>";
ofs.close();
- OdrMetricsRecord record {};
- ofs << record;
+ OdrMetricsRecord record{};
+ std::string expected_error = android::base::StringPrintf(
+ "odrefresh_metrics_version 0 is different than expected (%d)",
+ kOdrefreshMetricsVersion);
+ ASSERT_THAT(record.ReadFromFile(file_path),
+ HasError(WithMessage(expected_error)));
+}
+
+TEST_F(OdrMetricsRecordTest, UnexpectedType) {
+ ScratchDir dir(/*keep_files=*/false);
+ std::string file_path = dir.GetPath() + "/metrics-record.xml";
+
+ std::ofstream ofs(file_path);
+ ofs << "<odrefresh_metrics>";
+ ofs << "<odrefresh_metrics_version>" << kOdrefreshMetricsVersion
+ << "</odrefresh_metrics_version>";
+ ofs << "<art_apex_version>81966764218039518</art_apex_version>";
+ ofs << "<trigger>16909060</trigger>";
+ ofs << "<stage_reached>286397204</stage_reached>";
+ ofs << "<status>abcd</status>"; // It should be an int32.
+ ofs << "<cache_space_free_start_mib>1633837924</cache_space_free_start_mib>";
+ ofs << "<cache_space_free_end_mib>1903326068</cache_space_free_end_mib>";
+ ofs << "<primary_bcp_compilation_millis>825373492</primary_bcp_compilation_millis>";
+ ofs << "<secondary_bcp_compilation_millis>1094861636</secondary_bcp_compilation_millis>";
+ ofs << "<system_server_compilation_millis>1364349780</system_server_compilation_millis>";
+ ofs << "</odrefresh_metrics>";
+ ofs.close();
- ASSERT_TRUE(ofs.fail());
- ASSERT_TRUE(!ofs.good());
+ OdrMetricsRecord record{};
+ ASSERT_THAT(
+ record.ReadFromFile(file_path),
+ HasError(WithMessage("Odrefresh metric status is not an int32")));
}
} // namespace odrefresh
diff --git a/odrefresh/odr_metrics_test.cc b/odrefresh/odr_metrics_test.cc
index 4519f00363..f222caaf2e 100644
--- a/odrefresh/odr_metrics_test.cc
+++ b/odrefresh/odr_metrics_test.cc
@@ -24,6 +24,7 @@
#include <memory>
#include <string>
+#include "android-base/result-gmock.h"
#include "base/common_art_test.h"
namespace art {
@@ -35,7 +36,7 @@ class OdrMetricsTest : public CommonArtTest {
CommonArtTest::SetUp();
scratch_dir_ = std::make_unique<ScratchDir>();
- metrics_file_path_ = scratch_dir_->GetPath() + "/metrics.txt";
+ metrics_file_path_ = scratch_dir_->GetPath() + "/metrics.xml";
cache_directory_ = scratch_dir_->GetPath() + "/dir";
mkdir(cache_directory_.c_str(), S_IRWXU);
}
@@ -158,10 +159,10 @@ TEST_F(OdrMetricsTest, TimeValuesAreRecorded) {
EXPECT_TRUE(metrics.ToRecord(&record));
EXPECT_EQ(OdrMetrics::Stage::kPrimaryBootClasspath,
enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_NE(0, record.primary_bcp_compilation_seconds);
- EXPECT_GT(10, record.primary_bcp_compilation_seconds);
- EXPECT_EQ(0, record.secondary_bcp_compilation_seconds);
- EXPECT_EQ(0, record.system_server_compilation_seconds);
+ EXPECT_NE(0, record.primary_bcp_compilation_millis);
+ EXPECT_GT(10'000, record.primary_bcp_compilation_millis);
+ EXPECT_EQ(0, record.secondary_bcp_compilation_millis);
+ EXPECT_EQ(0, record.system_server_compilation_millis);
// Secondary boot classpath compilation time.
{
@@ -172,10 +173,10 @@ TEST_F(OdrMetricsTest, TimeValuesAreRecorded) {
EXPECT_TRUE(metrics.ToRecord(&record));
EXPECT_EQ(OdrMetrics::Stage::kSecondaryBootClasspath,
enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_NE(0, record.primary_bcp_compilation_seconds);
- EXPECT_NE(0, record.secondary_bcp_compilation_seconds);
- EXPECT_GT(10, record.secondary_bcp_compilation_seconds);
- EXPECT_EQ(0, record.system_server_compilation_seconds);
+ EXPECT_NE(0, record.primary_bcp_compilation_millis);
+ EXPECT_NE(0, record.secondary_bcp_compilation_millis);
+ EXPECT_GT(10'000, record.secondary_bcp_compilation_millis);
+ EXPECT_EQ(0, record.system_server_compilation_millis);
// system_server classpath compilation time.
{
@@ -186,10 +187,10 @@ TEST_F(OdrMetricsTest, TimeValuesAreRecorded) {
EXPECT_TRUE(metrics.ToRecord(&record));
EXPECT_EQ(OdrMetrics::Stage::kSystemServerClasspath,
enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_NE(0, record.primary_bcp_compilation_seconds);
- EXPECT_NE(0, record.secondary_bcp_compilation_seconds);
- EXPECT_NE(0, record.system_server_compilation_seconds);
- EXPECT_GT(10, record.system_server_compilation_seconds);
+ EXPECT_NE(0, record.primary_bcp_compilation_millis);
+ EXPECT_NE(0, record.secondary_bcp_compilation_millis);
+ EXPECT_NE(0, record.system_server_compilation_millis);
+ EXPECT_GT(10'000, record.system_server_compilation_millis);
}
TEST_F(OdrMetricsTest, CacheSpaceValuesAreUpdated) {
@@ -207,11 +208,8 @@ TEST_F(OdrMetricsTest, CacheSpaceValuesAreUpdated) {
EXPECT_EQ(0, snap.cache_space_free_end_mib);
}
- OdrMetricsRecord on_disk;
- std::ifstream ifs(GetMetricsFilePath());
- EXPECT_TRUE(ifs);
- ifs >> on_disk;
- EXPECT_TRUE(ifs);
+ OdrMetricsRecord on_disk{};
+ EXPECT_THAT(on_disk.ReadFromFile(GetMetricsFilePath()), android::base::testing::Ok());
EXPECT_EQ(snap.cache_space_free_start_mib, on_disk.cache_space_free_start_mib);
EXPECT_NE(0, on_disk.cache_space_free_end_mib);
}
diff --git a/odrefresh/odr_statslog_android.cc b/odrefresh/odr_statslog_android.cc
index 7db348e633..ee173d4b3d 100644
--- a/odrefresh/odr_statslog_android.cc
+++ b/odrefresh/odr_statslog_android.cc
@@ -103,16 +103,11 @@ int32_t TranslateTrigger(int32_t art_metrics_trigger) {
bool ReadValues(const char* metrics_file,
/*out*/ OdrMetricsRecord* record,
/*out*/ std::string* error_msg) {
- std::ifstream ifs(metrics_file);
- if (!ifs) {
- *error_msg = android::base::StringPrintf(
- "metrics file '%s' could not be opened: %s", metrics_file, strerror(errno));
- return false;
- }
-
- ifs >> *record;
- if (!ifs) {
- *error_msg = "file parsing error.";
+ const android::base::Result<void>& result = record->ReadFromFile(metrics_file);
+ if (!result.ok()) {
+ *error_msg = android::base::StringPrintf("Unable to open or parse metrics file %s (error: %s)",
+ metrics_file,
+ result.error().message().data());
return false;
}
@@ -151,16 +146,20 @@ bool UploadStatsIfAvailable(/*out*/std::string* error_msg) {
// Write values to statsd. The order of values passed is the same as the order of the
// fields in OdrMetricsRecord.
- int bytes_written = art::metrics::statsd::stats_write(metrics::statsd::ODREFRESH_REPORTED,
- record.art_apex_version,
- record.trigger,
- record.stage_reached,
- record.status,
- record.primary_bcp_compilation_seconds,
- record.secondary_bcp_compilation_seconds,
- record.system_server_compilation_seconds,
- record.cache_space_free_start_mib,
- record.cache_space_free_end_mib);
+ int bytes_written = art::metrics::statsd::stats_write(
+ metrics::statsd::ODREFRESH_REPORTED,
+ record.art_apex_version,
+ record.trigger,
+ record.stage_reached,
+ record.status,
+ record.primary_bcp_compilation_millis / 1000,
+ record.secondary_bcp_compilation_millis / 1000,
+ record.system_server_compilation_millis / 1000,
+ record.cache_space_free_start_mib,
+ record.cache_space_free_end_mib,
+ record.primary_bcp_compilation_millis,
+ record.secondary_bcp_compilation_millis,
+ record.system_server_compilation_millis);
if (bytes_written <= 0) {
*error_msg = android::base::StringPrintf("stats_write returned %d", bytes_written);
return false;
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index a15e6678f1..312a797b9d 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -460,15 +460,7 @@ void DeoptManager::AddDeoptimizationRequester() {
art::ScopedThreadStateChange stsc(self, art::ThreadState::kSuspended);
deoptimization_status_lock_.ExclusiveLock(self);
deopter_count_++;
- if (deopter_count_ == 1) {
- ScopedDeoptimizationContext sdc(self, this);
- art::instrumentation::Instrumentation* instrumentation =
- art::Runtime::Current()->GetInstrumentation();
- // Tell instrumentation we will be deopting single threads.
- instrumentation->EnableSingleThreadDeopt(kInstrumentationKey);
- } else {
- deoptimization_status_lock_.ExclusiveUnlock(self);
- }
+ deoptimization_status_lock_.ExclusiveUnlock(self);
}
void DeoptManager::DeoptimizeThread(art::Thread* target) {
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 9e0e78ebdb..98ddbb7210 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -415,7 +415,6 @@ libart_cc_defaults {
],
static_libs: [
"libstatslog_art",
- "libtinyxml2",
],
generated_sources: [
"apex-info-list-tinyxml",
diff --git a/runtime/app_info.cc b/runtime/app_info.cc
index c72951eebc..2dbbbf6a90 100644
--- a/runtime/app_info.cc
+++ b/runtime/app_info.cc
@@ -93,6 +93,12 @@ void AppInfo::RegisterOdexStatus(const std::string& code_path,
<< "\nodex_status=" << odex_status;
}
+bool AppInfo::HasRegisteredAppInfo() {
+ MutexLock mu(Thread::Current(), update_mutex_);
+
+ return package_name_.has_value();
+}
+
void AppInfo::GetPrimaryApkOptimizationStatus(
std::string* out_compiler_filter,
std::string* out_compilation_reason) {
@@ -110,6 +116,13 @@ void AppInfo::GetPrimaryApkOptimizationStatus(
*out_compilation_reason = kUnknownValue;
}
+AppInfo::CodeType AppInfo::GetRegisteredCodeType(const std::string& code_path) {
+ MutexLock mu(Thread::Current(), update_mutex_);
+
+ const auto it = registered_code_locations_.find(code_path);
+ return it != registered_code_locations_.end() ? it->second.code_type : CodeType::kUnknown;
+}
+
std::ostream& operator<<(std::ostream& os, AppInfo& rhs) {
MutexLock mu(Thread::Current(), rhs.update_mutex_);
diff --git a/runtime/app_info.h b/runtime/app_info.h
index 68f2c586da..43e2ef320b 100644
--- a/runtime/app_info.h
+++ b/runtime/app_info.h
@@ -77,6 +77,13 @@ class AppInfo {
void GetPrimaryApkOptimizationStatus(std::string* out_compiler_filter,
std::string* out_compilation_reason);
+ // Whether we've received a call to RegisterAppInfo.
+ bool HasRegisteredAppInfo();
+
+ // The registered code type for a given code path. Note that this will
+ // be kUnknown until an explicit registration for that path has been made.
+ CodeType GetRegisteredCodeType(const std::string& code_path);
+
private:
// Encapsulates optimization information about a particular code location.
struct CodeLocationInfo {
diff --git a/runtime/app_info_test.cc b/runtime/app_info_test.cc
index 4a365dec96..51dd42f6fb 100644
--- a/runtime/app_info_test.cc
+++ b/runtime/app_info_test.cc
@@ -24,12 +24,17 @@ namespace art {
TEST(AppInfoTest, RegisterAppInfo) {
AppInfo app_info;
+ EXPECT_FALSE(app_info.HasRegisteredAppInfo());
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kUnknown);
+
app_info.RegisterAppInfo(
"package_name",
std::vector<std::string>({"code_location"}),
"",
"",
AppInfo::CodeType::kPrimaryApk);
+ EXPECT_TRUE(app_info.HasRegisteredAppInfo());
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
std::string filter;
std::string reason;
@@ -48,11 +53,13 @@ TEST(AppInfoTest, RegisterAppInfoWithOdexStatus) {
"",
"",
AppInfo::CodeType::kPrimaryApk);
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
app_info.RegisterOdexStatus(
"code_location",
"filter",
"reason",
"odex_status");
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
std::string filter;
std::string reason;
@@ -69,17 +76,22 @@ TEST(AppInfoTest, RegisterAppInfoWithOdexStatusMultiplePrimary) {
"filter",
"reason",
"odex_status");
+ EXPECT_FALSE(app_info.HasRegisteredAppInfo());
app_info.RegisterOdexStatus(
"code_location2",
"filter2",
"reason2",
"odex_status");
+ EXPECT_FALSE(app_info.HasRegisteredAppInfo());
app_info.RegisterAppInfo(
"package_name",
std::vector<std::string>({"code_location"}),
"",
"",
AppInfo::CodeType::kPrimaryApk);
+ EXPECT_TRUE(app_info.HasRegisteredAppInfo());
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location2"), AppInfo::CodeType::kUnknown);
std::string filter;
std::string reason;
@@ -110,7 +122,7 @@ TEST(AppInfoTest, RegisterAppInfoWithOdexStatusNoPrimary) {
"filter",
"reason",
"odex_status");
-
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kSplitApk);
// The optimization status is unknown since we don't have primary apks.
app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index ad082aed1f..93400d9c7c 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -171,6 +171,18 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
has_sve));
}
+Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::IntersectWithHwcap() const {
+ Arm64FeaturesUniquePtr hwcaps = Arm64InstructionSetFeatures::FromHwcap();
+ return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(
+ fix_cortex_a53_835769_,
+ fix_cortex_a53_843419_,
+ has_crc_ && hwcaps->has_crc_,
+ has_lse_ && hwcaps->has_lse_,
+ has_fp16_ && hwcaps->has_fp16_,
+ has_dotprod_ && hwcaps->has_dotprod_,
+ has_sve_ && hwcaps->has_sve_));
+}
+
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
bool is_a53 = (bitmap & kA53Bitfield) != 0;
bool has_crc = (bitmap & kCRCBitField) != 0;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index eb98c01633..8f0013ac86 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -53,6 +53,10 @@ class Arm64InstructionSetFeatures final : public InstructionSetFeatures {
// Use external cpu_features library.
static Arm64FeaturesUniquePtr FromCpuFeatures();
+ // Return a new set of instruction set features, intersecting `this` features
+ // with hardware capabilities.
+ Arm64FeaturesUniquePtr IntersectWithHwcap() const;
+
bool Equals(const InstructionSetFeatures* other) const override;
// Note that newer CPUs do not have a53 erratum 835769 and 843419,
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index ec1e340245..7a1e6b05ad 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -53,6 +53,33 @@ std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromVarian
UNREACHABLE();
}
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromVariantAndHwcap(
+ InstructionSet isa, const std::string& variant, std::string* error_msg) {
+ auto variant_features = FromVariant(isa, variant, error_msg);
+ if (variant_features == nullptr) {
+ return nullptr;
+ }
+ // Pixel3a is wrongly reporting itself as cortex-a75, so validate the features
+ // with hwcaps.
+ // Note that when cross-compiling on device (using dex2oat32 for compiling
+ // arm64), the hwcaps will report that no feature is supported. This is
+ // currently our best approach to be safe/correct. Maybe using the
+ // cpu_features library could fix this issue.
+ if (isa == InstructionSet::kArm64) {
+ auto new_features = down_cast<const Arm64InstructionSetFeatures*>(variant_features.get())
+ ->IntersectWithHwcap();
+ if (!variant_features->Equals(new_features.get())) {
+ LOG(WARNING) << "Mismatch between instruction set variant of device ("
+ << *variant_features
+ << ") and features returned by the hardware (" << *new_features << ")";
+ }
+ return new_features;
+ } else {
+ // TODO: Implement this validation on all architectures.
+ return variant_features;
+ }
+}
+
std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromBitmap(InstructionSet isa,
uint32_t bitmap) {
std::unique_ptr<const InstructionSetFeatures> result;
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index b80d36f153..cee8c5d42f 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -39,6 +39,12 @@ class InstructionSetFeatures {
const std::string& variant,
std::string* error_msg);
+ // Process a CPU variant string for the given ISA and make sure the features advertised
+ // are supported by the hardware. This is needed for Pixel3a which wrongly
+ // reports itself as cortex-a75.
+ static std::unique_ptr<const InstructionSetFeatures> FromVariantAndHwcap(
+ InstructionSet isa, const std::string& variant, std::string* error_msg);
+
// Parse a bitmap for the given isa and create an InstructionSetFeatures.
static std::unique_ptr<const InstructionSetFeatures> FromBitmap(InstructionSet isa,
uint32_t bitmap);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c8dbc75e61..c233aecb64 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4144,10 +4144,11 @@ ObjPtr<mirror::Class> ClassLinker::CreateArrayClass(Thread* self,
class_loader)));
if (component_type == nullptr) {
DCHECK(self->IsExceptionPending());
- // We need to accept erroneous classes as component types.
+ // We need to accept erroneous classes as component types. Under AOT, we
+ // don't accept them as we cannot encode the erroneous class in an image.
const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get()));
- if (component_type == nullptr) {
+ if (component_type == nullptr || Runtime::Current()->IsAotCompiler()) {
DCHECK(self->IsExceptionPending());
return nullptr;
} else {
@@ -5088,11 +5089,19 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons
CHECK_EQ(prototype, method->GetInterfaceMethodIfProxy(image_pointer_size_));
}
-bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
+bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass,
+ bool can_init_statics,
bool can_init_parents) {
if (can_init_statics && can_init_parents) {
return true;
}
+ DCHECK(Runtime::Current()->IsAotCompiler());
+
+ // We currently don't support initializing at AOT time classes that need access
+ // checks.
+ if (klass->IsVerifiedNeedsAccessChecks()) {
+ return false;
+ }
if (!can_init_statics) {
// Check if there's a class initializer.
ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 0de62fef47..f3c61e3ef4 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -164,6 +164,9 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap,
gc_tracing_throughput_hist_ = metrics->YoungGcTracingThroughput();
gc_throughput_avg_ = metrics->YoungGcThroughputAvg();
gc_tracing_throughput_avg_ = metrics->YoungGcTracingThroughputAvg();
+ gc_scanned_bytes_ = metrics->YoungGcScannedBytes();
+ gc_freed_bytes_ = metrics->YoungGcFreedBytes();
+ gc_duration_ = metrics->YoungGcDuration();
} else {
gc_time_histogram_ = metrics->FullGcCollectionTime();
metrics_gc_count_ = metrics->FullGcCount();
@@ -171,6 +174,9 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap,
gc_tracing_throughput_hist_ = metrics->FullGcTracingThroughput();
gc_throughput_avg_ = metrics->FullGcThroughputAvg();
gc_tracing_throughput_avg_ = metrics->FullGcTracingThroughputAvg();
+ gc_scanned_bytes_ = metrics->FullGcScannedBytes();
+ gc_freed_bytes_ = metrics->FullGcFreedBytes();
+ gc_duration_ = metrics->FullGcDuration();
}
}
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 80b39824ec..4efe48c318 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -76,6 +76,9 @@ GarbageCollector::GarbageCollector(Heap* heap, const std::string& name)
gc_tracing_throughput_hist_(nullptr),
gc_throughput_avg_(nullptr),
gc_tracing_throughput_avg_(nullptr),
+ gc_scanned_bytes_(nullptr),
+ gc_freed_bytes_(nullptr),
+ gc_duration_(nullptr),
cumulative_timings_(name),
pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true),
is_transaction_active_(false),
@@ -189,15 +192,18 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
RegisterPause(duration_ns);
}
total_time_ns_ += duration_ns;
- uint64_t total_pause_time = 0;
+ uint64_t total_pause_time_ns = 0;
for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
MutexLock mu(self, pause_histogram_lock_);
pause_histogram_.AdjustAndAddValue(pause_time);
- total_pause_time += pause_time;
+ total_pause_time_ns += pause_time;
}
metrics::ArtMetrics* metrics = runtime->GetMetrics();
// Report STW pause time in microseconds.
- metrics->WorldStopTimeDuringGCAvg()->Add(total_pause_time / 1'000);
+ const uint64_t total_pause_time_us = total_pause_time_ns / 1'000;
+ metrics->WorldStopTimeDuringGCAvg()->Add(total_pause_time_us);
+ metrics->GcWorldStopTime()->Add(total_pause_time_us);
+ metrics->GcWorldStopCount()->AddOne();
// Report total collection time of all GCs put together.
metrics->TotalGcCollectionTime()->Add(NsToMs(duration_ns));
if (are_metrics_initialized_) {
@@ -216,6 +222,10 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
throughput = current_iteration->GetEstimatedThroughput() / MB;
gc_throughput_histogram_->Add(throughput);
gc_throughput_avg_->Add(throughput);
+
+ gc_scanned_bytes_->Add(current_iteration->GetScannedBytes());
+ gc_freed_bytes_->Add(current_iteration->GetFreedBytes());
+ gc_duration_->Add(NsToMs(current_iteration->GetDurationNs()));
}
is_transaction_active_ = false;
}
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index d439914621..d11aea36c9 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -166,6 +166,9 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark
metrics::MetricsBase<int64_t>* gc_tracing_throughput_hist_;
metrics::MetricsBase<uint64_t>* gc_throughput_avg_;
metrics::MetricsBase<uint64_t>* gc_tracing_throughput_avg_;
+ metrics::MetricsBase<uint64_t>* gc_scanned_bytes_;
+ metrics::MetricsBase<uint64_t>* gc_freed_bytes_;
+ metrics::MetricsBase<uint64_t>* gc_duration_;
uint64_t total_thread_cpu_time_ns_;
uint64_t total_time_ns_;
uint64_t total_freed_objects_;
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 25cac7efde..1edcdbdf91 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -350,11 +350,18 @@ void DlMallocSpace::CheckMoreCoreForPrecondition() {
}
#endif
+struct MspaceCbArgs {
+ size_t max_contiguous;
+ size_t used;
+};
+
static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) {
size_t chunk_size = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start);
+ MspaceCbArgs* mspace_cb_args = reinterpret_cast<MspaceCbArgs*>(arg);
+ mspace_cb_args->used += used_bytes;
if (used_bytes < chunk_size) {
size_t chunk_free_bytes = chunk_size - used_bytes;
- size_t& max_contiguous_allocation = *reinterpret_cast<size_t*>(arg);
+ size_t& max_contiguous_allocation = mspace_cb_args->max_contiguous;
max_contiguous_allocation = std::max(max_contiguous_allocation, chunk_free_bytes);
}
}
@@ -362,16 +369,17 @@ static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void*
bool DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os,
size_t failed_alloc_bytes) {
Thread* const self = Thread::Current();
- size_t max_contiguous_allocation = 0;
+ MspaceCbArgs mspace_cb_args = {0, 0};
// To allow the Walk/InspectAll() to exclusively-lock the mutator
// lock, temporarily release the shared access to the mutator
// lock here by transitioning to the suspended state.
Locks::mutator_lock_->AssertSharedHeld(self);
ScopedThreadSuspension sts(self, ThreadState::kSuspended);
- Walk(MSpaceChunkCallback, &max_contiguous_allocation);
- if (failed_alloc_bytes > max_contiguous_allocation) {
- os << "; failed due to fragmentation (largest possible contiguous allocation "
- << max_contiguous_allocation << " bytes)";
+ Walk(MSpaceChunkCallback, &mspace_cb_args);
+ if (failed_alloc_bytes > mspace_cb_args.max_contiguous) {
+ os << "; failed due to malloc_space fragmentation (largest possible contiguous allocation "
+ << mspace_cb_args.max_contiguous << " bytes, space in use " << mspace_cb_args.used
+ << " bytes, capacity = " << Capacity() << ")";
return true;
}
return false;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 6afd63e4a5..4deb089349 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1922,12 +1922,29 @@ bool ImageSpace::BootImageLayout::ReadHeader(const std::string& base_location,
return false;
}
- // Validate oat files. We do it here so that the boot image will be re-compiled in memory if it's
- // outdated.
- size_t component_count = (header.GetImageSpaceCount() == 1u) ? header.GetComponentCount() : 1u;
- for (size_t i = 0; i < header.GetImageSpaceCount(); i++) {
- if (!ValidateOatFile(base_location, base_filename, bcp_index + i, component_count, error_msg)) {
- return false;
+ bool validate_oat_files = true;
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr && runtime->GetHeap() != nullptr) {
+ for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
+ if (image_space->GetComponentCount() == header.GetImageSpaceCount() &&
+ image_space->GetImageHeader().GetImageChecksum() == header.GetImageChecksum()) {
+ // This image has been loaded by the runtime, meaning that the oat files were validated when
+ // the runtime loaded them, so we don't have to validate them again.
+ validate_oat_files = false;
+ break;
+ }
+ }
+ }
+
+ if (validate_oat_files) {
+ // Validate oat files. We do it here so that the boot image will be re-compiled in memory if
+ // it's outdated.
+ size_t component_count = (header.GetImageSpaceCount() == 1u) ? header.GetComponentCount() : 1u;
+ for (size_t i = 0; i < header.GetImageSpaceCount(); i++) {
+ if (!ValidateOatFile(
+ base_location, base_filename, bcp_index + i, component_count, error_msg)) {
+ return false;
+ }
}
}
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 1e328a31d2..6ec98ff4d8 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -372,7 +372,7 @@ void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* aot_c
REQUIRES_SHARED(Locks::mutator_lock_) {
// Use instrumentation entrypoints if instrumentation is installed.
if (UNLIKELY(EntryExitStubsInstalled()) && !IsProxyInit(method)) {
- if (!method->IsNative() && InterpretOnly()) {
+ if (!method->IsNative() && InterpretOnly(method)) {
UpdateEntryPoints(method, GetQuickToInterpreterBridge());
} else {
UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
@@ -380,7 +380,7 @@ void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* aot_c
return;
}
- if (UNLIKELY(IsForcedInterpretOnly())) {
+ if (UNLIKELY(IsForcedInterpretOnly() || IsDeoptimized(method))) {
UpdateEntryPoints(
method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
return;
@@ -896,11 +896,6 @@ void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desir
UpdateStubs();
}
-void Instrumentation::EnableSingleThreadDeopt(const char* key) {
- // Prepare for single thread deopt by installing instrumentation stubs.
- ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInstrumentationStubs);
-}
-
void Instrumentation::UpdateInstrumentationLevel(InstrumentationLevel requested_level) {
instrumentation_level_ = requested_level;
}
@@ -922,7 +917,9 @@ void Instrumentation::MaybeRestoreInstrumentationStack() {
Locks::mutator_lock_->AssertExclusiveHeld(self);
Runtime::Current()->GetThreadList()->ForEach([&](Thread* t) NO_THREAD_SAFETY_ANALYSIS {
no_remaining_deopts =
- no_remaining_deopts && !t->IsForceInterpreter() &&
+ no_remaining_deopts &&
+ !t->IsForceInterpreter() &&
+ !t->HasDebuggerShadowFrames() &&
std::all_of(t->GetInstrumentationStack()->cbegin(),
t->GetInstrumentationStack()->cend(),
[&](const auto& frame) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1058,7 +1055,7 @@ std::string Instrumentation::EntryPointString(const void* code) {
}
void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code) {
- if (!EntryExitStubsInstalled()) {
+ if (!AreExitStubsInstalled()) {
// Fast path: no instrumentation.
DCHECK(!IsDeoptimized(method));
UpdateEntryPoints(method, new_code);
@@ -1079,7 +1076,7 @@ void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* new_c
return;
}
- if (CodeNeedsEntryExitStub(new_code, method)) {
+ if (EntryExitStubsInstalled() && CodeNeedsEntryExitStub(new_code, method)) {
DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint() ||
class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()))
<< EntryPointString(method->GetEntryPointFromQuickCompiledCode())
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index b1631091ae..c811935e9d 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -524,13 +524,6 @@ class Instrumentation {
void InstallStubsForMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
- // Sets up instrumentation to allow single thread deoptimization using ForceInterpreterCount.
- void EnableSingleThreadDeopt(const char* key)
- REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
-
// Install instrumentation exit stub on every method of the stack of the given thread.
// This is used by:
// - the debugger to cause a deoptimization of the all frames in thread's stack (for
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 5e01aaa7fa..6d634ae120 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -568,7 +568,12 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread,
// Before allowing the jump, make sure no code is actively inspecting the method to avoid
// jumping from interpreter to OSR while e.g. single stepping. Note that we could selectively
// disable OSR when single stepping, but that's currently hard to know at this point.
- if (Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
+ if (Runtime::Current()->GetInstrumentation()->InterpreterStubsInstalled() ||
+ Runtime::Current()->GetInstrumentation()->IsDeoptimized(method) ||
+ thread->IsForceInterpreter() ||
+ method->GetDeclaringClass()->IsObsoleteObject() ||
+ Dbg::IsForcedInterpreterNeededForUpcall(thread, method) ||
+ Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
return false;
}
diff --git a/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc
index a44066e487..28ca997cec 100644
--- a/runtime/metrics/reporter.cc
+++ b/runtime/metrics/reporter.cc
@@ -126,10 +126,17 @@ void MetricsReporter::BackgroundThreadRun() {
// Configure the backends
if (config_.dump_to_logcat) {
- backends_.emplace_back(new LogBackend(LogSeverity::INFO));
+ backends_.emplace_back(new LogBackend(std::make_unique<TextFormatter>(), LogSeverity::INFO));
}
if (config_.dump_to_file.has_value()) {
- backends_.emplace_back(new FileBackend(config_.dump_to_file.value()));
+ std::unique_ptr<MetricsFormatter> formatter;
+ if (config_.metrics_format == "xml") {
+ formatter = std::make_unique<XmlFormatter>();
+ } else {
+ formatter = std::make_unique<TextFormatter>();
+ }
+
+ backends_.emplace_back(new FileBackend(std::move(formatter), config_.dump_to_file.value()));
}
if (config_.dump_to_statsd) {
auto backend = CreateStatsdBackend();
@@ -291,6 +298,7 @@ ReportingConfig ReportingConfig::FromFlags(bool is_system_server) {
.dump_to_logcat = gFlags.MetricsWriteToLogcat(),
.dump_to_file = gFlags.MetricsWriteToFile.GetValueOptional(),
.dump_to_statsd = gFlags.MetricsWriteToStatsd(),
+ .metrics_format = gFlags.MetricsFormat(),
.period_spec = period_spec,
.reporting_num_mods = reporting_num_mods,
.reporting_mods = reporting_mods,
diff --git a/runtime/metrics/reporter.h b/runtime/metrics/reporter.h
index daeaf1fa18..af9e0ca151 100644
--- a/runtime/metrics/reporter.h
+++ b/runtime/metrics/reporter.h
@@ -78,6 +78,9 @@ struct ReportingConfig {
// If set, provides a file name to enable metrics logging to a file.
std::optional<std::string> dump_to_file;
+ // Provides the desired output format for metrics written to a file.
+ std::string metrics_format;
+
// The reporting period configuration.
std::optional<ReportingPeriodSpec> period_spec;
diff --git a/runtime/metrics/statsd.cc b/runtime/metrics/statsd.cc
index f68d50730f..560e7dab32 100644
--- a/runtime/metrics/statsd.cc
+++ b/runtime/metrics/statsd.cc
@@ -106,6 +106,30 @@ constexpr std::optional<int32_t> EncodeDatumId(DatumId datum_id) {
case DatumId::kFullGcTracingThroughputAvg:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_TRACING_THROUGHPUT_AVG_MB_PER_SEC);
+ case DatumId::kGcWorldStopTime:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_TIME_US);
+ case DatumId::kGcWorldStopCount:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_COUNT);
+ case DatumId::kYoungGcScannedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_SCANNED_BYTES);
+ case DatumId::kYoungGcFreedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_FREED_BYTES);
+ case DatumId::kYoungGcDuration:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_DURATION_MS);
+ case DatumId::kFullGcScannedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_SCANNED_BYTES);
+ case DatumId::kFullGcFreedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_FREED_BYTES);
+ case DatumId::kFullGcDuration:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_DURATION_MS);
}
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 63778c7b0b..221cf67e8b 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1907,17 +1907,6 @@ OatFile* OatFile::Open(int zip_fd,
reservation,
error_msg);
if (with_dlopen != nullptr) {
- Runtime* runtime = Runtime::Current();
- // The runtime might not be available at this point if we're running
- // dex2oat or oatdump.
- if (runtime != nullptr) {
- size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
- Runtime::MadviseFileForRange(madvise_size_limit,
- with_dlopen->Size(),
- with_dlopen->Begin(),
- with_dlopen->End(),
- oat_location);
- }
return with_dlopen;
}
if (kPrintDlOpenErrorMessage) {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index c3a268d8b7..ecf3a04cc3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -229,6 +229,16 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
compilation_filter.c_str(),
compilation_reason.c_str()));
+ const bool has_registered_app_info = Runtime::Current()->GetAppInfo()->HasRegisteredAppInfo();
+ const AppInfo::CodeType code_type =
+ Runtime::Current()->GetAppInfo()->GetRegisteredCodeType(dex_location);
+ // We only want to madvise primary/split dex artifacts as a startup optimization. However,
+ // as the code_type for those artifacts may not be set until the initial app info registration,
+ // we conservatively madvise everything until the app info registration is complete.
+ const bool should_madvise_vdex_and_odex = !has_registered_app_info ||
+ code_type == AppInfo::CodeType::kPrimaryApk ||
+ code_type == AppInfo::CodeType::kSplitApk;
+
// Proceed with oat file loading.
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
@@ -244,6 +254,16 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
// Load the dex files from the oat file.
bool added_image_space = false;
if (oat_file->IsExecutable()) {
+ if (should_madvise_vdex_and_odex) {
+ VLOG(oat) << "Madvising oat file: " << oat_file->GetLocation();
+ size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
+ Runtime::MadviseFileForRange(madvise_size_limit,
+ oat_file->Size(),
+ oat_file->Begin(),
+ oat_file->End(),
+ oat_file->GetLocation());
+ }
+
ScopedTrace app_image_timing("AppImage:Loading");
// We need to throw away the image space if we are debuggable but the oat-file source of the
@@ -345,7 +365,8 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
if (oat_file != nullptr) {
VdexFile* vdex_file = oat_file->GetVdexFile();
- if (vdex_file != nullptr) {
+ if (should_madvise_vdex_and_odex && vdex_file != nullptr) {
+ VLOG(oat) << "Madvising vdex file: " << vdex_file->GetName();
// Opened vdex file from an oat file, madvise it to its loaded state.
// TODO(b/196052575): Unify dex and vdex madvise knobs and behavior.
const size_t madvise_size_limit = Runtime::Current()->GetMadviseWillNeedSizeVdex();
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e20f883446..2de3eb5c74 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -178,6 +178,7 @@
#include "well_known_classes.h"
#ifdef ART_TARGET_ANDROID
+#include <android/api-level.h>
#include <android/set_abort_message.h>
#include "com_android_apex.h"
namespace apex = com::android::apex;
@@ -3358,6 +3359,22 @@ void Runtime::MadviseFileForRange(size_t madvise_size_limit_bytes,
const uint8_t* map_begin,
const uint8_t* map_end,
const std::string& file_name) {
+#ifdef ART_TARGET_ANDROID
+ // Short-circuit the madvise optimization for background processes. This
+ // avoids IO and memory contention with foreground processes, particularly
+ // those involving app startup.
+ // Note: We can only safely short-circuit the madvise on T+, as it requires
+ // the framework to always immediately notify ART of process states.
+ static const int kApiLevel = android_get_device_api_level();
+ const bool accurate_process_state_at_startup = kApiLevel >= __ANDROID_API_T__;
+ if (accurate_process_state_at_startup) {
+ const Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr && !runtime->InJankPerceptibleProcessState()) {
+ return;
+ }
+ }
+#endif // ART_TARGET_ANDROID
+
// Ideal blockTransferSize for madvising files (128KiB)
static constexpr size_t kIdealIoTransferSizeBytes = 128*1024;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 78ba26dec0..26b795b967 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -4282,6 +4282,9 @@ void Thread::VisitRoots(RootVisitor* visitor) {
static void SweepCacheEntry(IsMarkedVisitor* visitor, const Instruction* inst, size_t* value)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ // WARNING: The interpreter will not modify the cache while this method is running in GC.
+ // However, ClearAllInterpreterCaches can still run if any dex file is closed.
+ // Therefore the cache entry can be nulled at any point through this method.
if (inst == nullptr) {
return;
}
@@ -4307,6 +4310,9 @@ static void SweepCacheEntry(IsMarkedVisitor* visitor, const Instruction* inst, s
case Opcode::CONST_STRING:
case Opcode::CONST_STRING_JUMBO: {
mirror::Object* object = reinterpret_cast<mirror::Object*>(*value);
+ if (object == nullptr) {
+ return;
+ }
mirror::Object* new_object = visitor->IsMarked(object);
// We know the string is marked because it's a strongly-interned string that
// is always alive (see b/117621117 for trying to make those strings weak).
diff --git a/test/1000-non-moving-space-stress/src-art/Main.java b/test/1000-non-moving-space-stress/src-art/Main.java
index 18bfdd3c04..3cd7224080 100644
--- a/test/1000-non-moving-space-stress/src-art/Main.java
+++ b/test/1000-non-moving-space-stress/src-art/Main.java
@@ -15,8 +15,11 @@
*/
import dalvik.system.VMRuntime;
+import java.lang.ref.Reference; // For reachabilityFence.
+import java.util.ArrayList;
public class Main {
+ private static final boolean SHOULD_PRINT = false; // True causes failure.
public static void main(String[] args) throws Exception {
VMRuntime runtime = VMRuntime.getRuntime();
@@ -35,15 +38,40 @@ public class Main {
Object[] moving_array = new Object[S];
}
} catch (OutOfMemoryError e) {
- // Stop here.
+ System.out.println("Unexpected OOME");
}
- System.out.println("passed");
+ Runtime.getRuntime().gc();
+ int numAllocs = 0;
+ ArrayList<Object> chunks = new ArrayList<>();
+ try {
+ final int MAX_PLAUSIBLE_ALLOCS = 1024 * 1024;
+ for (numAllocs = 0; numAllocs < MAX_PLAUSIBLE_ALLOCS; ++numAllocs) {
+ chunks.add(runtime.newNonMovableArray(Object.class, 252)); // About 1KB
+ }
+ // If we get here, we've allocated about 1GB of nonmovable memory, which
+ // should be impossible.
+ } catch (OutOfMemoryError e) {
+ chunks.remove(0); // Give us a little space back.
+ if (((Object[]) (chunks.get(42)))[17] != null) {
+ System.out.println("Bad entry in chunks array");
+ } else {
+ chunks.clear(); // Recover remaining space.
+ if (SHOULD_PRINT) {
+ System.out.println("Successfully allocated " + numAllocs + " non-movable KBs");
+ }
+ System.out.println("passed");
+ }
+ Reference.reachabilityFence(chunks);
+ return;
+ }
+ Reference.reachabilityFence(chunks);
+ System.out.println("Failed to exhaust non-movable space");
}
// When using the Concurrent Copying (CC) collector (default collector),
// this method allocates an object in the non-moving space and an object
// in the region space, make the former reference the later, and returns
- // nothing (so that none of these objects are reachable when upon return).
+ // nothing (so that none of these objects are reachable upon return).
static void $noinline$Alloc(VMRuntime runtime) {
Object[] non_moving_array = (Object[]) runtime.newNonMovableArray(Object.class, 1);
// Small object, unlikely to trigger garbage collection.
diff --git a/test/719-varhandle-concurrency/src/Main.java b/test/719-varhandle-concurrency/src/Main.java
index 1c2135bf7d..784e7cc103 100644
--- a/test/719-varhandle-concurrency/src/Main.java
+++ b/test/719-varhandle-concurrency/src/Main.java
@@ -46,7 +46,8 @@ public class Main {
* for example with gcstress, set a cap duration in MAX_RETRIES_DURATION. With this at least one
* iteration would run, but there could be fewer retries if each of them takes too long. */
private static final int RETRIES = 50;
- private static final Duration MAX_RETRIES_DURATION = Duration.ofMinutes(1);
+ // b/235431387: timeout reduced from 1 minute
+ private static final Duration MAX_RETRIES_DURATION = Duration.ofSeconds(15);
public static void main(String[] args) throws Throwable {
testConcurrentProcessing(new CompareAndExchangeRunnerFactory(), "compareAndExchange");
diff --git a/test/Android.bp b/test/Android.bp
index 8a1ca84af1..4289c18f41 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -1121,6 +1121,8 @@ java_library {
"2007-virtual-structural-finalizable/src-art/art/Test2007.java",
],
sdk_version: "core_platform",
+ // Make sure that this will be added to the sdk snapshot for S.
+ min_sdk_version: "S",
// Some ART run-tests contain constructs which break ErrorProne checks;
// disable `errorprone` builds.
errorprone: {
@@ -1284,6 +1286,8 @@ java_library {
"expected_cts_outputs_gen",
],
sdk_version: "core_current",
+ // Make sure that this will be added to the sdk snapshot for S.
+ min_sdk_version: "S",
}
art_cc_test {
@@ -1381,6 +1385,7 @@ filegroup {
srcs: [
":art-gtest-jars-AbstractMethod",
":art-gtest-jars-AllFields",
+ ":art-gtest-jars-ArrayClassWithUnresolvedComponent",
":art-gtest-jars-DefaultMethods",
":art-gtest-jars-ErroneousA",
":art-gtest-jars-ErroneousB",
@@ -1427,6 +1432,7 @@ filegroup {
":art-gtest-jars-MainStripped",
":art-gtest-jars-MainUncompressedAligned",
":art-gtest-jars-MultiDexUncompressedAligned",
+ ":art-gtest-jars-SuperWithAccessChecks",
":art-gtest-jars-VerifierDeps",
":art-gtest-jars-VerifierDepsMulti",
":art-gtest-jars-VerifySoftFailDuringClinit",
@@ -1832,6 +1838,20 @@ genrule {
}
genrule {
+ name: "art-gtest-jars-ArrayClassWithUnresolvedComponent",
+ defaults: ["art-gtest-jars-smali-defaults"],
+ srcs: ["ArrayClassWithUnresolvedComponent/*.smali"],
+ out: ["art-gtest-jars-ArrayClassWithUnresolvedComponent.dex"],
+}
+
+genrule {
+ name: "art-gtest-jars-SuperWithAccessChecks",
+ defaults: ["art-gtest-jars-smali-defaults"],
+ srcs: ["SuperWithAccessChecks/*.smali"],
+ out: ["art-gtest-jars-SuperWithAccessChecks.dex"],
+}
+
+genrule {
name: "art-gtest-jars-LinkageTest",
defaults: ["art-gtest-jars-smali-defaults"],
srcs: ["LinkageTest/*.smali"],
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali
new file mode 100644
index 0000000000..f68d70c7dd
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 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.
+
+.class public LClassWithMissingInterface;
+
+.super Ljava/lang/Object;
+.implements LMissingInterface;
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali
new file mode 100644
index 0000000000..134302cd9d
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 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.
+
+.class public LClassWithMissingSuper;
+
+.super LMissingClass;
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali
new file mode 100644
index 0000000000..f35e5f0a3f
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 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.
+
+.class public LClassWithStatic;
+
+.super Ljava/lang/Object;
+
+.method static constructor <clinit>()V
+ .registers 1
+ const-string v0, "[LClassWithMissingInterface;"
+ invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;
+ move-result-object v0
+ sput-object v0, LClassWithStatic;->field:Ljava/lang/Class;
+ return-void
+.end method
+
+.field public static field:Ljava/lang/Class;
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali
new file mode 100644
index 0000000000..68aecfbdad
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 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.
+
+.class public LClassWithStaticConst;
+
+.super Ljava/lang/Object;
+
+.method static constructor <clinit>()V
+ .registers 1
+ const-class v0, [LClassWithMissingInterface;
+ sput-object v0, LClassWithStaticConst;->field:Ljava/lang/Class;
+ return-void
+.end method
+
+.field public static field:Ljava/lang/Class;
diff --git a/test/SuperWithAccessChecks/ImplementsClass.smali b/test/SuperWithAccessChecks/ImplementsClass.smali
new file mode 100644
index 0000000000..e39877f45f
--- /dev/null
+++ b/test/SuperWithAccessChecks/ImplementsClass.smali
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 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.
+
+.class public LImplementsClass;
+
+.super Ljava/lang/Object;
+.implements LItf;
diff --git a/test/SuperWithAccessChecks/Itf.smali b/test/SuperWithAccessChecks/Itf.smali
new file mode 100644
index 0000000000..c91686ed03
--- /dev/null
+++ b/test/SuperWithAccessChecks/Itf.smali
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 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.
+
+.class public abstract interface LItf;
+
+.super Ljava/lang/Object;
+
+.method public foo()V
+ .registers 1
+ invoke-static {}, LMissingClass;->forName()V
+ return-void
+.end method
diff --git a/test/SuperWithAccessChecks/SubClass.smali b/test/SuperWithAccessChecks/SubClass.smali
new file mode 100644
index 0000000000..21c8f1cbd8
--- /dev/null
+++ b/test/SuperWithAccessChecks/SubClass.smali
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 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.
+
+.class public LSubClass;
+
+.super LSuperClass;
diff --git a/test/SuperWithAccessChecks/SuperClass.smali b/test/SuperWithAccessChecks/SuperClass.smali
new file mode 100644
index 0000000000..8e12521acf
--- /dev/null
+++ b/test/SuperWithAccessChecks/SuperClass.smali
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 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.
+
+.class public LSuperClass;
+
+.super Ljava/lang/Object;
+
+.method static foo()V
+ .registers 0
+ invoke-static {}, LMissingClass;->forName()V
+ return-void
+.end method
+
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 75dc8fe728..ef168ca871 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -650,11 +650,11 @@ if [ "$USE_JVMTI" = "y" ]; then
# needed anymore since the plugin can do it for us now.
FLAGS="${FLAGS} -Xplugin:${plugin}"
- # For jvmti tests, set the threshold of compilation to 0, so we jit on the first
- # use to provide better test coverage for jvmti + jit. This means we won't run
+ # For jvmti tests, set the threshold of compilation to 1, so we jit early to
+ # provide better test coverage for jvmti + jit. This means we won't run
# the default --jit configuration but it is not too important test scenario for
# jvmti tests. This is art specific flag, so don't use it with jvm.
- FLAGS="${FLAGS} -Xjitthreshold:0"
+ FLAGS="${FLAGS} -Xjitthreshold:1"
fi
fi
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
index 7905660dc9..caf94a783b 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
@@ -86,7 +86,8 @@ public class OdsignTestUtils {
String packagesOutput =
mTestInfo.getDevice().executeShellCommand("pm list packages -f --apex-only");
Pattern p = Pattern.compile(
- "^package:(.*)=(com(?:\\.google)?\\.android\\.art)$", Pattern.MULTILINE);
+ "^package:(.*)=(com(?:\\.google)?\\.android(?:\\.go)?\\.art)$",
+ Pattern.MULTILINE);
Matcher m = p.matcher(packagesOutput);
assertTrue("ART module not found. Packages are:\n" + packagesOutput, m.find());
String artApexPath = m.group(1);
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index 237ec960d4..ebe25205d6 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -230,6 +230,7 @@ art_gtest_user_module_names = [
# ART gtests that need root access to the device.
art_gtest_eng_only_module_names = [
+ "libnativeloader_e2e_tests",
"art_standalone_dexoptanalyzer_tests",
"art_standalone_profman_tests",
]
@@ -736,18 +737,11 @@ class Generator:
mts_test_shards = []
- # ART test (gtest & run-test) shard(s).
- # TODO: Also handle the case of gtests requiring root access to the device
- # (`art_gtest_eng_only_module_names`).
+ # ART run-tests shard(s).
art_run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
art_run_test_shards = split_list(art_run_test_module_names, NUM_MTS_ART_RUN_TEST_SHARDS)
for i in range(len(art_run_test_shards)):
art_tests_shard_i_tests = art_run_test_shards[i]
- # Append ART gtests to the last ART run-test shard for now.
- # If needed, consider moving them to their own shard to increase
- # the parallelization of code coverage runs.
- if i + 1 == len(art_run_test_shards):
- art_tests_shard_i_tests.extend(art_gtest_mts_user_module_names)
art_tests_shard_i = self.create_mts_test_shard(
"ART run-tests", art_tests_shard_i_tests, i, 2020,
["TODO(rpl): Find a way to express this list in a more concise fashion."])
@@ -775,6 +769,15 @@ class Generator:
other_cts_libcore_tests_shard_num, 2021)
mts_test_shards.append(other_cts_libcore_tests_shard)
+ # ART gtests shard.
+ # TODO: Also handle the case of gtests requiring root access to the device
+ # (`art_gtest_eng_only_module_names`).
+ art_gtests_shard_num = len(mts_test_shards)
+ art_gtests_shard_tests = art_gtest_mts_user_module_names
+ art_gtests_shard = self.create_mts_test_shard(
+ "ART gtests", art_gtests_shard_tests, art_gtests_shard_num, 2022)
+ mts_test_shards.append(art_gtests_shard)
+
for s in mts_test_shards:
s.regen_test_plan_file()
s.regen_test_list_file()