Merge "JDWP: allocate DebugInvokeReq only when requested"
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 964e84c..e6df12e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -347,26 +347,9 @@
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
- if (receiver != nullptr) {
- callback(&receiver, arg, root_info);
- }
- if (thread != nullptr) {
- callback(&thread, arg, root_info);
- }
- if (klass != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&klass), arg, root_info);
- }
- if (method != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
- }
-}
-
-void DebugInvokeReq::Clear() {
- invoke_needed = false;
- receiver = nullptr;
- thread = nullptr;
- klass = nullptr;
- method = nullptr;
+ receiver.VisitRootIfNonNull(callback, arg, root_info); // null for static method call.
+ klass.VisitRoot(callback, arg, root_info);
+ method.VisitRoot(callback, arg, root_info);
}
void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
@@ -3517,10 +3500,14 @@
};
// Allocate single step.
- SingleStepControl* single_step_control = new SingleStepControl(step_size, step_depth,
- visitor.stack_depth,
- visitor.method);
- CHECK(single_step_control != nullptr) << "Failed to allocate SingleStepControl";
+ SingleStepControl* single_step_control =
+ new (std::nothrow) SingleStepControl(step_size, step_depth,
+ visitor.stack_depth, visitor.method);
+ if (single_step_control == nullptr) {
+ LOG(ERROR) << "Failed to allocate SingleStepControl";
+ return JDWP::ERR_OUT_OF_MEMORY;
+ }
+
mirror::ArtMethod* m = single_step_control->GetMethod();
const int32_t line_number = visitor.line_number;
if (!m->IsNative()) {
@@ -3597,7 +3584,7 @@
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* targetThread = nullptr;
- DebugInvokeReq* req = nullptr;
+ std::unique_ptr<DebugInvokeReq> req;
Thread* self = Thread::Current();
{
ScopedObjectAccessUnchecked soa(self);
@@ -3608,8 +3595,13 @@
LOG(ERROR) << "InvokeMethod request for invalid thread id " << thread_id;
return error;
}
- req = targetThread->GetInvokeReq();
- if (!req->ready) {
+ if (targetThread->GetInvokeReq() != nullptr) {
+ // Thread is already invoking a method on behalf of the debugger.
+ LOG(ERROR) << "InvokeMethod request for thread already invoking a method: " << *targetThread;
+ return JDWP::ERR_ALREADY_INVOKING;
+ }
+ if (!targetThread->IsReadyForDebugInvoke()) {
+ // Thread is not suspended by an event so it cannot invoke a method.
LOG(ERROR) << "InvokeMethod request for thread not stopped by event: " << *targetThread;
return JDWP::ERR_INVALID_THREAD;
}
@@ -3643,11 +3635,10 @@
return JDWP::ERR_INVALID_OBJECT;
}
- mirror::Object* thread = gRegistry->Get<mirror::Object*>(thread_id, &error);
+ gRegistry->Get<mirror::Object*>(thread_id, &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
- // TODO: check that 'thread' is actually a java.lang.Thread!
mirror::Class* c = DecodeClass(class_id, &error);
if (c == nullptr) {
@@ -3705,14 +3696,17 @@
}
}
- req->receiver = receiver;
- req->thread = thread;
- req->klass = c;
- req->method = m;
- req->arg_count = arg_count;
- req->arg_values = arg_values;
- req->options = options;
- req->invoke_needed = true;
+ // Allocates a DebugInvokeReq.
+ req.reset(new (std::nothrow) DebugInvokeReq(receiver, c, m, options, arg_values, arg_count));
+ if (req.get() == nullptr) {
+ LOG(ERROR) << "Failed to allocate DebugInvokeReq";
+ return JDWP::ERR_OUT_OF_MEMORY;
+ }
+
+ // Attach the DebugInvokeReq to the target thread so it executes the method when
+ // it is resumed. Once the invocation completes, it will detach it and signal us
+ // before suspending itself.
+ targetThread->SetDebugInvokeReq(req.get());
}
// The fact that we've released the thread list lock is a bit risky --- if the thread goes
@@ -3746,7 +3740,7 @@
gJdwpState->ReleaseJdwpTokenForCommand();
// Wait for the request to finish executing.
- while (req->invoke_needed) {
+ while (targetThread->GetInvokeReq() != nullptr) {
req->cond.Wait(self);
}
}
@@ -3779,11 +3773,7 @@
// Copy the result.
*pResultTag = req->result_tag;
- if (IsPrimitiveTag(req->result_tag)) {
- *pResultValue = req->result_value.GetJ();
- } else {
- *pResultValue = gRegistry->Add(req->result_value.GetL());
- }
+ *pResultValue = req->result_value;
*pExceptionId = req->exception;
return req->error;
}
@@ -3793,60 +3783,55 @@
// We can be called while an exception is pending. We need
// to preserve that across the method invocation.
- StackHandleScope<2> hs(soa.Self());
- auto old_exception = hs.NewHandle<mirror::Throwable>(nullptr);
- {
- ThrowLocation old_throw_location;
- mirror::Throwable* old_exception_obj = soa.Self()->GetException();
- old_exception.Assign(old_exception_obj);
- soa.Self()->ClearException();
- }
+ StackHandleScope<4> hs(soa.Self());
+ auto old_exception = hs.NewHandle<mirror::Throwable>(soa.Self()->GetException());
+ soa.Self()->ClearException();
// Translate the method through the vtable, unless the debugger wants to suppress it.
- MutableHandle<mirror::ArtMethod> m(hs.NewHandle(pReq->method));
- if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != nullptr) {
- mirror::ArtMethod* actual_method = pReq->klass->FindVirtualMethodForVirtualOrInterface(m.Get());
+ MutableHandle<mirror::ArtMethod> m(hs.NewHandle(pReq->method.Read()));
+ if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver.Read() != nullptr) {
+ mirror::ArtMethod* actual_method = pReq->klass.Read()->FindVirtualMethodForVirtualOrInterface(m.Get());
if (actual_method != m.Get()) {
- VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.Get()) << " to " << PrettyMethod(actual_method);
+ VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.Get())
+ << " to " << PrettyMethod(actual_method);
m.Assign(actual_method);
}
}
VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m.Get())
- << " receiver=" << pReq->receiver
+ << " receiver=" << pReq->receiver.Read()
<< " arg_count=" << pReq->arg_count;
CHECK(m.Get() != nullptr);
CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
- pReq->result_value = InvokeWithJValues(soa, pReq->receiver, soa.EncodeMethod(m.Get()),
- reinterpret_cast<jvalue*>(pReq->arg_values));
+ JValue result = InvokeWithJValues(soa, pReq->receiver.Read(), soa.EncodeMethod(m.Get()),
+ reinterpret_cast<jvalue*>(pReq->arg_values));
- mirror::Throwable* exception = soa.Self()->GetException();
- soa.Self()->ClearException();
- pReq->exception = gRegistry->Add(exception);
pReq->result_tag = BasicTagFromDescriptor(m.Get()->GetShorty());
+ const bool is_object_result = (pReq->result_tag == JDWP::JT_OBJECT);
+ Handle<mirror::Object> object_result = hs.NewHandle(is_object_result ? result.GetL() : nullptr);
+ Handle<mirror::Throwable> exception = hs.NewHandle(soa.Self()->GetException());
+ soa.Self()->ClearException();
+ pReq->exception = gRegistry->Add(exception.Get());
if (pReq->exception != 0) {
- VLOG(jdwp) << " JDWP invocation returning with exception=" << exception
- << " " << exception->Dump();
- pReq->result_value.SetJ(0);
- } else if (pReq->result_tag == JDWP::JT_OBJECT) {
+ VLOG(jdwp) << " JDWP invocation returning with exception=" << exception.Get()
+ << " " << exception->Dump();
+ pReq->result_value = 0;
+ } else if (is_object_result) {
/* if no exception thrown, examine object result more closely */
- JDWP::JdwpTag new_tag = TagFromObject(soa, pReq->result_value.GetL());
+ JDWP::JdwpTag new_tag = TagFromObject(soa, object_result.Get());
if (new_tag != pReq->result_tag) {
VLOG(jdwp) << " 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.GetL());
+ // Register the object in the registry and reference its ObjectId. This ensures
+ // GC safety and prevents from accessing stale reference if the object is moved.
+ pReq->result_value = gRegistry->Add(object_result.Get());
+ } else {
+ // Primitive result.
+ DCHECK(IsPrimitiveTag(pReq->result_tag));
+ pReq->result_value = result.GetJ();
}
if (old_exception.Get() != nullptr) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 428ded7..0ac83f6 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -54,34 +54,28 @@
* Invoke-during-breakpoint support.
*/
struct DebugInvokeReq {
- DebugInvokeReq()
- : ready(false), invoke_needed(false),
- receiver(NULL), thread(NULL), klass(NULL), method(NULL),
- arg_count(0), arg_values(NULL), options(0), error(JDWP::ERR_NONE),
- result_tag(JDWP::JT_VOID), exception(0),
+ DebugInvokeReq(mirror::Object* invoke_receiver, mirror::Class* invoke_class,
+ mirror::ArtMethod* invoke_method, uint32_t invoke_options,
+ uint64_t* args, uint32_t args_count)
+ : receiver(invoke_receiver), klass(invoke_class), method(invoke_method),
+ arg_count(args_count), arg_values(args), options(invoke_options),
+ error(JDWP::ERR_NONE), result_tag(JDWP::JT_VOID), result_value(0), exception(0),
lock("a DebugInvokeReq lock", kBreakpointInvokeLock),
cond("a DebugInvokeReq condition variable", lock) {
}
- /* boolean; only set when we're in the tail end of an event handler */
- bool ready;
-
- /* boolean; set if the JDWP thread wants this thread to do work */
- bool invoke_needed;
-
/* request */
- mirror::Object* receiver; /* not used for ClassType.InvokeMethod */
- mirror::Object* thread;
- mirror::Class* klass;
- mirror::ArtMethod* method;
- uint32_t arg_count;
- uint64_t* arg_values; /* will be NULL if arg_count_ == 0 */
- uint32_t options;
+ GcRoot<mirror::Object> receiver; // not used for ClassType.InvokeMethod
+ GcRoot<mirror::Class> klass;
+ GcRoot<mirror::ArtMethod> method;
+ const uint32_t arg_count;
+ uint64_t* const arg_values; // will be NULL if arg_count_ == 0
+ const uint32_t options;
/* result */
JDWP::JdwpError error;
JDWP::JdwpTag result_tag;
- JValue result_value;
+ uint64_t result_value; // either a primitive value or an ObjectId
JDWP::ObjectId exception;
/* condition variable to wait on while the method executes */
@@ -91,8 +85,6 @@
void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Clear();
-
private:
DISALLOW_COPY_AND_ASSIGN(DebugInvokeReq);
};
@@ -581,6 +573,8 @@
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Invoke support for commands ClassType.InvokeMethod, ClassType.NewInstance and
+ // ObjectReference.InvokeMethod.
static JDWP::JdwpError InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId object_id,
JDWP::RefTypeId class_id, JDWP::MethodId method_id,
uint32_t arg_count, uint64_t* arg_values,
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index fc08d23..4bf7142 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -596,17 +596,15 @@
return;
}
- DebugInvokeReq* pReq = Dbg::GetInvokeReq();
while (true) {
- pReq->ready = true;
Dbg::SuspendSelf();
- pReq->ready = false;
/*
* 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) {
+ DebugInvokeReq* const pReq = Dbg::GetInvokeReq();
+ if (pReq == nullptr) {
/*LOGD("SuspendByPolicy: no invoke needed");*/
break;
}
@@ -614,10 +612,7 @@
/* grab this before posting/suspending again */
AcquireJdwpTokenForEvent(thread_self_id);
- /* leave pReq->invoke_needed_ raised so we can check reentrancy */
Dbg::ExecuteMethod(pReq);
-
- pReq->error = ERR_NONE;
}
}
@@ -650,7 +645,7 @@
*/
bool JdwpState::InvokeInProgress() {
DebugInvokeReq* pReq = Dbg::GetInvokeReq();
- return pReq->invoke_needed;
+ return pReq != nullptr;
}
void JdwpState::AcquireJdwpTokenForCommand() {
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0ce4de7..c7083dc 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -91,9 +91,9 @@
* If "is_constructor" is set, this returns "object_id" rather than the
* expected-to-be-void return value of the called function.
*/
-static JdwpError FinishInvoke(JdwpState*, Request* request, ExpandBuf* pReply,
- ObjectId thread_id, ObjectId object_id,
- RefTypeId class_id, MethodId method_id, bool is_constructor)
+static JdwpError RequestInvoke(JdwpState*, Request* request, ExpandBuf* pReply,
+ ObjectId thread_id, ObjectId object_id,
+ RefTypeId class_id, MethodId method_id, bool is_constructor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK(!is_constructor || object_id != 0);
@@ -131,37 +131,35 @@
return err;
}
- if (err == ERR_NONE) {
- if (is_constructor) {
- // If we invoked a constructor (which actually returns void), return the receiver,
- // unless we threw, in which case we return NULL.
- resultTag = JT_OBJECT;
- resultValue = (exceptObjId == 0) ? object_id : 0;
- }
+ if (is_constructor) {
+ // If we invoked a constructor (which actually returns void), return the receiver,
+ // unless we threw, in which case we return NULL.
+ resultTag = JT_OBJECT;
+ resultValue = (exceptObjId == 0) ? object_id : 0;
+ }
- size_t width = Dbg::GetTagWidth(resultTag);
- expandBufAdd1(pReply, resultTag);
- if (width != 0) {
- WriteValue(pReply, width, resultValue);
- }
- expandBufAdd1(pReply, JT_OBJECT);
- expandBufAddObjectId(pReply, exceptObjId);
+ size_t width = Dbg::GetTagWidth(resultTag);
+ expandBufAdd1(pReply, resultTag);
+ if (width != 0) {
+ WriteValue(pReply, width, resultValue);
+ }
+ expandBufAdd1(pReply, JT_OBJECT);
+ expandBufAddObjectId(pReply, exceptObjId);
- VLOG(jdwp) << " --> returned " << resultTag
- << StringPrintf(" %#" PRIx64 " (except=%#" PRIx64 ")", resultValue, exceptObjId);
+ VLOG(jdwp) << " --> returned " << resultTag
+ << StringPrintf(" %#" PRIx64 " (except=%#" PRIx64 ")", resultValue, exceptObjId);
- /* show detailed debug output */
- if (resultTag == JT_STRING && exceptObjId == 0) {
- if (resultValue != 0) {
- if (VLOG_IS_ON(jdwp)) {
- std::string result_string;
- JDWP::JdwpError error = Dbg::StringToUtf8(resultValue, &result_string);
- CHECK_EQ(error, JDWP::ERR_NONE);
- VLOG(jdwp) << " string '" << result_string << "'";
- }
- } else {
- VLOG(jdwp) << " string (null)";
+ /* show detailed debug output */
+ if (resultTag == JT_STRING && exceptObjId == 0) {
+ if (resultValue != 0) {
+ if (VLOG_IS_ON(jdwp)) {
+ std::string result_string;
+ JDWP::JdwpError error = Dbg::StringToUtf8(resultValue, &result_string);
+ CHECK_EQ(error, JDWP::ERR_NONE);
+ VLOG(jdwp) << " string '" << result_string << "'";
}
+ } else {
+ VLOG(jdwp) << " string (null)";
}
}
@@ -693,7 +691,7 @@
ObjectId thread_id = request->ReadThreadId();
MethodId method_id = request->ReadMethodId();
- return FinishInvoke(state, request, pReply, thread_id, 0, class_id, method_id, false);
+ return RequestInvoke(state, request, pReply, thread_id, 0, class_id, method_id, false);
}
/*
@@ -717,7 +715,7 @@
if (object_id == 0) {
return ERR_OUT_OF_MEMORY;
}
- return FinishInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, true);
+ return RequestInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, true);
}
/*
@@ -879,7 +877,7 @@
RefTypeId class_id = request->ReadRefTypeId();
MethodId method_id = request->ReadMethodId();
- return FinishInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, false);
+ return RequestInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, false);
}
static JdwpError OR_DisableCollection(JdwpState*, Request* request, ExpandBuf*)
diff --git a/runtime/oat.h b/runtime/oat.h
index 5540ade..79cb024 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '5', '7', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '5', '8', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 94d1059..da1fb6e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1138,8 +1138,6 @@
Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) {
wait_mutex_ = new Mutex("a thread wait mutex");
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
- tlsPtr_.debug_invoke_req = new DebugInvokeReq;
- tlsPtr_.single_step_control = nullptr;
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
tlsPtr_.name = new std::string(kThreadNameDuringStartup);
tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
@@ -1291,7 +1289,6 @@
CleanupCpu();
}
- delete tlsPtr_.debug_invoke_req;
if (tlsPtr_.single_step_control != nullptr) {
delete tlsPtr_.single_step_control;
}
@@ -2416,4 +2413,21 @@
delete ssc;
}
+void Thread::SetDebugInvokeReq(DebugInvokeReq* req) {
+ CHECK(Dbg::IsDebuggerActive());
+ CHECK(GetInvokeReq() == nullptr) << "Debug invoke req already active in thread " << *this;
+ CHECK(Thread::Current() != this) << "Debug invoke can't be dispatched by the thread itself";
+ CHECK(req != nullptr);
+ tlsPtr_.debug_invoke_req = req;
+}
+
+void Thread::ClearDebugInvokeReq() {
+ CHECK(Dbg::IsDebuggerActive());
+ CHECK(GetInvokeReq() != nullptr) << "Debug invoke req not active in thread " << *this;
+ CHECK(Thread::Current() == this) << "Debug invoke must be finished by the thread itself";
+ // We do not own the DebugInvokeReq* so we must not delete it, it is the responsibility of
+ // the owner (the JDWP thread).
+ tlsPtr_.debug_invoke_req = nullptr;
+}
+
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index af02dc7..325c821 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -707,6 +707,16 @@
return tlsPtr_.single_step_control;
}
+ // Indicates whether this thread is ready to invoke a method for debugging. This
+ // is only true if the thread has been suspended by a debug event.
+ bool IsReadyForDebugInvoke() const {
+ return tls32_.ready_for_debug_invoke;
+ }
+
+ void SetReadyForDebugInvoke(bool ready) {
+ tls32_.ready_for_debug_invoke = ready;
+ }
+
// Activates single step control for debugging. The thread takes the
// ownership of the given SingleStepControl*. It is deleted by a call
// to DeactivateSingleStepControl or upon thread destruction.
@@ -715,6 +725,17 @@
// Deactivates single step control for debugging.
void DeactivateSingleStepControl();
+ // Sets debug invoke request for debugging. When the thread is resumed,
+ // it executes the method described by this request then suspends itself.
+ // The thread does not take ownership of the given DebugInvokeReq*, it is
+ // owned by the JDWP thread which is waiting for the execution of the
+ // method.
+ void SetDebugInvokeReq(DebugInvokeReq* req);
+
+ // Clears debug invoke request for debugging. When the thread completes
+ // method invocation, it clears its debug invoke request, signals the
+ // JDWP thread and suspends itself.
+ void ClearDebugInvokeReq();
// Returns the fake exception used to activate deoptimization.
static mirror::Throwable* GetDeoptimizationException() {
@@ -966,7 +987,8 @@
explicit tls_32bit_sized_values(bool is_daemon) :
suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
- thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false) {
+ thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false),
+ ready_for_debug_invoke(false) {
}
union StateAndFlags state_and_flags;
@@ -1010,6 +1032,11 @@
// used to distinguish runnable threads that are suspended due to
// a normal suspend check from other threads.
bool32_t suspended_at_suspend_check;
+
+ // True if the thread has been suspended by a debugger event. This is
+ // used to invoke method from the debugger which is only allowed when
+ // the thread is suspended by an event.
+ bool32_t ready_for_debug_invoke;
} tls32_;
struct PACKED(8) tls_64bit_sized_values {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index d4c1e8c..ddfbebd 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -860,7 +860,8 @@
}
void ThreadList::SuspendSelfForDebugger() {
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
+ self->SetReadyForDebugInvoke(true);
// The debugger thread must not suspend itself due to debugger activity!
Thread* debug_thread = Dbg::GetDebugThread();
@@ -881,11 +882,10 @@
VLOG(threads) << *self << " self-suspending (debugger)";
// Tell JDWP we've completed invocation and are ready to suspend.
- DebugInvokeReq* pReq = self->GetInvokeReq();
- DCHECK(pReq != NULL);
- if (pReq->invoke_needed) {
- // Clear this before signaling.
- pReq->Clear();
+ DebugInvokeReq* const pReq = self->GetInvokeReq();
+ if (pReq != nullptr) {
+ // Clear debug invoke request before signaling.
+ self->ClearDebugInvokeReq();
VLOG(jdwp) << "invoke complete, signaling";
MutexLock mu(self, pReq->lock);
@@ -916,6 +916,7 @@
CHECK_EQ(self->GetSuspendCount(), 0);
}
+ self->SetReadyForDebugInvoke(false);
VLOG(threads) << *self << " self-reviving (debugger)";
}