Handler-ize InstrumentationListener and related code
We are going to be making instrumentation listeners that can cause
suspensions during their running. We explicitly handler-ize all the
instrumentation listener functions in order to ensure this is safe.
Bug: 34414073
Test: ./test.py --host --ntrace --trace -j40
Change-Id: Ic719080d0991b104d41b7757df8d1f332c72cd04
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d1afcb8..07ca35f 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -36,7 +36,9 @@
#include "mirror/string.h"
#include "oat.h"
#include "obj_ptr-inl.h"
+#include "primitive.h"
#include "quick/quick_method_frame_info.h"
+#include "read_barrier-inl.h"
#include "runtime-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
@@ -340,6 +342,10 @@
return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_));
}
+inline Primitive::Type ArtMethod::GetReturnTypePrimitive() {
+ return Primitive::GetType(GetReturnTypeDescriptor()[0]);
+}
+
inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d8dfdd7..3a8d279 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -27,6 +27,7 @@
#include "modifiers.h"
#include "obj_ptr.h"
#include "offsets.h"
+#include "primitive.h"
#include "read_barrier_option.h"
namespace art {
@@ -569,6 +570,8 @@
const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
+ ALWAYS_INLINE Primitive::Type GetReturnTypePrimitive() REQUIRES_SHARED(Locks::mutator_lock_);
+
const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index cfa56a5..7e70b75 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -149,7 +149,9 @@
DebugInstrumentationListener() {}
virtual ~DebugInstrumentationListener() {}
- void MethodEntered(Thread* thread, mirror::Object* this_object, ArtMethod* method,
+ void MethodEntered(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
uint32_t dex_pc)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsNative()) {
@@ -171,12 +173,15 @@
// also group it with other events for this location like BREAKPOINT or SINGLE_STEP.
thread->SetDebugMethodEntry();
} else {
- Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr);
+ Dbg::UpdateDebugger(thread, this_object.Get(), method, 0, Dbg::kMethodEntry, nullptr);
}
}
- void MethodExited(Thread* thread, mirror::Object* this_object, ArtMethod* method,
- uint32_t dex_pc, const JValue& return_value)
+ void MethodExited(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ const JValue& return_value)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsNative()) {
// TODO: post location events is a suspension point and native method entry stubs aren't.
@@ -189,18 +194,22 @@
events |= Dbg::kMethodEntry;
thread->ClearDebugMethodEntry();
}
- Dbg::UpdateDebugger(thread, this_object, method, dex_pc, events, &return_value);
+ Dbg::UpdateDebugger(thread, this_object.Get(), method, dex_pc, events, &return_value);
}
- void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object ATTRIBUTE_UNUSED,
- ArtMethod* method, uint32_t dex_pc)
+ void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+ ArtMethod* method,
+ uint32_t dex_pc)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
// We're not recorded to listen to this kind of event, so complain.
LOG(ERROR) << "Unexpected method unwind event in debugger " << ArtMethod::PrettyMethod(method)
<< " " << dex_pc;
}
- void DexPcMoved(Thread* thread, mirror::Object* this_object, ArtMethod* method,
+ void DexPcMoved(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
uint32_t new_dex_pc)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
if (IsListeningToMethodExit() && IsReturn(method, new_dex_pc)) {
@@ -217,26 +226,33 @@
events = Dbg::kMethodEntry;
thread->ClearDebugMethodEntry();
}
- Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, events, nullptr);
+ Dbg::UpdateDebugger(thread, this_object.Get(), method, new_dex_pc, events, nullptr);
}
}
- void FieldRead(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc, ArtField* field)
+ void FieldRead(Thread* thread ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
- Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field);
+ Dbg::PostFieldAccessEvent(method, dex_pc, this_object.Get(), field);
}
- void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc, ArtField* field,
+ void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
const JValue& field_value)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
- Dbg::PostFieldModificationEvent(method, dex_pc, this_object, field, &field_value);
+ Dbg::PostFieldModificationEvent(method, dex_pc, this_object.Get(), field, &field_value);
}
- void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, mirror::Throwable* exception_object)
+ void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
+ Handle<mirror::Throwable> exception_object)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
- Dbg::PostException(exception_object);
+ Dbg::PostException(exception_object.Get());
}
// We only care about branches in the Jit.
@@ -248,10 +264,10 @@
// We only care about invokes in the Jit.
void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object*,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method,
uint32_t dex_pc,
- ArtMethod*)
+ ArtMethod* target ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
LOG(ERROR) << "Unexpected invoke event in debugger " << ArtMethod::PrettyMethod(method)
<< " " << dex_pc;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d862ff2..540b2e7 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -20,6 +20,7 @@
#include "arch/context.h"
#include "art_method-inl.h"
+#include "art_field-inl.h"
#include "atomic.h"
#include "class_linker.h"
#include "debugger.h"
@@ -31,6 +32,7 @@
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "jvalue-inl.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/object_array-inl.h"
@@ -45,6 +47,30 @@
constexpr bool kVerboseInstrumentation = false;
+void InstrumentationListener::MethodExited(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ Handle<mirror::Object> return_value) {
+ DCHECK_EQ(method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetReturnTypePrimitive(),
+ Primitive::kPrimNot);
+ JValue v;
+ v.SetL(return_value.Get());
+ MethodExited(thread, this_object, method, dex_pc, v);
+}
+
+void InstrumentationListener::FieldWritten(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
+ Handle<mirror::Object> field_value) {
+ DCHECK(!field->IsPrimitiveType());
+ JValue v;
+ v.SetL(field_value.Get());
+ FieldWritten(thread, this_object, method, dex_pc, field, v);
+}
+
// Instrumentation works on non-inlined frames by updating returned PCs
// of compiled frames.
static constexpr StackVisitor::StackWalkKind kInstrumentationStackWalk =
@@ -916,48 +942,75 @@
return class_linker->GetQuickOatCodeFor(method);
}
-void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
+void Instrumentation::MethodEnterEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const {
if (HasMethodEntryListeners()) {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : method_entry_listeners_) {
if (listener != nullptr) {
- listener->MethodEntered(thread, this_object, method, dex_pc);
+ listener->MethodEntered(thread, thiz, method, dex_pc);
}
}
}
}
-void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+void Instrumentation::MethodExitEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* method,
- uint32_t dex_pc, const JValue& return_value) const {
+ uint32_t dex_pc,
+ const JValue& return_value) const {
if (HasMethodExitListeners()) {
- for (InstrumentationListener* listener : method_exit_listeners_) {
- if (listener != nullptr) {
- listener->MethodExited(thread, this_object, method, dex_pc, return_value);
+ Thread* self = Thread::Current();
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
+ if (method->GetInterfaceMethodIfProxy(kRuntimePointerSize)
+ ->GetReturnTypePrimitive() != Primitive::kPrimNot) {
+ for (InstrumentationListener* listener : method_exit_listeners_) {
+ if (listener != nullptr) {
+ listener->MethodExited(thread, thiz, method, dex_pc, return_value);
+ }
+ }
+ } else {
+ Handle<mirror::Object> ret(hs.NewHandle(return_value.GetL()));
+ for (InstrumentationListener* listener : method_exit_listeners_) {
+ if (listener != nullptr) {
+ listener->MethodExited(thread, thiz, method, dex_pc, ret);
+ }
}
}
}
}
-void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object,
+void Instrumentation::MethodUnwindEvent(Thread* thread,
+ mirror::Object* this_object,
ArtMethod* method,
uint32_t dex_pc) const {
if (HasMethodUnwindListeners()) {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : method_unwind_listeners_) {
if (listener != nullptr) {
- listener->MethodUnwind(thread, this_object, method, dex_pc);
+ listener->MethodUnwind(thread, thiz, method, dex_pc);
}
}
}
}
-void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
+void Instrumentation::DexPcMovedEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : dex_pc_listeners_) {
if (listener != nullptr) {
- listener->DexPcMoved(thread, this_object, method, dex_pc);
+ listener->DexPcMoved(thread, thiz, method, dex_pc);
}
}
}
@@ -974,36 +1027,56 @@
}
void Instrumentation::InvokeVirtualOrInterfaceImpl(Thread* thread,
- mirror::Object* this_object,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee) const {
- // We cannot have thread suspension since that would cause the this_object parameter to
- // potentially become a dangling pointer. An alternative could be to put it in a handle instead.
- ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) {
if (listener != nullptr) {
- listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee);
+ listener->InvokeVirtualOrInterface(thread, thiz, caller, dex_pc, callee);
}
}
}
-void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
+void Instrumentation::FieldReadEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
ArtField* field) const {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : field_read_listeners_) {
if (listener != nullptr) {
- listener->FieldRead(thread, this_object, method, dex_pc, field);
+ listener->FieldRead(thread, thiz, method, dex_pc, field);
}
}
}
-void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
- ArtField* field, const JValue& field_value) const {
- for (InstrumentationListener* listener : field_write_listeners_) {
- if (listener != nullptr) {
- listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);
+void Instrumentation::FieldWriteEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
+ const JValue& field_value) const {
+ Thread* self = Thread::Current();
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Object> thiz(hs.NewHandle(this_object));
+ if (field->IsPrimitiveType()) {
+ for (InstrumentationListener* listener : field_write_listeners_) {
+ if (listener != nullptr) {
+ listener->FieldWritten(thread, thiz, method, dex_pc, field, field_value);
+ }
+ }
+ } else {
+ Handle<mirror::Object> val(hs.NewHandle(field_value.GetL()));
+ for (InstrumentationListener* listener : field_write_listeners_) {
+ if (listener != nullptr) {
+ listener->FieldWritten(thread, thiz, method, dex_pc, field, val);
+ }
}
}
}
@@ -1018,7 +1091,7 @@
thread->ClearException();
for (InstrumentationListener* listener : exception_caught_listeners_) {
if (listener != nullptr) {
- listener->ExceptionCaught(thread, h_exception.Get());
+ listener->ExceptionCaught(thread, h_exception);
}
}
thread->SetException(h_exception.Get());
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 01071a5..363985f 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -36,6 +36,7 @@
} // namespace mirror
class ArtField;
class ArtMethod;
+template <typename T> class Handle;
union JValue;
class Thread;
@@ -62,37 +63,70 @@
virtual ~InstrumentationListener() {}
// Call-back for when a method is entered.
- virtual void MethodEntered(Thread* thread, mirror::Object* this_object,
+ virtual void MethodEntered(Thread* thread,
+ Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
- // Call-back for when a method is exited.
- virtual void MethodExited(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
+ virtual void MethodExited(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ Handle<mirror::Object> return_value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Call-back for when a method is exited. The implementor should either handler-ize the return
+ // value (if appropriate) or use the alternate MethodExited callback instead if they need to
+ // go through a suspend point.
+ virtual void MethodExited(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
const JValue& return_value)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Call-back for when a method is popped due to an exception throw. A method will either cause a
// MethodExited call-back or a MethodUnwind call-back when its activation is removed.
- virtual void MethodUnwind(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc)
+ virtual void MethodUnwind(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Call-back for when the dex pc moves in a method.
- virtual void DexPcMoved(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t new_dex_pc)
+ virtual void DexPcMoved(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t new_dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Call-back for when we read from a field.
- virtual void FieldRead(Thread* thread, mirror::Object* this_object, ArtMethod* method,
- uint32_t dex_pc, ArtField* field) = 0;
+ virtual void FieldRead(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field) = 0;
+
+ virtual void FieldWritten(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
+ Handle<mirror::Object> field_value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Call-back for when we write into a field.
- virtual void FieldWritten(Thread* thread, mirror::Object* this_object, ArtMethod* method,
- uint32_t dex_pc, ArtField* field, const JValue& field_value) = 0;
+ virtual void FieldWritten(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
+ const JValue& field_value)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Call-back when an exception is caught.
- virtual void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object)
+ virtual void ExceptionCaught(Thread* thread,
+ Handle<mirror::Throwable> exception_object)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Call-back for when we execute a branch.
@@ -104,11 +138,10 @@
// Call-back for when we get an invokevirtual or an invokeinterface.
virtual void InvokeVirtualOrInterface(Thread* thread,
- mirror::Object* this_object,
+ Handle<mirror::Object> this_object,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee)
- REQUIRES(Roles::uninterruptible_)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
@@ -323,8 +356,10 @@
}
// Inform listeners that a method has been exited.
- void MethodExitEvent(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
+ void MethodExitEvent(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
const JValue& return_value) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (UNLIKELY(HasMethodExitListeners())) {
@@ -465,31 +500,42 @@
// exclusive access to mutator lock which you can't get if the runtime isn't started.
void SetEntrypointsInstrumented(bool instrumented) NO_THREAD_SAFETY_ANALYSIS;
- void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc) const
+ void MethodEnterEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+ void MethodExitEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* method,
- uint32_t dex_pc, const JValue& return_value) const
+ uint32_t dex_pc,
+ const JValue& return_value) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc) const
+ void DexPcMovedEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_);
void BranchImpl(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const
REQUIRES_SHARED(Locks::mutator_lock_);
void InvokeVirtualOrInterfaceImpl(Thread* thread,
- mirror::Object* this_object,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
- ArtField* field) const
+ void FieldReadEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
- ArtField* field, const JValue& field_value) const
+ void FieldWriteEventImpl(Thread* thread,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
+ const JValue& field_value) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Read barrier-aware utility functions for accessing deoptimized_methods_
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 9926ee7..2a601c9 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -23,11 +23,13 @@
#include "dex_file.h"
#include "gc/scoped_gc_critical_section.h"
#include "handle_scope-inl.h"
+#include "jni_internal.h"
#include "jvalue.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread_list.h"
-#include "thread-current-inl.h"
+#include "thread-inl.h"
+#include "well_known_classes.h"
namespace art {
namespace instrumentation {
@@ -35,16 +37,22 @@
class TestInstrumentationListener FINAL : public instrumentation::InstrumentationListener {
public:
TestInstrumentationListener()
- : received_method_enter_event(false), received_method_exit_event(false),
- received_method_unwind_event(false), received_dex_pc_moved_event(false),
- received_field_read_event(false), received_field_written_event(false),
- received_exception_caught_event(false), received_branch_event(false),
+ : received_method_enter_event(false),
+ received_method_exit_event(false),
+ received_method_exit_object_event(false),
+ received_method_unwind_event(false),
+ received_dex_pc_moved_event(false),
+ received_field_read_event(false),
+ received_field_written_event(false),
+ received_field_written_object_event(false),
+ received_exception_caught_event(false),
+ received_branch_event(false),
received_invoke_virtual_or_interface_event(false) {}
virtual ~TestInstrumentationListener() {}
void MethodEntered(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -52,7 +60,16 @@
}
void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+ ArtMethod* method ATTRIBUTE_UNUSED,
+ uint32_t dex_pc ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> return_value ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ received_method_exit_object_event = true;
+ }
+
+ void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED,
const JValue& return_value ATTRIBUTE_UNUSED)
@@ -61,7 +78,7 @@
}
void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -69,7 +86,7 @@
}
void DexPcMoved(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t new_dex_pc ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -77,7 +94,7 @@
}
void FieldRead(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED,
ArtField* field ATTRIBUTE_UNUSED)
@@ -86,7 +103,17 @@
}
void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+ ArtMethod* method ATTRIBUTE_UNUSED,
+ uint32_t dex_pc ATTRIBUTE_UNUSED,
+ ArtField* field ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> field_value ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ received_field_written_object_event = true;
+ }
+
+ void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED,
ArtField* field ATTRIBUTE_UNUSED,
@@ -96,7 +123,7 @@
}
void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Throwable* exception_object ATTRIBUTE_UNUSED)
+ Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
received_exception_caught_event = true;
}
@@ -110,7 +137,7 @@
}
void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* caller ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED,
ArtMethod* callee ATTRIBUTE_UNUSED)
@@ -121,10 +148,12 @@
void Reset() {
received_method_enter_event = false;
received_method_exit_event = false;
+ received_method_exit_object_event = false;
received_method_unwind_event = false;
received_dex_pc_moved_event = false;
received_field_read_event = false;
received_field_written_event = false;
+ received_field_written_object_event = false;
received_exception_caught_event = false;
received_branch_event = false;
received_invoke_virtual_or_interface_event = false;
@@ -132,10 +161,12 @@
bool received_method_enter_event;
bool received_method_exit_event;
+ bool received_method_exit_object_event;
bool received_method_unwind_event;
bool received_dex_pc_moved_event;
bool received_field_read_event;
bool received_field_written_event;
+ bool received_field_written_object_event;
bool received_exception_caught_event;
bool received_branch_event;
bool received_invoke_virtual_or_interface_event;
@@ -171,6 +202,13 @@
}
void TestEvent(uint32_t instrumentation_event) {
+ TestEvent(instrumentation_event, nullptr, nullptr, false);
+ }
+
+ void TestEvent(uint32_t instrumentation_event,
+ ArtMethod* event_method,
+ ArtField* event_field,
+ bool with_object) {
ScopedObjectAccess soa(Thread::Current());
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
TestInstrumentationListener listener;
@@ -180,15 +218,20 @@
instr->AddListener(&listener, instrumentation_event);
}
- ArtMethod* const event_method = nullptr;
mirror::Object* const event_obj = nullptr;
const uint32_t event_dex_pc = 0;
// Check the listener is registered and is notified of the event.
EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
- EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
- ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc);
- EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event));
+ EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
+ ReportEvent(instr,
+ instrumentation_event,
+ soa.Self(),
+ event_method,
+ event_obj,
+ event_field,
+ event_dex_pc);
+ EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
listener.Reset();
{
@@ -199,9 +242,15 @@
// Check the listener is not registered and is not notified of the event.
EXPECT_FALSE(HasEventListener(instr, instrumentation_event));
- EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
- ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc);
- EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event));
+ EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
+ ReportEvent(instr,
+ instrumentation_event,
+ soa.Self(),
+ event_method,
+ event_obj,
+ event_field,
+ event_dex_pc);
+ EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
}
void DeoptimizeMethod(Thread* self, ArtMethod* method, bool enable_deoptimization)
@@ -317,8 +366,12 @@
}
}
- static void ReportEvent(const instrumentation::Instrumentation* instr, uint32_t event_type,
- Thread* self, ArtMethod* method, mirror::Object* obj,
+ static void ReportEvent(const instrumentation::Instrumentation* instr,
+ uint32_t event_type,
+ Thread* self,
+ ArtMethod* method,
+ mirror::Object* obj,
+ ArtField* field,
uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) {
switch (event_type) {
@@ -337,11 +390,11 @@
instr->DexPcMovedEvent(self, obj, method, dex_pc);
break;
case instrumentation::Instrumentation::kFieldRead:
- instr->FieldReadEvent(self, obj, method, dex_pc, nullptr);
+ instr->FieldReadEvent(self, obj, method, dex_pc, field);
break;
case instrumentation::Instrumentation::kFieldWritten: {
JValue value;
- instr->FieldWriteEvent(self, obj, method, dex_pc, nullptr, value);
+ instr->FieldWriteEvent(self, obj, method, dex_pc, field, value);
break;
}
case instrumentation::Instrumentation::kExceptionCaught: {
@@ -364,12 +417,14 @@
}
static bool DidListenerReceiveEvent(const TestInstrumentationListener& listener,
- uint32_t event_type) {
+ uint32_t event_type,
+ bool with_object) {
switch (event_type) {
case instrumentation::Instrumentation::kMethodEntered:
return listener.received_method_enter_event;
case instrumentation::Instrumentation::kMethodExited:
- return listener.received_method_exit_event;
+ return (!with_object && listener.received_method_exit_event) ||
+ (with_object && listener.received_method_exit_object_event);
case instrumentation::Instrumentation::kMethodUnwind:
return listener.received_method_unwind_event;
case instrumentation::Instrumentation::kDexPcMoved:
@@ -377,7 +432,8 @@
case instrumentation::Instrumentation::kFieldRead:
return listener.received_field_read_event;
case instrumentation::Instrumentation::kFieldWritten:
- return listener.received_field_written_event;
+ return (!with_object && listener.received_field_written_event) ||
+ (with_object && listener.received_field_written_object_event);
case instrumentation::Instrumentation::kExceptionCaught:
return listener.received_exception_caught_event;
case instrumentation::Instrumentation::kBranch:
@@ -419,8 +475,42 @@
TestEvent(instrumentation::Instrumentation::kMethodEntered);
}
-TEST_F(InstrumentationTest, MethodExitEvent) {
- TestEvent(instrumentation::Instrumentation::kMethodExited);
+TEST_F(InstrumentationTest, MethodExitObjectEvent) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject class_loader = LoadDex("Instrumentation");
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+ ASSERT_TRUE(klass != nullptr);
+ ArtMethod* method = klass->FindDeclaredDirectMethod("returnReference",
+ "()Ljava/lang/Object;",
+ kRuntimePointerSize);
+ ASSERT_TRUE(method != nullptr);
+ TestEvent(instrumentation::Instrumentation::kMethodExited,
+ /*event_method*/ method,
+ /*event_field*/ nullptr,
+ /*with_object*/ true);
+}
+
+TEST_F(InstrumentationTest, MethodExitPrimEvent) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject class_loader = LoadDex("Instrumentation");
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+ ASSERT_TRUE(klass != nullptr);
+ ArtMethod* method = klass->FindDeclaredDirectMethod("returnPrimitive",
+ "()I",
+ kRuntimePointerSize);
+ ASSERT_TRUE(method != nullptr);
+ TestEvent(instrumentation::Instrumentation::kMethodExited,
+ /*event_method*/ method,
+ /*event_field*/ nullptr,
+ /*with_object*/ false);
}
TEST_F(InstrumentationTest, MethodUnwindEvent) {
@@ -435,8 +525,40 @@
TestEvent(instrumentation::Instrumentation::kFieldRead);
}
-TEST_F(InstrumentationTest, FieldWriteEvent) {
- TestEvent(instrumentation::Instrumentation::kFieldWritten);
+TEST_F(InstrumentationTest, FieldWriteObjectEvent) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject class_loader = LoadDex("Instrumentation");
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+ ASSERT_TRUE(klass != nullptr);
+ ArtField* field = klass->FindDeclaredStaticField("referenceField", "Ljava/lang/Object;");
+ ASSERT_TRUE(field != nullptr);
+
+ TestEvent(instrumentation::Instrumentation::kFieldWritten,
+ /*event_method*/ nullptr,
+ /*event_field*/ field,
+ /*with_object*/ true);
+}
+
+TEST_F(InstrumentationTest, FieldWritePrimEvent) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject class_loader = LoadDex("Instrumentation");
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+ ASSERT_TRUE(klass != nullptr);
+ ArtField* field = klass->FindDeclaredStaticField("primitiveField", "I");
+ ASSERT_TRUE(field != nullptr);
+
+ TestEvent(instrumentation::Instrumentation::kFieldWritten,
+ /*event_method*/ nullptr,
+ /*event_field*/ field,
+ /*with_object*/ false);
}
TEST_F(InstrumentationTest, ExceptionCaughtEvent) {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index b191dd7..32a2378 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -307,6 +307,8 @@
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), inst->GetDexPc(insns),
result);
+ // Re-load since it might have moved during the MethodExitEvent.
+ result.SetL(shadow_frame.GetVRegReference(ref_idx));
}
if (interpret_one_instruction) {
/* Signal mterp to return to caller */
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 3550d56..cabd162 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -740,7 +740,7 @@
}
void Trace::DexPcMoved(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method,
uint32_t new_dex_pc) {
// We're not recorded to listen to this kind of event, so complain.
@@ -749,7 +749,7 @@
}
void Trace::FieldRead(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method,
uint32_t dex_pc,
ArtField* field ATTRIBUTE_UNUSED)
@@ -760,7 +760,7 @@
}
void Trace::FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method,
uint32_t dex_pc,
ArtField* field ATTRIBUTE_UNUSED,
@@ -771,8 +771,10 @@
<< " " << dex_pc;
}
-void Trace::MethodEntered(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
- ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) {
+void Trace::MethodEntered(Thread* thread,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+ ArtMethod* method,
+ uint32_t dex_pc ATTRIBUTE_UNUSED) {
uint32_t thread_clock_diff = 0;
uint32_t wall_clock_diff = 0;
ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -780,8 +782,10 @@
thread_clock_diff, wall_clock_diff);
}
-void Trace::MethodExited(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
- ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED,
+void Trace::MethodExited(Thread* thread,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+ ArtMethod* method,
+ uint32_t dex_pc ATTRIBUTE_UNUSED,
const JValue& return_value ATTRIBUTE_UNUSED) {
uint32_t thread_clock_diff = 0;
uint32_t wall_clock_diff = 0;
@@ -790,8 +794,10 @@
thread_clock_diff, wall_clock_diff);
}
-void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED,
- ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) {
+void Trace::MethodUnwind(Thread* thread,
+ Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
+ ArtMethod* method,
+ uint32_t dex_pc ATTRIBUTE_UNUSED) {
uint32_t thread_clock_diff = 0;
uint32_t wall_clock_diff = 0;
ReadClocks(thread, &thread_clock_diff, &wall_clock_diff);
@@ -800,7 +806,7 @@
}
void Trace::ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED,
- mirror::Throwable* exception_object ATTRIBUTE_UNUSED)
+ Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {
LOG(ERROR) << "Unexpected exception caught event in tracing";
}
@@ -812,7 +818,7 @@
}
void Trace::InvokeVirtualOrInterface(Thread*,
- mirror::Object*,
+ Handle<mirror::Object>,
ArtMethod* method,
uint32_t dex_pc,
ArtMethod*) {
diff --git a/runtime/trace.h b/runtime/trace.h
index 485e9a1..ad10250 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -140,36 +140,54 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_);
// InstrumentationListener implementation.
- void MethodEntered(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc)
+ void MethodEntered(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
OVERRIDE;
- void MethodExited(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc,
+ void MethodExited(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
const JValue& return_value)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
OVERRIDE;
- void MethodUnwind(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc)
+ void MethodUnwind(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
OVERRIDE;
- void DexPcMoved(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t new_dex_pc)
+ void DexPcMoved(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t new_dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_)
OVERRIDE;
- void FieldRead(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc, ArtField* field)
+ void FieldRead(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
- void FieldWritten(Thread* thread, mirror::Object* this_object,
- ArtMethod* method, uint32_t dex_pc, ArtField* field,
+ void FieldWritten(Thread* thread,
+ Handle<mirror::Object> this_object,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtField* field,
const JValue& field_value)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
- void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object)
+ void ExceptionCaught(Thread* thread,
+ Handle<mirror::Throwable> exception_object)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
- void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset)
+ void Branch(Thread* thread,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ int32_t dex_pc_offset)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
void InvokeVirtualOrInterface(Thread* thread,
- mirror::Object* this_object,
+ Handle<mirror::Object> this_object,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee)
diff --git a/test/Instrumentation/Instrumentation.java b/test/Instrumentation/Instrumentation.java
index 09d4342..b44f78f 100644
--- a/test/Instrumentation/Instrumentation.java
+++ b/test/Instrumentation/Instrumentation.java
@@ -15,8 +15,21 @@
*/
public class Instrumentation {
+ private static int primitiveField;
+ private static Object referenceField;
+
// Direct method
private void instanceMethod() {
System.out.println("instanceMethod");
}
+
+ private Object returnReference() {
+ System.out.println("returnReference");
+ return null;
+ }
+
+ private int returnPrimitive() {
+ System.out.println("returnPrimitive");
+ return 0;
+ }
}