summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2017-03-21 11:21:36 -0700
committer Alex Light <allight@google.com> 2017-03-22 09:43:30 -0700
commit9556cb2bfc6788656d53a9ddb2159ca2040f6e38 (patch)
tree7ff34f1d63f8520208dd4689171968bbc59f983b
parentf11cbe1361cc93f07e7a5ab1c9f0af4605552535 (diff)
Make original dex file be more deduplicated
Now when retransformClasses is called we will use the DexCache from the transformed class to hold the original dex file. This means that there should be fewer redundant copies of dex files lying around from class redefinition. Bug: 31455788 Test: mma -j40 test-art-host-gtest Test: ./test/testrunner/testrunner.py --host -j40 Change-Id: I60150e436d79aabb36da31ec679cb7ec4c820573
-rw-r--r--runtime/class_linker_test.cc2
-rw-r--r--runtime/mirror/class_ext.cc4
-rw-r--r--runtime/mirror/class_ext.h8
-rw-r--r--runtime/openjdkjvmti/ti_class.cc2
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc43
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h4
-rw-r--r--runtime/openjdkjvmti/transform.cc25
-rw-r--r--test/981-dedup-original-dex/expected.txt0
-rw-r--r--test/981-dedup-original-dex/info.txt4
-rwxr-xr-xtest/981-dedup-original-dex/run17
-rw-r--r--test/981-dedup-original-dex/src/Main.java139
-rw-r--r--test/981-dedup-original-dex/src/Transform.java21
-rw-r--r--test/981-dedup-original-dex/src/Transform2.java21
-rw-r--r--test/ti-agent/common_load.cc1
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) {