diff options
Diffstat (limited to 'runtime/debugger.cc')
-rw-r--r-- | runtime/debugger.cc | 245 |
1 files changed, 187 insertions, 58 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 2872a02e16..9012f006af 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -126,14 +126,14 @@ static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs) return os; } -class DebugInstrumentationListener : public instrumentation::InstrumentationListener { +class DebugInstrumentationListener FINAL : public instrumentation::InstrumentationListener { public: DebugInstrumentationListener() {} virtual ~DebugInstrumentationListener() {} - virtual void MethodEntered(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (method->IsNative()) { // TODO: post location events is a suspension point and native method entry stubs aren't. return; @@ -141,10 +141,9 @@ class DebugInstrumentationListener : public instrumentation::InstrumentationList Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry, nullptr); } - virtual void MethodExited(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, - uint32_t dex_pc, const JValue& return_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void MethodExited(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, const JValue& return_value) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (method->IsNative()) { // TODO: post location events is a suspension point and native method entry stubs aren't. return; @@ -152,26 +151,41 @@ class DebugInstrumentationListener : public instrumentation::InstrumentationList Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit, &return_value); } - virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void MethodUnwind(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // We're not recorded to listen to this kind of event, so complain. LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method) << " " << dex_pc; } - virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, - mirror::ArtMethod* method, uint32_t new_dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + void DexPcMoved(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t new_dex_pc) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc); } - virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, - mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, - mirror::Throwable* exception_object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Dbg::PostException(thread, throw_location, catch_method, catch_dex_pc, exception_object); + void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, mirror::ArtField* field) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field); + } + + void FieldWritten(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, + uint32_t dex_pc, mirror::ArtField* field, const JValue& field_value) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostFieldModificationEvent(method, dex_pc, this_object, field, &field_value); + } + + void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, + mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, + mirror::Throwable* exception_object) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Dbg::PostException(throw_location, catch_method, catch_dex_pc, exception_object); } + + private: + DISALLOW_COPY_AND_ASSIGN(DebugInstrumentationListener); } gDebugInstrumentationListener; // JDWP is allowed unless the Zygote forbids it. @@ -211,6 +225,7 @@ size_t Dbg::alloc_record_count_ = 0; Mutex* Dbg::deoptimization_lock_ = nullptr; std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_; size_t Dbg::full_deoptimization_event_count_ = 0; +size_t Dbg::delayed_full_undeoptimization_count_ = 0; // Breakpoints. static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); @@ -231,6 +246,14 @@ void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, uint32_t tid, } } +void DebugInvokeReq::Clear() { + invoke_needed = false; + receiver = nullptr; + thread = nullptr; + klass = nullptr; + method = nullptr; +} + void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type) { if (method != nullptr) { @@ -238,6 +261,16 @@ void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, uint32_t t } } +bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const { + return dex_pcs.find(dex_pc) == dex_pcs.end(); +} + +void SingleStepControl::Clear() { + is_active = false; + method = nullptr; + dex_pcs.clear(); +} + void DeoptimizationRequest::VisitRoots(RootCallback* callback, void* arg) { if (method != nullptr) { callback(reinterpret_cast<mirror::Object**>(&method), arg, 0, kRootDebugger); @@ -607,6 +640,14 @@ bool Dbg::IsDisposed() { return gDisposed; } +// All the instrumentation events the debugger is registered for. +static constexpr uint32_t kListenerEvents = instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kDexPcMoved | + instrumentation::Instrumentation::kFieldRead | + instrumentation::Instrumentation::kFieldWritten | + instrumentation::Instrumentation::kExceptionCaught; + void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -625,6 +666,7 @@ void Dbg::GoActive() { MutexLock mu(Thread::Current(), *deoptimization_lock_); CHECK_EQ(deoptimization_requests_.size(), 0U); CHECK_EQ(full_deoptimization_event_count_, 0U); + CHECK_EQ(delayed_full_undeoptimization_count_, 0U); } Runtime* runtime = Runtime::Current(); @@ -633,11 +675,7 @@ void Dbg::GoActive() { ThreadState old_state = self->SetStateUnsafe(kRunnable); CHECK_NE(old_state, kRunnable); runtime->GetInstrumentation()->EnableDeoptimization(); - runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, - instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kDexPcMoved | - instrumentation::Instrumentation::kExceptionCaught); + runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, kListenerEvents); gDebuggerActive = true; CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); runtime->GetThreadList()->ResumeAll(); @@ -667,12 +705,9 @@ void Dbg::Disconnected() { MutexLock mu(Thread::Current(), *deoptimization_lock_); deoptimization_requests_.clear(); full_deoptimization_event_count_ = 0U; + delayed_full_undeoptimization_count_ = 0U; } - runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, - instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kDexPcMoved | - instrumentation::Instrumentation::kExceptionCaught); + runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, kListenerEvents); runtime->GetInstrumentation()->DisableDeoptimization(); gDebuggerActive = false; } @@ -1217,7 +1252,8 @@ JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int c LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count; return JDWP::ERR_INVALID_LENGTH; } - const char* descriptor = ClassHelper(dst->GetClass()).GetDescriptor(); + ClassHelper ch(dst->GetClass()); + const char* descriptor = ch.GetDescriptor(); JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor + 1); if (IsPrimitiveTag(tag)) { @@ -1571,6 +1607,13 @@ void Dbg::OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return OutputJValue(tag, return_value, pReply); } +void Dbg::OutputFieldValue(JDWP::FieldId field_id, const JValue* field_value, + JDWP::ExpandBuf* pReply) { + mirror::ArtField* f = FromFieldId(field_id); + JDWP::JdwpTag tag = BasicTagFromDescriptor(FieldHelper(f).GetTypeDescriptor()); + OutputJValue(tag, field_value, pReply); +} + JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id, std::vector<uint8_t>& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -2443,21 +2486,70 @@ JDWP::JdwpError Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame return visitor.error_; } +JDWP::ObjectId Dbg::GetThisObjectIdForEvent(mirror::Object* this_object) { + // If 'this_object' isn't already in the registry, we know that we're not looking for it, so + // there's no point adding it to the registry and burning through ids. + // When registering an event request with an instance filter, we've been given an existing object + // id so it must already be present in the registry when the event fires. + JDWP::ObjectId this_id = 0; + if (this_object != nullptr && gRegistry->Contains(this_object)) { + this_id = gRegistry->Add(this_object); + } + return this_id; +} + void Dbg::PostLocationEvent(mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object, int event_flags, const JValue* return_value) { + if (!IsDebuggerActive()) { + return; + } + DCHECK(m != nullptr); + DCHECK_EQ(m->IsStatic(), this_object == nullptr); JDWP::JdwpLocation location; SetLocation(location, m, dex_pc); - // If 'this_object' isn't already in the registry, we know that we're not looking for it, - // so there's no point adding it to the registry and burning through ids. - JDWP::ObjectId this_id = 0; - if (gRegistry->Contains(this_object)) { - this_id = gRegistry->Add(this_object); - } + // We need 'this' for InstanceOnly filters only. + JDWP::ObjectId this_id = GetThisObjectIdForEvent(this_object); gJdwpState->PostLocationEvent(&location, this_id, event_flags, return_value); } -void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location, +void Dbg::PostFieldAccessEvent(mirror::ArtMethod* m, int dex_pc, + mirror::Object* this_object, mirror::ArtField* f) { + if (!IsDebuggerActive()) { + return; + } + DCHECK(m != nullptr); + DCHECK(f != nullptr); + JDWP::JdwpLocation location; + SetLocation(location, m, dex_pc); + + JDWP::RefTypeId type_id = gRegistry->AddRefType(f->GetDeclaringClass()); + JDWP::FieldId field_id = ToFieldId(f); + JDWP::ObjectId this_id = gRegistry->Add(this_object); + + gJdwpState->PostFieldEvent(&location, type_id, field_id, this_id, nullptr, false); +} + +void Dbg::PostFieldModificationEvent(mirror::ArtMethod* m, int dex_pc, + mirror::Object* this_object, mirror::ArtField* f, + const JValue* field_value) { + if (!IsDebuggerActive()) { + return; + } + DCHECK(m != nullptr); + DCHECK(f != nullptr); + DCHECK(field_value != nullptr); + JDWP::JdwpLocation location; + SetLocation(location, m, dex_pc); + + JDWP::RefTypeId type_id = gRegistry->AddRefType(f->GetDeclaringClass()); + JDWP::FieldId field_id = ToFieldId(f); + JDWP::ObjectId this_id = gRegistry->Add(this_object); + + gJdwpState->PostFieldEvent(&location, type_id, field_id, this_id, field_value, true); +} + +void Dbg::PostException(const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, mirror::Throwable* exception_object) { if (!IsDebuggerActive()) { @@ -2469,8 +2561,8 @@ void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location, JDWP::JdwpLocation catch_location; SetLocation(catch_location, catch_method, catch_dex_pc); - // We need 'this' for InstanceOnly filters. - JDWP::ObjectId this_id = gRegistry->Add(throw_location.GetThis()); + // We need 'this' for InstanceOnly filters only. + JDWP::ObjectId this_id = GetThisObjectIdForEvent(throw_location.GetThis()); JDWP::ObjectId exception_id = gRegistry->Add(exception_object); JDWP::RefTypeId exception_class_id = gRegistry->AddRefType(exception_object->GetClass()); @@ -2520,7 +2612,7 @@ void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, } else if (single_step_control->step_size == JDWP::SS_MIN) { event_flags |= kSingleStep; VLOG(jdwp) << "SS new instruction"; - } else if (single_step_control->dex_pcs.find(dex_pc) == single_step_control->dex_pcs.end()) { + } else if (single_step_control->ContainsDexPc(dex_pc)) { event_flags |= kSingleStep; VLOG(jdwp) << "SS new line"; } @@ -2542,7 +2634,7 @@ void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, if (single_step_control->step_size == JDWP::SS_MIN) { event_flags |= kSingleStep; VLOG(jdwp) << "SS new instruction"; - } else if (single_step_control->dex_pcs.find(dex_pc) == single_step_control->dex_pcs.end()) { + } else if (single_step_control->ContainsDexPc(dex_pc)) { event_flags |= kSingleStep; VLOG(jdwp) << "SS new line"; } @@ -2579,20 +2671,24 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { LOG(WARNING) << "Ignoring empty deoptimization request."; break; case DeoptimizationRequest::kFullDeoptimization: - VLOG(jdwp) << "Deoptimize the world"; + VLOG(jdwp) << "Deoptimize the world ..."; instrumentation->DeoptimizeEverything(); + VLOG(jdwp) << "Deoptimize the world DONE"; break; case DeoptimizationRequest::kFullUndeoptimization: - VLOG(jdwp) << "Undeoptimize the world"; + VLOG(jdwp) << "Undeoptimize the world ..."; instrumentation->UndeoptimizeEverything(); + VLOG(jdwp) << "Undeoptimize the world DONE"; break; case DeoptimizationRequest::kSelectiveDeoptimization: - VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.method); + VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.method) << " ..."; instrumentation->Deoptimize(request.method); + VLOG(jdwp) << "Deoptimize method " << PrettyMethod(request.method) << " DONE"; break; case DeoptimizationRequest::kSelectiveUndeoptimization: - VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.method); + VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.method) << " ..."; instrumentation->Undeoptimize(request.method); + VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(request.method) << " DONE"; break; default: LOG(FATAL) << "Unsupported deoptimization request kind " << request.kind; @@ -2600,17 +2696,43 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { } } +void Dbg::DelayFullUndeoptimization() { + MutexLock mu(Thread::Current(), *deoptimization_lock_); + ++delayed_full_undeoptimization_count_; + DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_); +} + +void Dbg::ProcessDelayedFullUndeoptimizations() { + // TODO: avoid taking the lock twice (once here and once in ManageDeoptimization). + { + MutexLock mu(Thread::Current(), *deoptimization_lock_); + while (delayed_full_undeoptimization_count_ > 0) { + DeoptimizationRequest req; + req.kind = DeoptimizationRequest::kFullUndeoptimization; + req.method = nullptr; + RequestDeoptimizationLocked(req); + --delayed_full_undeoptimization_count_; + } + } + ManageDeoptimization(); +} + void Dbg::RequestDeoptimization(const DeoptimizationRequest& req) { if (req.kind == DeoptimizationRequest::kNothing) { // Nothing to do. return; } MutexLock mu(Thread::Current(), *deoptimization_lock_); + RequestDeoptimizationLocked(req); +} + +void Dbg::RequestDeoptimizationLocked(const DeoptimizationRequest& req) { switch (req.kind) { case DeoptimizationRequest::kFullDeoptimization: { DCHECK(req.method == nullptr); if (full_deoptimization_event_count_ == 0) { - VLOG(jdwp) << "Request full deoptimization"; + VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() + << " for full deoptimization"; deoptimization_requests_.push_back(req); } ++full_deoptimization_event_count_; @@ -2621,20 +2743,23 @@ void Dbg::RequestDeoptimization(const DeoptimizationRequest& req) { DCHECK_GT(full_deoptimization_event_count_, 0U); --full_deoptimization_event_count_; if (full_deoptimization_event_count_ == 0) { - VLOG(jdwp) << "Request full undeoptimization"; + VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() + << " for full undeoptimization"; deoptimization_requests_.push_back(req); } break; } case DeoptimizationRequest::kSelectiveDeoptimization: { DCHECK(req.method != nullptr); - VLOG(jdwp) << "Request deoptimization of " << PrettyMethod(req.method); + VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() + << " for deoptimization of " << PrettyMethod(req.method); deoptimization_requests_.push_back(req); break; } case DeoptimizationRequest::kSelectiveUndeoptimization: { DCHECK(req.method != nullptr); - VLOG(jdwp) << "Request undeoptimization of " << PrettyMethod(req.method); + VLOG(jdwp) << "Queue request #" << deoptimization_requests_.size() + << " for undeoptimization of " << PrettyMethod(req.method); deoptimization_requests_.push_back(req); break; } @@ -2662,7 +2787,9 @@ void Dbg::ManageDeoptimization() { const ThreadState old_state = self->SetStateUnsafe(kRunnable); { MutexLock mu(self, *deoptimization_lock_); + size_t req_index = 0; for (const DeoptimizationRequest& request : deoptimization_requests_) { + VLOG(jdwp) << "Process deoptimization request #" << req_index++; ProcessDeoptimizationRequest(request); } deoptimization_requests_.clear(); @@ -2909,8 +3036,9 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // struct DebugCallbackContext { - explicit DebugCallbackContext(SingleStepControl* single_step_control, int32_t line_number) - : single_step_control_(single_step_control), line_number_(line_number), + explicit DebugCallbackContext(SingleStepControl* single_step_control, int32_t line_number, + const DexFile::CodeItem* code_item) + : single_step_control_(single_step_control), line_number_(line_number), code_item_(code_item), last_pc_valid(false), last_pc(0) { } @@ -2937,7 +3065,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize ~DebugCallbackContext() { // If the line number was the last in the position table... if (last_pc_valid) { - size_t end = MethodHelper(single_step_control_->method).GetCodeItem()->insns_size_in_code_units_; + size_t end = code_item_->insns_size_in_code_units_; for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) { single_step_control_->dex_pcs.insert(dex_pc); } @@ -2946,15 +3074,17 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize SingleStepControl* const single_step_control_; const int32_t line_number_; + const DexFile::CodeItem* const code_item_; bool last_pc_valid; uint32_t last_pc; }; single_step_control->dex_pcs.clear(); mirror::ArtMethod* m = single_step_control->method; if (!m->IsNative()) { - DebugCallbackContext context(single_step_control, line_number); MethodHelper mh(m); - mh.GetDexFile().DecodeDebugInfo(mh.GetCodeItem(), m->IsStatic(), m->GetDexMethodIndex(), + const DexFile::CodeItem* const code_item = mh.GetCodeItem(); + DebugCallbackContext context(single_step_control, line_number, code_item); + mh.GetDexFile().DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback, NULL, &context); } @@ -2974,8 +3104,8 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize VLOG(jdwp) << "Single-step current line: " << line_number; VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->stack_depth; VLOG(jdwp) << "Single-step dex_pc values:"; - for (std::set<uint32_t>::iterator it = single_step_control->dex_pcs.begin(); it != single_step_control->dex_pcs.end(); ++it) { - VLOG(jdwp) << StringPrintf(" %#x", *it); + for (uint32_t dex_pc : single_step_control->dex_pcs) { + VLOG(jdwp) << StringPrintf(" %#x", dex_pc); } } @@ -2990,8 +3120,7 @@ void Dbg::UnconfigureStep(JDWP::ObjectId thread_id) { if (error == JDWP::ERR_NONE) { SingleStepControl* single_step_control = thread->GetSingleStepControl(); DCHECK(single_step_control != nullptr); - single_step_control->is_active = false; - single_step_control->dex_pcs.clear(); + single_step_control->Clear(); } } |