Simplify class redefinition.
- For debuggable apps, do not compile with compact dex.
- Eagerly return the dex file (and don't do lazy unquickening) in
ArtClassDefinition.
Test: cpct/carservice/carservice_test
Test: suite/test-mapping-presubmit-retry_cloud-tf
Test: unbundled/launcher/nexus_unit_test_multi_device_platform
Test: FrameworksIkeTests:com.android.internal.net.eap.test.EapMsChapV2Test
Test: test.py
Bug: 196414062
Change-Id: I45ae220fa62aa41ff5fd8dd0e893090842ee18a6
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ee65241..4770a37 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1203,6 +1203,12 @@
thread_count_ = 1;
}
+ // For debuggable apps, we do not want to generate compact dex as class
+ // redefinition will want a proper dex file.
+ if (compiler_options_->GetDebuggable()) {
+ compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone;
+ }
+
PaletteShouldReportDex2oatCompilation(&should_report_dex2oat_compilation_);
AssignTrueIfExists(args, M::ForcePaletteCompilationHooks, &should_report_dex2oat_compilation_);
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index a480ac0..2c9cc1c 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -36,6 +36,7 @@
#include "class_linker-inl.h"
#include "class_root-inl.h"
#include "dex/dex_file.h"
+#include "dex/art_dex_file_loader.h"
#include "fixed_up_dex_file.h"
#include "handle.h"
#include "handle_scope-inl.h"
@@ -132,177 +133,6 @@
return OK;
}
-static void DequickenDexFile(const art::DexFile* dex_file,
- const char* descriptor,
- /*out*/std::vector<unsigned char>* dex_data)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- std::unique_ptr<FixedUpDexFile> fixed_dex_file(
- FixedUpDexFile::Create(*dex_file, descriptor));
- dex_data->resize(fixed_dex_file->Size());
- memcpy(dex_data->data(), fixed_dex_file->Begin(), fixed_dex_file->Size());
-}
-
-// Gets the data surrounding the given class.
-static void GetDexDataForRetransformation(art::Handle<art::mirror::Class> klass,
- /*out*/std::vector<unsigned char>* dex_data)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::StackHandleScope<3> hs(art::Thread::Current());
- art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
- const art::DexFile* dex_file = nullptr;
- if (!ext.IsNull()) {
- art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile()));
- if (!orig_dex.IsNull()) {
- if (orig_dex->IsArrayInstance()) {
- DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
- art::Handle<art::mirror::ByteArray> orig_dex_bytes(hs.NewHandle(orig_dex->AsByteArray()));
- dex_data->resize(orig_dex_bytes->GetLength());
- memcpy(dex_data->data(), orig_dex_bytes->GetData(), dex_data->size());
- return;
- } else if (orig_dex->IsDexCache()) {
- dex_file = orig_dex->AsDexCache()->GetDexFile();
- } else {
- DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
- << "Expected java/lang/Long but found object of type "
- << orig_dex->GetClass()->PrettyClass();
- art::ObjPtr<art::mirror::Class> prim_long_class(
- art::GetClassRoot(art::ClassRoot::kPrimitiveLong));
- art::JValue val;
- if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) {
- // This should never happen.
- LOG(FATAL) << "Unable to unbox a primitive long value!";
- }
- dex_file = reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
- }
- }
- }
- if (dex_file == nullptr) {
- dex_file = &klass->GetDexFile();
- }
- std::string storage;
- DequickenDexFile(dex_file, klass->GetDescriptor(&storage), dex_data);
-}
-
-static bool DexNeedsDequickening(art::Handle<art::mirror::Class> klass,
- /*out*/ bool* from_class_ext)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
- if (ext.IsNull()) {
- // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
- *from_class_ext = false;
- return true;
- }
- art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
- if (orig_dex.IsNull()) {
- // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
- *from_class_ext = false;
- return true;
- } else if (!orig_dex->IsArrayInstance()) {
- // We were redefined but the original is held in a dex-cache or dex file. This means that the
- // original dex file is the one from the disk, which might be quickened.
- DCHECK(orig_dex->IsDexCache() || orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"));
- *from_class_ext = true;
- return true;
- } else {
- // An array instance means the original-dex-file is from a redefineClasses which cannot have any
- // quickening, so it's fine to use directly.
- DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
- *from_class_ext = true;
- return false;
- }
-}
-
-static const art::DexFile* GetQuickenedDexFile(art::Handle<art::mirror::Class> klass)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
- if (ext.IsNull() || ext->GetOriginalDexFile() == nullptr) {
- return &klass->GetDexFile();
- }
-
- art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
- DCHECK(!orig_dex->IsArrayInstance());
- if (orig_dex->IsDexCache()) {
- return orig_dex->AsDexCache()->GetDexFile();
- }
-
- DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
- << "Expected java/lang/Long but found object of type "
- << orig_dex->GetClass()->PrettyClass();
- art::ObjPtr<art::mirror::Class> prim_long_class(
- art::GetClassRoot(art::ClassRoot::kPrimitiveLong));
- art::JValue val;
- if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) {
- LOG(FATAL) << "Unable to unwrap a long value!";
- }
- return reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
-}
-
-template<typename GetOriginalDexFile>
-void ArtClassDefinition::InitWithDex(GetOriginalDexFile get_original,
- const art::DexFile* quick_dex) {
- art::Thread* self = art::Thread::Current();
- DCHECK(quick_dex != nullptr);
- if (art::MemMap::kCanReplaceMapping && kEnableOnDemandDexDequicken) {
- size_t dequick_size = quick_dex->GetDequickenedSize();
- std::string mmap_name("anon-mmap-for-redefine: ");
- mmap_name += name_;
- std::string error;
- dex_data_mmap_ = art::MemMap::MapAnonymous(mmap_name.c_str(),
- dequick_size,
- PROT_NONE,
- /*low_4gb=*/ false,
- &error);
- mmap_name += "-TEMP";
- temp_mmap_ = art::MemMap::MapAnonymous(mmap_name.c_str(),
- dequick_size,
- PROT_READ | PROT_WRITE,
- /*low_4gb=*/ false,
- &error);
- if (UNLIKELY(dex_data_mmap_.IsValid() && temp_mmap_.IsValid())) {
- // Need to save the initial dexfile so we don't need to search for it in the fault-handler.
- initial_dex_file_unquickened_ = quick_dex;
- dex_data_ = art::ArrayRef<const unsigned char>(dex_data_mmap_.Begin(),
- dex_data_mmap_.Size());
- if (from_class_ext_) {
- // We got initial from class_ext so the current one must have undergone redefinition so no
- // cdex or quickening stuff.
- // We can only do this if it's not a first load.
- DCHECK(klass_ != nullptr);
- const art::DexFile& cur_dex = self->DecodeJObject(klass_)->AsClass()->GetDexFile();
- current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
- } else {
- // This class hasn't been redefined before. The dequickened current data is the same as the
- // dex_data_mmap_ when it's filled it. We don't need to copy anything because the mmap will
- // not be cleared until after everything is done.
- current_dex_file_ = art::ArrayRef<const unsigned char>(dex_data_mmap_.Begin(),
- dequick_size);
- }
- return;
- }
- }
- dex_data_mmap_.Reset();
- temp_mmap_.Reset();
- // Failed to mmap a large enough area (or on-demand dequickening was disabled). This is
- // unfortunate. Since currently the size is just a guess though we might as well try to do it
- // manually.
- get_original(/*out*/&dex_data_memory_);
- dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
- if (from_class_ext_) {
- // We got initial from class_ext so the current one must have undergone redefinition so no
- // cdex or quickening stuff.
- // We can only do this if it's not a first load.
- DCHECK(klass_ != nullptr);
- const art::DexFile& cur_dex = self->DecodeJObject(klass_)->AsClass()->GetDexFile();
- current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
- } else {
- // No redefinition must have ever happened so the (dequickened) cur_dex is the same as the
- // initial dex_data. We need to copy it into another buffer to keep it around if we have a
- // real redefinition.
- current_dex_memory_.resize(dex_data_.size());
- memcpy(current_dex_memory_.data(), dex_data_.data(), current_dex_memory_.size());
- current_dex_file_ = art::ArrayRef<const unsigned char>(current_dex_memory_);
- }
-}
-
jvmtiError ArtClassDefinition::Init(art::Thread* self, jclass klass) {
jvmtiError res = InitCommon(self, klass);
if (res != OK) {
@@ -311,31 +141,51 @@
art::ScopedObjectAccess soa(self);
art::StackHandleScope<1> hs(self);
art::Handle<art::mirror::Class> m_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
- if (!DexNeedsDequickening(m_klass, &from_class_ext_)) {
- // We don't need to do any dequickening. We want to copy the data just so we don't need to deal
- // with the GC moving it around.
- art::ObjPtr<art::mirror::ByteArray> orig_dex(
- m_klass->GetExtData()->GetOriginalDexFile()->AsByteArray());
- dex_data_memory_.resize(orig_dex->GetLength());
- memcpy(dex_data_memory_.data(), orig_dex->GetData(), dex_data_memory_.size());
- dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+ art::ObjPtr<art::mirror::ClassExt> ext(m_klass->GetExtData());
+ if (!ext.IsNull()) {
+ art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
+ if (!orig_dex.IsNull()) {
+ if (orig_dex->IsArrayInstance()) {
+ // An array instance means the original-dex-file is from a redefineClasses which cannot have any
+ // compact dex, so it's fine to use directly.
+ art::ObjPtr<art::mirror::ByteArray> byte_array(orig_dex->AsByteArray());
+ dex_data_memory_.resize(byte_array->GetLength());
+ memcpy(dex_data_memory_.data(), byte_array->GetData(), dex_data_memory_.size());
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
- // Since we are here we must not have any quickened instructions since we were redefined.
- const art::DexFile& cur_dex = m_klass->GetDexFile();
- DCHECK(from_class_ext_);
- current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
- return OK;
+ const art::DexFile& cur_dex = m_klass->GetDexFile();
+ current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+ return OK;
+ }
+
+ if (orig_dex->IsDexCache()) {
+ res = Init(*orig_dex->AsDexCache()->GetDexFile());
+ if (res != OK) {
+ return res;
+ }
+ } else {
+ DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
+ << "Expected java/lang/Long but found object of type "
+ << orig_dex->GetClass()->PrettyClass();
+ art::ObjPtr<art::mirror::Class> prim_long_class(
+ art::GetClassRoot(art::ClassRoot::kPrimitiveLong));
+ art::JValue val;
+ if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) {
+ // This should never happen.
+ LOG(FATAL) << "Unable to unbox a primitive long value!";
+ }
+ res = Init(*reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ())));
+ if (res != OK) {
+ return res;
+ }
+ }
+ const art::DexFile& cur_dex = m_klass->GetDexFile();
+ current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+ return OK;
+ }
}
-
- // We need to dequicken stuff. This is often super slow (10's of ms). Instead we will do it
- // dynamically.
- const art::DexFile* quick_dex = GetQuickenedDexFile(m_klass);
- auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- GetDexDataForRetransformation(m_klass, dex_data);
- };
- InitWithDex(get_original, quick_dex);
- return OK;
+ // No redefinition must have ever happened so we can use the class's dex file.
+ return Init(m_klass->GetDexFile());
}
jvmtiError ArtClassDefinition::Init(art::Thread* self, const jvmtiClassDefinition& def) {
@@ -350,9 +200,9 @@
return OK;
}
-void ArtClassDefinition::InitFirstLoad(const char* descriptor,
- art::Handle<art::mirror::ClassLoader> klass_loader,
- const art::DexFile& dex_file) {
+jvmtiError ArtClassDefinition::InitFirstLoad(const char* descriptor,
+ art::Handle<art::mirror::ClassLoader> klass_loader,
+ const art::DexFile& dex_file) {
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
initialized_ = true;
@@ -363,11 +213,47 @@
name_ = descriptor_str.substr(1, descriptor_str.size() - 2);
// Android doesn't really have protection domains.
protection_domain_ = nullptr;
- auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- DequickenDexFile(&dex_file, descriptor, dex_data);
- };
- InitWithDex(get_original, &dex_file);
+ return Init(dex_file);
+}
+
+jvmtiError ArtClassDefinition::Init(const art::DexFile& dex_file) {
+ if (dex_file.IsCompactDexFile()) {
+ std::string error_msg;
+ std::vector<std::unique_ptr<const art::DexFile>> dex_files;
+ const art::ArtDexFileLoader dex_file_loader;
+ if (!dex_file_loader.Open(dex_file.GetLocation().c_str(),
+ dex_file.GetLocation().c_str(),
+ /* verify= */ false,
+ /* verify_checksum= */ false,
+ &error_msg,
+ &dex_files)) {
+ return ERR(INTERNAL);
+ }
+ const std::vector<const art::OatDexFile*>& oat_dex_files =
+ dex_file.GetOatDexFile()->GetOatFile()->GetOatDexFiles();
+ const art::DexFile* original_dex_file = nullptr;
+ for (uint32_t i = 0; i < oat_dex_files.size(); ++i) {
+ if (dex_file.GetOatDexFile() == oat_dex_files[i]) {
+ original_dex_file = dex_files[i].get();
+ break;
+ }
+ }
+ // Keep the dex_data alive.
+ dex_data_memory_.resize(original_dex_file->Size());
+ memcpy(dex_data_memory_.data(), original_dex_file->Begin(), original_dex_file->Size());
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+
+ // In case dex_data gets re-used for redefinition, keep the dex file live
+ // with current_dex_memory.
+ current_dex_memory_.resize(dex_data_.size());
+ memcpy(current_dex_memory_.data(), dex_data_.data(), current_dex_memory_.size());
+ current_dex_file_ = art::ArrayRef<const unsigned char>(current_dex_memory_);
+ } else {
+ // Dex file will always stay live, use it directly.
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_file.Begin(), dex_file.Size());
+ current_dex_file_ = dex_data_;
+ }
+ return OK;
}
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
index cb0853b..3caeed9 100644
--- a/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -65,13 +65,12 @@
current_dex_memory_(),
current_dex_file_(),
redefined_(false),
- from_class_ext_(false),
initialized_(false),
structural_transform_update_(false) {}
- void InitFirstLoad(const char* descriptor,
- art::Handle<art::mirror::ClassLoader> klass_loader,
- const art::DexFile& dex_file);
+ jvmtiError InitFirstLoad(const char* descriptor,
+ art::Handle<art::mirror::ClassLoader> klass_loader,
+ const art::DexFile& dex_file);
jvmtiError Init(art::Thread* self, jclass klass);
jvmtiError Init(art::Thread* self, const jvmtiClassDefinition& def);
@@ -156,10 +155,7 @@
private:
jvmtiError InitCommon(art::Thread* self, jclass klass);
-
- template<typename GetOriginalDexFile>
- void InitWithDex(GetOriginalDexFile get_original, const art::DexFile* quick_dex)
- REQUIRES_SHARED(art::Locks::mutator_lock_);
+ jvmtiError Init(const art::DexFile& dex_file);
jclass klass_;
jobject loader_;
@@ -191,9 +187,6 @@
bool redefined_;
- // If we got the initial dex_data_ from a class_ext
- bool from_class_ext_;
-
bool initialized_;
// Set if we had a new dex from the given transform type.