// We could make this much more selective in the future so we only return true when we // actually care about the method at this time (ie active frames had locals changed). For now we // just assume that if anything has changed any frame's locals we care about all methods. This only // impacts whether we are able to OSR or not so maybe not really important to maintain frame // specific information. bool JvmtiMethodInspectionCallback::HaveLocalsChanged() { return manager_->HaveLocalsChanged();
}
// See if we can enable all JVMTI functions. if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) { // We are still early enough to change the compiler options and get full JVMTI support.
LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to "
<< "debuggable state. Please pass '--debuggable' to dex2oat and "
<< "'-Xcompiler-option --debuggable' to dalvikvm in the future.";
DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!";
art::ScopedSuspendAll ssa(__FUNCTION__); // TODO check if we need to hold deoptimization_status_lock_ here.
art::MutexLock mu(self, deoptimization_status_lock_);
runtime->AddCompilerOption("--debuggable");
runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
runtime->DeoptimizeBootImage(); return;
}
// Runtime has already started in non-debuggable mode. Only kArtTiVersion agents can be // retrieved and they will all be best-effort.
LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was "
<< "loaded too late to change runtime state to support all capabilities. Only "
<< "kArtTiVersion (0x" << std::hex << kArtTiVersion << ") environments are "
<< "available. Some functionality might not work properly.";
// Transition the runtime to debuggable: // 1. Wait for any background verification tasks to finish. We don't support // background verification after moving to debuggable state.
runtime->GetOatFileManager().WaitForBackgroundVerificationTasksToFinish();
// Do the transition in ScopedJITSuspend, so we don't start any JIT compilations // before the transition to debuggable is finished.
art::jit::ScopedJitSuspend suspend_jit;
art::ScopedSuspendAll ssa(__FUNCTION__);
// 2. Discard any JITed code that was generated before, since they would be // compiled without debug support.
art::jit::Jit* jit = runtime->GetJit(); if (jit != nullptr) {
jit->GetCodeCache()->InvalidateAllCompiledCode();
jit->GetCodeCache()->TransitionToDebuggable();
jit->GetJitCompiler()->SetDebuggableCompilerOption(true);
}
// 3. Change the state to JavaDebuggable, so that debug features can be // enabled from now on.
runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggable);
// 4. Update all entrypoints to avoid using any AOT code.
runtime->GetInstrumentation()->UpdateEntrypointsForDebuggable();
}
if (MethodHasBreakpointsLocked(method)) { // Don't need to do anything extra.
breakpoint_status_[method]++; // Another thread might be deoptimizing the very method we just added new breakpoints for. // Wait for any deopts to finish before moving on.
breakpoint_status_lock_.ExclusiveUnlock(self);
WaitForDeoptimizationToFinish(self); return;
}
breakpoint_status_[method] = 1;
breakpoint_status_lock_.ExclusiveUnlock(self);
} auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (instrumentation->IsForcedInterpretOnly()) { // We are already interpreting everything so no need to do anything.
deoptimization_status_lock_.ExclusiveUnlock(self); return;
} elseif (is_default) {
AddDeoptimizeAllMethodsLocked(self);
} else {
PerformLimitedDeoptimization(self, method);
}
}
art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended); // Ideally we should do a ScopedSuspendAll right here to get the full mutator_lock_ that we might // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves.
deoptimization_status_lock_.ExclusiveLock(self); bool is_last_breakpoint;
{
art::MutexLock mu(self, breakpoint_status_lock_);
DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without "
<< "breakpoints present!";
breakpoint_status_[method] -= 1;
is_last_breakpoint = (breakpoint_status_[method] == 0);
} auto instrumentation = art::Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) { // We don't need to do anything since we are interpreting everything anyway.
deoptimization_status_lock_.ExclusiveUnlock(self); return;
} elseif (is_last_breakpoint) { if (UNLIKELY(is_default)) {
RemoveDeoptimizeAllMethodsLocked(self);
} else {
PerformLimitedUndeoptimization(self, method);
}
} else { // Another thread might be deoptimizing the very methods we just removed breakpoints from. Wait // for any deopts to finish before moving on.
WaitForDeoptimizationToFinish(self);
}
}
void DeoptManager::WaitForDeoptimizationToFinishLocked(art::Thread* self) { while (performing_deoptimization_) {
deoptimization_condition_.Wait(self);
}
}
// Users should make sure that only gc-critical-section safe code is used while a // ScopedDeoptimizationContext exists. class ScopedDeoptimizationContext : public art::ValueObject { public:
ScopedDeoptimizationContext(art::Thread* self, DeoptManager* deopt)
RELEASE(deopt->deoptimization_status_lock_)
ACQUIRE(art::Locks::mutator_lock_)
ACQUIRE(art::Roles::uninterruptible_)
: self_(self),
deopt_(deopt),
critical_section_(self_, "JVMTI Deoptimizing methods"),
uninterruptible_cause_(nullptr) {
deopt_->WaitForDeoptimizationToFinishLocked(self_);
DCHECK(!deopt->performing_deoptimization_)
<< "Already performing deoptimization on another thread!"; // Use performing_deoptimization_ to keep track of the lock.
deopt_->performing_deoptimization_ = true;
deopt_->deoptimization_status_lock_.Unlock(self_);
uninterruptible_cause_ = critical_section_.Enter(art::gc::kGcCauseInstrumentation,
art::gc::kCollectorTypeCriticalSection);
art::Runtime::Current()->GetThreadList()->SuspendAll("JMVTI Deoptimizing methods", /*long_suspend=*/ false);
}
~ScopedDeoptimizationContext()
RELEASE(art::Locks::mutator_lock_)
RELEASE(art::Roles::uninterruptible_) { // Can be suspended again.
critical_section_.Exit(uninterruptible_cause_); // Release the mutator lock.
art::Runtime::Current()->GetThreadList()->ResumeAll(); // Let other threads know it's fine to proceed.
art::MutexLock lk(self_, deopt_->deoptimization_status_lock_);
deopt_->performing_deoptimization_ = false;
deopt_->deoptimization_condition_.Broadcast(self_);
}
// Do the transition in ScopedJITSuspend, so we don't start any JIT compilations // before the transition to debuggable is finished.
art::jit::ScopedJitSuspend suspend_jit;
// If we attach a debugger to a non-debuggable runtime, we switch the runtime to debuggable to // provide a consistent (though still best effort) support. Since we are detaching the debugger // now, switch it back to non-debuggable if there are no other debugger / profiling tools are // active.
runtime->GetInstrumentation()->DisableDeoptimization(kInstrumentationKey, /*try_switch_to_non_debuggable=*/true);
runtime->GetInstrumentation()->DisableDeoptimization(kDeoptManagerInstrumentationKey, /*try_switch_to_non_debuggable=*/true);
}
void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self) {
DCHECK_GT(global_deopt_count_, 0u) << "Request to remove non-existent global deoptimization!";
global_deopt_count_--; if (global_deopt_count_ == 0) {
PerformGlobalUndeoptimization(self);
} else {
WaitForDeoptimizationToFinish(self);
}
}
jvmtiError DeoptManager::RemoveDeoptimizeThreadMethods(art::ScopedObjectAccessUnchecked& soa, jthread jtarget) {
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
art::Thread* target = nullptr;
jvmtiError err = OK; if (!ThreadUtil::GetNativeThread(jtarget, soa, &target, &err)) { return err;
} // We don't need additional locking here because we hold the Thread_list_lock_.
DCHECK_GT(target->ForceInterpreterCount(), 0u);
target->DecrementForceInterpreterCount(); return OK;
}
void DeoptManager::RemoveDeoptimizationRequester() {
art::Thread* self = art::Thread::Current();
art::ScopedThreadStateChange sts(self, art::ThreadState::kSuspended);
deoptimization_status_lock_.ExclusiveLock(self);
DCHECK_GT(deopter_count_, 0u) << "Removing deoptimization requester without any being present";
deopter_count_--; if (deopter_count_ == 0) {
ScopedDeoptimizationContext sdc(self, this);
art::Runtime::Current()->GetInstrumentation()->DisableDeoptimization(
kInstrumentationKey, /*try_switch_to_non_debuggable=*/false); return;
} else {
deoptimization_status_lock_.ExclusiveUnlock(self);
}
}
void DeoptManager::AddDeoptimizationRequester() {
art::Thread* self = art::Thread::Current();
art::ScopedThreadStateChange stsc(self, art::ThreadState::kSuspended);
deoptimization_status_lock_.ExclusiveLock(self);
deopter_count_++; if (deopter_count_ == 1) { // When we add a deoptimization requester, we should enable entry / exit hooks. We only call // this in debuggable runtimes and hence it won't be necessary to update entrypoints but we // still need to inform instrumentation that we need to actually run entry / exit hooks. Though // entrypoints are capable of running entry / exit hooks they won't run them unless enabled.
ScopedDeoptimizationContext sdc(self, this);
art::Runtime::Current()->GetInstrumentation()->EnableEntryExitHooks(kInstrumentationKey); return;
}
deoptimization_status_lock_.ExclusiveUnlock(self);
}
void DeoptManager::DeoptimizeThread(art::Thread* target) { // We might or might not be running on the target thread (self) so get Thread::Current // directly.
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kSuspended);
art::gc::ScopedGCCriticalSection sgccs(art::Thread::Current(),
art::gc::GcCause::kGcCauseDebugger,
art::gc::CollectorType::kCollectorTypeDebugger);
art::ScopedSuspendAll ssa("Instrument thread stack"); // Prepare the stack so methods can be deoptimized as and when required. // This by itself doesn't cause any methods to deoptimize but enables // deoptimization on demand.
art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(target, /* force_deopt= */ false);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.