diff options
author | 2017-03-24 15:21:34 +0000 | |
---|---|---|
committer | 2017-03-24 15:27:20 -0700 | |
commit | 2f814aab42591b7b4093d79851d9d2920538a5ef (patch) | |
tree | 7ff34f1d63f8520208dd4689171968bbc59f983b | |
parent | a7c04c4fe6623985ba2d0cca942d5e1e584f4358 (diff) |
Revert "Revert "Make original dex file be more deduplicated""
This reverts commit a7c04c4fe6623985ba2d0cca942d5e1e584f4358.
Reason for revert: Issue with target buildbots was fixed
Test: m clean && ./test.py --target -j8
Bug: 31455788
Change-Id: If00ef9433a9308810a40ea4e0d809987c934cef8
-rw-r--r-- | runtime/class_linker_test.cc | 2 | ||||
-rw-r--r-- | runtime/mirror/class_ext.cc | 4 | ||||
-rw-r--r-- | runtime/mirror/class_ext.h | 8 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_class.cc | 2 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_redefine.cc | 43 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_redefine.h | 4 | ||||
-rw-r--r-- | runtime/openjdkjvmti/transform.cc | 25 | ||||
-rw-r--r-- | test/981-dedup-original-dex/expected.txt | 0 | ||||
-rw-r--r-- | test/981-dedup-original-dex/info.txt | 4 | ||||
-rwxr-xr-x | test/981-dedup-original-dex/run | 17 | ||||
-rw-r--r-- | test/981-dedup-original-dex/src/Main.java | 139 | ||||
-rw-r--r-- | test/981-dedup-original-dex/src/Transform.java | 21 | ||||
-rw-r--r-- | test/981-dedup-original-dex/src/Transform2.java | 21 | ||||
-rw-r--r-- | test/ti-agent/common_load.cc | 1 |
14 files changed, 251 insertions, 40 deletions
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 9f04e598eb..b421810113 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -618,7 +618,7 @@ struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") { addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods"); - addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_bytes_), "originalDexFile"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_), "originalDexFile"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError"); } }; diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 5dc3aca094..94e4b88f6c 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -117,9 +117,9 @@ void ClassExt::SetVerifyError(ObjPtr<Object> err) { } } -void ClassExt::SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) { +void ClassExt::SetOriginalDexFile(ObjPtr<Object> bytes) { DCHECK(!Runtime::Current()->IsActiveTransaction()); - SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_), bytes); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_), bytes); } void ClassExt::SetClass(ObjPtr<Class> dalvik_system_ClassExt) { diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index fac955a45e..708665d46b 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -60,11 +60,11 @@ class MANAGED ClassExt : public Object { OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_)); } - ByteArray* GetOriginalDexFileBytes() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetFieldObject<ByteArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_)); + Object* GetOriginalDexFile() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<Object>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_)); } - void SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) REQUIRES_SHARED(Locks::mutator_lock_); + void SetOriginalDexFile(ObjPtr<Object> bytes) REQUIRES_SHARED(Locks::mutator_lock_); void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches) REQUIRES_SHARED(Locks::mutator_lock_); @@ -89,7 +89,7 @@ class MANAGED ClassExt : public Object { HeapReference<PointerArray> obsolete_methods_; - HeapReference<ByteArray> original_dex_file_bytes_; + HeapReference<Object> original_dex_file_; // The saved verification error of this class. HeapReference<Object> verify_error_; diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 2d1b25ed26..38fd1d4af3 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -259,7 +259,7 @@ struct ClassCallback : public art::ClassLoadCallback { } // Actually set the ClassExt's original bytes once we have actually succeeded. - ext->SetOriginalDexFileBytes(arr.Get()); + ext->SetOriginalDexFile(arr.Get()); // Set the return values *final_class_def = &dex_file->GetClassDef(0); *final_dex_file = dex_file.release(); diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 9c1d6ef0a5..7faddfb0f9 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -469,7 +469,7 @@ void Redefiner::RecordFailure(jvmtiError result, result_ = result; } -art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() { +art::mirror::Object* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFile() { // If we have been specifically given a new set of bytes use that if (original_dex_file_.size() != 0) { return art::mirror::ByteArray::AllocateAndFill( @@ -481,24 +481,21 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi // See if we already have one set. art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData()); if (!ext.IsNull()) { - art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes()); - if (!old_original_bytes.IsNull()) { + art::ObjPtr<art::mirror::Object> old_original_dex_file(ext->GetOriginalDexFile()); + if (!old_original_dex_file.IsNull()) { // We do. Use it. - return old_original_bytes.Ptr(); + return old_original_dex_file.Ptr(); } } - // Copy the current dex_file - const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile(); + // return the current dex_cache which has the dex file in it. + art::ObjPtr<art::mirror::DexCache> current_dex_cache(GetMirrorClass()->GetDexCache()); // TODO Handle this or make it so it cannot happen. - if (current_dex_file.NumClassDefs() != 1) { + if (current_dex_cache->GetDexFile()->NumClassDefs() != 1) { LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses " << "on this class might fail if no transformations are applied to it!"; } - return art::mirror::ByteArray::AllocateAndFill( - driver_->self_, - reinterpret_cast<const signed char*>(current_dex_file.Begin()), - current_dex_file.Size()); + return current_dex_cache.Ptr(); } struct CallbackCtx { @@ -847,9 +844,9 @@ class RedefinitionDataHolder { return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass)); } - art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) const + art::mirror::Object* GetOriginalDexFile(jint klass_index) const REQUIRES_SHARED(art::Locks::mutator_lock_) { - return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile)); + return art::down_cast<art::mirror::Object*>(GetSlot(klass_index, kSlotOrigDexFile)); } void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader) @@ -872,7 +869,7 @@ class RedefinitionDataHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotMirrorClass, klass); } - void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes) + void SetOriginalDexFile(jint klass_index, art::mirror::Object* bytes) REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotOrigDexFile, bytes); } @@ -985,9 +982,9 @@ class RedefinitionDataIter { art::mirror::Class* GetMirrorClass() const REQUIRES_SHARED(art::Locks::mutator_lock_) { return holder_.GetMirrorClass(idx_); } - art::mirror::ByteArray* GetOriginalDexFileBytes() const + art::mirror::Object* GetOriginalDexFile() const REQUIRES_SHARED(art::Locks::mutator_lock_) { - return holder_.GetOriginalDexFileBytes(idx_); + return holder_.GetOriginalDexFile(idx_); } int32_t GetIndex() const { return idx_; @@ -1010,9 +1007,9 @@ class RedefinitionDataIter { void SetMirrorClass(art::mirror::Class* klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { holder_.SetMirrorClass(idx_, klass); } - void SetOriginalDexFileBytes(art::mirror::ByteArray* bytes) + void SetOriginalDexFile(art::mirror::Object* bytes) REQUIRES_SHARED(art::Locks::mutator_lock_) { - holder_.SetOriginalDexFileBytes(idx_, bytes); + holder_.SetOriginalDexFile(idx_, bytes); } private: @@ -1138,8 +1135,8 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( } // We won't always need to set this field. - cur_data->SetOriginalDexFileBytes(AllocateOrGetOriginalDexFileBytes()); - if (cur_data->GetOriginalDexFileBytes() == nullptr) { + cur_data->SetOriginalDexFile(AllocateOrGetOriginalDexFile()); + if (cur_data->GetOriginalDexFile() == nullptr) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file"); @@ -1285,7 +1282,7 @@ jvmtiError Redefiner::Run() { art::mirror::Class* klass = data.GetMirrorClass(); // TODO Rewrite so we don't do a stack walk for each and every class. redef.FindAndAllocateObsoleteMethods(klass); - redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFileBytes()); + redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile()); } // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any // are, force a full-world deoptimization before finishing redefinition. If we don't do this then @@ -1365,7 +1362,7 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> void Redefiner::ClassRedefinition::UpdateClass( art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache, - art::ObjPtr<art::mirror::ByteArray> original_dex_file) { + art::ObjPtr<art::mirror::Object> original_dex_file) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0); UpdateMethods(mclass, new_dex_cache, class_def); @@ -1379,7 +1376,7 @@ void Redefiner::ClassRedefinition::UpdateClass( mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str()))); art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData()); CHECK(!ext.IsNull()); - ext->SetOriginalDexFileBytes(original_dex_file); + ext->SetOriginalDexFile(original_dex_file); } // This function does all (java) allocations we need to do for the Class being redefined. diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 4313a9476e..6c09d46e89 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -137,7 +137,7 @@ class Redefiner { REQUIRES_SHARED(art::Locks::mutator_lock_); // This may return nullptr with a OOME pending if allocation fails. - art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes() + art::mirror::Object* AllocateOrGetOriginalDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); void RecordFailure(jvmtiError e, const std::string& err) { @@ -196,7 +196,7 @@ class Redefiner { void UpdateClass(art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache, - art::ObjPtr<art::mirror::ByteArray> original_dex_file) + art::ObjPtr<art::mirror::Object> original_dex_file) REQUIRES(art::Locks::mutator_lock_); void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index bd52cbb7f9..06aecbaee3 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -150,16 +150,27 @@ jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env, art::Handle<art::mirror::Class> klass, /*out*/jint* dex_data_len, /*out*/unsigned char** dex_data) { - art::StackHandleScope<2> hs(art::Thread::Current()); + art::StackHandleScope<3> hs(art::Thread::Current()); art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData())); if (!ext.IsNull()) { - art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes())); + art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile())); if (!orig_dex.IsNull()) { - *dex_data_len = static_cast<jint>(orig_dex->GetLength()); - return CopyDataIntoJvmtiBuffer(env, - reinterpret_cast<const unsigned char*>(orig_dex->GetData()), - *dex_data_len, - /*out*/dex_data); + if (orig_dex->IsArrayInstance()) { + DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte()); + art::Handle<art::mirror::ByteArray> orig_dex_bytes( + hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray()))); + *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength()); + return CopyDataIntoJvmtiBuffer( + env, + reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()), + *dex_data_len, + /*out*/dex_data); + } else { + DCHECK(orig_dex->IsDexCache()); + const art::DexFile* dex_file = orig_dex->AsDexCache()->GetDexFile(); + *dex_data_len = static_cast<jint>(dex_file->Size()); + return CopyDataIntoJvmtiBuffer(env, dex_file->Begin(), dex_file->Size(), /*out*/dex_data); + } } } // TODO De-quicken the dex file before passing it to the agents. diff --git a/test/981-dedup-original-dex/expected.txt b/test/981-dedup-original-dex/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/981-dedup-original-dex/expected.txt diff --git a/test/981-dedup-original-dex/info.txt b/test/981-dedup-original-dex/info.txt new file mode 100644 index 0000000000..62696e00d7 --- /dev/null +++ b/test/981-dedup-original-dex/info.txt @@ -0,0 +1,4 @@ +Tests basic functions in the jvmti plugin. + +This checks that we do not needlessly duplicate the contents of retransformed +classes original dex files. diff --git a/test/981-dedup-original-dex/run b/test/981-dedup-original-dex/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/981-dedup-original-dex/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 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. + +./default-run "$@" --jvmti diff --git a/test/981-dedup-original-dex/src/Main.java b/test/981-dedup-original-dex/src/Main.java new file mode 100644 index 0000000000..cd3f007532 --- /dev/null +++ b/test/981-dedup-original-dex/src/Main.java @@ -0,0 +1,139 @@ +/* + * 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.lang.reflect.Field; +import java.util.Base64; + +import dalvik.system.ClassExt; + +public class Main { + + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + /** + * base64 encoded class/dex file for + * class Transform2 { + * public void sayHi() { + * System.out.println("Goodbye2"); + * } + * } + */ + private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode( + "ZGV4CjAzNQAjXDED2iflQ3NXbPtBRVjQVMqoDU9nDz/QAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" + + "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" + + "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTIADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJp" + + "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" + + "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" + + "A291dAAHcHJpbnRsbgAFc2F5SGkAAQAHDgADAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" + + "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" + + "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" + + "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA"); + + public static void main(String[] args) { + try { + doTest(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void assertSame(Object a, Object b) throws Exception { + if (a != b) { + throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " + + "'" + (b != null ? b.toString() : "null") + "'"); + } + } + + private static Object getObjectField(Object o, String name) throws Exception { + return getObjectField(o, o.getClass(), name); + } + + private static Object getObjectField(Object o, Class<?> type, String name) throws Exception { + Field f = type.getDeclaredField(name); + f.setAccessible(true); + return f.get(o); + } + + private static Object getOriginalDexFile(Class<?> k) throws Exception { + ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData"); + if (ext_data_object == null) { + return null; + } + + return getObjectField(ext_data_object, "originalDexFile"); + } + + public static void doTest() throws Exception { + // Make sure both of these are loaded prior to transformations being added so they have the same + // original dex files. + Transform t1 = new Transform(); + Transform2 t2 = new Transform2(); + + assertSame(null, getOriginalDexFile(t1.getClass())); + assertSame(null, getOriginalDexFile(t2.getClass())); + assertSame(null, getOriginalDexFile(Main.class)); + + addCommonTransformationResult("Transform", new byte[0], DEX_BYTES_1); + addCommonTransformationResult("Transform2", new byte[0], DEX_BYTES_2); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class, Transform2.class); + + assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass())); + assertSame(null, getOriginalDexFile(Main.class)); + // Make sure that the original dex file is a DexCache object. + assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache")); + + // Check that we end up with a byte[] if we do a direct RedefineClasses + enableCommonRetransformation(false); + doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1); + assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass()); + } + + // Transforms the class + private static native void doCommonClassRetransformation(Class<?>... target); + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); + private static native void enableCommonRetransformation(boolean enable); + private static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/981-dedup-original-dex/src/Transform.java b/test/981-dedup-original-dex/src/Transform.java new file mode 100644 index 0000000000..3c97907ddc --- /dev/null +++ b/test/981-dedup-original-dex/src/Transform.java @@ -0,0 +1,21 @@ +/* + * 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() { + System.out.println("hello"); + } +} diff --git a/test/981-dedup-original-dex/src/Transform2.java b/test/981-dedup-original-dex/src/Transform2.java new file mode 100644 index 0000000000..eb22842184 --- /dev/null +++ b/test/981-dedup-original-dex/src/Transform2.java @@ -0,0 +1,21 @@ +/* + * 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 Transform2 { + public void sayHi() { + System.out.println("hello2"); + } +} diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index fddae3af02..8cb14bdfc5 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -121,6 +121,7 @@ static AgentLib agents[] = { { "943-private-recursive-jit", common_redefine::OnLoad, nullptr }, { "944-transform-classloaders", common_redefine::OnLoad, nullptr }, { "945-obsolete-native", common_redefine::OnLoad, nullptr }, + { "981-dedup-original-dex", common_retransform::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { |