diff options
| -rw-r--r-- | runtime/debugger.cc | 145 | ||||
| -rw-r--r-- | runtime/debugger.h | 3 | ||||
| -rw-r--r-- | runtime/jdwp/jdwp_event.cc | 4 |
3 files changed, 109 insertions, 43 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc index e2f6085ae2..ef5db2d5ea 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -183,16 +183,20 @@ class AllocRecord { class Breakpoint { public: - Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, bool need_full_deoptimization) + Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, + DeoptimizationRequest::Kind deoptimization_kind) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : method_(nullptr), dex_pc_(dex_pc), need_full_deoptimization_(need_full_deoptimization) { + : method_(nullptr), dex_pc_(dex_pc), deoptimization_kind_(deoptimization_kind) { + CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing || + deoptimization_kind_ == DeoptimizationRequest::kSelectiveDeoptimization || + deoptimization_kind_ == DeoptimizationRequest::kFullDeoptimization); ScopedObjectAccessUnchecked soa(Thread::Current()); method_ = soa.EncodeMethod(method); } Breakpoint(const Breakpoint& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : method_(nullptr), dex_pc_(other.dex_pc_), - need_full_deoptimization_(other.need_full_deoptimization_) { + deoptimization_kind_(other.deoptimization_kind_) { ScopedObjectAccessUnchecked soa(Thread::Current()); method_ = soa.EncodeMethod(other.Method()); } @@ -206,8 +210,8 @@ class Breakpoint { return dex_pc_; } - bool NeedFullDeoptimization() const { - return need_full_deoptimization_; + DeoptimizationRequest::Kind GetDeoptimizationKind() const { + return deoptimization_kind_; } private: @@ -216,7 +220,7 @@ class Breakpoint { uint32_t dex_pc_; // Indicates whether breakpoint needs full deoptimization or selective deoptimization. - bool need_full_deoptimization_; + DeoptimizationRequest::Kind deoptimization_kind_; }; static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs) @@ -736,6 +740,12 @@ bool Dbg::IsDisposed() { return gDisposed; } +bool Dbg::RequiresDeoptimization() { + // We don't need deoptimization if everything runs with interpreter after + // enabling -Xint mode. + return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly(); +} + void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -768,7 +778,9 @@ void Dbg::GoActive() { Thread* self = Thread::Current(); ThreadState old_state = self->SetStateUnsafe(kRunnable); CHECK_NE(old_state, kRunnable); - runtime->GetInstrumentation()->EnableDeoptimization(); + if (RequiresDeoptimization()) { + runtime->GetInstrumentation()->EnableDeoptimization(); + } instrumentation_events_ = 0; gDebuggerActive = true; CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); @@ -806,7 +818,9 @@ void Dbg::Disconnected() { instrumentation_events_); instrumentation_events_ = 0; } - runtime->GetInstrumentation()->DisableDeoptimization(); + if (RequiresDeoptimization()) { + runtime->GetInstrumentation()->DisableDeoptimization(); + } gDebuggerActive = false; } gRegistry->Clear(); @@ -3035,9 +3049,11 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { } void Dbg::DelayFullUndeoptimization() { - MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); - ++delayed_full_undeoptimization_count_; - DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_); + if (RequiresDeoptimization()) { + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + ++delayed_full_undeoptimization_count_; + DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_); + } } void Dbg::ProcessDelayedFullUndeoptimizations() { @@ -3196,64 +3212,101 @@ static const Breakpoint* FindFirstBreakpointForMethod(mirror::ArtMethod* m) } // Sanity checks all existing breakpoints on the same method. -static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, bool need_full_deoptimization) +static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, + DeoptimizationRequest::Kind deoptimization_kind) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::breakpoint_lock_) { for (const Breakpoint& breakpoint : gBreakpoints) { - CHECK_EQ(need_full_deoptimization, breakpoint.NeedFullDeoptimization()); + if (breakpoint.Method() == m) { + CHECK_EQ(deoptimization_kind, breakpoint.GetDeoptimizationKind()); + } } - if (need_full_deoptimization) { + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) { // We should have deoptimized everything but not "selectively" deoptimized this method. - CHECK(Runtime::Current()->GetInstrumentation()->AreAllMethodsDeoptimized()); - CHECK(!Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); - } else { + CHECK(instrumentation->AreAllMethodsDeoptimized()); + CHECK(!instrumentation->IsDeoptimized(m)); + } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) { // We should have "selectively" deoptimized this method. // Note: while we have not deoptimized everything for this method, we may have done it for // another event. - CHECK(Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); + CHECK(instrumentation->IsDeoptimized(m)); + } else { + // This method does not require deoptimization. + CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing); + CHECK(!instrumentation->IsDeoptimized(m)); } } -// Installs a breakpoint at the specified location. Also indicates through the deoptimization -// request if we need to deoptimize. -void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) { - Thread* const self = Thread::Current(); - mirror::ArtMethod* m = FromMethodId(location->method_id); - DCHECK(m != nullptr) << "No method for method id " << location->method_id; - +static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self, + mirror::ArtMethod* m) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (!Dbg::RequiresDeoptimization()) { + // We already run in interpreter-only mode so we don't need to deoptimize anything. + VLOG(jdwp) << "No need for deoptimization when fully running with interpreter for method " + << PrettyMethod(m); + return DeoptimizationRequest::kNothing; + } const Breakpoint* existing_breakpoint; { ReaderMutexLock mu(self, *Locks::breakpoint_lock_); existing_breakpoint = FindFirstBreakpointForMethod(m); } - bool need_full_deoptimization; if (existing_breakpoint == nullptr) { // There is no breakpoint on this method yet: we need to deoptimize. If this method may be // inlined, we deoptimize everything; otherwise we deoptimize only this method. // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension. // Therefore we must not hold any lock when we call it. - need_full_deoptimization = IsMethodPossiblyInlined(self, m); + bool need_full_deoptimization = IsMethodPossiblyInlined(self, m); if (need_full_deoptimization) { - req->SetKind(DeoptimizationRequest::kFullDeoptimization); - req->SetMethod(nullptr); + VLOG(jdwp) << "Need full deoptimization because of possible inlining of method " + << PrettyMethod(m); + return DeoptimizationRequest::kFullDeoptimization; } else { - req->SetKind(DeoptimizationRequest::kSelectiveDeoptimization); - req->SetMethod(m); + // We don't need to deoptimize if the method has not been compiled. + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr; + if (is_compiled) { + VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m); + return DeoptimizationRequest::kSelectiveDeoptimization; + } else { + // Method is not compiled: we don't need to deoptimize. + VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m); + return DeoptimizationRequest::kNothing; + } } } else { // There is at least one breakpoint for this method: we don't need to deoptimize. - req->SetKind(DeoptimizationRequest::kNothing); - req->SetMethod(nullptr); - - need_full_deoptimization = existing_breakpoint->NeedFullDeoptimization(); + // Let's check that all breakpoints are configured the same way for deoptimization. + VLOG(jdwp) << "Breakpoint already set: no deoptimization is required"; + DeoptimizationRequest::Kind deoptimization_kind = existing_breakpoint->GetDeoptimizationKind(); if (kIsDebugBuild) { ReaderMutexLock mu(self, *Locks::breakpoint_lock_); - SanityCheckExistingBreakpoints(m, need_full_deoptimization); + SanityCheckExistingBreakpoints(m, deoptimization_kind); } + return DeoptimizationRequest::kNothing; + } +} + +// Installs a breakpoint at the specified location. Also indicates through the deoptimization +// request if we need to deoptimize. +void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) { + Thread* const self = Thread::Current(); + mirror::ArtMethod* m = FromMethodId(location->method_id); + DCHECK(m != nullptr) << "No method for method id " << location->method_id; + + const DeoptimizationRequest::Kind deoptimization_kind = GetRequiredDeoptimizationKind(self, m); + req->SetKind(deoptimization_kind); + if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) { + req->SetMethod(m); + } else { + CHECK(deoptimization_kind == DeoptimizationRequest::kNothing || + deoptimization_kind == DeoptimizationRequest::kFullDeoptimization); + req->SetMethod(nullptr); } { WriterMutexLock mu(self, *Locks::breakpoint_lock_); - gBreakpoints.push_back(Breakpoint(m, location->dex_pc, need_full_deoptimization)); + gBreakpoints.push_back(Breakpoint(m, location->dex_pc, deoptimization_kind)); VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": " << gBreakpoints[gBreakpoints.size() - 1]; } @@ -3265,12 +3318,13 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ WriterMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); mirror::ArtMethod* m = FromMethodId(location->method_id); DCHECK(m != nullptr) << "No method for method id " << location->method_id; - bool need_full_deoptimization = false; + DeoptimizationRequest::Kind deoptimization_kind = DeoptimizationRequest::kNothing; for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) { if (gBreakpoints[i].DexPc() == location->dex_pc && gBreakpoints[i].Method() == m) { VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i]; - need_full_deoptimization = gBreakpoints[i].NeedFullDeoptimization(); - DCHECK_NE(need_full_deoptimization, Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); + deoptimization_kind = gBreakpoints[i].GetDeoptimizationKind(); + DCHECK_EQ(deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization, + Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); gBreakpoints.erase(gBreakpoints.begin() + i); break; } @@ -3278,21 +3332,26 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ const Breakpoint* const existing_breakpoint = FindFirstBreakpointForMethod(m); if (existing_breakpoint == nullptr) { // There is no more breakpoint on this method: we need to undeoptimize. - if (need_full_deoptimization) { + if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) { // This method required full deoptimization: we need to undeoptimize everything. req->SetKind(DeoptimizationRequest::kFullUndeoptimization); req->SetMethod(nullptr); - } else { + } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) { // This method required selective deoptimization: we need to undeoptimize only that method. req->SetKind(DeoptimizationRequest::kSelectiveUndeoptimization); req->SetMethod(m); + } else { + // This method had no need for deoptimization: do nothing. + CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing); + req->SetKind(DeoptimizationRequest::kNothing); + req->SetMethod(nullptr); } } else { // There is at least one breakpoint for this method: we don't need to undeoptimize. req->SetKind(DeoptimizationRequest::kNothing); req->SetMethod(nullptr); if (kIsDebugBuild) { - SanityCheckExistingBreakpoints(m, need_full_deoptimization); + SanityCheckExistingBreakpoints(m, deoptimization_kind); } } } diff --git a/runtime/debugger.h b/runtime/debugger.h index 488ba7f728..92031634cd 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -523,6 +523,9 @@ class Dbg { LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Indicates whether we need deoptimization for debugging. + static bool RequiresDeoptimization(); + // Records deoptimization request in the queue. static void RequestDeoptimization(const DeoptimizationRequest& req) LOCKS_EXCLUDED(Locks::deoptimization_lock_) diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 44f713ca96..1e0a2d2c22 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -125,6 +125,10 @@ struct ModBasket { }; static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { + if (!Dbg::RequiresDeoptimization()) { + // We don't need deoptimization for debugging. + return false; + } switch (eventKind) { case EK_METHOD_ENTRY: case EK_METHOD_EXIT: |