diff options
-rw-r--r-- | dex2oat/dex2oat.cc | 4 | ||||
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/class_loader_context.cc | 31 | ||||
-rw-r--r-- | runtime/class_loader_context.h | 22 | ||||
-rw-r--r-- | runtime/class_loader_context_test.cc | 44 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 4 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 96 | ||||
-rw-r--r-- | runtime/oat_file_manager.h | 26 | ||||
-rwxr-xr-x | test/172-app-image-twice/check | 18 | ||||
-rw-r--r-- | test/172-app-image-twice/debug_print_class.cc | 33 | ||||
-rw-r--r-- | test/172-app-image-twice/expected.txt | 1 | ||||
-rw-r--r-- | test/172-app-image-twice/info.txt | 1 | ||||
-rw-r--r-- | test/172-app-image-twice/profile | 1 | ||||
-rw-r--r-- | test/172-app-image-twice/run | 20 | ||||
-rw-r--r-- | test/172-app-image-twice/src/Main.java | 48 | ||||
-rw-r--r-- | test/172-app-image-twice/src/TestClass.java | 18 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/knownfailures.json | 1 |
18 files changed, 306 insertions, 64 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 63518be15f..6b65aca943 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1257,10 +1257,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + } else if (class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false)) { + /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), diff --git a/runtime/Android.bp b/runtime/Android.bp index 116453b1bf..64e6796ba0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -422,6 +422,7 @@ gensrcs { srcs: [ "arch/instruction_set.h", "base/mutex.h", + "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 98174142f1..2bd541118b 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -672,9 +672,10 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( + const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -683,15 +684,21 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return false; + return VerificationResult::kMismatch; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (special_shared_library_ || expected_context.special_shared_library_) { - return true; + if (expected_context.special_shared_library_) { + // Special case where we are the only entry in the class path. + if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { + return VerificationResult::kVerifies; + } + return VerificationResult::kForcedToSkipChecks; + } else if (special_shared_library_) { + return VerificationResult::kForcedToSkipChecks; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -699,7 +706,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -710,14 +717,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (verify_checksums) { @@ -772,7 +779,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } // Compare the checksums. @@ -781,11 +788,11 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } } } - return true; + return VerificationResult::kVerifies; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 1c83007f41..a4268aa09a 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,8 +22,10 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" +#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" +#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -34,6 +36,18 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: + enum class VerificationResult { + kVerifies, + kForcedToSkipChecks, + kMismatch, + }; + + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -109,7 +123,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - bool VerifyClassLoaderContextMatch(const std::string& context_spec, + VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -141,12 +155,6 @@ class ClassLoaderContext { static std::unique_ptr<ClassLoaderContext> Default(); private: - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4689ae4c3f..5e3f48c100 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,6 +608,17 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } + +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { + std::string context_spec = "PCL[]"; + std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); + ASSERT_TRUE(context != nullptr); + PretendContextOpenedDexFiles(context.get()); + // Ensure that the special shared library marks as verified for the first thing in the class path. + ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), + ClassLoaderContext::VerificationResult::kVerifies); +} + TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); @@ -619,28 +630,36 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), + ClassLoaderContext::VerificationResult::kVerifies); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), + ClassLoaderContext::VerificationResult::kMismatch); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -652,7 +671,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -661,7 +681,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -669,7 +690,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), + ClassLoaderContext::VerificationResult::kVerifies); } } // namespace art diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 9c8b6512a7..241102ea83 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1217,7 +1217,9 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); + + bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) == + ClassLoaderContext::VerificationResult::kVerifies; if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 16e6cf375c..59a1045ba2 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -276,9 +276,19 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, - std::vector<const DexFile*>& dex_files_unloaded, - std::string* error_msg /*out*/) { +static bool CheckClassCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) { + std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); + + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector<const DexFile*> dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + // Generate type index information for each dex file. std::vector<TypeIndexInfo> loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -355,9 +365,10 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool OatFileManager::HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) const { +OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( + const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -367,28 +378,59 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return false; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return CheckCollisionResult::kSkippedUnsupportedClassLoader; } - // If the pat file loading context matches the context used during compilation then we accept + // If the oat file loading context matches the context used during compilation then we accept // the oat file without addition checks - if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { - return false; + ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( + oat_file->GetClassLoaderContext(), + /*verify_names*/ true, + /*verify_checksums*/ true); + switch (result) { + case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: + return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; + case ClassLoaderContext::VerificationResult::kMismatch: + // Mismatched context, do the actual collision check. + break; + case ClassLoaderContext::VerificationResult::kVerifies: + return CheckCollisionResult::kNoCollisions; } // The class loader context does not match. Perform a full duplicate classes check. + return CheckClassCollision(oat_file, context, error_msg) + ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; +} - std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector<std::unique_ptr<const DexFile>> opened_dex_files; +bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { + // Take the file only if it has no collisions, or we must take it because of preopting. + // Also accept oat files for shared libraries and unsupported class loaders. + return result != CheckCollisionResult::kPerformedHasCollisions; +} - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector<const DexFile*> dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); +bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + // If we verified the class loader context (skipping due to the special marker doesn't + // count), then also avoid the collision check. + bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; + // If we skipped the collision check, we need to reverify to be sure its OK to load the + // image. + if (!load_image && + check_collision_result == + CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { + // We can load the app image only if there are no collisions. If we know the + // class loader but didn't do the full collision check in HasCollisions(), + // do it now. b/77342775 + load_image = !CheckClassCollision(source_oat_file, context, error_msg); + } + return load_image; + } + return false; } std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( @@ -473,16 +515,17 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast<uintptr_t>(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; + CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.<init>(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = - !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); + check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); + bool accept_oat_file = AcceptOatFile(check_collision_result); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (runtime->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -529,10 +572,11 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr<gc::space::ImageSpace> image_space; - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + if (ShouldLoadAppImage(check_collision_result, + source_oat_file, + context.get(), + &error_msg)) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); - } else { - image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 038474e31f..80456e9b75 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,23 +108,39 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: + enum class CheckCollisionResult { + kSkippedUnsupportedClassLoader, + kSkippedClassLoaderContextSharedLibrary, + kNoCollisions, + kPerformedHasCollisions, + }; + // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns false. + // this returns kSkippedUnsupportedClassLoader. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. + CheckCollisionResult CheckCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); + // Return true if we should accept the oat file. + bool AcceptOatFile(CheckCollisionResult result) const; + + // Return true if we should attempt to load the app image. + bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg); + std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check new file mode 100755 index 0000000000..26a97a48ae --- /dev/null +++ b/test/172-app-image-twice/check @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Remove all lines not containing "passed". +grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc new file mode 100644 index 0000000000..6c3de20f2d --- /dev/null +++ b/test/172-app-image-twice/debug_print_class.cc @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#include "debug_print.h" +#include "dex/dex_file.h" +#include "mirror/class-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); + LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() + << "/" << static_cast<const void*>(&klass->GetDexFile()) + << " " << DescribeSpace(klass); +} + +} // namespace art diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/172-app-image-twice/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt new file mode 100644 index 0000000000..028046e872 --- /dev/null +++ b/test/172-app-image-twice/info.txt @@ -0,0 +1 @@ +Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile new file mode 100644 index 0000000000..70cb2efbb5 --- /dev/null +++ b/test/172-app-image-twice/profile @@ -0,0 +1 @@ +LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run new file mode 100644 index 0000000000..d1ad043552 --- /dev/null +++ b/test/172-app-image-twice/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# 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. + +# Build an app image with TestClass (specified by profile) and class loader +# context that skips the duplicate class checks. +exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java new file mode 100644 index 0000000000..a1c151a6bc --- /dev/null +++ b/test/172-app-image-twice/src/Main.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import java.lang.reflect.Method; + +public class Main { + private static String TEST_NAME = "172-app-image-twice"; + + public static void main(String args[]) throws Exception { + System.loadLibrary(args[0]); + + Class<?> tc1 = Class.forName("TestClass"); + + String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + Class<?> bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); + Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); + addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); + + Class<?> tc2 = Class.forName("TestClass"); + + // Add extra logging to simulate libcore logging, this logging should not be compared + // against. + System.out.println("Extra logging"); + + if (tc1 != tc2) { + System.out.println("Class mismatch!"); + debugPrintClass(tc1); + debugPrintClass(tc2); + } else { + System.out.println("passed"); + } + } + + public static native void debugPrintClass(Class<?> cls); +} diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java new file mode 100644 index 0000000000..5381718f6e --- /dev/null +++ b/test/172-app-image-twice/src/TestClass.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class TestClass { +} diff --git a/test/Android.bp b/test/Android.bp index 0c6b449877..76189f62a9 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -423,6 +423,7 @@ cc_defaults { "154-gc-loop/heap_interface.cc", "167-visit-locks/visit_locks.cc", "169-threadgroup-jni/jni_daemon_thread.cc", + "172-app-image-twice/debug_print_class.cc", "1945-proxy-method-arguments/get_args.cc", "203-multi-checkpoint/multi_checkpoint.cc", "305-other-fault-handler/fault_handler.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index f3137587f6..f473a99a27 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -734,6 +734,7 @@ "164-resolution-trampoline-dex-cache", "167-visit-locks", "168-vmstack-annotated", + "172-app-image-twice", "201-built-in-except-detail-messages", "203-multi-checkpoint", "304-method-tracing", |