Revert "Revert "Basic structural redefinition support""
This reverts commit 5a2301d897294ff4ee6de71f459dc2566dc3fa1a.
Bug: 134162467
Reason for revert: Relanding as unclear if issue is due to topic.
Change-Id: Ib1d1cf2e9132e30c9649b760ae9ae2d8ceacf843
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 81d1fc7..b0511a9 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -1618,11 +1618,10 @@
namespace {
-using ArrayPtr = art::ObjPtr<art::mirror::Array>;
+using ObjectPtr = art::ObjPtr<art::mirror::Object>;
-static void ReplaceObjectReferences(ArrayPtr old_arr_ptr, ArrayPtr new_arr_ptr)
+static void ReplaceObjectReferences(ObjectPtr old_obj_ptr, ObjectPtr new_obj_ptr)
REQUIRES(art::Locks::mutator_lock_,
- art::Locks::user_code_suspension_lock_,
art::Roles::uninterruptible_) {
art::Runtime::Current()->GetHeap()->VisitObjectsPaused(
[&](art::mirror::Object* ref) REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -1630,21 +1629,31 @@
class ResizeReferenceVisitor {
public:
using CompressedObj = art::mirror::CompressedReference<art::mirror::Object>;
- ResizeReferenceVisitor(ArrayPtr old_arr, ArrayPtr new_arr)
- : old_arr_(old_arr), new_arr_(new_arr) {}
+ ResizeReferenceVisitor(ObjectPtr old_arr, ObjectPtr new_arr)
+ : old_obj_(old_arr), new_obj_(new_arr) {}
- // Ignore class roots. These do not need to be handled for arrays.
- void VisitRootIfNonNull(CompressedObj* root ATTRIBUTE_UNUSED) const {}
- void VisitRoot(CompressedObj* root ATTRIBUTE_UNUSED) const {}
+ // Ignore class roots.
+ void VisitRootIfNonNull(CompressedObj* root) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (root != nullptr) {
+ VisitRoot(root);
+ }
+ }
+ void VisitRoot(CompressedObj* root) const REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (root->AsMirrorPtr() == old_obj_) {
+ root->Assign(new_obj_);
+ art::WriteBarrier::ForEveryFieldWrite(new_obj_);
+ }
+ }
void operator()(art::ObjPtr<art::mirror::Object> obj,
art::MemberOffset off,
bool is_static ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- if (obj->GetFieldObject<art::mirror::Object>(off) == old_arr_) {
+ if (obj->GetFieldObject<art::mirror::Object>(off) == old_obj_) {
LOG(DEBUG) << "Updating field at offset " << off.Uint32Value() << " of type "
<< obj->GetClass()->PrettyClass();
- obj->SetFieldObject</*transaction*/ false>(off, new_arr_);
+ obj->SetFieldObject</*transaction*/ false>(off, new_obj_);
}
}
@@ -1656,23 +1665,31 @@
}
private:
- ArrayPtr old_arr_;
- ArrayPtr new_arr_;
+ ObjectPtr old_obj_;
+ ObjectPtr new_obj_;
};
- ResizeReferenceVisitor rrv(old_arr_ptr, new_arr_ptr);
- ref->VisitReferences(rrv, rrv);
+ ResizeReferenceVisitor rrv(old_obj_ptr, new_obj_ptr);
+ if (ref->IsClass()) {
+ // Class object native roots are the ArtField and ArtMethod 'declaring_class_' fields
+ // which we don't want to be messing with as it would break ref-visitor assumptions about
+ // what a class looks like. We want to keep the default behavior in other cases (such as
+ // dex-cache) though. Unfortunately there is no way to tell from the visitor where exactly
+ // the root came from.
+ // TODO It might be nice to have the visitors told where the reference came from.
+ ref->VisitReferences</*kVisitNativeRoots*/false>(rrv, rrv);
+ } else {
+ ref->VisitReferences</*kVisitNativeRoots*/true>(rrv, rrv);
+ }
});
}
-static void ReplaceStrongRoots(art::Thread* self, ArrayPtr old_arr_ptr, ArrayPtr new_arr_ptr)
- REQUIRES(art::Locks::mutator_lock_,
- art::Locks::user_code_suspension_lock_,
- art::Roles::uninterruptible_) {
+static void ReplaceStrongRoots(art::Thread* self, ObjectPtr old_obj_ptr, ObjectPtr new_obj_ptr)
+ REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
// replace root references expcept java frames.
struct ResizeRootVisitor : public art::RootVisitor {
public:
- ResizeRootVisitor(ArrayPtr new_val, ArrayPtr old_val)
+ ResizeRootVisitor(ObjectPtr new_val, ObjectPtr old_val)
: new_val_(new_val), old_val_(old_val) {}
// TODO It's somewhat annoying to have to have this function implemented twice. It might be
@@ -1686,7 +1703,16 @@
// Java frames might have the JIT doing optimizations (for example loop-unrolling or
// eliding bounds checks) so we need deopt them once we're done here.
if (info.GetType() == art::RootType::kRootJavaFrame) {
- threads_with_roots_.insert(info.GetThreadId());
+ const art::JavaFrameRootInfo& jfri =
+ art::down_cast<const art::JavaFrameRootInfo&>(info);
+ if (jfri.GetVReg() == art::JavaFrameRootInfo::kMethodDeclaringClass) {
+ info.Describe(LOG_STREAM(INFO) << "Not changing declaring-class during stack walk. "
+ "Found obsolete java frame id ");
+ continue;
+ } else {
+ info.Describe(LOG_STREAM(INFO) << "Found java frame id ");
+ threads_with_roots_.insert(info.GetThreadId());
+ }
}
*obj = new_val_.Ptr();
}
@@ -1703,7 +1729,16 @@
// Java frames might have the JIT doing optimizations (for example loop-unrolling or
// eliding bounds checks) so we need deopt them once we're done here.
if (info.GetType() == art::RootType::kRootJavaFrame) {
- threads_with_roots_.insert(info.GetThreadId());
+ const art::JavaFrameRootInfo& jfri =
+ art::down_cast<const art::JavaFrameRootInfo&>(info);
+ if (jfri.GetVReg() == art::JavaFrameRootInfo::kMethodDeclaringClass) {
+ info.Describe(LOG_STREAM(INFO) << "Not changing declaring-class during stack walk. "
+ "Found obsolete java frame id ");
+ continue;
+ } else {
+ info.Describe(LOG_STREAM(INFO) << "Found java frame id ");
+ threads_with_roots_.insert(info.GetThreadId());
+ }
}
obj->Assign(new_val_);
}
@@ -1715,11 +1750,11 @@
}
private:
- ArrayPtr new_val_;
- ArrayPtr old_val_;
+ ObjectPtr new_val_;
+ ObjectPtr old_val_;
std::unordered_set<uint32_t> threads_with_roots_;
};
- ResizeRootVisitor rrv(new_arr_ptr, old_arr_ptr);
+ ResizeRootVisitor rrv(new_obj_ptr, old_obj_ptr);
art::Runtime::Current()->VisitRoots(&rrv, art::VisitRootFlags::kVisitRootFlagAllRoots);
// Handle java Frames. Annoyingly the JIT can embed information about the length of the array into
// the compiled code. By changing the length of the array we potentially invalidate these
@@ -1732,6 +1767,7 @@
art::Thread* t = thread_list->FindThreadByThreadId(id);
CHECK(t != nullptr) << "id " << id << " does not refer to a valid thread."
<< " Where did the roots come from?";
+ LOG(DEBUG) << "Instrumenting thread stack of thread " << *t;
// TODO Use deopt manager. We need a version that doesn't acquire all the locks we
// already have.
// TODO We technically only need to do this if the frames are not already being interpreted.
@@ -1743,11 +1779,9 @@
static void ReplaceWeakRoots(art::Thread* self,
EventHandler* event_handler,
- ArrayPtr old_arr_ptr,
- ArrayPtr new_arr_ptr)
- REQUIRES(art::Locks::mutator_lock_,
- art::Locks::user_code_suspension_lock_,
- art::Roles::uninterruptible_) {
+ ObjectPtr old_obj_ptr,
+ ObjectPtr new_obj_ptr)
+ REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
// Handle tags. We want to do this seprately from other weak-refs (handled below) because we need
// to send additional events and handle cases where the agent might have tagged the new
// replacement object during the VMObjectAlloc. We do this by removing all tags associated with
@@ -1767,8 +1801,8 @@
// Get the tags and clear them (so we don't need to special-case the normal weak-ref visitor)
jlong new_tag = 0;
jlong obsolete_tag = 0;
- bool had_new_tag = env->object_tag_table->RemoveLocked(new_arr_ptr, &new_tag);
- bool had_obsolete_tag = env->object_tag_table->RemoveLocked(old_arr_ptr, &obsolete_tag);
+ bool had_new_tag = env->object_tag_table->RemoveLocked(new_obj_ptr, &new_tag);
+ bool had_obsolete_tag = env->object_tag_table->RemoveLocked(old_obj_ptr, &obsolete_tag);
// Dispatch event.
if (had_obsolete_tag || had_new_tag) {
event_handler->DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>(env,
@@ -1779,60 +1813,56 @@
non_obsolete_tags[env] = new_tag;
}
// After weak-ref update we need to go back and re-add obsoletes. We wait to avoid having to
- // deal with the visit-weaks overwriting the initial new_arr_ptr tag and generally making things
+ // deal with the visit-weaks overwriting the initial new_obj_ptr tag and generally making things
// difficult.
env->object_tag_table->Unlock();
});
// Handle weak-refs.
struct ReplaceWeaksVisitor : public art::IsMarkedVisitor {
public:
- ReplaceWeaksVisitor(ArrayPtr old_arr, ArrayPtr new_arr)
- : old_arr_(old_arr), new_arr_(new_arr) {}
+ ReplaceWeaksVisitor(ObjectPtr old_obj, ObjectPtr new_obj)
+ : old_obj_(old_obj), new_obj_(new_obj) {}
art::mirror::Object* IsMarked(art::mirror::Object* obj)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- if (obj == old_arr_) {
- return new_arr_.Ptr();
+ if (obj == old_obj_) {
+ return new_obj_.Ptr();
} else {
return obj;
}
}
private:
- ArrayPtr old_arr_;
- ArrayPtr new_arr_;
+ ObjectPtr old_obj_;
+ ObjectPtr new_obj_;
};
- ReplaceWeaksVisitor rwv(old_arr_ptr, new_arr_ptr);
+ ReplaceWeaksVisitor rwv(old_obj_ptr, new_obj_ptr);
art::Runtime::Current()->SweepSystemWeaks(&rwv);
- // Re-add the object tags. At this point all weak-references to the old_arr_ptr are gone.
+ // Re-add the object tags. At this point all weak-references to the old_obj_ptr are gone.
event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) {
// Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it.
art::Locks::mutator_lock_->AssertExclusiveHeld(self);
env->object_tag_table->Lock();
if (obsolete_tags.find(env) != obsolete_tags.end()) {
- env->object_tag_table->SetLocked(old_arr_ptr, obsolete_tags[env]);
+ env->object_tag_table->SetLocked(old_obj_ptr, obsolete_tags[env]);
}
if (non_obsolete_tags.find(env) != non_obsolete_tags.end()) {
- env->object_tag_table->SetLocked(new_arr_ptr, non_obsolete_tags[env]);
+ env->object_tag_table->SetLocked(new_obj_ptr, non_obsolete_tags[env]);
}
env->object_tag_table->Unlock();
});
}
-static void PerformArrayReferenceReplacement(art::Thread* self,
- EventHandler* event_handler,
- ArrayPtr old_arr_ptr,
- ArrayPtr new_arr_ptr)
- REQUIRES(art::Locks::mutator_lock_,
- art::Locks::user_code_suspension_lock_,
- art::Roles::uninterruptible_) {
- ReplaceObjectReferences(old_arr_ptr, new_arr_ptr);
- ReplaceStrongRoots(self, old_arr_ptr, new_arr_ptr);
- ReplaceWeakRoots(self, event_handler, old_arr_ptr, new_arr_ptr);
-}
-
} // namespace
+void HeapExtensions::ReplaceReference(art::Thread* self,
+ art::ObjPtr<art::mirror::Object> old_obj_ptr,
+ art::ObjPtr<art::mirror::Object> new_obj_ptr) {
+ ReplaceObjectReferences(old_obj_ptr, new_obj_ptr);
+ ReplaceStrongRoots(self, old_obj_ptr, new_obj_ptr);
+ ReplaceWeakRoots(self, HeapExtensions::gEventHandler, old_obj_ptr, new_obj_ptr);
+}
+
jvmtiError HeapExtensions::ChangeArraySize(jvmtiEnv* env, jobject arr, jsize new_size) {
if (ArtJvmTiEnv::AsArtJvmTiEnv(env)->capabilities.can_tag_objects != 1) {
return ERR(MUST_POSSESS_CAPABILITY);
@@ -1921,7 +1951,7 @@
UNREACHABLE();
}
// Actually replace all the pointers.
- PerformArrayReferenceReplacement(self, gEventHandler, old_arr.Get(), new_arr.Get());
+ ReplaceReference(self, old_arr.Get(), new_arr.Get());
return OK;
}