summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/instrumentation.h4
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc108
-rw-r--r--runtime/openjdkjvmti/events-inl.h10
-rw-r--r--runtime/openjdkjvmti/events.cc15
-rw-r--r--runtime/openjdkjvmti/events.h1
-rw-r--r--runtime/openjdkjvmti/ti_method.cc543
-rw-r--r--runtime/openjdkjvmti/ti_method.h23
-rw-r--r--runtime/openjdkjvmti/ti_thread.cc5
-rw-r--r--runtime/openjdkjvmti/ti_thread.h6
9 files changed, 655 insertions, 60 deletions
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 90b5def9fe..b8ea59725d 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -196,6 +196,10 @@ class Instrumentation {
}
bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool CanDeoptimize() {
+ return deoptimization_enabled_;
+ }
+
// Executes everything with interpreter.
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index aff2ea4c57..af770724ab 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -503,112 +503,112 @@ class JvmtiFunctions {
}
static jvmtiError GetLocalObject(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jobject* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalInstance(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jobject* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jobject* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalInstance(env, thread, depth, value_ptr);
}
static jvmtiError GetLocalInt(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jint* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalLong(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jlong* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalFloat(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jfloat* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError GetLocalDouble(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jdouble* value_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLocalVariable(env, thread, depth, slot, value_ptr);
}
static jvmtiError SetLocalObject(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jobject value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalInt(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jint value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalLong(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jlong value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalFloat(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jfloat value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
static jvmtiError SetLocalDouble(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED,
- jint slot ATTRIBUTE_UNUSED,
- jdouble value ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_access_local_variables);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::SetLocalVariable(env, thread, depth, slot, value);
}
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 43177ab845..91e40553f0 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -414,9 +414,10 @@ inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
bool added) {
ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
: ArtJvmtiEvent::kClassFileLoadHookRetransformable;
- return caps.can_retransform_classes == 1 &&
- IsEventEnabledAnywhere(event) &&
- env->event_masks.IsEnabledAnywhere(event);
+ return (added && caps.can_access_local_variables == 1) ||
+ (caps.can_retransform_classes == 1 &&
+ IsEventEnabledAnywhere(event) &&
+ env->event_masks.IsEnabledAnywhere(event));
}
inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
@@ -428,6 +429,9 @@ inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
}
+ if (added && caps.can_access_local_variables == 1) {
+ HandleLocalAccessCapabilityAdded();
+ }
}
}
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 7a930d4163..2944a453e0 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -610,6 +610,21 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener,
}
}
+void EventHandler::HandleLocalAccessCapabilityAdded() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative);
+ art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation();
+ art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(),
+ art::gc::kGcCauseInstrumentation,
+ art::gc::kCollectorTypeInstrumentation);
+ art::ScopedSuspendAll ssa("Deoptimize everything for local variable access", true);
+ // TODO This should be disabled when there are no environments using it.
+ if (!instr->CanDeoptimize()) {
+ instr->EnableDeoptimization();
+ }
+ // TODO We should be able to support can_access_local_variables without this.
+ instr->DeoptimizeEverything("jvmti-local-variable-access");
+}
+
// Handle special work for the given event type, if necessary.
void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
switch (event) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 5f37dcf0a7..617519eaa3 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -210,6 +210,7 @@ class EventHandler {
unsigned char** new_class_data) const;
void HandleEventType(ArtJvmtiEvent event, bool enable);
+ void HandleLocalAccessCapabilityAdded();
// List of all JvmTiEnv objects that have been created, in their creation order.
// NB Some elements might be null representing envs that have been deleted. They should be skipped
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index ab25319732..ed5645a1ab 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -34,16 +34,22 @@
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/mutex-inl.h"
#include "dex_file_annotations.h"
#include "events-inl.h"
#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "modifiers.h"
#include "nativehelper/ScopedLocalRef.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_thread.h"
#include "ti_phase.h"
namespace openjdkjvmti {
@@ -525,4 +531,541 @@ jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* i
return IsMethodT(env, m, test, is_synthetic_ptr);
}
+struct FindFrameAtDepthVisitor : art::StackVisitor {
+ public:
+ FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ found_frame_(false),
+ cnt_(0),
+ depth_(static_cast<size_t>(depth)) { }
+
+ bool FoundFrame() {
+ return found_frame_;
+ }
+
+ bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+ if (GetMethod()->IsRuntimeMethod()) {
+ return true;
+ }
+ if (cnt_ == depth_) {
+ // We found our frame, exit.
+ found_frame_ = true;
+ return false;
+ } else {
+ cnt_++;
+ return true;
+ }
+ }
+
+ private:
+ bool found_frame_;
+ size_t cnt_;
+ size_t depth_;
+};
+
+class CommonLocalVariableClosure : public art::Closure {
+ public:
+ CommonLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot)
+ : result_(ERR(INTERNAL)), caller_(caller), depth_(depth), slot_(slot) {}
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ // Must have been a bad depth.
+ result_ = ERR(NO_MORE_FRAMES);
+ return;
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (method->IsNative() || !visitor.IsShadowFrame()) {
+ // TODO We really should support get/set for non-shadow frames.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ } else if (method->GetCodeItem()->registers_size_ <= slot_) {
+ result_ = ERR(INVALID_SLOT);
+ return;
+ }
+ uint32_t pc = visitor.GetDexPc(/*abort_on_failure*/ false);
+ if (pc == art::DexFile::kDexNoIndex) {
+ // Cannot figure out current PC.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ }
+ std::string descriptor;
+ art::Primitive::Type slot_type = art::Primitive::kPrimVoid;
+ jvmtiError err = GetSlotType(method, pc, &descriptor, &slot_type);
+ if (err != OK) {
+ result_ = err;
+ return;
+ }
+
+ err = GetTypeError(method, slot_type, descriptor);
+ if (err != OK) {
+ result_ = err;
+ return;
+ }
+ result_ = Execute(method, visitor);
+ }
+
+ jvmtiError GetResult() const {
+ return result_;
+ }
+
+ protected:
+ virtual jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ REQUIRES(art::Locks::mutator_lock_) = 0;
+ virtual jvmtiError GetTypeError(art::ArtMethod* method,
+ art::Primitive::Type type,
+ const std::string& descriptor)
+ REQUIRES(art::Locks::mutator_lock_) = 0;
+
+ jvmtiError GetSlotType(art::ArtMethod* method,
+ uint32_t dex_pc,
+ /*out*/std::string* descriptor,
+ /*out*/art::Primitive::Type* type)
+ REQUIRES(art::Locks::mutator_lock_) {
+ const art::DexFile* dex_file = method->GetDexFile();
+ const art::DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (dex_file == nullptr || code_item == nullptr) {
+ return ERR(OPAQUE_FRAME);
+ }
+
+ struct GetLocalVariableInfoContext {
+ explicit GetLocalVariableInfoContext(jint slot,
+ uint32_t pc,
+ std::string* out_descriptor,
+ art::Primitive::Type* out_type)
+ : found_(false), jslot_(slot), pc_(pc), descriptor_(out_descriptor), type_(out_type) {
+ *descriptor_ = "";
+ *type_ = art::Primitive::kPrimVoid;
+ }
+
+ static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
+ reinterpret_cast<GetLocalVariableInfoContext*>(raw_ctx)->Handle(entry);
+ }
+
+ void Handle(const art::DexFile::LocalInfo& entry) {
+ if (found_) {
+ return;
+ } else if (entry.start_address_ <= pc_ &&
+ entry.end_address_ > pc_ &&
+ entry.reg_ == jslot_) {
+ found_ = true;
+ *type_ = art::Primitive::GetType(entry.descriptor_[0]);
+ *descriptor_ = entry.descriptor_;
+ }
+ return;
+ }
+
+ bool found_;
+ jint jslot_;
+ uint32_t pc_;
+ std::string* descriptor_;
+ art::Primitive::Type* type_;
+ };
+
+ GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
+ if (!dex_file->DecodeDebugLocalInfo(code_item,
+ method->IsStatic(),
+ method->GetDexMethodIndex(),
+ GetLocalVariableInfoContext::Callback,
+ &context) || !context.found_) {
+ // Something went wrong with decoding the debug information. It might as well not be there.
+ return ERR(INVALID_SLOT);
+ } else {
+ return OK;
+ }
+ }
+
+ jvmtiError result_;
+ art::Thread* caller_;
+ jint depth_;
+ jint slot_;
+};
+
+class GetLocalVariableClosure : public CommonLocalVariableClosure {
+ public:
+ GetLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* val)
+ : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
+
+ protected:
+ jvmtiError GetTypeError(art::ArtMethod* method ATTRIBUTE_UNUSED,
+ art::Primitive::Type slot_type,
+ const std::string& descriptor ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (slot_type) {
+ case art::Primitive::kPrimByte:
+ case art::Primitive::kPrimChar:
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimShort:
+ case art::Primitive::kPrimBoolean:
+ return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimLong:
+ case art::Primitive::kPrimFloat:
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimNot:
+ return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected primitive type " << slot_type;
+ UNREACHABLE();
+ }
+ }
+
+ jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (type_) {
+ case art::Primitive::kPrimNot: {
+ uint32_t ptr_val;
+ if (!visitor.GetVReg(method,
+ static_cast<uint16_t>(slot_),
+ art::kReferenceVReg,
+ &ptr_val)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ art::ObjPtr<art::mirror::Object> obj(reinterpret_cast<art::mirror::Object*>(ptr_val));
+ val_->l = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
+ break;
+ }
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimFloat: {
+ if (!visitor.GetVReg(method,
+ static_cast<uint16_t>(slot_),
+ type_ == art::Primitive::kPrimFloat ? art::kFloatVReg : art::kIntVReg,
+ reinterpret_cast<uint32_t*>(&val_->i))) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimLong: {
+ auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
+ auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
+ if (!visitor.GetVRegPair(method,
+ static_cast<uint16_t>(slot_),
+ lo_type,
+ high_type,
+ reinterpret_cast<uint64_t*>(&val_->j))) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "unexpected register type " << type_;
+ UNREACHABLE();
+ }
+ }
+ return OK;
+ }
+
+ private:
+ art::Primitive::Type type_;
+ jvalue* val_;
+};
+
+jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* val) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ GetLocalVariableClosure c(self, depth, slot, type, val);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+class SetLocalVariableClosure : public CommonLocalVariableClosure {
+ public:
+ SetLocalVariableClosure(art::Thread* caller,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue val)
+ : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
+
+ protected:
+ jvmtiError GetTypeError(art::ArtMethod* method,
+ art::Primitive::Type slot_type,
+ const std::string& descriptor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (slot_type) {
+ case art::Primitive::kPrimNot: {
+ if (type_ != art::Primitive::kPrimNot) {
+ return ERR(TYPE_MISMATCH);
+ } else if (val_.l == nullptr) {
+ return OK;
+ } else {
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ art::ObjPtr<art::mirror::Class> set_class =
+ caller_->DecodeJObject(val_.l)->GetClass();
+ art::ObjPtr<art::mirror::ClassLoader> loader =
+ method->GetDeclaringClass()->GetClassLoader();
+ art::ObjPtr<art::mirror::Class> slot_class =
+ cl->LookupClass(caller_, descriptor.c_str(), loader);
+ DCHECK(!slot_class.IsNull());
+ return slot_class->IsAssignableFrom(set_class) ? OK : ERR(TYPE_MISMATCH);
+ }
+ }
+ case art::Primitive::kPrimByte:
+ case art::Primitive::kPrimChar:
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimShort:
+ case art::Primitive::kPrimBoolean:
+ return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimLong:
+ case art::Primitive::kPrimFloat:
+ case art::Primitive::kPrimDouble:
+ return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
+ case art::Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected primitive type " << slot_type;
+ UNREACHABLE();
+ }
+ }
+
+ jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
+ OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ switch (type_) {
+ case art::Primitive::kPrimNot: {
+ uint32_t ptr_val;
+ art::ObjPtr<art::mirror::Object> obj(caller_->DecodeJObject(val_.l));
+ ptr_val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(obj.Ptr()));
+ if (!visitor.SetVReg(method,
+ static_cast<uint16_t>(slot_),
+ ptr_val,
+ art::kReferenceVReg)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimInt:
+ case art::Primitive::kPrimFloat: {
+ if (!visitor.SetVReg(method,
+ static_cast<uint16_t>(slot_),
+ static_cast<uint32_t>(val_.i),
+ type_ == art::Primitive::kPrimFloat ? art::kFloatVReg
+ : art::kIntVReg)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ case art::Primitive::kPrimDouble:
+ case art::Primitive::kPrimLong: {
+ auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
+ auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
+ if (!visitor.SetVRegPair(method,
+ static_cast<uint16_t>(slot_),
+ static_cast<uint64_t>(val_.j),
+ lo_type,
+ high_type)) {
+ return ERR(OPAQUE_FRAME);
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "unexpected register type " << type_;
+ UNREACHABLE();
+ }
+ }
+ return OK;
+ }
+
+ private:
+ art::Primitive::Type type_;
+ jvalue val_;
+};
+
+jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue val) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ SetLocalVariableClosure c(self, depth, slot, type, val);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+class GetLocalInstanceClosure : public art::Closure {
+ public:
+ GetLocalInstanceClosure(art::Thread* caller, jint depth, jobject* val)
+ : result_(ERR(INTERNAL)),
+ caller_(caller),
+ depth_(depth),
+ val_(val) {}
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ // Must have been a bad depth.
+ result_ = ERR(NO_MORE_FRAMES);
+ return;
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (!visitor.IsShadowFrame() && !method->IsNative() && !method->IsProxyMethod()) {
+ // TODO We really should support get/set for non-shadow frames.
+ result_ = ERR(OPAQUE_FRAME);
+ return;
+ }
+ result_ = OK;
+ art::ObjPtr<art::mirror::Object> obj = visitor.GetThisObject();
+ *val_ = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
+ }
+
+ jvmtiError GetResult() const {
+ return result_;
+ }
+
+ private:
+ jvmtiError result_;
+ art::Thread* caller_;
+ jint depth_;
+ jobject* val_;
+};
+
+jvmtiError MethodUtil::GetLocalInstance(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jthread thread,
+ jint depth,
+ jobject* data) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ GetLocalInstanceClosure c(self, depth, data);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ if (!target->RequestSynchronousCheckpoint(&c)) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else {
+ return c.GetResult();
+ }
+}
+
+#define FOR_JVMTI_JVALUE_TYPES(fn) \
+ fn(jint, art::Primitive::kPrimInt, i) \
+ fn(jlong, art::Primitive::kPrimLong, j) \
+ fn(jfloat, art::Primitive::kPrimFloat, f) \
+ fn(jdouble, art::Primitive::kPrimDouble, d) \
+ fn(jobject, art::Primitive::kPrimNot, l)
+
+namespace impl {
+
+template<typename T> void WriteJvalue(T, jvalue*);
+template<typename T> void ReadJvalue(jvalue, T*);
+template<typename T> art::Primitive::Type GetJNIType();
+
+#define JNI_TYPE_CHAR(type, prim, id) \
+template<> art::Primitive::Type GetJNIType<type>() { \
+ return prim; \
+}
+
+FOR_JVMTI_JVALUE_TYPES(JNI_TYPE_CHAR);
+
+#undef JNI_TYPE_CHAR
+
+#define RW_JVALUE(type, prim, id) \
+ template<> void ReadJvalue<type>(jvalue in, type* out) { \
+ *out = in.id; \
+ } \
+ template<> void WriteJvalue<type>(type in, jvalue* out) { \
+ out->id = in; \
+ }
+
+FOR_JVMTI_JVALUE_TYPES(RW_JVALUE);
+
+#undef RW_JVALUE
+
+} // namespace impl
+
+template<typename T>
+jvmtiError MethodUtil::SetLocalVariable(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ T data) {
+ jvalue v = {.j = 0};
+ art::Primitive::Type type = impl::GetJNIType<T>();
+ impl::WriteJvalue(data, &v);
+ return SetLocalVariableGeneric(env, thread, depth, slot, type, v);
+}
+
+template<typename T>
+jvmtiError MethodUtil::GetLocalVariable(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ T* data) {
+ if (data == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ jvalue v = {.j = 0};
+ art::Primitive::Type type = impl::GetJNIType<T>();
+ jvmtiError err = GetLocalVariableGeneric(env, thread, depth, slot, type, &v);
+ if (err != OK) {
+ return err;
+ } else {
+ impl::ReadJvalue(v, data);
+ return OK;
+ }
+}
+
+#define GET_SET_LV(type, prim, id) \
+ template jvmtiError MethodUtil::GetLocalVariable<type>(jvmtiEnv*, jthread, jint, jint, type*); \
+ template jvmtiError MethodUtil::SetLocalVariable<type>(jvmtiEnv*, jthread, jint, jint, type);
+
+FOR_JVMTI_JVALUE_TYPES(GET_SET_LV);
+
+#undef GET_SET_LV
+
+#undef FOR_JVMTI_JVALUE_TYPES
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index f506594c64..aabaedb932 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -34,6 +34,7 @@
#include "jni.h"
#include "jvmti.h"
+#include "primitive.h"
namespace openjdkjvmti {
@@ -84,6 +85,28 @@ class MethodUtil {
jmethodID method,
jint* entry_count_ptr,
jvmtiLocalVariableEntry** table_ptr);
+
+ template<typename T>
+ static jvmtiError SetLocalVariable(jvmtiEnv* env, jthread thread, jint depth, jint slot, T data);
+
+ template<typename T>
+ static jvmtiError GetLocalVariable(jvmtiEnv* env, jthread thread, jint depth, jint slot, T* data);
+
+ static jvmtiError GetLocalInstance(jvmtiEnv* env, jthread thread, jint depth, jobject* data);
+
+ private:
+ static jvmtiError SetLocalVariableGeneric(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue value);
+ static jvmtiError GetLocalVariableGeneric(jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ art::Primitive::Type type,
+ jvalue* value);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 9acea2a288..7d42879055 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -171,9 +171,8 @@ static art::Thread* GetNativeThreadLocked(jthread thread,
}
// Get the native thread. The spec says a null object denotes the current thread.
-static art::Thread* GetNativeThread(jthread thread,
- const art::ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
+art::Thread* ThreadUtil::GetNativeThread(jthread thread,
+ const art::ScopedObjectAccessAlreadyRunnable& soa) {
if (thread == nullptr) {
return art::Thread::Current();
}
diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h
index 0f7e8379fd..083bf8d7a5 100644
--- a/runtime/openjdkjvmti/ti_thread.h
+++ b/runtime/openjdkjvmti/ti_thread.h
@@ -35,10 +35,12 @@
#include "jni.h"
#include "jvmti.h"
+#include "base/macros.h"
#include "base/mutex.h"
namespace art {
class ArtField;
+class ScopedObjectAccessAlreadyRunnable;
class Thread;
} // namespace art
@@ -86,6 +88,10 @@ class ThreadUtil {
const jthread* threads,
jvmtiError* results);
+ static art::Thread* GetNativeThread(jthread thread,
+ const art::ScopedObjectAccessAlreadyRunnable& soa)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
private:
// We need to make sure only one thread tries to suspend threads at a time so we can get the
// 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a