diff options
35 files changed, 831 insertions, 688 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 5c49f193a5..d376f291cd 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -92,7 +92,7 @@ ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Erroneou ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes -ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested +ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB @@ -102,6 +102,7 @@ ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) +ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_test_DEX_DEPS := Main ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY @@ -146,6 +147,11 @@ ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \ dexoptanalyzerd +ART_GTEST_image_space_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) +ART_GTEST_image_space_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + ART_GTEST_dex2oat_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) ART_GTEST_dex2oat_test_TARGET_DEPS := \ @@ -627,6 +633,9 @@ ART_GTEST_oat_file_assistant_test_TARGET_DEPS := ART_GTEST_dexoptanalyzer_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_HOST_DEPS := ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := +ART_GTEST_image_space_test_DEX_DEPS := +ART_GTEST_image_space_test_HOST_DEPS := +ART_GTEST_image_space_test_TARGET_DEPS := ART_GTEST_dex2oat_test_DEX_DEPS := ART_GTEST_dex2oat_test_HOST_DEPS := ART_GTEST_dex2oat_test_TARGET_DEPS := diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 1047d3beb6..86e54294ae 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -23,7 +23,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "intrinsics.h" #include "mirror/array-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "thread.h" #include "utils/arm64/assembler_arm64.h" @@ -1450,16 +1450,47 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { } } +// The cut off for unrolling the loop in String.equals() intrinsic for const strings. +// The normal loop plus the pre-header is 9 instructions without string compression and 12 +// instructions with string compression. We can compare up to 8 bytes in 4 instructions +// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up +// to 10 instructions for the unrolled loop. +constexpr size_t kShortConstStringEqualsCutoffInBytes = 32; + +static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) { + if (candidate->IsLoadString()) { + HLoadString* load_string = candidate->AsLoadString(); + const DexFile& dex_file = load_string->GetDexFile(); + return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length); + } + return nullptr; +} + void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // Temporary registers to store lengths of strings and for calculations. - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); + // For the generic implementation and for long const strings we need a temporary. + // We do not need it for short const strings, up to 8 bytes, see code generation below. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) { + locations->AddTemp(Location::RequiresRegister()); + } + + // TODO: If the String.equals() is used only for an immediately following HIf, we can + // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks. + // Then we shall need an extra temporary register instead of the output register. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } @@ -1473,8 +1504,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { UseScratchRegisterScope scratch_scope(masm); Register temp = scratch_scope.AcquireW(); - Register temp1 = WRegisterFrom(locations->GetTemp(0)); - Register temp2 = WRegisterFrom(locations->GetTemp(1)); + Register temp1 = scratch_scope.AcquireW(); vixl::aarch64::Label loop; vixl::aarch64::Label end; @@ -1510,46 +1540,98 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { __ B(&return_false, ne); } - // Load `count` fields of this and argument strings. - __ Ldr(temp, MemOperand(str.X(), count_offset)); - __ Ldr(temp1, MemOperand(arg.X(), count_offset)); - // Check if `count` fields are equal, return false if they're not. - // Also compares the compression style, if differs return false. - __ Cmp(temp, temp1); - __ B(&return_false, ne); - // Return true if both strings are empty. Even with string compression `count == 0` means empty. - static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, - "Expecting 0=compressed, 1=uncompressed"); - __ Cbz(temp, &return_true); + // Check if one of the inputs is a const string. Do not special-case both strings + // being const, such cases should be handled by constant folding if needed. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + if (const_string != nullptr) { + std::swap(str, arg); // Make sure the const string is in `str`. + } + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + + if (const_string != nullptr) { + // Load `count` field of the argument string and check if it matches the const string. + // Also compares the compression style, if differs return false. + __ Ldr(temp, MemOperand(arg.X(), count_offset)); + __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed))); + __ B(&return_false, ne); + } else { + // Load `count` fields of this and argument strings. + __ Ldr(temp, MemOperand(str.X(), count_offset)); + __ Ldr(temp1, MemOperand(arg.X(), count_offset)); + // Check if `count` fields are equal, return false if they're not. + // Also compares the compression style, if differs return false. + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } // Assertions that must hold in order to compare strings 8 bytes at a time. DCHECK_ALIGNED(value_offset, 8); static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); - if (mirror::kUseStringCompression) { - // For string compression, calculate the number of bytes to compare (not chars). - // This could in theory exceed INT32_MAX, so treat temp as unsigned. - __ Lsr(temp, temp, 1u); // Extract length. - __ And(temp1, temp1, Operand(1)); // Extract compression flag. - __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. - } - - // Store offset of string value in preparation for comparison loop - __ Mov(temp1, value_offset); + if (const_string != nullptr && + const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes + : kShortConstStringEqualsCutoffInBytes / 2u)) { + // Load and compare the contents. Though we know the contents of the short const string + // at compile time, materializing constants may be more code than loading from memory. + int32_t offset = value_offset; + size_t remaining_bytes = + RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u); + temp = temp.X(); + temp1 = temp1.X(); + while (remaining_bytes > 8u) { + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + __ Ldp(temp, temp1, MemOperand(str.X(), offset)); + __ Ldp(temp2, out, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp2); + __ Ccmp(temp1, out, NoFlag, eq); + __ B(&return_false, ne); + offset += 2u * sizeof(uint64_t); + remaining_bytes -= 2u * sizeof(uint64_t); + } + if (remaining_bytes != 0u) { + __ Ldr(temp, MemOperand(str.X(), offset)); + __ Ldr(temp1, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } + } else { + // Return true if both strings are empty. Even with string compression `count == 0` means empty. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); + __ Cbz(temp, &return_true); + + if (mirror::kUseStringCompression) { + // For string compression, calculate the number of bytes to compare (not chars). + // This could in theory exceed INT32_MAX, so treat temp as unsigned. + __ And(temp1, temp, Operand(1)); // Extract compression flag. + __ Lsr(temp, temp, 1u); // Extract length. + __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. + } - temp1 = temp1.X(); - temp2 = temp2.X(); - // Loop to compare strings 8 bytes at a time starting at the front of the string. - // Ok to do this because strings are zero-padded to kObjectAlignment. - __ Bind(&loop); - __ Ldr(out, MemOperand(str.X(), temp1)); - __ Ldr(temp2, MemOperand(arg.X(), temp1)); - __ Add(temp1, temp1, Operand(sizeof(uint64_t))); - __ Cmp(out, temp2); - __ B(&return_false, ne); - // With string compression, we have compared 8 bytes, otherwise 4 chars. - __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); - __ B(&loop, hi); + // Store offset of string value in preparation for comparison loop + __ Mov(temp1, value_offset); + + temp1 = temp1.X(); + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + // Loop to compare strings 8 bytes at a time starting at the front of the string. + // Ok to do this because strings are zero-padded to kObjectAlignment. + __ Bind(&loop); + __ Ldr(out, MemOperand(str.X(), temp1)); + __ Ldr(temp2, MemOperand(arg.X(), temp1)); + __ Add(temp1, temp1, Operand(sizeof(uint64_t))); + __ Cmp(out, temp2); + __ B(&return_false, ne); + // With string compression, we have compared 8 bytes, otherwise 4 chars. + __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); + __ B(&loop, hi); + } // Return true and exit the function. // If loop does not result in returning false, we return true. diff --git a/runtime/Android.bp b/runtime/Android.bp index c871301052..d3a81a9add 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -548,6 +548,7 @@ art_cc_test { "gc/reference_queue_test.cc", "gc/space/dlmalloc_space_static_test.cc", "gc/space/dlmalloc_space_random_test.cc", + "gc/space/image_space_test.cc", "gc/space/large_object_space_test.cc", "gc/space/rosalloc_space_static_test.cc", "gc/space/rosalloc_space_random_test.cc", diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 59e6ac0ba3..4902ad42d7 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -441,56 +441,12 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, UNREACHABLE(); } -// We use the method's DexFile and declaring class name to find the OatMethod for an obsolete -// method. This is extremely slow but we need it if we want to be able to have obsolete native -// methods since we need this to find the size of its stack frames. -// -// NB We could (potentially) do this differently and rely on the way the transformation is applied -// in order to use the entrypoint to find this information. However, for debugging reasons (most -// notably making sure that new invokes of obsolete methods fail) we choose to instead get the data -// directly from the dex file. -static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, bool* found) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(method->IsObsolete() && method->IsNative()); - const DexFile* dex_file = method->GetDexFile(); - - // recreate the class_def_index from the descriptor. - std::string descriptor_storage; - const DexFile::TypeId* declaring_class_type_id = - dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage)); - CHECK(declaring_class_type_id != nullptr); - dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id); - const DexFile::ClassDef* declaring_class_type_def = - dex_file->FindClassDef(declaring_class_type_index); - CHECK(declaring_class_type_def != nullptr); - uint16_t declaring_class_def_index = dex_file->GetIndexForClassDef(*declaring_class_type_def); - - size_t oat_method_index = GetOatMethodIndexFromMethodIndex(*dex_file, - declaring_class_def_index, - method->GetDexMethodIndex()); - - OatFile::OatClass oat_class = OatFile::FindOatClass(*dex_file, - declaring_class_def_index, - found); - if (!(*found)) { - return OatFile::OatMethod::Invalid(); - } - return oat_class.GetOatMethod(oat_method_index); -} - static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method, PointerSize pointer_size, bool* found) REQUIRES_SHARED(Locks::mutator_lock_) { - if (UNLIKELY(method->IsObsolete())) { - // We shouldn't be calling this with obsolete methods except for native obsolete methods for - // which we need to use the oat method to figure out how large the quick frame is. - DCHECK(method->IsNative()) << "We should only be finding the OatMethod of obsolete methods in " - << "order to allow stack walking. Other obsolete methods should " - << "never need to access this information."; - DCHECK_EQ(pointer_size, kRuntimePointerSize) << "Obsolete method in compiler!"; - return FindOatMethodFromDexFileFor(method, found); - } + // We shouldn't be calling this with obsolete methods. + DCHECK(!method->IsObsolete()); // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 8b0c51c998..e58c6f541e 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -53,7 +53,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); // Verify the environment is as we expect - uint32_t checksum; + std::vector<uint32_t> checksums; std::string error_msg; ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str())) << "Expected pre-compiled boot image to be at: " << GetSystemImageFile(); @@ -61,7 +61,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { << "Expected dex file to be at: " << GetDexSrc1(); ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str())) << "Expected stripped dex file to be at: " << GetStrippedDexSrc1(); - ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg)) + ASSERT_FALSE(DexFile::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg)) << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1(); ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str())) << "Expected dex file to be at: " << GetDexSrc2(); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 02ba33ceb1..9ad4063ce1 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -72,23 +72,13 @@ struct DexFile::AnnotationValue { uint8_t type_; }; -bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { - CHECK(checksum != nullptr); +bool DexFile::GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg) { + CHECK(checksums != nullptr); uint32_t magic; - // Strip ":...", which is the location - const char* zip_entry_name = kClassesDex; - const char* file_part = filename; - std::string file_part_storage; - - if (DexFile::IsMultiDexLocation(filename)) { - file_part_storage = GetBaseLocation(filename); - file_part = file_part_storage.c_str(); - zip_entry_name = filename + file_part_storage.size() + 1; - DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator); - } - - File fd = OpenAndReadMagic(file_part, &magic, error_msg); + File fd = OpenAndReadMagic(filename, &magic, error_msg); if (fd.Fd() == -1) { DCHECK(!error_msg->empty()); return false; @@ -97,17 +87,25 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* std::unique_ptr<ZipArchive> zip_archive( ZipArchive::OpenFromFd(fd.Release(), filename, error_msg)); if (zip_archive.get() == nullptr) { - *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", file_part, + *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename, error_msg->c_str()); return false; } - std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg)); + + uint32_t i = 0; + std::string zip_entry_name = GetMultiDexClassesDexName(i++); + std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg)); if (zip_entry.get() == nullptr) { - *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part, - zip_entry_name, error_msg->c_str()); + *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, + zip_entry_name.c_str(), error_msg->c_str()); return false; } - *checksum = zip_entry->GetCrc32(); + + do { + checksums->push_back(zip_entry->GetCrc32()); + zip_entry_name = DexFile::GetMultiDexClassesDexName(i++); + zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg)); + } while (zip_entry.get() != nullptr); return true; } if (IsDexMagic(magic)) { @@ -116,7 +114,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* if (dex_file.get() == nullptr) { return false; } - *checksum = dex_file->GetHeader().checksum_; + checksums->push_back(dex_file->GetHeader().checksum_); return true; } *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 20bd52b060..58b8e792ee 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -424,11 +424,18 @@ class DexFile { struct AnnotationValue; - // Returns the checksum of a file for comparison with GetLocationChecksum(). - // For .dex files, this is the header checksum. - // For zip files, this is the classes.dex zip entry CRC32 checksum. - // Return true if the checksum could be found, false otherwise. - static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); + // Returns the checksums of a file for comparison with GetLocationChecksum(). + // For .dex files, this is the single header checksum. + // For zip files, this is the zip entry CRC32 checksum for classes.dex and + // each additional multidex entry classes2.dex, classes3.dex, etc. + // Return true if the checksums could be found, false otherwise. + static bool GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg); + + // Check whether a location denotes a multidex dex file. This is a very simple check: returns + // whether the string contains the separator character. + static bool IsMultiDexLocation(const char* location); // Opens .dex file, backed by existing memory static std::unique_ptr<const DexFile> Open(const uint8_t* base, @@ -1161,10 +1168,6 @@ class DexFile { // Initialize section info for sections only found in map. Returns true on success. void InitializeSectionsFromMapList(); - // Check whether a location denotes a multidex dex file. This is a very simple check: returns - // whether the string contains the separator character. - static bool IsMultiDexLocation(const char* location); - // The base address of the memory mapping. const uint8_t* const begin_; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 9dca4c0621..9131715fec 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -326,12 +326,32 @@ TEST_F(DexFileTest, GetLocationChecksum) { } TEST_F(DexFileTest, GetChecksum) { - uint32_t checksum; + std::vector<uint32_t> checksums; ScopedObjectAccess soa(Thread::Current()); std::string error_msg; - EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileNames()[0].c_str(), &checksum, &error_msg)) + EXPECT_TRUE(DexFile::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), &checksums, &error_msg)) << error_msg; - EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum); + ASSERT_EQ(1U, checksums.size()); + EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]); +} + +TEST_F(DexFileTest, GetMultiDexChecksums) { + std::string error_msg; + std::vector<uint32_t> checksums; + std::string multidex_file = GetTestDexFileName("MultiDex"); + EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(), + &checksums, + &error_msg)) << error_msg; + + std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex"); + ASSERT_EQ(2U, dexes.size()); + ASSERT_EQ(2U, checksums.size()); + + EXPECT_EQ(dexes[0]->GetLocation(), DexFile::GetMultiDexLocation(0, multidex_file.c_str())); + EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]); + + EXPECT_EQ(dexes[1]->GetLocation(), DexFile::GetMultiDexLocation(1, multidex_file.c_str())); + EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]); } TEST_F(DexFileTest, ClassDefs) { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 55bd1d4736..2163a20e87 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -692,7 +692,7 @@ class ImageSpaceLoader { if (validate_oat_file) { TimingLogger::ScopedTiming timing("ValidateOatFile", &logger); CHECK(space->oat_file_ != nullptr); - if (!ValidateOatFile(*space, *space->oat_file_, error_msg)) { + if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) { DCHECK(!error_msg->empty()); return nullptr; } @@ -1387,33 +1387,6 @@ class ImageSpaceLoader { return oat_file; } - - static bool ValidateOatFile(const ImageSpace& space, - const OatFile& oat_file, - std::string* error_msg) { - for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { - const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); - uint32_t dex_file_location_checksum; - if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) { - *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: " - "%s", - dex_file_location.c_str(), - space.GetName(), - error_msg->c_str()); - return false; - } - if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) { - *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and " - "dex file '%s' (0x%x != 0x%x)", - oat_file.GetLocation().c_str(), - dex_file_location.c_str(), - oat_dex_file->GetDexFileLocationChecksum(), - dex_file_location_checksum); - return false; - } - } - return true; - } }; static constexpr uint64_t kLowSpaceValue = 50 * MB; @@ -1790,6 +1763,63 @@ std::string ImageSpace::GetMultiImageBootClassPath( return bootcp_oss.str(); } +bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) { + for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { + const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); + + // Skip multidex locations - These will be checked when we visit their + // corresponding primary non-multidex location. + if (DexFile::IsMultiDexLocation(dex_file_location.c_str())) { + continue; + } + + std::vector<uint32_t> checksums; + if (!DexFile::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) { + *error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' " + "referenced by oat file %s: %s", + dex_file_location.c_str(), + oat_file.GetLocation().c_str(), + error_msg->c_str()); + return false; + } + CHECK(!checksums.empty()); + if (checksums[0] != oat_dex_file->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file " + "'%s' and dex file '%s' (0x%x != 0x%x)", + oat_file.GetLocation().c_str(), + dex_file_location.c_str(), + oat_dex_file->GetDexFileLocationChecksum(), + checksums[0]); + return false; + } + + // Verify checksums for any related multidex entries. + for (size_t i = 1; i < checksums.size(); i++) { + std::string multi_dex_location = DexFile::GetMultiDexLocation(i, dex_file_location.c_str()); + const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(), + nullptr, + error_msg); + if (multi_dex == nullptr) { + *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'", + oat_file.GetLocation().c_str(), + multi_dex_location.c_str()); + return false; + } + + if (checksums[i] != multi_dex->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file " + "'%s' and dex file '%s' (0x%x != 0x%x)", + oat_file.GetLocation().c_str(), + multi_dex_location.c_str(), + multi_dex->GetDexFileLocationChecksum(), + checksums[i]); + return false; + } + } + } + return true; +} + void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name, const std::string& boot_classpath, std::vector<std::string>* image_file_names) { diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 489a2890fe..199bbdd00a 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -131,6 +131,17 @@ class ImageSpace : public MemMapSpace { const std::vector<const char*>& oat_filenames, const std::vector<const char*>& image_filenames); + // Returns true if the dex checksums in the given oat file match the + // checksums of the original dex files on disk. This is intended to be used + // to validate the boot image oat file, which may contain dex entries from + // multiple different (possibly multidex) dex files on disk. Prefer the + // OatFileAssistant for validating regular app oat files because the + // OatFileAssistant caches dex checksums that are reused to check both the + // oat and odex file. + // + // This function is exposed for testing purposes. + static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg); + // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields. uint8_t* GetImageEnd() const { return Begin() + GetImageHeader().GetImageSize(); diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc new file mode 100644 index 0000000000..7a380746a1 --- /dev/null +++ b/runtime/gc/space/image_space_test.cc @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include "dexopt_test.h" + +namespace art { +namespace gc { +namespace space { + +TEST_F(DexoptTest, ValidateOatFile) { + std::string dex1 = GetScratchDir() + "/Dex1.jar"; + std::string multidex1 = GetScratchDir() + "/MultiDex1.jar"; + std::string dex2 = GetScratchDir() + "/Dex2.jar"; + std::string oat_location = GetScratchDir() + "/Oat.oat"; + + Copy(GetDexSrc1(), dex1); + Copy(GetMultiDexSrc1(), multidex1); + Copy(GetDexSrc2(), dex2); + + std::string error_msg; + std::vector<std::string> args; + args.push_back("--dex-file=" + dex1); + args.push_back("--dex-file=" + multidex1); + args.push_back("--dex-file=" + dex2); + args.push_back("--oat-file=" + oat_location); + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + nullptr, + &error_msg)); + ASSERT_TRUE(oat != nullptr) << error_msg; + + // Originally all the dex checksums should be up to date. + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Invalidate the dex1 checksum. + Copy(GetDexSrc2(), dex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the dex1 checksum. + Copy(GetDexSrc1(), dex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Invalidate the non-main multidex checksum. + Copy(GetMultiDexSrc2(), multidex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the multidex checksum. + Copy(GetMultiDexSrc1(), multidex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Invalidate the dex2 checksum. + Copy(GetDexSrc1(), dex2); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // restore the dex2 checksum. + Copy(GetDexSrc2(), dex2); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Replace the multidex file with a non-multidex file. + Copy(GetDexSrc1(), multidex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the multidex file + Copy(GetMultiDexSrc1(), multidex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Replace dex1 with a multidex file. + Copy(GetMultiDexSrc1(), dex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the dex1 file. + Copy(GetDexSrc1(), dex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Remove the dex2 file. + EXPECT_EQ(0, unlink(dex2.c_str())); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the dex2 file. + Copy(GetDexSrc2(), dex2); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Remove the multidex file. + EXPECT_EQ(0, unlink(multidex1.c_str())); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 9b8445dc9e..c2407d7772 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -308,7 +308,7 @@ inline int32_t String::GetHashCode() { } template<typename MemoryType> -bool String::AllASCII(const MemoryType* const chars, const int length) { +inline bool String::AllASCII(const MemoryType* chars, const int length) { static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType"); for (int i = 0; i < length; ++i) { // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII @@ -320,6 +320,13 @@ bool String::AllASCII(const MemoryType* const chars, const int length) { return true; } +inline bool String::DexFileStringAllASCII(const char* chars, const int length) { + // For strings from the dex file we just need to check that + // the terminating character is at the right position. + DCHECK_EQ(AllASCII(reinterpret_cast<const uint8_t*>(chars), length), chars[length] == 0); + return chars[length] == 0; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 409c6c2896..38f6dd4b6f 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -184,7 +184,9 @@ class MANAGED String FINAL : public Object { bool IsValueNull() REQUIRES_SHARED(Locks::mutator_lock_); template<typename MemoryType> - static bool AllASCII(const MemoryType* const chars, const int length); + static bool AllASCII(const MemoryType* chars, const int length); + + static bool DexFileStringAllASCII(const char* chars, const int length); ALWAYS_INLINE static bool IsCompressed(int32_t count) { return GetCompressionFlagFromCount(count) == StringCompressionFlag::kCompressed; diff --git a/runtime/oat.h b/runtime/oat.h index a764e0eada..0f6657b7ed 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '1', '1', '1', '\0' }; // Revert^3 hash-based DexCache types. + static constexpr uint8_t kOatVersion[] = { '1', '1', '2', '\0' }; // Manual bump (Revert^3 hash-based DexCache types; stack maps). static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 528eddc306..493da271d1 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1510,77 +1510,6 @@ std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*> return out.str(); } -bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) { - if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { - // No dependencies. - return true; - } - - // Assumption: this is not performance-critical. So it's OK to do this with a std::string and - // Split() instead of manual parsing of the combined char*. - std::vector<std::string> split; - Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); - if (split.size() % 2 != 0) { - // Expected pairs of location and checksum. - *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies); - return false; - } - - for (auto it = split.begin(), end = split.end(); it != end; it += 2) { - std::string& location = *it; - std::string& checksum = *(it + 1); - int64_t converted = strtoll(checksum.c_str(), nullptr, 10); - if (converted == 0) { - // Conversion error. - *msg = StringPrintf("Conversion error for %s", checksum.c_str()); - return false; - } - - uint32_t dex_checksum; - std::string error_msg; - if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(), - &dex_checksum, - &error_msg)) { - if (converted != dex_checksum) { - *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u", - location.c_str(), converted, dex_checksum); - return false; - } - } else { - // Problem retrieving checksum. - // TODO: odex files? - *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(), - error_msg.c_str()); - return false; - } - } - - return true; -} - -bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies, - std::vector<std::string>* locations) { - DCHECK(locations != nullptr); - if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { - return true; - } - - // Assumption: this is not performance-critical. So it's OK to do this with a std::string and - // Split() instead of manual parsing of the combined char*. - std::vector<std::string> split; - Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); - if (split.size() % 2 != 0) { - // Expected pairs of location and checksum. - return false; - } - - for (auto it = split.begin(), end = split.end(); it != end; it += 2) { - locations->push_back(*it); - } - - return true; -} - OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 111755e7a1..d24283afee 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -290,15 +290,6 @@ class OatFile { // Create a dependency list (dex locations and checksums) for the given dex files. static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files); - // Check the given dependency list against their dex files - thus the name "Static," this does - // not check the class-loader environment, only whether there have been file updates. - static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg); - - // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic - // locations of multidex files. - static bool GetDexLocationsFromDependencies(const char* dex_dependencies, - std::vector<std::string>* locations); - // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on // error and sets found to false. static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 77cdd28d3a..5ae2fc51b7 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -38,6 +38,8 @@ namespace art { +using android::base::StringPrintf; + std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) { switch (status) { case OatFileAssistant::kOatCannotOpen: @@ -264,7 +266,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( const OatFile& oat_file, const char* dex_location) { std::vector<std::unique_ptr<const DexFile>> dex_files; - // Load the primary dex file. + // Load the main dex file. std::string error_msg; const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile( dex_location, nullptr, &error_msg); @@ -280,12 +282,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( } dex_files.push_back(std::move(dex_file)); - // Load secondary multidex files + // Load the rest of the multidex entries for (size_t i = 1; ; i++) { - std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location); - oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr); + std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location); + oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr); if (oat_dex_file == nullptr) { - // There are no more secondary dex files to load. + // There are no more multidex entries to load. break; } @@ -300,10 +302,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( } bool OatFileAssistant::HasOriginalDexFiles() { - // Ensure GetRequiredDexChecksum has been run so that + // Ensure GetRequiredDexChecksums has been run so that // has_original_dex_files_ is initialized. We don't care about the result of - // GetRequiredDexChecksum. - GetRequiredDexChecksum(); + // GetRequiredDexChecksums. + GetRequiredDexChecksums(); return has_original_dex_files_; } @@ -316,88 +318,66 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { } bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) { - if (file.GetHeader().GetNumberOfDexFiles() <= 0) { - VLOG(oat) << "Vdex does not contain any dex files"; + const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(); + if (required_dex_checksums == nullptr) { + LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; + return true; + } + + uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles(); + if (required_dex_checksums->size() != number_of_dex_files) { + *error_msg = StringPrintf("expected %zu dex files but found %u", + required_dex_checksums->size(), + number_of_dex_files); return false; } - // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not - // just the primary. Because otherwise we may fail to see a secondary - // checksum failure in the case when the original (multidex) files are - // stripped but we have a newer odex file. - const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); - if (dex_checksum_pointer != nullptr) { - uint32_t actual_checksum = file.GetLocationChecksum(0); - if (*dex_checksum_pointer != actual_checksum) { - VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_ - << ". Expected: " << *dex_checksum_pointer - << ", Actual: " << actual_checksum; + for (uint32_t i = 0; i < number_of_dex_files; i++) { + uint32_t expected_checksum = (*required_dex_checksums)[i]; + uint32_t actual_checksum = file.GetLocationChecksum(i); + if (expected_checksum != actual_checksum) { + std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + *error_msg = StringPrintf("Dex checksum does not match for dex: %s." + "Expected: %u, actual: %u", + dex.c_str(), + expected_checksum, + actual_checksum); return false; } } - // Verify the dex checksums for any secondary multidex files - for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) { - std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); - uint32_t expected_secondary_checksum = 0; - if (DexFile::GetChecksum(secondary_dex_location.c_str(), - &expected_secondary_checksum, - error_msg)) { - uint32_t actual_secondary_checksum = file.GetLocationChecksum(i); - if (expected_secondary_checksum != actual_secondary_checksum) { - VLOG(oat) << "Dex checksum does not match for secondary dex: " - << secondary_dex_location - << ". Expected: " << expected_secondary_checksum - << ", Actual: " << actual_secondary_checksum; - return false; - } - } else { - // If we can't get the checksum for the secondary location, we assume - // the dex checksum is up to date for this and all other secondary dex - // files. - break; - } - } return true; } bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) { - // Note: GetOatDexFile will return null if the dex checksum doesn't match - // what we provide, which verifies the primary dex checksum for us. - const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); - const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile( - dex_location_.c_str(), dex_checksum_pointer, error_msg); - if (oat_dex_file == nullptr) { + const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(); + if (required_dex_checksums == nullptr) { + LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; + return true; + } + + uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount(); + if (required_dex_checksums->size() != number_of_dex_files) { + *error_msg = StringPrintf("expected %zu dex files but found %u", + required_dex_checksums->size(), + number_of_dex_files); return false; } - // Verify the dex checksums for any secondary multidex files - for (size_t i = 1; ; i++) { - std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); - const OatFile::OatDexFile* secondary_oat_dex_file - = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr); - if (secondary_oat_dex_file == nullptr) { - // There are no more secondary dex files to check. - break; + for (uint32_t i = 0; i < number_of_dex_files; i++) { + std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + uint32_t expected_checksum = (*required_dex_checksums)[i]; + const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str()); + return false; } - - uint32_t expected_secondary_checksum = 0; - if (DexFile::GetChecksum(secondary_dex_location.c_str(), - &expected_secondary_checksum, error_msg)) { - uint32_t actual_secondary_checksum - = secondary_oat_dex_file->GetDexFileLocationChecksum(); - if (expected_secondary_checksum != actual_secondary_checksum) { - VLOG(oat) << "Dex checksum does not match for secondary dex: " - << secondary_dex_location - << ". Expected: " << expected_secondary_checksum - << ", Actual: " << actual_secondary_checksum; - return false; - } - } else { - // If we can't get the checksum for the secondary location, we assume - // the dex checksum is up to date for this and all other secondary dex - // files. - break; + uint32_t actual_checksum = oat_dex_file->GetDexFileLocationChecksum(); + if (expected_checksum != actual_checksum) { + VLOG(oat) << "Dex checksum does not match for dex: " << dex + << ". Expected: " << expected_checksum + << ", Actual: " << actual_checksum; + return false; } } return true; @@ -710,13 +690,16 @@ std::string OatFileAssistant::ImageLocation() { return image_spaces[0]->GetImageLocation(); } -const uint32_t* OatFileAssistant::GetRequiredDexChecksum() { - if (!required_dex_checksum_attempted_) { - required_dex_checksum_attempted_ = true; - required_dex_checksum_found_ = false; +const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { + if (!required_dex_checksums_attempted_) { + required_dex_checksums_attempted_ = true; + required_dex_checksums_found_ = false; + cached_required_dex_checksums_.clear(); std::string error_msg; - if (DexFile::GetChecksum(dex_location_.c_str(), &cached_required_dex_checksum_, &error_msg)) { - required_dex_checksum_found_ = true; + if (DexFile::GetMultiDexChecksums(dex_location_.c_str(), + &cached_required_dex_checksums_, + &error_msg)) { + required_dex_checksums_found_ = true; has_original_dex_files_ = true; } else { // This can happen if the original dex file has been stripped from the @@ -724,19 +707,23 @@ const uint32_t* OatFileAssistant::GetRequiredDexChecksum() { VLOG(oat) << "OatFileAssistant: " << error_msg; has_original_dex_files_ = false; - // Get the checksum from the odex if we can. + // Get the checksums from the odex if we can. const OatFile* odex_file = odex_.GetFile(); if (odex_file != nullptr) { - const OatFile::OatDexFile* odex_dex_file - = odex_file->GetOatDexFile(dex_location_.c_str(), nullptr); - if (odex_dex_file != nullptr) { - cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum(); - required_dex_checksum_found_ = true; + required_dex_checksums_found_ = true; + for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) { + std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr); + if (odex_dex_file == nullptr) { + required_dex_checksums_found_ = false; + break; + } + cached_required_dex_checksums_.push_back(odex_dex_file->GetDexFileLocationChecksum()); } } } } - return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr; + return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr; } const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 6d47ad2228..3ede29f5e0 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -400,13 +400,13 @@ class OatFileAssistant { // the oat file assistant. static std::string ImageLocation(); - // Gets the dex checksum required for an up-to-date oat file. - // Returns dex_checksum if a required checksum was located. Returns - // null if the required checksum was not found. - // The caller shouldn't clean up or free the returned pointer. - // This sets the has_original_dex_files_ field to true if a checksum was - // found for the dex_location_ dex file. - const uint32_t* GetRequiredDexChecksum(); + // Gets the dex checksums required for an up-to-date oat file. + // Returns cached_required_dex_checksums if the required checksums were + // located. Returns null if the required checksums were not found. The + // caller shouldn't clean up or free the returned pointer. This sets the + // has_original_dex_files_ field to true if the checksums were found for the + // dex_location_ dex file. + const std::vector<uint32_t>* GetRequiredDexChecksums(); // Returns the loaded image info. // Loads the image info if needed. Returns null if the image info failed @@ -430,11 +430,11 @@ class OatFileAssistant { // Whether we will attempt to load oat files executable. bool load_executable_ = false; - // Cached value of the required dex checksum. - // This should be accessed only by the GetRequiredDexChecksum() method. - uint32_t cached_required_dex_checksum_; - bool required_dex_checksum_attempted_ = false; - bool required_dex_checksum_found_; + // Cached value of the required dex checksums. + // This should be accessed only by the GetRequiredDexChecksums() method. + std::vector<uint32_t> cached_required_dex_checksums_; + bool required_dex_checksums_attempted_ = false; + bool required_dex_checksums_found_; bool has_original_dex_files_; OatFileInfo odex_; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index f777340cfd..6cfe3d1315 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -237,16 +237,16 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { EXPECT_EQ(2u, dex_files.size()); } -// Case: We have a MultiDEX file where the secondary dex file is out of date. +// Case: We have a MultiDEX file where the non-main multdex entry is out of date. // Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { - std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar"; +TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) { + std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar"; // Compile code for GetMultiDexSrc1. Copy(GetMultiDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum + // Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum // is out of date. Copy(GetMultiDexSrc2(), dex_location); @@ -256,6 +256,37 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a stripped MultiDEX file where the non-main multidex entry is +// out of date with respect to the odex file. +TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) { + std::string dex_location = GetScratchDir() + "/StrippedMultiDexNonMainOutOfDate.jar"; + std::string odex_location = GetOdexDir() + "/StrippedMultiDexNonMainOutOfDate.odex"; + + // Compile the oat from GetMultiDexSrc1. + Copy(GetMultiDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + // Compile the odex from GetMultiDexSrc2, which has a different non-main + // dex checksum. + Copy(GetMultiDexSrc2(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kInterpretOnly); + + // Strip the dex file. + Copy(GetStrippedDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, /*load_executable*/false); + + // Because the dex file is stripped, the odex file is considered the source + // of truth for the dex checksums. The oat file should be considered + // unusable. + std::unique_ptr<OatFile> best_file = oat_file_assistant.GetBestOatFile(); + ASSERT_TRUE(best_file.get() != nullptr); + EXPECT_EQ(best_file->GetLocation(), odex_location); + EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus()); +} + // Case: We have a MultiDEX file and up-to-date OAT file for it with relative // encoded dex locations. // Expect: The oat file status is kNoDexOptNeeded. @@ -336,16 +367,16 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); } -// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is -// out of date and there is no corresponding ODEX file. -TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) { +// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry +// is out of date and there is no corresponding ODEX file. +TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { // This test case is only meaningful if vdex is enabled. if (!kIsVdexEnabled) { return; } - std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar"; - std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat"; + std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar"; + std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat"; Copy(GetMultiDexSrc1(), dex_location); GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index b416b9dbad..d5fe1f382a 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -62,54 +62,4 @@ TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { "/data/app/foo/base.apk", "o/base.apk")); } -static std::vector<const DexFile*> ToConstDexFiles( - const std::vector<std::unique_ptr<const DexFile>>& in) { - std::vector<const DexFile*> ret; - for (auto& d : in) { - ret.push_back(d.get()); - } - return ret; -} - -TEST_F(OatFileTest, DexFileDependencies) { - std::string error_msg; - - // No dependencies. - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg; - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg; - - // Ill-formed dependencies. - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg)); - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg)); - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg)); - - // Unsatisfiable dependency. - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg)); - - // Load some dex files to be able to do a real test. - ScopedObjectAccess soa(Thread::Current()); - - std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main"); - std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1); - std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1); - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg)) - << error_msg << " " << encoding1; - std::vector<std::string> split1; - EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1)); - ASSERT_EQ(split1.size(), 1U); - EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation()); - - std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - EXPECT_GT(dex_files2.size(), 1U); - std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2); - std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2); - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg)) - << error_msg << " " << encoding2; - std::vector<std::string> split2; - EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2)); - ASSERT_EQ(split2.size(), 2U); - EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation()); - EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation()); -} - } // namespace art diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index d767c33282..843fd8c8e4 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -68,66 +68,6 @@ namespace openjdkjvmti { using android::base::StringPrintf; -// A helper that fills in a classes obsolete_methods_ and obsolete_dex_caches_ classExt fields as -// they are created. This ensures that we can always call any method of an obsolete ArtMethod object -// almost as soon as they are created since the GetObsoleteDexCache method will succeed. -class ObsoleteMap { - public: - art::ArtMethod* FindObsoleteVersion(art::ArtMethod* original) - REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) { - auto method_pair = id_map_.find(original); - if (method_pair != id_map_.end()) { - art::ArtMethod* res = obsolete_methods_->GetElementPtrSize<art::ArtMethod*>( - method_pair->second, art::kRuntimePointerSize); - DCHECK(res != nullptr); - DCHECK_EQ(original, res->GetNonObsoleteMethod()); - return res; - } else { - return nullptr; - } - } - - void RecordObsolete(art::ArtMethod* original, art::ArtMethod* obsolete) - REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) { - DCHECK(original != nullptr); - DCHECK(obsolete != nullptr); - int32_t slot = next_free_slot_++; - DCHECK_LT(slot, obsolete_methods_->GetLength()); - DCHECK(nullptr == - obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(slot, art::kRuntimePointerSize)); - DCHECK(nullptr == obsolete_dex_caches_->Get(slot)); - obsolete_methods_->SetElementPtrSize(slot, obsolete, art::kRuntimePointerSize); - obsolete_dex_caches_->Set(slot, original_dex_cache_); - id_map_.insert({original, slot}); - } - - ObsoleteMap(art::ObjPtr<art::mirror::PointerArray> obsolete_methods, - art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches, - art::ObjPtr<art::mirror::DexCache> original_dex_cache) - : next_free_slot_(0), - obsolete_methods_(obsolete_methods), - obsolete_dex_caches_(obsolete_dex_caches), - original_dex_cache_(original_dex_cache) { - // Figure out where the first unused slot in the obsolete_methods_ array is. - while (obsolete_methods_->GetElementPtrSize<art::ArtMethod*>( - next_free_slot_, art::kRuntimePointerSize) != nullptr) { - DCHECK(obsolete_dex_caches_->Get(next_free_slot_) != nullptr); - next_free_slot_++; - } - // Sanity check that the same slot in obsolete_dex_caches_ is free. - DCHECK(obsolete_dex_caches_->Get(next_free_slot_) == nullptr); - } - - private: - int32_t next_free_slot_; - std::unordered_map<art::ArtMethod*, int32_t> id_map_; - // Pointers to the fields in mirror::ClassExt. These can be held as ObjPtr since this is only used - // when we have an exclusive mutator_lock_ (i.e. all threads are suspended). - art::ObjPtr<art::mirror::PointerArray> obsolete_methods_; - art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches_; - art::ObjPtr<art::mirror::DexCache> original_dex_cache_; -}; - // This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does // some basic sanity checks that the obsolete method is sane. class ObsoleteMethodStackVisitor : public art::StackVisitor { @@ -136,7 +76,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::Thread* thread, art::LinearAlloc* allocator, const std::unordered_set<art::ArtMethod*>& obsoleted_methods, - ObsoleteMap* obsolete_maps) + /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps) : StackVisitor(thread, /*context*/nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), @@ -154,7 +94,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::Thread* thread, art::LinearAlloc* allocator, const std::unordered_set<art::ArtMethod*>& obsoleted_methods, - ObsoleteMap* obsolete_maps) + /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps) REQUIRES(art::Locks::mutator_lock_) { ObsoleteMethodStackVisitor visitor(thread, allocator, @@ -164,7 +104,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { } bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) { - art::ScopedAssertNoThreadSuspension snts("Fixing up the stack for obsolete methods."); art::ArtMethod* old_method = GetMethod(); if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) { // We cannot ensure that the right dex file is used in inlined frames so we don't support @@ -174,8 +113,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // TODO We should really support redefining intrinsics. // We don't support intrinsics so check for them here. DCHECK(!old_method->IsIntrinsic()); - art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method); - if (new_obsolete_method == nullptr) { + art::ArtMethod* new_obsolete_method = nullptr; + auto obsolete_method_pair = obsolete_maps_->find(old_method); + if (obsolete_method_pair == obsolete_maps_->end()) { // Create a new Obsolete Method and put it in the list. art::Runtime* runtime = art::Runtime::Current(); art::ClassLinker* cl = runtime->GetClassLinker(); @@ -189,7 +129,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass()); new_obsolete_method->SetIsObsolete(); new_obsolete_method->SetDontCompile(); - obsolete_maps_->RecordObsolete(old_method, new_obsolete_method); + obsolete_maps_->insert({old_method, new_obsolete_method}); // Update JIT Data structures to point to the new method. art::jit::Jit* jit = art::Runtime::Current()->GetJit(); if (jit != nullptr) { @@ -197,6 +137,8 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // structures to keep track of the new obsolete method. jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method); } + } else { + new_obsolete_method = obsolete_method_pair->second; } DCHECK(new_obsolete_method != nullptr); SetMethod(new_obsolete_method); @@ -210,9 +152,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // The set of all methods which could be obsoleted. const std::unordered_set<art::ArtMethod*>& obsoleted_methods_; // A map from the original to the newly allocated obsolete method for frames on this thread. The - // values in this map are added to the obsolete_methods_ (and obsolete_dex_caches_) fields of - // the redefined classes ClassExt as it is filled. - ObsoleteMap* obsolete_maps_; + // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of + // the redefined classes ClassExt by the caller. + std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_; }; jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED, @@ -489,12 +431,11 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi } struct CallbackCtx { - ObsoleteMap* obsolete_map; art::LinearAlloc* allocator; + std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map; std::unordered_set<art::ArtMethod*> obsolete_methods; - explicit CallbackCtx(ObsoleteMap* map, art::LinearAlloc* alloc) - : obsolete_map(map), allocator(alloc) {} + explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {} }; void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS { @@ -502,7 +443,7 @@ void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SA ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t, data->allocator, data->obsolete_methods, - data->obsolete_map); + &data->obsolete_map); } // This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is @@ -513,18 +454,9 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C art::mirror::ClassExt* ext = art_klass->GetExtData(); CHECK(ext->GetObsoleteMethods() != nullptr); art::ClassLinker* linker = driver_->runtime_->GetClassLinker(); - // This holds pointers to the obsolete methods map fields which are updated as needed. - ObsoleteMap map(ext->GetObsoleteMethods(), ext->GetObsoleteDexCaches(), art_klass->GetDexCache()); - CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader())); + CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader())); // Add all the declared methods to the map for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) { - // It is possible to simply filter out some methods where they cannot really become obsolete, - // such as native methods and keep their original (possibly optimized) implementations. We don't - // do this, however, since we would need to mark these functions (still in the classes - // declared_methods array) as obsolete so we will find the correct dex file to get meta-data - // from (for example about stack-frame size). Furthermore we would be unable to get some useful - // error checking from the interpreter which ensure we don't try to start executing obsolete - // methods. ctx.obsolete_methods.insert(&m); // TODO Allow this or check in IsModifiableClass. DCHECK(!m.IsIntrinsic()); @@ -534,6 +466,36 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C art::ThreadList* list = art::Runtime::Current()->GetThreadList(); list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx)); } + FillObsoleteMethodMap(art_klass, ctx.obsolete_map); +} + +// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to +// figure out their DexCaches. +void Redefiner::ClassRedefinition::FillObsoleteMethodMap( + art::mirror::Class* art_klass, + const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) { + int32_t index = 0; + art::mirror::ClassExt* ext_data = art_klass->GetExtData(); + art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods(); + art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches = + ext_data->GetObsoleteDexCaches(); + int32_t num_method_slots = obsolete_methods->GetLength(); + // Find the first empty index. + for (; index < num_method_slots; index++) { + if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>( + index, art::kRuntimePointerSize) == nullptr) { + break; + } + } + // Make sure we have enough space. + CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index)); + CHECK(obsolete_dex_caches->Get(index) == nullptr); + // Fill in the map. + for (auto& obs : obsoletes) { + obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize); + obsolete_dex_caches->Set(index, art_klass->GetDexCache()); + index++; + } } // Try and get the declared method. First try to get a virtual method then a direct method if that's diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 65ee2912e2..c441377b10 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -155,6 +155,12 @@ class Redefiner { void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) REQUIRES(art::Locks::mutator_lock_); + void FillObsoleteMethodMap( + art::mirror::Class* art_klass, + const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) + REQUIRES(art::Locks::mutator_lock_); + + // Checks that the dex file contains only the single expected class and that the top-level class // data has not been modified in an incompatible manner. bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_); diff --git a/runtime/stack.cc b/runtime/stack.cc index 51a24e4e01..d7ba1d75d8 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -874,13 +874,9 @@ void StackVisitor::WalkStack(bool include_transitions) { CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee) << " Found: " << ArtMethod::PrettyMethod(GetMethod()); } else { - // Instrumentation generally doesn't distinguish between a method's obsolete and - // non-obsolete version. - CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(), - GetMethod()->GetNonObsoleteMethod()) - << "Expected: " - << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod()) - << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod()); + CHECK_EQ(instrumentation_frame.method_, GetMethod()) + << "Expected: " << ArtMethod::PrettyMethod(instrumentation_frame.method_) + << " Found: " << ArtMethod::PrettyMethod(GetMethod()); } if (num_frames_ != 0) { // Check agreement of frame Ids only if num_frames_ is computed to avoid infinite @@ -907,7 +903,7 @@ void StackVisitor::WalkStack(bool include_transitions) { << " native=" << method->IsNative() << std::noboolalpha << " entrypoints=" << method->GetEntryPointFromQuickCompiledCode() - << "," << (method->IsNative() ? method->GetEntryPointFromJni() : nullptr) + << "," << method->GetEntryPointFromJni() << " next=" << *cur_quick_frame_; } diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java index df0a3ddf48..5a43a4f23f 100644 --- a/test/021-string2/src/Main.java +++ b/test/021-string2/src/Main.java @@ -117,6 +117,9 @@ public class Main { " " + $noinline$equals(s0_3, s0_1) + " " + $noinline$equals(s0_3, s0_2) + " " + $noinline$equals(s0_3, s0_3)); + + testEqualsConstString(); + testConstStringEquals(); } public static void testCompareToAndEquals() { @@ -539,6 +542,266 @@ public class Main { } } + public static void testEqualsConstString() { + Assert.assertTrue($noinline$equalsConstString0("")); + Assert.assertFalse($noinline$equalsConstString0("1")); + + Assert.assertTrue($noinline$equalsConstString7("0123456")); + Assert.assertFalse($noinline$equalsConstString7("012345")); + Assert.assertFalse($noinline$equalsConstString7("01234567")); + Assert.assertFalse($noinline$equalsConstString7("012345x")); + Assert.assertFalse($noinline$equalsConstString7("012345\u0440")); + + Assert.assertTrue($noinline$equalsConstString14("01234567890123")); + Assert.assertFalse($noinline$equalsConstString14("0123456789012")); + Assert.assertFalse($noinline$equalsConstString14("012345678901234")); + Assert.assertFalse($noinline$equalsConstString14("0123456789012x")); + Assert.assertFalse($noinline$equalsConstString14("0123456789012\u0440")); + + Assert.assertTrue($noinline$equalsConstString24("012345678901234567890123")); + Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012")); + Assert.assertFalse($noinline$equalsConstString24("0123456789012345678901234")); + Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012x")); + Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012\u0440")); + + Assert.assertTrue($noinline$equalsConstString29("01234567890123456789012345678")); + Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567")); + Assert.assertFalse($noinline$equalsConstString29("012345678901234567890123456789")); + Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567x")); + Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567\u0440")); + + Assert.assertTrue($noinline$equalsConstString35("01234567890123456789012345678901234")); + Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123")); + Assert.assertFalse($noinline$equalsConstString35("012345678901234567890123456789012345")); + Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$equalsConstString35("0123456789012345678901234567890123\u0440")); + + Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456")); + + Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("01234567890123")); + + Assert.assertTrue($noinline$equalsConstNonAsciiString24("\u044012345678901234567890123")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u0440123456789012345678901234")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\012345678901234567890123")); + + Assert.assertTrue( + $noinline$equalsConstNonAsciiString29("\u04401234567890123456789012345678")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString29("\u044012345678901234567890123456789")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString29("01234567890123456789012345678")); + + Assert.assertTrue( + $noinline$equalsConstNonAsciiString35("\u04401234567890123456789012345678901234")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("\u044012345678901234567890123456789012345")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("01234567890123456789012345678901234")); + } + + public static void testConstStringEquals() { + Assert.assertTrue($noinline$constString0Equals("")); + Assert.assertFalse($noinline$constString0Equals("1")); + + Assert.assertTrue($noinline$constString7Equals("0123456")); + Assert.assertFalse($noinline$constString7Equals("012345")); + Assert.assertFalse($noinline$constString7Equals("01234567")); + Assert.assertFalse($noinline$constString7Equals("012345x")); + Assert.assertFalse($noinline$constString7Equals("012345\u0440")); + + Assert.assertTrue($noinline$constString14Equals("01234567890123")); + Assert.assertFalse($noinline$constString14Equals("0123456789012")); + Assert.assertFalse($noinline$constString14Equals("012345678901234")); + Assert.assertFalse($noinline$constString14Equals("0123456789012x")); + Assert.assertFalse($noinline$constString14Equals("0123456789012\u0440")); + + Assert.assertTrue($noinline$constString24Equals("012345678901234567890123")); + Assert.assertFalse($noinline$constString24Equals("01234567890123456789012")); + Assert.assertFalse($noinline$constString24Equals("0123456789012345678901234")); + Assert.assertFalse($noinline$constString24Equals("01234567890123456789012x")); + Assert.assertFalse($noinline$constString24Equals("01234567890123456789012\u0440")); + + Assert.assertTrue($noinline$constString29Equals("01234567890123456789012345678")); + Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567")); + Assert.assertFalse($noinline$constString29Equals("012345678901234567890123456789")); + Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567x")); + Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567\u0440")); + + Assert.assertTrue($noinline$constString35Equals("01234567890123456789012345678901234")); + Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123")); + Assert.assertFalse($noinline$constString35Equals("012345678901234567890123456789012345")); + Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$constString35Equals("0123456789012345678901234567890123\u0040")); + + Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456")); + + Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012x")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("01234567890123")); + + Assert.assertTrue($noinline$constNonAsciiString24Equals("\u044012345678901234567890123")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\u0440123456789012345678901234")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012x")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\012345678901234567890123")); + + Assert.assertTrue( + $noinline$constNonAsciiString29Equals("\u04401234567890123456789012345678")); + Assert.assertFalse( + $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567")); + Assert.assertFalse( + $noinline$constNonAsciiString29Equals("\u044012345678901234567890123456789")); + Assert.assertFalse( + $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567x")); + Assert.assertFalse($noinline$constNonAsciiString29Equals("01234567890123456789012345678")); + + Assert.assertTrue( + $noinline$constNonAsciiString35Equals("\u04401234567890123456789012345678901234")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("\u044012345678901234567890123456789012345")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234")); + } + + public static boolean $noinline$equalsConstString0(String s) { + if (doThrow) { throw new Error(); } + return s.equals(""); + } + + public static boolean $noinline$equalsConstString7(String s) { + if (doThrow) { throw new Error(); } + return s.equals("0123456"); + } + + public static boolean $noinline$equalsConstString14(String s) { + if (doThrow) { throw new Error(); } + return s.equals("01234567890123"); + } + + public static boolean $noinline$equalsConstString24(String s) { + if (doThrow) { throw new Error(); } + return s.equals("012345678901234567890123"); + } + + public static boolean $noinline$equalsConstString29(String s) { + if (doThrow) { throw new Error(); } + return s.equals("01234567890123456789012345678"); + } + + public static boolean $noinline$equalsConstString35(String s) { + if (doThrow) { throw new Error(); } + return s.equals("01234567890123456789012345678901234"); + } + + public static boolean $noinline$equalsConstNonAsciiString7(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u0440123456"); + } + + public static boolean $noinline$equalsConstNonAsciiString14(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u04401234567890123"); + } + + public static boolean $noinline$equalsConstNonAsciiString24(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u044012345678901234567890123"); + } + + public static boolean $noinline$equalsConstNonAsciiString29(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u04401234567890123456789012345678"); + } + + public static boolean $noinline$equalsConstNonAsciiString35(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u04401234567890123456789012345678901234"); + } + + public static boolean $noinline$constString0Equals(String s) { + if (doThrow) { throw new Error(); } + return s.equals(""); + } + + public static boolean $noinline$constString7Equals(String s) { + if (doThrow) { throw new Error(); } + return "0123456".equals(s); + } + + public static boolean $noinline$constString14Equals(String s) { + if (doThrow) { throw new Error(); } + return "01234567890123".equals(s); + } + + public static boolean $noinline$constString24Equals(String s) { + if (doThrow) { throw new Error(); } + return "012345678901234567890123".equals(s); + } + + public static boolean $noinline$constString29Equals(String s) { + if (doThrow) { throw new Error(); } + return "01234567890123456789012345678".equals(s); + } + + public static boolean $noinline$constString35Equals(String s) { + if (doThrow) { throw new Error(); } + return "01234567890123456789012345678901234".equals(s); + } + + public static boolean $noinline$constNonAsciiString7Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u0440123456".equals(s); + } + + public static boolean $noinline$constNonAsciiString14Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u04401234567890123".equals(s); + } + + public static boolean $noinline$constNonAsciiString24Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u044012345678901234567890123".equals(s); + } + + public static boolean $noinline$constNonAsciiString29Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u04401234567890123456789012345678".equals(s); + } + + public static boolean $noinline$constNonAsciiString35Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u04401234567890123456789012345678901234".equals(s); + } + public static int $noinline$compareTo(String lhs, String rhs) { if (doThrow) { throw new Error(); } return lhs.compareTo(rhs); diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index ed7524c7ad..52f3f84406 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -329,7 +329,7 @@ public class Main { /// CHECK-NOT: cbz // Terminate the scope for the CHECK-NOT search at the reference or length comparison, // whichever comes first. - /// CHECK: cmp {{w.*,}} {{w.*}} + /// CHECK: cmp {{w.*,}} {{w.*|#.*}} public static boolean stringArgumentNotNull(Object obj) { obj.getClass(); return "foo".equals(obj); @@ -380,10 +380,10 @@ public class Main { // so repeat the check twice. /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}] /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0] - /// CHECK: cmp {{w\d+}}, {{w\d+}} + /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}} /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}] /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0] - /// CHECK: cmp {{w\d+}}, {{w\d+}} + /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}} public static boolean stringArgumentIsString() { return "foo".equals(myString); } diff --git a/test/945-obsolete-native/build b/test/945-obsolete-native/build deleted file mode 100755 index 898e2e54a2..0000000000 --- a/test/945-obsolete-native/build +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# Copyright 2016 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. - -./default-build "$@" --experimental agents diff --git a/test/945-obsolete-native/expected.txt b/test/945-obsolete-native/expected.txt deleted file mode 100644 index 83efda144d..0000000000 --- a/test/945-obsolete-native/expected.txt +++ /dev/null @@ -1,9 +0,0 @@ -hello -Not doing anything here -goodbye -hello -transforming calling function -goodbye -Hello - Transformed -Not doing anything here -Goodbye - Transformed diff --git a/test/945-obsolete-native/info.txt b/test/945-obsolete-native/info.txt deleted file mode 100644 index c8b892cedd..0000000000 --- a/test/945-obsolete-native/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests basic obsolete method support diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc deleted file mode 100644 index 061e7afbbc..0000000000 --- a/test/945-obsolete-native/obsolete_native.cc +++ /dev/null @@ -1,51 +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. - */ - -#include <inttypes.h> -#include <memory> -#include <stdio.h> - -#include "android-base/stringprintf.h" - -#include "android-base/stringprintf.h" -#include "base/logging.h" -#include "base/macros.h" -#include "jni.h" -#include "openjdkjvmti/jvmti.h" -#include "ScopedLocalRef.h" -#include "ti-agent/common_helper.h" -#include "ti-agent/common_load.h" - -namespace art { -namespace Test945ObsoleteNative { - -extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative( - JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { - BindFunctions(jvmti_env, env, "Transform"); -} - -extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env, - jclass klass ATTRIBUTE_UNUSED, - jobject runnable) { - jclass runnable_klass = env->FindClass("java/lang/Runnable"); - DCHECK(runnable_klass != nullptr); - jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V"); - env->CallVoidMethod(runnable, run_method); -} - - -} // namespace Test945ObsoleteNative -} // namespace art diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run deleted file mode 100755 index c6e62ae6cd..0000000000 --- a/test/945-obsolete-native/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# Copyright 2016 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. - -./default-run "$@" --jvmti diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java deleted file mode 100644 index 5e2154e9a3..0000000000 --- a/test/945-obsolete-native/src/Main.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2016 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.util.Base64; - -public class Main { - // class Transform { - // public void sayHi(Runnable r) { - // System.out.println("Hello - Transformed"); - // doExecute(r); - // System.out.println("Goodbye - Transformed"); - // } - // - // private static native void doExecute(Runnable r); - // } - private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( - "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" + - "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" + - "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" + - "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" + - "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" + - "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" + - "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" + - "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" + - "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR"); - private static final byte[] DEX_BYTES = Base64.getDecoder().decode( - "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" + - "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" + - "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" + - "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" + - "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" + - "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" + - "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" + - "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" + - "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" + - "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" + - "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" + - "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" + - "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" + - "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" + - "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" + - "AA=="); - - public static void main(String[] args) { - bindTest945ObsoleteNative(); - doTest(new Transform()); - } - - public static void doTest(Transform t) { - t.sayHi(() -> { System.out.println("Not doing anything here"); }); - t.sayHi(() -> { - System.out.println("transforming calling function"); - doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); - }); - t.sayHi(() -> { System.out.println("Not doing anything here"); }); - } - - // Transforms the class - private static native void doCommonClassRedefinition(Class<?> target, - byte[] classfile, - byte[] dexfile); - - private static native void bindTest945ObsoleteNative(); -} diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java deleted file mode 100644 index 2b7cc1b3a1..0000000000 --- a/test/945-obsolete-native/src/Transform.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2016 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 Transform { - public void sayHi(Runnable r) { - System.out.println("hello"); - doExecute(r); - System.out.println("goodbye"); - } - - private static native void doExecute(Runnable r); -} diff --git a/test/Android.bp b/test/Android.bp index 00c890a834..d3244a683a 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -274,7 +274,6 @@ art_cc_defaults { "933-misc-events/misc_events.cc", "936-search-onload/search_onload.cc", "944-transform-classloaders/classloader.cc", - "945-obsolete-native/obsolete_native.cc", ], shared_libs: [ "libbase", diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 351857d1d9..c5a93568c6 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -122,7 +122,6 @@ static AgentLib agents[] = { { "942-private-recursive", common_redefine::OnLoad, nullptr }, { "943-private-recursive-jit", common_redefine::OnLoad, nullptr }, { "944-transform-classloaders", common_redefine::OnLoad, nullptr }, - { "945-obsolete-native", common_redefine::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 6e123ce7e4..729a3e5ac4 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Exit as a stop-gap measure for b/35308152. -exit 0 - if [ ! -d libcore ]; then echo "Script needs to be run at the root of the android tree" exit 1 |