Implement JDWP InvokeMethod and breakpoints on exception throwing.
Change-Id: I1142bee843104f0850fd7270752104d5d73a44f0
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 037edfc..570e937 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -34,7 +34,7 @@
void JniAbort(const char* jni_function_name) {
Thread* self = Thread::Current();
- const Method* current_method = self->GetCurrentMethod();
+ Method* current_method = self->GetCurrentMethod();
std::ostringstream os;
os << "Aborting because JNI app bug detected (see above for details)";
diff --git a/src/debugger.cc b/src/debugger.cc
index b2f9629..dea4339 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -716,8 +716,7 @@
}
bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
- UNIMPLEMENTED(FATAL);
- return false;
+ return gRegistry->Get<Class*>(instClassId)->InstanceOf(gRegistry->Get<Class*>(classId));
}
JDWP::FieldId ToFieldId(Field* f) {
@@ -752,6 +751,14 @@
#endif
}
+void SetLocation(JDWP::JdwpLocation& location, Method* m, uintptr_t native_pc) {
+ Class* c = m->GetDeclaringClass();
+ location.typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+ location.classId = gRegistry->Add(c);
+ location.methodId = ToMethodId(m);
+ location.idx = m->IsNative() ? -1 : m->ToDexPC(native_pc);
+}
+
std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
Method* m = FromMethodId(methodId);
return MethodHelper(m).GetName();
@@ -1119,8 +1126,7 @@
}
uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
- UNIMPLEMENTED(FATAL);
- return 0;
+ return DecodeThread(threadId)->GetSuspendCount();
}
bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
@@ -1131,8 +1137,6 @@
return DecodeThread(threadId)->IsSuspended();
}
-//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
-
void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
struct ThreadListVisitor {
static void Visit(Thread* t, void* arg) {
@@ -1212,15 +1216,7 @@
if (depth == desired_frame_number) {
*pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
-
- Method* m = f.GetMethod();
- Class* c = m->GetDeclaringClass();
-
- pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
- pLoc->classId = gRegistry->Add(c);
- pLoc->methodId = ToMethodId(m);
- pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
-
+ SetLocation(*pLoc, f.GetMethod(), pc);
found = true;
}
++depth;
@@ -1276,15 +1272,15 @@
Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
}
-bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
+bool Dbg::GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
Method** sp = reinterpret_cast<Method**>(frameId);
Frame f;
f.SetSP(sp);
- uint16_t reg = DemangleSlot(0, f);
Method* m = f.GetMethod();
Object* o = NULL;
if (!m->IsNative() && !m->IsStatic()) {
+ uint16_t reg = DemangleSlot(0, f);
o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
}
*pThisId = gRegistry->Add(o);
@@ -1436,8 +1432,29 @@
UNIMPLEMENTED(FATAL);
}
-void Dbg::PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception) {
- UNIMPLEMENTED(FATAL);
+void Dbg::PostException(Method** sp, Method* throwMethod, uintptr_t throwNativePc, Method* catchMethod, uintptr_t catchNativePc, Object* exception) {
+ JDWP::JdwpLocation throw_location;
+ SetLocation(throw_location, throwMethod, throwNativePc);
+ JDWP::JdwpLocation catch_location;
+ SetLocation(catch_location, catchMethod, catchNativePc);
+
+ // We need 'this' for InstanceOnly filters.
+ JDWP::ObjectId this_id;
+ GetThisObject(reinterpret_cast<JDWP::FrameId>(sp), &this_id);
+
+ /*
+ * Hand the event to the JDWP exception handler. Note we're using the
+ * "NoReg" objectID on the exception, which is not strictly correct --
+ * the exception object WILL be passed up to the debugger if the
+ * debugger is interested in the event. We do this because the current
+ * implementation of the debugger object registry never throws anything
+ * away, and some people were experiencing a fatal build up of exception
+ * objects when dealing with certain libraries.
+ */
+ JDWP::ObjectId exception_id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(exception));
+ JDWP::RefTypeId exception_class_id = gRegistry->Add(exception->GetClass());
+
+ gJdwpState->PostException(&throw_location, exception_id, exception_class_id, &catch_location, this_id);
}
void Dbg::PostClassPrepare(Class* c) {
@@ -1462,17 +1479,182 @@
UNIMPLEMENTED(FATAL);
}
-JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptObj) {
- UNIMPLEMENTED(FATAL);
- return JDWP::ERR_NONE;
+JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptionId) {
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+
+ Thread* targetThread = NULL;
+ DebugInvokeReq* req = NULL;
+ {
+ ScopedThreadListLock thread_list_lock;
+ targetThread = DecodeThread(threadId);
+ if (targetThread == NULL) {
+ LOG(ERROR) << "InvokeMethod request for non-existent thread " << threadId;
+ return JDWP::ERR_INVALID_THREAD;
+ }
+ req = targetThread->GetInvokeReq();
+ if (!req->ready) {
+ LOG(ERROR) << "InvokeMethod request for thread not stopped by event: " << *targetThread;
+ return JDWP::ERR_INVALID_THREAD;
+ }
+
+ /*
+ * We currently have a bug where we don't successfully resume the
+ * target thread if the suspend count is too deep. We're expected to
+ * require one "resume" for each "suspend", but when asked to execute
+ * a method we have to resume fully and then re-suspend it back to the
+ * same level. (The easiest way to cause this is to type "suspend"
+ * multiple times in jdb.)
+ *
+ * It's unclear what this means when the event specifies "resume all"
+ * and some threads are suspended more deeply than others. This is
+ * a rare problem, so for now we just prevent it from hanging forever
+ * by rejecting the method invocation request. Without this, we will
+ * be stuck waiting on a suspended thread.
+ */
+ int suspend_count = targetThread->GetSuspendCount();
+ if (suspend_count > 1) {
+ LOG(ERROR) << *targetThread << " suspend count too deep for method invocation: " << suspend_count;
+ return JDWP::ERR_THREAD_SUSPENDED; // Probably not expected here.
+ }
+
+ /*
+ * TODO: ought to screen the various IDs, and verify that the argument
+ * list is valid.
+ */
+ req->receiver_ = gRegistry->Get<Object*>(objectId);
+ req->thread_ = gRegistry->Get<Object*>(threadId);
+ req->class_ = gRegistry->Get<Class*>(classId);
+ req->method_ = FromMethodId(methodId);
+ req->num_args_ = numArgs;
+ req->arg_array_ = argArray;
+ req->options_ = options;
+ req->invoke_needed_ = true;
+ }
+
+ // The fact that we've released the thread list lock is a bit risky --- if the thread goes
+ // away we're sitting high and dry -- but we must release this before the ResumeAllThreads
+ // call, and it's unwise to hold it during WaitForSuspend.
+
+ {
+ /*
+ * We change our (JDWP thread) status, which should be THREAD_RUNNING,
+ * so the VM can suspend for a GC if the invoke request causes us to
+ * run out of memory. It's also a good idea to change it before locking
+ * the invokeReq mutex, although that should never be held for long.
+ */
+ ScopedThreadStateChange tsc(Thread::Current(), Thread::kVmWait);
+
+ LOG(VERBOSE) << " Transferring control to event thread";
+ {
+ MutexLock mu(req->lock_);
+
+ if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
+ LOG(VERBOSE) << " Resuming all threads";
+ thread_list->ResumeAll(true);
+ } else {
+ LOG(VERBOSE) << " Resuming event thread only";
+ thread_list->Resume(targetThread, true);
+ }
+
+ // Wait for the request to finish executing.
+ while (req->invoke_needed_) {
+ req->cond_.Wait(req->lock_);
+ }
+ }
+ LOG(VERBOSE) << " Control has returned from event thread";
+
+ /* wait for thread to re-suspend itself */
+ targetThread->WaitUntilSuspended();
+ //dvmWaitForSuspend(targetThread);
+ }
+
+ /*
+ * Suspend the threads. We waited for the target thread to suspend
+ * itself, so all we need to do is suspend the others.
+ *
+ * The suspendAllThreads() call will double-suspend the event thread,
+ * so we want to resume the target thread once to keep the books straight.
+ */
+ if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
+ LOG(VERBOSE) << " Suspending all threads";
+ thread_list->SuspendAll(true);
+ LOG(VERBOSE) << " Resuming event thread to balance the count";
+ thread_list->Resume(targetThread, true);
+ }
+
+ // Copy the result.
+ *pResultTag = req->result_tag;
+ if (IsPrimitiveTag(req->result_tag)) {
+ *pResultValue = req->result_value.j;
+ } else {
+ *pResultValue = gRegistry->Add(req->result_value.l);
+ }
+ *pExceptionId = req->exception;
+ return req->error;
}
void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
- UNIMPLEMENTED(FATAL);
+ Thread* self = Thread::Current();
+
+ // We can be called while an exception is pending in the VM. We need
+ // to preserve that across the method invocation.
+ SirtRef<Throwable> old_exception(self->GetException());
+ self->ClearException();
+
+ ScopedThreadStateChange tsc(self, Thread::kRunnable);
+
+ // Translate the method through the vtable, unless the debugger wants to suppress it.
+ Method* m = pReq->method_;
+ if ((pReq->options_ & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver_ != NULL) {
+ m = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_);
+ }
+ CHECK(m != NULL);
+
+ CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
+
+ pReq->result_value = InvokeWithJValues(self, pReq->receiver_, m, reinterpret_cast<JValue*>(pReq->arg_array_));
+
+ pReq->exception = gRegistry->Add(self->GetException());
+ pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty());
+ if (pReq->exception != 0) {
+ Object* exc = self->GetException();
+ LOG(VERBOSE) << " JDWP invocation returning with exception=" << exc << " " << PrettyTypeOf(exc);
+ self->ClearException();
+ pReq->result_value.j = 0;
+ } else if (pReq->result_tag == JDWP::JT_OBJECT) {
+ /* if no exception thrown, examine object result more closely */
+ JDWP::JdwpTag new_tag = TagFromObject(pReq->result_value.l);
+ if (new_tag != pReq->result_tag) {
+ LOG(VERBOSE) << " JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
+ pReq->result_tag = new_tag;
+ }
+
+ /*
+ * Register the object. We don't actually need an ObjectId yet,
+ * but we do need to be sure that the GC won't move or discard the
+ * object when we switch out of RUNNING. The ObjectId conversion
+ * will add the object to the "do not touch" list.
+ *
+ * We can't use the "tracked allocation" mechanism here because
+ * the object is going to be handed off to a different thread.
+ */
+ gRegistry->Add(pReq->result_value.l);
+ }
+
+ if (old_exception.get() != NULL) {
+ self->SetException(old_exception.get());
+ }
}
+/*
+ * Register an object ID that might not have been registered previously.
+ *
+ * Normally this wouldn't happen -- the conversion to an ObjectId would
+ * have added the object to the registry -- but in some cases (e.g.
+ * throwing exceptions) we really want to do the registration late.
+ */
void Dbg::RegisterObjectId(JDWP::ObjectId id) {
- UNIMPLEMENTED(FATAL);
+ gRegistry->Add(reinterpret_cast<Object*>(id));
}
/*
diff --git a/src/debugger.h b/src/debugger.h
index 2faca36..381f1db 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -44,20 +44,20 @@
bool ready;
/* boolean; set if the JDWP thread wants this thread to do work */
- bool invoke_needed;
+ bool invoke_needed_;
/* request */
- Object* obj; /* not used for ClassType.InvokeMethod */
- Object* thread;
+ Object* receiver_; /* not used for ClassType.InvokeMethod */
+ Object* thread_;
Class* class_;
- Method* method;
- uint32_t num_args;
- uint64_t* arg_array; /* will be NULL if numArgs==0 */
- uint32_t options;
+ Method* method_;
+ uint32_t num_args_;
+ uint64_t* arg_array_; /* will be NULL if numArgs==0 */
+ uint32_t options_;
/* result */
JDWP::JdwpError error;
- uint8_t result_tag;
+ JDWP::JdwpTag result_tag;
JValue result_value;
JDWP::ObjectId exception;
@@ -199,7 +199,7 @@
static void ResumeThread(JDWP::ObjectId threadId);
static void SuspendSelf();
- static bool GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId);
+ static bool GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId);
static void GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen);
static void SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width);
@@ -213,7 +213,7 @@
kMethodExit = 0x08,
};
static void PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags);
- static void PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception);
+ static void PostException(Method** sp, Method* throwMethod, uintptr_t throwNativePc, Method* catchMethod, uintptr_t catchNativePc, Object* exception);
static void PostThreadStart(Thread* t);
static void PostThreadDeath(Thread* t);
static void PostClassPrepare(Class* c);
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 5f5541e..7bb4dc2 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -86,7 +86,7 @@
DCHECK(obj != NULL);
// TODO: stronger sanity check on the object (such as in heap)
- DCHECK_ALIGNED(reinterpret_cast<intptr_t>(obj), 8);
+ DCHECK_ALIGNED(reinterpret_cast<uintptr_t>(obj), 8);
DCHECK(table_ != NULL);
DCHECK_LE(alloc_entries_, max_entries_);
DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
diff --git a/src/jdwp/jdwp.h b/src/jdwp/jdwp.h
index b628e6c..36e2657 100644
--- a/src/jdwp/jdwp.h
+++ b/src/jdwp/jdwp.h
@@ -31,6 +31,7 @@
namespace art {
+struct Method;
struct Thread;
namespace JDWP {
@@ -56,6 +57,7 @@
static inline RefTypeId ReadRefTypeId(const uint8_t** pBuf) { return Read8BE(pBuf); }
static inline FrameId ReadFrameId(const uint8_t** pBuf) { return Read8BE(pBuf); }
static inline JdwpTag ReadTag(const uint8_t** pBuf) { return static_cast<JdwpTag>(Read1(pBuf)); }
+static inline JdwpTypeTag ReadTypeTag(const uint8_t** pBuf) { return static_cast<JdwpTypeTag>(Read1(pBuf)); }
static inline void SetFieldId(uint8_t* buf, FieldId val) { return Set4BE(buf, val); }
static inline void SetMethodId(uint8_t* buf, MethodId val) { return Set4BE(buf, val); }
static inline void SetObjectId(uint8_t* buf, ObjectId val) { return Set8BE(buf, val); }
@@ -71,10 +73,10 @@
* Holds a JDWP "location".
*/
struct JdwpLocation {
- uint8_t typeTag; /* class or interface? */
- RefTypeId classId; /* method->clazz */
- MethodId methodId; /* method in which "idx" resides */
- uint64_t idx; /* relative index into code block */
+ JdwpTypeTag typeTag;
+ RefTypeId classId;
+ MethodId methodId;
+ uint64_t idx; // A Dex PC.
};
std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs);
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index 15e1151..424d87f 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -535,7 +535,7 @@
* The JDWP thread has told us (and possibly all other threads) to
* resume. See if it has left anything in our DebugInvokeReq mailbox.
*/
- if (!pReq->invoke_needed) {
+ if (!pReq->invoke_needed_) {
/*LOGD("SuspendByPolicy: no invoke needed");*/
break;
}
@@ -543,14 +543,14 @@
/* grab this before posting/suspending again */
SetWaitForEventThread(Dbg::GetThreadSelfId());
- /* leave pReq->invoke_needed raised so we can check reentrancy */
+ /* leave pReq->invoke_needed_ raised so we can check reentrancy */
LOG(VERBOSE) << "invoking method...";
Dbg::ExecuteMethod(pReq);
pReq->error = ERR_NONE;
/* clear this before signaling */
- pReq->invoke_needed = false;
+ pReq->invoke_needed_ = false;
LOG(VERBOSE) << "invoke complete, signaling and self-suspending";
MutexLock mu(pReq->lock_);
@@ -567,7 +567,7 @@
*/
bool JdwpState::InvokeInProgress() {
DebugInvokeReq* pReq = Dbg::GetInvokeReq();
- return pReq->invoke_needed;
+ return pReq->invoke_needed_;
}
/*
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 9b5c080..238775e 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -49,7 +49,7 @@
*/
static void jdwpReadLocation(const uint8_t** pBuf, JdwpLocation* pLoc) {
memset(pLoc, 0, sizeof(*pLoc)); /* allows memcmp() later */
- pLoc->typeTag = Read1(pBuf);
+ pLoc->typeTag = ReadTypeTag(pBuf);
pLoc->classId = ReadObjectId(pBuf);
pLoc->methodId = ReadMethodId(pBuf);
pLoc->idx = Read8BE(pBuf);
@@ -1467,16 +1467,16 @@
* Returns the value of "this" for the specified frame.
*/
static JdwpError handleSF_ThisObject(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
- ObjectId threadId = ReadObjectId(&buf);
+ ReadObjectId(&buf); // Skip thread id.
FrameId frameId = ReadFrameId(&buf);
ObjectId objectId;
- if (!Dbg::GetThisObject(threadId, frameId, &objectId)) {
+ if (!Dbg::GetThisObject(frameId, &objectId)) {
return ERR_INVALID_FRAMEID;
}
uint8_t objectTag = Dbg::GetObjectTag(objectId);
- LOG(VERBOSE) << StringPrintf(" Req for 'this' in thread=%llx frame=%llx --> %llx '%c'", threadId, frameId, objectId, (char)objectTag);
+ LOG(VERBOSE) << StringPrintf(" Req for 'this' in frame=%llx --> %llx '%c'", frameId, objectId, (char)objectTag);
expandBufAdd1(pReply, objectTag);
expandBufAddObjectId(pReply, objectId);
diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc
index 07ffc52..98490e2 100644
--- a/src/jdwp/jdwp_main.cc
+++ b/src/jdwp/jdwp_main.cc
@@ -451,9 +451,9 @@
}
std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs) {
- // TODO: do we really want the Class* and Method* as pointers?
- os << rhs.typeTag << " " << (void*) rhs.classId << " " << (void*) rhs.methodId << " " << rhs.idx
- << " (" << Dbg::GetClassDescriptor(rhs.classId) << "." << Dbg::GetMethodName(rhs.classId, rhs.methodId) << ")";
+ os << "JdwpLocation["
+ << Dbg::GetClassDescriptor(rhs.classId) << "." << Dbg::GetMethodName(rhs.classId, rhs.methodId)
+ << "@" << rhs.idx << " " << rhs.typeTag << "]";
return os;
}
@@ -481,6 +481,17 @@
return os;
}
+std::ostream& operator<<(std::ostream& os, const JdwpTypeTag& value) {
+ switch (value) {
+ case TT_CLASS: os << "TT_CLASS"; break;
+ case TT_INTERFACE: os << "TT_INTERFACE"; break;
+ case TT_ARRAY: os << "TT_ARRAY"; break;
+ default:
+ os << "JdwpTypeTag[" << static_cast<int32_t>(value) << "]";
+ }
+ return os;
+}
+
} // namespace JDWP
} // namespace art
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 718659f..7e145a8 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -236,6 +236,42 @@
return arg_array.release();
}
+static byte* CreateArgArray(Method* method, JValue* args) {
+ const char* shorty = MethodHelper(method).GetShorty();
+ size_t shorty_len = strlen(shorty);
+ size_t num_bytes = NumArgArrayBytes(shorty);
+ UniquePtr<byte[]> arg_array(new byte[num_bytes]);
+ for (size_t i = 1, offset = 0; i < shorty_len; ++i) {
+ switch (shorty[i]) {
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'I':
+ *reinterpret_cast<uint32_t*>(&arg_array[offset]) = args[i - 1].i;
+ offset += 4;
+ break;
+ case 'F':
+ *reinterpret_cast<float*>(&arg_array[offset]) = args[i - 1].f;
+ offset += 4;
+ break;
+ case 'L':
+ *reinterpret_cast<Object**>(&arg_array[offset]) = args[i - 1].l;
+ offset += sizeof(Object*);
+ break;
+ case 'D':
+ *reinterpret_cast<double*>(&arg_array[offset]) = args[i - 1].d;
+ offset += 8;
+ break;
+ case 'J':
+ *reinterpret_cast<uint64_t*>(&arg_array[offset]) = args[i - 1].j;
+ offset += 8;
+ break;
+ }
+ }
+ return arg_array.release();
+}
+
static JValue InvokeWithArgArray(JNIEnv* public_env, Object* receiver, Method* method, byte* args) {
JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
JValue result;
@@ -657,6 +693,11 @@
return InvokeWithArgArray(env, receiver, method, arg_array.get());
}
+JValue InvokeWithJValues(Thread* self, Object* receiver, Method* m, JValue* args) {
+ UniquePtr<byte[]> arg_array(CreateArgArray(m, args));
+ return InvokeWithArgArray(self->GetJniEnv(), receiver, m, arg_array.get());
+}
+
class JNI {
public:
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 6950dae..f39740b 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -62,6 +62,7 @@
size_t NumArgArrayBytes(const char* shorty);
JValue InvokeWithJValues(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args);
+JValue InvokeWithJValues(Thread* self, Object* receiver, Method* m, JValue* args);
struct JavaVMExt : public JavaVM {
JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options);
diff --git a/src/monitor.cc b/src/monitor.cc
index 69eb9ba..8d04e2d 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -238,8 +238,7 @@
// When debugging, save the current monitor holder for future
// acquisition failures to use in sampled logging.
if (lock_profiling_threshold_ != 0) {
- locking_method_ = self->GetCurrentMethod();
- locking_pc_ = self->GetCurrentReturnPc();
+ locking_method_ = self->GetCurrentMethod(&locking_pc_);
}
}
diff --git a/src/monitor_android.cc b/src/monitor_android.cc
index 39dc524..f80d248 100644
--- a/src/monitor_android.cc
+++ b/src/monitor_android.cc
@@ -76,9 +76,11 @@
cp = EventLogWriteInt(cp, wait_ms);
// Emit the source code file name, <= 37 bytes.
+ uintptr_t pc;
+ Method* m = self->GetCurrentMethod(&pc);
const char* filename;
uint32_t line_number;
- TranslateLocation(self->GetCurrentMethod(), self->GetCurrentReturnPc(), filename, line_number);
+ TranslateLocation(m, pc, filename, line_number);
cp = EventLogWriteString(cp, filename, strlen(filename));
// Emit the source code line number, 5 bytes.
diff --git a/src/thread.cc b/src/thread.cc
index 53155b6..26eaf68 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1347,10 +1347,10 @@
void Thread::DeliverException() {
const bool kDebugExceptionDelivery = false;
- Throwable *exception = GetException(); // Get exception from thread
+ Throwable* exception = GetException(); // Get exception from thread
CHECK(exception != NULL);
// Don't leave exception visible while we try to find the handler, which may cause class
- // resolution
+ // resolution.
ClearException();
if (kDebugExceptionDelivery) {
DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << std::endl);
@@ -1360,22 +1360,27 @@
CatchBlockStackVisitor catch_finder(exception->GetClass(), long_jump_context);
WalkStackUntilUpCall(&catch_finder, true);
+ Method** sp;
+ uintptr_t throw_native_pc;
+ Method* throw_method = GetCurrentMethod(&throw_native_pc, &sp);
+ uintptr_t catch_native_pc = catch_finder.handler_pc_;
+ Method* catch_method = catch_finder.handler_frame_.GetMethod();
+ Dbg::PostException(sp, throw_method, throw_native_pc, catch_method, catch_native_pc, exception);
+
if (kDebugExceptionDelivery) {
- Method* handler_method = catch_finder.handler_frame_.GetMethod();
- if (handler_method == NULL) {
+ if (catch_method == NULL) {
LOG(INFO) << "Handler is upcall";
} else {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const DexFile& dex_file =
- class_linker->FindDexFile(handler_method->GetDeclaringClass()->GetDexCache());
- int line_number = dex_file.GetLineNumFromPC(handler_method,
- handler_method->ToDexPC(catch_finder.handler_pc_));
- LOG(INFO) << "Handler: " << PrettyMethod(handler_method)
- << " (line: " << line_number << ")";
+ class_linker->FindDexFile(catch_method->GetDeclaringClass()->GetDexCache());
+ int line_number = dex_file.GetLineNumFromPC(catch_method,
+ catch_method->ToDexPC(catch_finder.handler_pc_));
+ LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")";
}
}
SetException(exception);
- long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP()));
+ long_jump_context->SetSP(reinterpret_cast<uintptr_t>(catch_finder.handler_frame_.GetSP()));
long_jump_context->SetPC(catch_finder.handler_pc_);
long_jump_context->DoLongJump();
}
@@ -1389,25 +1394,24 @@
return result;
}
-const Method* Thread::GetCurrentMethod() const {
- Method* m = top_of_managed_stack_.GetMethod();
+Method* Thread::GetCurrentMethod(uintptr_t* pc, Method*** sp) const {
+ Frame f = top_of_managed_stack_;
+ Method* m = f.GetMethod();
// We use JNI internally for exception throwing, so it's possible to arrive
// here via a "FromCode" function, in which case there's a synthetic
// callee-save method at the top of the stack. These shouldn't be user-visible,
// so if we find one, skip it and return the compiled method underneath.
if (m != NULL && m->IsCalleeSaveMethod()) {
- Frame f = top_of_managed_stack_;
f.Next();
m = f.GetMethod();
}
- return m;
-}
-
-uint32_t Thread::GetCurrentReturnPc() const {
- if (top_of_managed_stack_.GetMethod() == NULL) {
- return 0;
+ if (pc != NULL) {
+ *pc = ManglePc(f.GetReturnPC());
}
- return ManglePc(top_of_managed_stack_.GetReturnPC());
+ if (sp != NULL) {
+ *sp = f.GetSP();
+ }
+ return m;
}
bool Thread::HoldsLock(Object* object) {
diff --git a/src/thread.h b/src/thread.h
index 823b72c..c4c619a 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -241,11 +241,12 @@
return &stats_;
}
- // Returns the Method* for the current method.
- // This is used by the JNI implementation for logging and diagnostic purposes.
- const Method* GetCurrentMethod() const;
+ int GetSuspendCount() const {
+ return suspend_count_;
+ }
- uint32_t GetCurrentReturnPc() const;
+ // Returns the current Method* and native PC (not dex PC) for this thread.
+ Method* GetCurrentMethod(uintptr_t* pc = NULL, Method*** sp = NULL) const;
bool IsExceptionPending() const {
return exception_ != NULL;
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 2179697..e646231 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -311,7 +311,10 @@
void ThreadList::Resume(Thread* thread, bool for_debugger) {
DCHECK(thread != Thread::Current());
- thread_list_lock_.AssertHeld();
+
+ if (!for_debugger) { // The debugger is very naughty. See Dbg::InvokeMethod.
+ thread_list_lock_.AssertHeld();
+ }
if (verbose_) {
LOG(INFO) << "Resume(" << *thread << ") starting..." << (for_debugger ? " (debugger)" : "");