diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/debugger.cc | 157 | ||||
-rw-r--r-- | runtime/debugger.h | 26 | ||||
-rw-r--r-- | runtime/instrumentation.cc | 349 | ||||
-rw-r--r-- | runtime/instrumentation.h | 59 | ||||
-rw-r--r-- | runtime/jdwp/jdwp.h | 6 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_event.cc | 98 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_main.cc | 1 | ||||
-rw-r--r-- | runtime/locks.cc | 4 | ||||
-rw-r--r-- | runtime/locks.h | 8 | ||||
-rw-r--r-- | runtime/native/java_lang_Thread.cc | 1 | ||||
-rw-r--r-- | runtime/stack.cc | 4 | ||||
-rw-r--r-- | runtime/thread_state.h | 1 | ||||
-rw-r--r-- | runtime/trace.cc | 2 |
13 files changed, 552 insertions, 164 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc index bcf7267..4ea1366 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -176,14 +176,27 @@ static size_t gAllocRecordMax GUARDED_BY(gAllocTrackerLock) = 0; static size_t gAllocRecordHead GUARDED_BY(gAllocTrackerLock) = 0; static size_t gAllocRecordCount GUARDED_BY(gAllocTrackerLock) = 0; -// Breakpoints and single-stepping. +// Deoptimization support. +struct MethodInstrumentationRequest { + bool deoptimize; + + // Method for selective deoptimization. NULL means full deoptimization. + mirror::ArtMethod* method; + + MethodInstrumentationRequest(bool deoptimize, mirror::ArtMethod* method) + : deoptimize(deoptimize), method(method) {} +}; +// TODO we need to visit associated methods as roots. +static std::vector<MethodInstrumentationRequest> gDeoptimizationRequests GUARDED_BY(Locks::deoptimization_lock_); + +// Breakpoints. static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc) LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); - for (size_t i = 0; i < gBreakpoints.size(); ++i) { + for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) { if (gBreakpoints[i].method == m && gBreakpoints[i].dex_pc == dex_pc) { VLOG(jdwp) << "Hit breakpoint #" << i << ": " << gBreakpoints[i]; return true; @@ -520,11 +533,17 @@ void Dbg::GoActive() { CHECK_EQ(gBreakpoints.size(), 0U); } + { + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + CHECK_EQ(gDeoptimizationRequests.size(), 0U); + } + Runtime* runtime = Runtime::Current(); runtime->GetThreadList()->SuspendAll(); Thread* self = Thread::Current(); ThreadState old_state = self->SetStateUnsafe(kRunnable); CHECK_NE(old_state, kRunnable); + runtime->GetInstrumentation()->EnableDeoptimization(); runtime->GetInstrumentation()->AddListener(&gDebugInstrumentationListener, instrumentation::Instrumentation::kMethodEntered | instrumentation::Instrumentation::kMethodExited | @@ -549,6 +568,14 @@ void Dbg::Disconnected() { runtime->GetThreadList()->SuspendAll(); Thread* self = Thread::Current(); ThreadState old_state = self->SetStateUnsafe(kRunnable); + { + // Since we're going to disable deoptimization, we clear the deoptimization requests queue. + // This prevent us from having any pending deoptimization request when the debugger attaches to + // us again while no event has been requested yet. + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + gDeoptimizationRequests.clear(); + } + runtime->GetInstrumentation()->DisableDeoptimization(); runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener, instrumentation::Instrumentation::kMethodEntered | instrumentation::Instrumentation::kMethodExited | @@ -1691,6 +1718,7 @@ JDWP::JdwpThreadStatus Dbg::ToJdwpThreadStatus(ThreadState state) { case kWaitingForDebuggerSend: case kWaitingForDebuggerSuspension: case kWaitingForDebuggerToAttach: + case kWaitingForDeoptimization: case kWaitingForGcToComplete: case kWaitingForCheckPointsToRun: case kWaitingForJniOnLoad: @@ -2384,22 +2412,129 @@ void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, } } +static void ProcessDeoptimizationRequests() + LOCKS_EXCLUDED(Locks::deoptimization_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (const MethodInstrumentationRequest& request : gDeoptimizationRequests) { + mirror::ArtMethod* const method = request.method; + if (method != nullptr) { + // Selective deoptimization. + if (request.deoptimize) { + VLOG(jdwp) << "Deoptimize method " << PrettyMethod(method); + instrumentation->Deoptimize(method); + } else { + VLOG(jdwp) << "Undeoptimize method " << PrettyMethod(method); + instrumentation->Undeoptimize(method); + } + } else { + // Full deoptimization. + if (request.deoptimize) { + VLOG(jdwp) << "Deoptimize the world"; + instrumentation->DeoptimizeEverything(); + } else { + VLOG(jdwp) << "Undeoptimize the world"; + instrumentation->UndeoptimizeEverything(); + } + } + } + gDeoptimizationRequests.clear(); +} + +// Process deoptimization requests after suspending all mutator threads. +void Dbg::ManageDeoptimization() { + Thread* const self = Thread::Current(); + { + // Avoid suspend/resume if there is no pending request. + MutexLock mu(self, *Locks::deoptimization_lock_); + if (gDeoptimizationRequests.empty()) { + return; + } + } + CHECK_EQ(self->GetState(), kRunnable); + self->TransitionFromRunnableToSuspended(kWaitingForDeoptimization); + // We need to suspend mutator threads first. + Runtime* const runtime = Runtime::Current(); + runtime->GetThreadList()->SuspendAll(); + const ThreadState old_state = self->SetStateUnsafe(kRunnable); + ProcessDeoptimizationRequests(); + CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); + runtime->GetThreadList()->ResumeAll(); + self->TransitionFromSuspendedToRunnable(); +} + +// Enable full deoptimization. +void Dbg::EnableFullDeoptimization() { + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + VLOG(jdwp) << "Request full deoptimization"; + gDeoptimizationRequests.push_back(MethodInstrumentationRequest(true, nullptr)); +} + +// Disable full deoptimization. +void Dbg::DisableFullDeoptimization() { + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + VLOG(jdwp) << "Request full undeoptimization"; + gDeoptimizationRequests.push_back(MethodInstrumentationRequest(false, nullptr)); +} + void Dbg::WatchLocation(const JDWP::JdwpLocation* location) { - MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); + bool need_deoptimization = true; mirror::ArtMethod* m = FromMethodId(location->method_id); - gBreakpoints.push_back(Breakpoint(m, location->dex_pc)); - VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": " << gBreakpoints[gBreakpoints.size() - 1]; + { + MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); + + // If there is no breakpoint on this method yet, we need to deoptimize it. + for (const Breakpoint& breakpoint : gBreakpoints) { + if (breakpoint.method == m) { + // We already set a breakpoint on this method, hence we deoptimized it. + DCHECK(Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); + need_deoptimization = false; + break; + } + } + + gBreakpoints.push_back(Breakpoint(m, location->dex_pc)); + VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": " << gBreakpoints[gBreakpoints.size() - 1]; + } + + if (need_deoptimization) { + // Request its deoptimization. This will be done after updating the JDWP event list. + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + gDeoptimizationRequests.push_back(MethodInstrumentationRequest(true, m)); + VLOG(jdwp) << "Request deoptimization of " << PrettyMethod(m); + } } void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location) { - MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); + bool can_undeoptimize = true; mirror::ArtMethod* m = FromMethodId(location->method_id); - for (size_t i = 0; i < gBreakpoints.size(); ++i) { - if (gBreakpoints[i].method == m && gBreakpoints[i].dex_pc == location->dex_pc) { - VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i]; - gBreakpoints.erase(gBreakpoints.begin() + i); - return; + DCHECK(Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); + { + MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); + for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) { + if (gBreakpoints[i].method == m && gBreakpoints[i].dex_pc == location->dex_pc) { + VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i]; + gBreakpoints.erase(gBreakpoints.begin() + i); + break; + } } + + // If there is no breakpoint on this method, we can undeoptimize it. + for (const Breakpoint& breakpoint : gBreakpoints) { + if (breakpoint.method == m) { + can_undeoptimize = false; + break; + } + } + } + + if (can_undeoptimize) { + // Request its undeoptimization. This will be done after updating the JDWP event list. + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + gDeoptimizationRequests.push_back(MethodInstrumentationRequest(false, m)); + VLOG(jdwp) << "Request undeoptimization of " << PrettyMethod(m); } } diff --git a/runtime/debugger.h b/runtime/debugger.h index acbb2c6..a3f8b9c 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -137,8 +137,9 @@ class Dbg { * when the debugger attaches. */ static void Connected(); - static void GoActive() LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::mutator_lock_); - static void Disconnected() LOCKS_EXCLUDED(Locks::mutator_lock_); + static void GoActive() + LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_, Locks::mutator_lock_); + static void Disconnected() LOCKS_EXCLUDED(Locks::deoptimization_lock_, Locks::mutator_lock_); static void Disposed(); // Returns true if we're actually debugging with a real debugger, false if it's @@ -385,12 +386,29 @@ class Dbg { LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Full Deoptimization control. Only used for method entry/exit and single-stepping. + static void EnableFullDeoptimization() + LOCKS_EXCLUDED(Locks::deoptimization_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void DisableFullDeoptimization() + EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Manage deoptimization after updating JDWP events list. This must be done while all mutator + // threads are suspended. + static void ManageDeoptimization() + LOCKS_EXCLUDED(Locks::deoptimization_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Breakpoints. static void WatchLocation(const JDWP::JdwpLocation* pLoc) - LOCKS_EXCLUDED(Locks::breakpoint_lock_) + LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void UnwatchLocation(const JDWP::JdwpLocation* pLoc) - LOCKS_EXCLUDED(Locks::breakpoint_lock_) + LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Single-stepping. static JDWP::JdwpError ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 710d9dd..0b11543 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -23,6 +23,7 @@ #include "class_linker.h" #include "debugger.h" #include "dex_file-inl.h" +#include "interpreter/interpreter.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" @@ -58,76 +59,87 @@ static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg) } bool Instrumentation::InstallStubsForClass(mirror::Class* klass) { + for (size_t i = 0, e = klass->NumDirectMethods(); i < e; i++) { + InstallStubsForMethod(klass->GetDirectMethod(i)); + } + for (size_t i = 0, e = klass->NumVirtualMethods(); i < e; i++) { + InstallStubsForMethod(klass->GetVirtualMethod(i)); + } + return true; +} + +static void UpdateEntrypoints(mirror::ArtMethod* method, const void* code) { + method->SetEntryPointFromCompiledCode(code); + if (!method->IsResolutionMethod()) { + if (code == GetCompiledCodeToInterpreterBridge()) { + method->SetEntryPointFromInterpreter(art::interpreter::artInterpreterToInterpreterBridge); + } else { + method->SetEntryPointFromInterpreter(art::artInterpreterToCompiledCodeBridge); + } + } +} + +void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) { + if (method->IsAbstract() || method->IsProxyMethod()) { + // Do not change stubs for these methods. + return; + } + const void* new_code; bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - bool is_initialized = klass->IsInitialized(); - for (size_t i = 0; i < klass->NumDirectMethods(); i++) { - mirror::ArtMethod* method = klass->GetDirectMethod(i); - if (!method->IsAbstract() && !method->IsProxyMethod()) { - const void* new_code; - if (uninstall) { - if (forced_interpret_only_ && !method->IsNative()) { - new_code = GetCompiledCodeToInterpreterBridge(); - } else if (is_initialized || !method->IsStatic() || method->IsConstructor()) { - new_code = class_linker->GetOatCodeFor(method); - } else { - new_code = GetResolutionTrampoline(class_linker); - } - } else { // !uninstall - if (!interpreter_stubs_installed_ || method->IsNative()) { - // Do not overwrite resolution trampoline. When the trampoline initializes the method's - // class, all its static methods' code will be set to the instrumentation entry point. - // For more details, see ClassLinker::FixupStaticTrampolines. - if (is_initialized || !method->IsStatic() || method->IsConstructor()) { - new_code = GetQuickInstrumentationEntryPoint(); - } else { - new_code = GetResolutionTrampoline(class_linker); - } - } else { - new_code = GetCompiledCodeToInterpreterBridge(); - } - } - method->SetEntryPointFromCompiledCode(new_code); + bool is_class_initialized = method->GetDeclaringClass()->IsInitialized(); + if (uninstall) { + if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) { + new_code = GetCompiledCodeToInterpreterBridge(); + } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { + new_code = class_linker->GetOatCodeFor(method); + } else { + new_code = GetResolutionTrampoline(class_linker); } - } - for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { - mirror::ArtMethod* method = klass->GetVirtualMethod(i); - if (!method->IsAbstract() && !method->IsProxyMethod()) { - const void* new_code; - if (uninstall) { - if (forced_interpret_only_ && !method->IsNative()) { - new_code = GetCompiledCodeToInterpreterBridge(); - } else { - new_code = class_linker->GetOatCodeFor(method); - } - } else { // !uninstall - if (!interpreter_stubs_installed_ || method->IsNative()) { + } else { // !uninstall + if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) { + new_code = GetCompiledCodeToInterpreterBridge(); + } else { + // Do not overwrite resolution trampoline. When the trampoline initializes the method's + // class, all its static methods code will be set to the instrumentation entry point. + // For more details, see ClassLinker::FixupStaticTrampolines. + if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { + // Do not overwrite interpreter to prevent from posting method entry/exit events twice. + new_code = class_linker->GetOatCodeFor(method); + if (entry_exit_stubs_installed_ && new_code != GetCompiledCodeToInterpreterBridge()) { new_code = GetQuickInstrumentationEntryPoint(); - } else { - new_code = GetCompiledCodeToInterpreterBridge(); } + } else { + new_code = GetResolutionTrampoline(class_linker); } - method->SetEntryPointFromCompiledCode(new_code); } } - return true; + UpdateEntrypoints(method, new_code); } // Places the instrumentation exit pc as the return PC for every quick frame. This also allows // deoptimization of quick frames to interpreter frames. +// Since we may already have done this previously, we need to push new instrumentation frame before +// existing instrumentation frames. static void InstrumentationInstallStack(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { struct InstallStackVisitor : public StackVisitor { - InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc) + InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc, + bool is_deoptimization_enabled) : StackVisitor(thread, context), instrumentation_stack_(thread->GetInstrumentationStack()), - instrumentation_exit_pc_(instrumentation_exit_pc), last_return_pc_(0) {} + existing_instrumentation_frames_count_(instrumentation_stack_->size()), + instrumentation_exit_pc_(instrumentation_exit_pc), + is_deoptimization_enabled_(is_deoptimization_enabled), + reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0), + last_return_pc_(0) { + } virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); if (GetCurrentQuickFrame() == NULL) { if (kVerboseInstrumentation) { LOG(INFO) << " Ignoring a shadow frame. Frame " << GetFrameId() - << " Method=" << PrettyMethod(m); + << " Method=" << PrettyMethod(m); } return true; // Ignore shadow frames. } @@ -149,22 +161,45 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) LOG(INFO) << " Installing exit stub in " << DescribeLocation(); } uintptr_t return_pc = GetReturnPc(); - CHECK_NE(return_pc, instrumentation_exit_pc_); - CHECK_NE(return_pc, 0U); - InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId(), - false); - if (kVerboseInstrumentation) { - LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump(); + if (return_pc == instrumentation_exit_pc_) { + // We've reached a frame which has already been installed with instrumentation exit stub. + // We should have already installed instrumentation on previous frames. + reached_existing_instrumentation_frames_ = true; + + CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size()); + const InstrumentationStackFrame& frame = instrumentation_stack_->at(instrumentation_stack_depth_); + CHECK_EQ(m, frame.method_) << "Expected " << PrettyMethod(m) + << ", Found " << PrettyMethod(frame.method_); + return_pc = frame.return_pc_; + if (kVerboseInstrumentation) { + LOG(INFO) << "Ignoring already instrumented " << frame.Dump(); + } + } else { + CHECK_NE(return_pc, 0U); + CHECK(!reached_existing_instrumentation_frames_); + InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId(), + false); + if (kVerboseInstrumentation) { + LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump(); + } + + // Insert frame before old ones so we do not corrupt the instrumentation stack. + auto it = instrumentation_stack_->end() - existing_instrumentation_frames_count_; + instrumentation_stack_->insert(it, instrumentation_frame); + SetReturnPc(instrumentation_exit_pc_); } - instrumentation_stack_->push_back(instrumentation_frame); dex_pcs_.push_back(m->ToDexPc(last_return_pc_)); - SetReturnPc(instrumentation_exit_pc_); last_return_pc_ = return_pc; + ++instrumentation_stack_depth_; return true; // Continue. } std::deque<InstrumentationStackFrame>* const instrumentation_stack_; + const size_t existing_instrumentation_frames_count_; std::vector<uint32_t> dex_pcs_; const uintptr_t instrumentation_exit_pc_; + const bool is_deoptimization_enabled_; + bool reached_existing_instrumentation_frames_; + size_t instrumentation_stack_depth_; uintptr_t last_return_pc_; }; if (kVerboseInstrumentation) { @@ -172,21 +207,27 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) thread->GetThreadName(thread_name); LOG(INFO) << "Installing exit stubs in " << thread_name; } + + Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); UniquePtr<Context> context(Context::Create()); uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc(); - InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc); + InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc, + instrumentation->IsDeoptimizationEnabled()); visitor.WalkStack(true); - - // Create method enter events for all methods current on the thread's stack. - Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); - typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It; - for (It it = thread->GetInstrumentationStack()->rbegin(), - end = thread->GetInstrumentationStack()->rend(); it != end; ++it) { - mirror::Object* this_object = (*it).this_object_; - mirror::ArtMethod* method = (*it).method_; - uint32_t dex_pc = visitor.dex_pcs_.back(); - visitor.dex_pcs_.pop_back(); - instrumentation->MethodEnterEvent(thread, this_object, method, dex_pc); + CHECK_EQ(visitor.dex_pcs_.size(), thread->GetInstrumentationStack()->size()); + + if (!instrumentation->IsDeoptimizationEnabled()) { + // Create method enter events for all methods currently on the thread's stack. We only do this + // if no debugger is attached to prevent from posting events twice. + typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It; + for (It it = thread->GetInstrumentationStack()->rbegin(), + end = thread->GetInstrumentationStack()->rend(); it != end; ++it) { + mirror::Object* this_object = (*it).this_object_; + mirror::ArtMethod* method = (*it).method_; + uint32_t dex_pc = visitor.dex_pcs_.back(); + visitor.dex_pcs_.pop_back(); + instrumentation->MethodEnterEvent(thread, this_object, method, dex_pc); + } } thread->VerifyStack(); } @@ -233,9 +274,12 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg) CHECK(m == instrumentation_frame.method_) << PrettyMethod(m); } SetReturnPc(instrumentation_frame.return_pc_); - // Create the method exit events. As the methods didn't really exit the result is 0. - instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m, - GetDexPc(), JValue()); + if (!instrumentation_->IsDeoptimizationEnabled()) { + // Create the method exit events. As the methods didn't really exit the result is 0. + // We only do this if no debugger is attached to prevent from posting events twice. + instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m, + GetDexPc(), JValue()); + } frames_removed_++; removed_stub = true; break; @@ -274,18 +318,12 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg) void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) { Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); - bool require_entry_exit_stubs = false; - bool require_interpreter = false; if ((events & kMethodEntered) != 0) { method_entry_listeners_.push_back(listener); - require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners; - require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners; have_method_entry_listeners_ = true; } if ((events & kMethodExited) != 0) { method_exit_listeners_.push_back(listener); - require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners; - require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners; have_method_exit_listeners_ = true; } if ((events & kMethodUnwind) != 0) { @@ -294,21 +332,17 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev } if ((events & kDexPcMoved) != 0) { dex_pc_listeners_.push_back(listener); - require_interpreter = true; have_dex_pc_listeners_ = true; } if ((events & kExceptionCaught) != 0) { exception_caught_listeners_.push_back(listener); have_exception_caught_listeners_ = true; } - ConfigureStubs(require_entry_exit_stubs, require_interpreter); UpdateInterpreterHandlerTable(); } void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) { Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); - bool require_entry_exit_stubs = false; - bool require_interpreter = false; if ((events & kMethodEntered) != 0) { bool contains = std::find(method_entry_listeners_.begin(), method_entry_listeners_.end(), @@ -317,10 +351,6 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t method_entry_listeners_.remove(listener); } have_method_entry_listeners_ = method_entry_listeners_.size() > 0; - require_entry_exit_stubs |= have_method_entry_listeners_ && - !kDeoptimizeForAccurateMethodEntryExitListeners; - require_interpreter = have_method_entry_listeners_ && - kDeoptimizeForAccurateMethodEntryExitListeners; } if ((events & kMethodExited) != 0) { bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(), @@ -329,10 +359,6 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t method_exit_listeners_.remove(listener); } have_method_exit_listeners_ = method_exit_listeners_.size() > 0; - require_entry_exit_stubs |= have_method_exit_listeners_ && - !kDeoptimizeForAccurateMethodEntryExitListeners; - require_interpreter = have_method_exit_listeners_ && - kDeoptimizeForAccurateMethodEntryExitListeners; } if ((events & kMethodUnwind) != 0) { method_unwind_listeners_.remove(listener); @@ -344,13 +370,11 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t dex_pc_listeners_.remove(listener); } have_dex_pc_listeners_ = dex_pc_listeners_.size() > 0; - require_interpreter |= have_dex_pc_listeners_; } if ((events & kExceptionCaught) != 0) { exception_caught_listeners_.remove(listener); have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0; } - ConfigureStubs(require_entry_exit_stubs, require_interpreter); UpdateInterpreterHandlerTable(); } @@ -394,9 +418,12 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require interpreter_stubs_installed_ = false; entry_exit_stubs_installed_ = false; runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this); - instrumentation_stubs_installed_ = false; - MutexLock mu(self, *Locks::thread_list_lock_); - Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this); + // Restore stack only if there is no method currently deoptimized. + if (deoptimized_methods_.empty()) { + instrumentation_stubs_installed_ = false; + MutexLock mu(self, *Locks::thread_list_lock_); + Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this); + } } } @@ -444,22 +471,115 @@ void Instrumentation::ResetQuickAllocEntryPoints() { } void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const { + const void* new_code; if (LIKELY(!instrumentation_stubs_installed_)) { - method->SetEntryPointFromCompiledCode(code); + new_code = code; } else { - if (!interpreter_stubs_installed_ || method->IsNative()) { - // Do not overwrite resolution trampoline. When the trampoline initializes the method's - // class, all its static methods' code will be set to the instrumentation entry point. - // For more details, see ClassLinker::FixupStaticTrampolines. - if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) { - method->SetEntryPointFromCompiledCode(code); - } else { - method->SetEntryPointFromCompiledCode(GetQuickInstrumentationEntryPoint()); - } + if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) { + new_code = GetCompiledCodeToInterpreterBridge(); + } else if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker()) || + code == GetCompiledCodeToInterpreterBridge()) { + new_code = code; + } else if (entry_exit_stubs_installed_) { + new_code = GetQuickInstrumentationEntryPoint(); } else { - method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge()); + new_code = code; } } + UpdateEntrypoints(method, new_code); +} + +void Instrumentation::Deoptimize(mirror::ArtMethod* method) { + CHECK(!method->IsNative()); + CHECK(!method->IsProxyMethod()); + CHECK(!method->IsAbstract()); + + std::pair<std::set<mirror::ArtMethod*>::iterator, bool> pair = deoptimized_methods_.insert(method); + bool already_deoptimized = !pair.second; + CHECK(!already_deoptimized) << "Method " << PrettyMethod(method) << " is already deoptimized"; + + if (!interpreter_stubs_installed_) { + UpdateEntrypoints(method, GetCompiledCodeToInterpreterBridge()); + + // Install instrumentation exit stub and instrumentation frames. We may already have installed + // these previously so it will only cover the newly created frames. + instrumentation_stubs_installed_ = true; + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + Runtime::Current()->GetThreadList()->ForEach(InstrumentationInstallStack, this); + } +} + +void Instrumentation::Undeoptimize(mirror::ArtMethod* method) { + CHECK(!method->IsNative()); + CHECK(!method->IsProxyMethod()); + CHECK(!method->IsAbstract()); + + auto it = deoptimized_methods_.find(method); + CHECK(it != deoptimized_methods_.end()) << "Method " << PrettyMethod(method) << " is not deoptimized"; + deoptimized_methods_.erase(it); + + // Restore code and possibly stack only if we did not deoptimize everything. + if (!interpreter_stubs_installed_) { + // Restore its code or resolution trampoline. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (method->IsStatic() && !method->IsConstructor() && !method->GetDeclaringClass()->IsInitialized()) { + UpdateEntrypoints(method, GetResolutionTrampoline(class_linker)); + } else { + UpdateEntrypoints(method, class_linker->GetOatCodeFor(method)); + } + + // If there is no deoptimized method left, we can restore the stack of each thread. + if (deoptimized_methods_.empty()) { + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this); + instrumentation_stubs_installed_ = false; + } + } +} + +bool Instrumentation::IsDeoptimized(mirror::ArtMethod* method) const { + DCHECK(method != nullptr); + return deoptimized_methods_.count(method); +} + +void Instrumentation::EnableDeoptimization() { + CHECK(deoptimized_methods_.empty()); +} + +void Instrumentation::DisableDeoptimization() { + // If we deoptimized everything, undo it. + if (interpreter_stubs_installed_) { + UndeoptimizeEverything(); + } + // Undeoptimized selected methods. + while (!deoptimized_methods_.empty()) { + auto it_begin = deoptimized_methods_.begin(); + Undeoptimize(*it_begin); + } + CHECK(deoptimized_methods_.empty()); +} + +bool Instrumentation::IsDeoptimizationEnabled() const { + return interpreter_stubs_installed_ || !deoptimized_methods_.empty(); +} + +void Instrumentation::DeoptimizeEverything() { + CHECK(!interpreter_stubs_installed_); + ConfigureStubs(false, true); +} + +void Instrumentation::UndeoptimizeEverything() { + CHECK(interpreter_stubs_installed_); + ConfigureStubs(false, false); +} + +void Instrumentation::EnableMethodTracing() { + bool require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners; + ConfigureStubs(!require_interpreter, require_interpreter); +} + +void Instrumentation::DisableMethodTracing() { + ConfigureStubs(false, false); } const void* Instrumentation::GetQuickCodeFor(const mirror::ArtMethod* method) const { @@ -596,20 +716,19 @@ uint64_t Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* mirror::Object* this_object = instrumentation_frame.this_object_; MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value); - bool deoptimize = false; - if (interpreter_stubs_installed_) { - // Deoptimize unless we're returning to an upcall. - NthCallerVisitor visitor(self, 1, true); - visitor.WalkStack(true); - deoptimize = visitor.caller != NULL; - if (deoptimize && kVerboseInstrumentation) { - LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller); - } + // Deoptimize if the caller needs to continue execution in the interpreter. Do nothing if we get + // back to an upcall. + NthCallerVisitor visitor(self, 1, true); + visitor.WalkStack(true); + bool deoptimize = (visitor.caller != NULL) && + (interpreter_stubs_installed_ || IsDeoptimized(visitor.caller)); + if (deoptimize && kVerboseInstrumentation) { + LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller); } if (deoptimize) { if (kVerboseInstrumentation) { LOG(INFO) << "Deoptimizing from " << PrettyMethod(method) - << " result is " << std::hex << return_value.GetJ(); + << " result is " << std::hex << return_value.GetJ(); } self->SetDeoptimizationReturnValue(return_value); return static_cast<uint64_t>(GetQuickDeoptimizationEntryPoint()) | diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 72a646e..41b545d 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -22,6 +22,7 @@ #include "locks.h" #include <stdint.h> +#include <set> #include <list> namespace art { @@ -120,6 +121,47 @@ class Instrumentation { EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + // Deoptimization. + void EnableDeoptimization() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + void DisableDeoptimization() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsDeoptimizationEnabled() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Executes everything with interpreter. + void DeoptimizeEverything() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + + // Executes everything with compiled code (or interpreter if there is no code). + void UndeoptimizeEverything() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + + // Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static + // method (except a class initializer) set to the resolution trampoline will be deoptimized only + // once its declaring class is initialized. + void Deoptimize(mirror::ArtMethod* method) + LOCKS_EXCLUDED(Locks::thread_list_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Undeoptimze the method by restoring its entrypoints. Nevertheless, a static method + // (except a class initializer) set to the resolution trampoline will be updated only once its + // declaring class is initialized. + void Undeoptimize(mirror::ArtMethod* method) + LOCKS_EXCLUDED(Locks::thread_list_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool IsDeoptimized(mirror::ArtMethod* method) const; + + // Enable method tracing by installing instrumentation entry/exit stubs. + void EnableMethodTracing() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + + // Disable method tracing by uninstalling instrumentation entry/exit stubs. + void DisableMethodTracing() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + InterpreterHandlerTable GetInterpreterHandlerTable() const { return interpreter_handler_table_; } @@ -129,7 +171,8 @@ class Instrumentation { void ResetQuickAllocEntryPoints(); // Update the code of a method respecting any installed stubs. - void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const; + void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Get the quick code for the given method. More efficient than asking the class linker as it // will short-cut to GetCode if instrumentation and static method resolution stubs aren't @@ -232,6 +275,9 @@ class Instrumentation { // Call back for configure stubs. bool InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void InstallStubsForMethod(mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: // Does the job of installing or removing instrumentation code within methods. void ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) @@ -294,6 +340,11 @@ class Instrumentation { std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); + // The set of methods being deoptimized (by the debugger) which must be executed with interpreter + // only. + // TODO we need to visit these methods as roots. + std::set<mirror::ArtMethod*> deoptimized_methods_; + // Current interpreter handler table. This is updated each time the thread state flags are // modified. InterpreterHandlerTable interpreter_handler_table_; @@ -317,9 +368,9 @@ struct InstrumentationStackFrame { mirror::Object* this_object_; mirror::ArtMethod* method_; - const uintptr_t return_pc_; - const size_t frame_id_; - const bool interpreter_entry_; + uintptr_t return_pc_; + size_t frame_id_; + bool interpreter_entry_; }; } // namespace instrumentation diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index fd78bf2..ebc844e 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -328,9 +328,11 @@ struct JdwpState { AtomicInteger event_serial_; // Linked list of events requested by the debugger (breakpoints, class prep, etc). - Mutex event_list_lock_; + Mutex event_list_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; JdwpEvent* event_list_ GUARDED_BY(event_list_lock_); - int event_list_size_ GUARDED_BY(event_list_lock_); // Number of elements in event_list_. + size_t event_list_size_ GUARDED_BY(event_list_lock_); // Number of elements in event_list_. + size_t full_deoptimization_requests_ GUARDED_BY(event_list_lock_); // Number of events requiring + // full deoptimization. // Used to synchronize suspension of the event thread (to avoid receiving "resume" // events before the thread has finished suspending itself). diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index b05b49d..4aa7f13 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -135,6 +135,18 @@ static void dumpEvent(const JdwpEvent* pEvent) { } } +static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { + switch (eventKind) { + case EK_METHOD_ENTRY: + case EK_METHOD_EXIT: + case EK_METHOD_EXIT_WITH_RETURN_VALUE: + case EK_SINGLE_STEP: + return true; + default: + return false; + } +} + /* * Add an event to the list. Ordering is not important. * @@ -170,16 +182,31 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { } } - /* - * Add to list. - */ - MutexLock mu(Thread::Current(), event_list_lock_); - if (event_list_ != NULL) { - pEvent->next = event_list_; - event_list_->prev = pEvent; + { + /* + * Add to list. + */ + MutexLock mu(Thread::Current(), event_list_lock_); + if (event_list_ != NULL) { + pEvent->next = event_list_; + event_list_->prev = pEvent; + } + event_list_ = pEvent; + ++event_list_size_; + + /** + * Do we need to enable full deoptimization ? + */ + if (NeedsFullDeoptimization(pEvent->eventKind)) { + if (full_deoptimization_requests_ == 0) { + // This is the first event that needs full deoptimization: enable it. + Dbg::EnableFullDeoptimization(); + } + ++full_deoptimization_requests_; + } } - event_list_ = pEvent; - ++event_list_size_; + + Dbg::ManageDeoptimization(); return ERR_NONE; } @@ -225,6 +252,17 @@ void JdwpState::UnregisterEvent(JdwpEvent* pEvent) { --event_list_size_; CHECK(event_list_size_ != 0 || event_list_ == NULL); + + /** + * Can we disable full deoptimization ? + */ + if (NeedsFullDeoptimization(pEvent->eventKind)) { + --full_deoptimization_requests_; + if (full_deoptimization_requests_ == 0) { + // We no longer need full deoptimization. + Dbg::DisableFullDeoptimization(); + } + } } /* @@ -235,20 +273,25 @@ void JdwpState::UnregisterEvent(JdwpEvent* pEvent) { * explicitly remove one-off single-step events.) */ void JdwpState::UnregisterEventById(uint32_t requestId) { - MutexLock mu(Thread::Current(), event_list_lock_); + bool found = false; + { + MutexLock mu(Thread::Current(), event_list_lock_); - JdwpEvent* pEvent = event_list_; - while (pEvent != NULL) { - if (pEvent->requestId == requestId) { - UnregisterEvent(pEvent); - EventFree(pEvent); - return; /* there can be only one with a given ID */ + for (JdwpEvent* pEvent = event_list_; pEvent != nullptr; pEvent = pEvent->next) { + if (pEvent->requestId == requestId) { + found = true; + UnregisterEvent(pEvent); + EventFree(pEvent); + break; /* there can be only one with a given ID */ + } } - - pEvent = pEvent->next; } - // ALOGD("Odd: no match when removing event reqId=0x%04x", requestId); + if (found) { + Dbg::ManageDeoptimization(); + } else { + LOG(DEBUG) << StringPrintf("Odd: no match when removing event reqId=0x%04x", requestId); + } } /* @@ -692,6 +735,8 @@ bool JdwpState::PostVMStart() { expandBufAdd8BE(pReq, threadId); } + Dbg::ManageDeoptimization(); + /* send request and possibly suspend ourselves */ SendRequestAndPossiblySuspend(pReq, suspend_policy, threadId); @@ -753,14 +798,12 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in return false; } - JdwpEvent** match_list = NULL; int match_count = 0; ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; - { MutexLock mu(Thread::Current(), event_list_lock_); - match_list = AllocMatchList(event_list_size_); + JdwpEvent** match_list = AllocMatchList(event_list_size_); if ((eventFlags & Dbg::kBreakpoint) != 0) { FindMatchingEvents(EK_BREAKPOINT, &basket, match_list, &match_count); } @@ -800,6 +843,8 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in CleanupMatchList(match_list, match_count); } + Dbg::ManageDeoptimization(); + SendRequestAndPossiblySuspend(pReq, suspend_policy, basket.threadId); return match_count != 0; } @@ -859,6 +904,8 @@ bool JdwpState::PostThreadChange(ObjectId threadId, bool start) { CleanupMatchList(match_list, match_count); } + Dbg::ManageDeoptimization(); + SendRequestAndPossiblySuspend(pReq, suspend_policy, basket.threadId); return match_count != 0; @@ -912,13 +959,12 @@ bool JdwpState::PostException(const JdwpLocation* pThrowLoc, return false; } - JdwpEvent** match_list = NULL; int match_count = 0; ExpandBuf* pReq = NULL; JdwpSuspendPolicy suspend_policy = SP_NONE; { MutexLock mu(Thread::Current(), event_list_lock_); - match_list = AllocMatchList(event_list_size_); + JdwpEvent** match_list = AllocMatchList(event_list_size_); FindMatchingEvents(EK_EXCEPTION, &basket, match_list, &match_count); if (match_count != 0) { VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total)" @@ -954,6 +1000,8 @@ bool JdwpState::PostException(const JdwpLocation* pThrowLoc, CleanupMatchList(match_list, match_count); } + Dbg::ManageDeoptimization(); + SendRequestAndPossiblySuspend(pReq, suspend_policy, basket.threadId); return match_count != 0; @@ -1024,6 +1072,8 @@ bool JdwpState::PostClassPrepare(JdwpTypeTag tag, RefTypeId refTypeId, const std CleanupMatchList(match_list, match_count); } + Dbg::ManageDeoptimization(); + SendRequestAndPossiblySuspend(pReq, suspend_policy, basket.threadId); return match_count != 0; diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index 93deee5..127ebfa 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -214,6 +214,7 @@ JdwpState::JdwpState(const JdwpOptions* options) event_list_lock_("JDWP event list lock", kJdwpEventListLock), event_list_(NULL), event_list_size_(0), + full_deoptimization_requests_(0), event_thread_lock_("JDWP event thread lock"), event_thread_cond_("JDWP event thread condition variable", event_thread_lock_), event_thread_id_(0), diff --git a/runtime/locks.cc b/runtime/locks.cc index 5b462a1..d08206a 100644 --- a/runtime/locks.cc +++ b/runtime/locks.cc @@ -22,6 +22,7 @@ namespace art { Mutex* Locks::abort_lock_ = NULL; Mutex* Locks::breakpoint_lock_ = NULL; +Mutex* Locks::deoptimization_lock_ = NULL; ReaderWriterMutex* Locks::classlinker_classes_lock_ = NULL; ReaderWriterMutex* Locks::heap_bitmap_lock_ = NULL; Mutex* Locks::logging_lock_ = NULL; @@ -38,6 +39,7 @@ void Locks::Init() { // Already initialized. DCHECK(abort_lock_ != NULL); DCHECK(breakpoint_lock_ != NULL); + DCHECK(deoptimization_lock_ != NULL); DCHECK(classlinker_classes_lock_ != NULL); DCHECK(heap_bitmap_lock_ != NULL); DCHECK(logging_lock_ != NULL); @@ -53,6 +55,8 @@ void Locks::Init() { DCHECK(breakpoint_lock_ == NULL); breakpoint_lock_ = new Mutex("breakpoint lock", kBreakpointLock); + DCHECK(deoptimization_lock_ == NULL); + deoptimization_lock_ = new Mutex("deoptimization lock", kDeoptimizationLock); DCHECK(classlinker_classes_lock_ == NULL); classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock", kClassLinkerClassesLock); diff --git a/runtime/locks.h b/runtime/locks.h index 341319c..9164be6 100644 --- a/runtime/locks.h +++ b/runtime/locks.h @@ -53,6 +53,7 @@ enum LockLevel { kBreakpointLock, kThreadListLock, kBreakpointInvokeLock, + kDeoptimizationLock, kTraceLock, kProfilerLock, kJdwpEventListLock, @@ -143,11 +144,14 @@ class Locks { // attaching and detaching. static Mutex* thread_list_lock_ ACQUIRED_AFTER(runtime_shutdown_lock_); - // Guards breakpoints and single-stepping. + // Guards breakpoints. static Mutex* breakpoint_lock_ ACQUIRED_AFTER(thread_list_lock_); + // Guards deoptimization requests. + static Mutex* deoptimization_lock_ ACQUIRED_AFTER(breakpoint_lock_); + // Guards trace requests. - static Mutex* trace_lock_ ACQUIRED_AFTER(breakpoint_lock_); + static Mutex* trace_lock_ ACQUIRED_AFTER(deoptimization_lock_); // Guards profile objects. static Mutex* profiler_lock_ ACQUIRED_AFTER(trace_lock_); diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 5b34cfb..011e165 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -80,6 +80,7 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha case kWaitingForDebuggerToAttach: return kJavaWaiting; case kWaitingInMainDebuggerLoop: return kJavaWaiting; case kWaitingForDebuggerSuspension: return kJavaWaiting; + case kWaitingForDeoptimization: return kJavaWaiting; case kWaitingForJniOnLoad: return kJavaWaiting; case kWaitingForSignalCatcherOutput: return kJavaWaiting; case kWaitingInMainSignalCatcherLoop: return kJavaWaiting; diff --git a/runtime/stack.cc b/runtime/stack.cc index 4e3fb4a..e583ced 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -323,10 +323,10 @@ void StackVisitor::WalkStack(bool include_transitions) { } else if (instrumentation_frame.interpreter_entry_) { mirror::ArtMethod* callee = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs); CHECK_EQ(GetMethod(), callee) << "Expected: " << PrettyMethod(callee) << " Found: " - << PrettyMethod(GetMethod()); + << PrettyMethod(GetMethod()); } else if (instrumentation_frame.method_ != GetMethod()) { LOG(FATAL) << "Expected: " << PrettyMethod(instrumentation_frame.method_) - << " Found: " << PrettyMethod(GetMethod()); + << " Found: " << PrettyMethod(GetMethod()); } if (num_frames_ != 0) { // Check agreement of frame Ids only if num_frames_ is computed to avoid infinite diff --git a/runtime/thread_state.h b/runtime/thread_state.h index 7615c41..57bf4f1 100644 --- a/runtime/thread_state.h +++ b/runtime/thread_state.h @@ -37,6 +37,7 @@ enum ThreadState { kWaitingForJniOnLoad, // WAITING TS_WAIT waiting for execution of dlopen and JNI on load code kWaitingForSignalCatcherOutput, // WAITING TS_WAIT waiting for signal catcher IO to complete kWaitingInMainSignalCatcherLoop, // WAITING TS_WAIT blocking/reading/processing signals + kWaitingForDeoptimization, // WAITING TS_WAIT waiting for deoptimization suspend all kStarting, // NEW TS_WAIT native thread started, not yet ready to run managed code kNative, // RUNNABLE TS_RUNNING running in a JNI native method kSuspended, // RUNNABLE TS_RUNNING suspended by GC or debugger diff --git a/runtime/trace.cc b/runtime/trace.cc index da2c80a..5d053b6 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -383,6 +383,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int instrumentation::Instrumentation::kMethodEntered | instrumentation::Instrumentation::kMethodExited | instrumentation::Instrumentation::kMethodUnwind); + runtime->GetInstrumentation()->EnableMethodTracing(); } } } @@ -412,6 +413,7 @@ void Trace::Stop() { MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, NULL); } else { + runtime->GetInstrumentation()->DisableMethodTracing(); runtime->GetInstrumentation()->RemoveListener(the_trace, instrumentation::Instrumentation::kMethodEntered | instrumentation::Instrumentation::kMethodExited | |