jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env,
jthread java_thread,
jint start_depth,
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr) { // It is not great that we have to hold these locks for so long, but it is necessary to ensure // that the thread isn't dying on us.
art::ScopedObjectAccess soa(art::Thread::Current());
art::Locks::thread_list_lock_->ExclusiveLock(soa.Self());
if (start_depth >= 0) { // Fast path: Regular order of stack trace. Fill into the frame_buffer directly.
GetStackTraceDirectClosure closure(frame_buffer, static_cast<size_t>(start_depth), static_cast<size_t>(max_frame_count)); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE);
}
*count_ptr = static_cast<jint>(closure.index); if (closure.index == 0) {
JVMTI_LOG(INFO, jvmti_env) << "The stack is not large enough for a start_depth of "
<< start_depth << "."; return ERR(ILLEGAL_ARGUMENT);
} return ERR(NONE);
} else {
GetStackTraceVectorClosure closure(0, 0); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE);
}
void Work(art::Thread* thread, art::Thread* self)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!data->mutex) { // Skip threads that are still starting. if (thread->IsStillStarting()) { return;
}
template <typename Data> staticvoid RunCheckpointAndWait(Data* data, size_t max_frame_count)
REQUIRES_SHARED(art::Locks::mutator_lock_) { // Note: requires the mutator lock as the checkpoint requires the mutator lock.
GetAllStackTracesVectorClosure<Data> closure(max_frame_count, data); // TODO(b/253671779): Replace this use of RunCheckpointUnchecked() with RunCheckpoint(). This is // currently not possible, since the following undesirable call chain (abbreviated here) is then // possible and exercised by current tests: (jvmti) GetAllStackTraces -> <this function> -> // RunCheckpoint -> GetStackTraceVisitor -> EncodeMethodId -> Class::EnsureMethodIds -> // Class::Alloc -> AllocObjectWithAllocator -> potentially suspends, or runs GC, etc. -> CHECK // failure.
size_t barrier_count = art::Runtime::Current()->GetThreadList()->RunCheckpointUnchecked(&closure); if (barrier_count == 0) { return;
}
art::Thread* self = art::Thread::Current();
art::ScopedThreadStateChange tsc(self, art::ThreadState::kWaitingForCheckPointsToRun);
closure.barrier.Increment(self, barrier_count);
}
// Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to // allocate one big chunk for this and the actual frames, which means we need // to either be conservative or rearrange things later (the latter is implemented).
std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[data.frames.size()]);
std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos;
frame_infos.reserve(data.frames.size());
// Now run through and add data for each thread.
size_t sum_frames = 0; for (size_t index = 0; index < data.frames.size(); ++index) {
jvmtiStackInfo& stack_info = stack_info_array.get()[index];
memset(&stack_info, 0, sizeof(jvmtiStackInfo));
// For the time being, set the thread to null. We'll fix it up in the second stage.
stack_info.thread = nullptr;
stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;
// No errors, yet. Now put it all into an output buffer.
size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * data.frames.size(),
alignof(jvmtiFrameInfo));
size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo); unsignedchar* chunk_data;
jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data); if (alloc_result != ERR(NONE)) { return alloc_result;
}
jvmtiStackInfo* stack_info = reinterpret_cast<jvmtiStackInfo*>(chunk_data); // First copy in all the basic data.
memcpy(stack_info, stack_info_array.get(), sizeof(jvmtiStackInfo) * data.frames.size());
// Now copy the frames and fix up the pointers.
jvmtiFrameInfo* frame_info = reinterpret_cast<jvmtiFrameInfo*>(
chunk_data + rounded_stack_info_size); for (size_t i = 0; i < data.frames.size(); ++i) {
jvmtiStackInfo& old_stack_info = stack_info_array.get()[i];
jvmtiStackInfo& new_stack_info = stack_info[i];
// Translate the global ref into a local ref.
new_stack_info.thread = static_cast<JNIEnv*>(current->GetJniEnv())->NewLocalRef(data.thread_peers[i]);
if (old_stack_info.frame_count > 0) { // Only copy when there's data - leave the nullptr alone.
size_t frames_size = static_cast<size_t>(old_stack_info.frame_count) * sizeof(jvmtiFrameInfo);
memcpy(frame_info, old_stack_info.frame_buffer, frames_size);
new_stack_info.frame_buffer = frame_info;
frame_info += old_stack_info.frame_count;
}
}
// Decode all threads to raw pointers. Put them into a handle scope to avoid any moving GC bugs.
art::VariableSizedHandleScope hs(current); for (jint i = 0; i != thread_count; ++i) { if (thread_list[i] == nullptr) { return ERR(INVALID_THREAD);
}
art::ObjPtr<art::mirror::Object> thread = soa.Decode<art::mirror::Object>(thread_list[i]); if (!thread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) { return ERR(INVALID_THREAD);
}
data.handles.push_back(hs.NewHandle(thread));
}
// Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to // allocate one big chunk for this and the actual frames, which means we need // to either be conservative or rearrange things later (the latter is implemented).
std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[data.frames.size()]);
std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos;
frame_infos.reserve(data.frames.size());
// Now run through and add data for each thread.
size_t sum_frames = 0; for (size_t index = 0; index < data.frames.size(); ++index) {
jvmtiStackInfo& stack_info = stack_info_array.get()[index];
memset(&stack_info, 0, sizeof(jvmtiStackInfo));
// For the time being, set the thread to null. We don't have good ScopedLocalRef // infrastructure.
stack_info.thread = nullptr;
stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;
// No errors, yet. Now put it all into an output buffer. Note that this is not frames.size(), // potentially.
size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * thread_count,
alignof(jvmtiFrameInfo));
size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo); unsignedchar* chunk_data;
jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data); if (alloc_result != ERR(NONE)) { return alloc_result;
}
for (size_t i = 0; i < static_cast<size_t>(thread_count); ++i) { // Check whether we found a running thread for this. // Note: For simplicity, and with the expectation that the list is usually small, use a simple // search. (The list is *not* sorted!) auto it = std::find(data.thread_list_indices.begin(), data.thread_list_indices.end(), i); if (it == data.thread_list_indices.end()) { // No native thread. Must be new or dead. We need to fill out the stack info now. // (Need to read the Java "started" field to know whether this is starting or terminated.)
art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread_list[i]);
art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z");
CHECK(started_field != nullptr); bool started = started_field->GetBoolean(peer) != 0;
constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
stack_info[i].thread = reinterpret_cast<JNIEnv*>(soa.Env())->NewLocalRef(thread_list[i]);
stack_info[i].state = started ? kTerminatedState : kStartedState;
stack_info[i].frame_count = 0;
stack_info[i].frame_buffer = nullptr;
} else { // Had a native thread and frames.
size_t f_index = it - data.thread_list_indices.begin();
void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) { // This is not StackVisitor::ComputeNumFrames, as runtime methods and transitions must not be // counted.
art::StackVisitor::WalkStack(
[&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::ArtMethod* m = stack_visitor->GetMethod(); if (m != nullptr && !m->IsRuntimeMethod()) {
count++;
} returntrue;
},
self, /* context= */ nullptr,
art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
}
size_t count;
};
jvmtiError StackUtil::GetFrameCount([[maybe_unused]] jvmtiEnv* env,
jthread java_thread,
jint* count_ptr) { // It is not great that we have to hold these locks for so long, but it is necessary to ensure // that the thread isn't dying on us.
art::ScopedObjectAccess soa(art::Thread::Current());
art::Locks::thread_list_lock_->ExclusiveLock(soa.Self());
DCHECK(thread != nullptr);
art::ThreadState state = thread->GetState(); if (state == art::ThreadState::kStarting || thread->IsStillStarting()) {
art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self()); return ERR(THREAD_NOT_ALIVE);
}
if (count_ptr == nullptr) {
art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self()); return ERR(NULL_POINTER);
}
GetFrameCountClosure closure; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE);
}
jvmtiError StackUtil::GetFrameLocation([[maybe_unused]] jvmtiEnv* env,
jthread java_thread,
jint depth,
jmethodID* method_ptr,
jlocation* location_ptr) { // It is not great that we have to hold these locks for so long, but it is necessary to ensure // that the thread isn't dying on us.
art::ScopedObjectAccess soa(art::Thread::Current());
art::Locks::thread_list_lock_->ExclusiveLock(soa.Self());
GetLocationClosure closure(static_cast<size_t>(depth)); // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. if (!thread->RequestSynchronousCheckpoint(&closure)) { return ERR(THREAD_NOT_ALIVE);
}
if (closure.method == nullptr) { return ERR(NO_MORE_FRAMES);
}
struct MonitorVisitor : public art::StackVisitor, public art::SingleRootVisitor { // We need a context because VisitLocks needs it retrieve the monitor objects. explicit MonitorVisitor(art::Thread* thread)
REQUIRES_SHARED(art::Locks::mutator_lock_)
: art::StackVisitor(thread,
art::Context::Create(),
art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
hs(art::Thread::Current()),
current_stack_depth(0) {}
void Run(art::Thread* target) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current()); // Find the monitors on the stack.
MonitorVisitor visitor(target);
visitor.WalkStack(/* include_transitions= */ false); // Find any other monitors, including ones acquired in native code.
art::RootInfo root_info(art::kRootVMInternal);
target->GetJniEnv()->VisitMonitorRoots(&visitor, root_info);
err_ = handle_results_(visitor);
}
jvmtiError GetError() { return err_;
}
private:
jvmtiError err_;
Fn handle_results_;
};
template <typename Fn> static jvmtiError GetOwnedMonitorInfoCommon(const art::ScopedObjectAccessAlreadyRunnable& soa,
jthread thread,
Fn handle_results)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::Thread* self = art::Thread::Current();
MonitorInfoClosure<Fn> closure(handle_results); bool called_method = false;
{
art::Locks::thread_list_lock_->ExclusiveLock(self);
art::Thread* target = nullptr;
jvmtiError err = ERR(INTERNAL); if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return err;
} if (target != self) {
called_method = true; // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. // Since this deals with object references we need to avoid going to sleep.
art::ScopedAssertNoThreadSuspension sants("Getting owned monitor usage"); if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) { return ERR(THREAD_NOT_ALIVE);
}
} else {
art::Locks::thread_list_lock_->ExclusiveUnlock(self);
}
} // Cannot call the closure on the current thread if we have thread_list_lock since we need to call // into the verifier which can cause the current thread to suspend for gc. Suspending would be a // bad thing to do if we hold the ThreadListLock. For other threads since we are running it on a // checkpoint we are fine but if the thread is the current one we need to drop the mutex first. if (!called_method) {
closure.Run(self);
} return closure.GetError();
}
jvmtiError StackUtil::GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
jthread thread,
jint* info_cnt,
jvmtiMonitorStackDepthInfo** info_ptr) { if (info_cnt == nullptr || info_ptr == nullptr) { return ERR(NULL_POINTER);
}
art::ScopedObjectAccess soa(art::Thread::Current());
std::vector<art::GcRoot<art::mirror::Object>> mons;
std::vector<uint32_t> depths; auto handle_fun = [&] (MonitorVisitor& visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) { for (size_t i = 0; i < visitor.monitors.size(); i++) {
mons.push_back(art::GcRoot<art::mirror::Object>(visitor.monitors[i].Get()));
depths.push_back(visitor.stack_depths[i]);
} return OK;
};
jvmtiError err = GetOwnedMonitorInfoCommon(soa, thread, handle_fun); if (err != OK) { return err;
} auto nbytes = sizeof(jvmtiMonitorStackDepthInfo) * mons.size();
err = env->Allocate(nbytes, reinterpret_cast<unsignedchar**>(info_ptr)); if (err != OK) { return err;
}
*info_cnt = mons.size(); for (uint32_t i = 0; i < mons.size(); i++) {
(*info_ptr)[i] = {
soa.AddLocalReference<jobject>(mons[i].Read()), static_cast<jint>(depths[i])
};
} return err;
}
jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env,
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr) { if (owned_monitor_count_ptr == nullptr || owned_monitors_ptr == nullptr) { return ERR(NULL_POINTER);
}
art::ScopedObjectAccess soa(art::Thread::Current());
std::vector<art::GcRoot<art::mirror::Object>> mons; auto handle_fun = [&] (MonitorVisitor& visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) { for (size_t i = 0; i < visitor.monitors.size(); i++) {
mons.push_back(art::GcRoot<art::mirror::Object>(visitor.monitors[i].Get()));
} return OK;
};
jvmtiError err = GetOwnedMonitorInfoCommon(soa, thread, handle_fun); if (err != OK) { return err;
} auto nbytes = sizeof(jobject) * mons.size();
err = env->Allocate(nbytes, reinterpret_cast<unsignedchar**>(owned_monitors_ptr)); if (err != OK) { return err;
}
*owned_monitor_count_ptr = mons.size(); for (uint32_t i = 0; i < mons.size(); i++) {
(*owned_monitors_ptr)[i] = soa.AddLocalReference<jobject>(mons[i].Read());
} return err;
}
ScopedNoUserCodeSuspension snucs(self); // From now on we know we cannot get suspended by user-code. // NB This does a SuspendCheck (during thread state change) so we need to make // sure we don't have the 'suspend_lock' locked here.
art::ScopedObjectAccess soa(self);
art::Locks::thread_list_lock_->ExclusiveLock(self);
jvmtiError err = ERR(INTERNAL); if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return err;
} if (target != self) { // TODO This is part of the spec but we could easily avoid needing to do it. // We would just put all the logic into a sync-checkpoint.
art::Locks::thread_suspend_count_lock_->ExclusiveLock(self); if (target->GetUserCodeSuspendCount() == 0) {
art::Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return ERR(THREAD_NOT_SUSPENDED);
}
art::Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
} // We hold the user_code_suspension_lock_ so the target thread is staying // suspended until we are done (unless it's 'self' in which case we don't care // since we aren't going to be returning). // TODO We could implement this using a synchronous checkpoint and not bother // with any of the suspension stuff. The spec does specifically say to return // THREAD_NOT_SUSPENDED though. Find the requested stack frame.
std::unique_ptr<art::Context> context(art::Context::Create());
FindFrameAtDepthVisitor visitor(target, context.get(), depth);
visitor.WalkStack(); if (!visitor.FoundFrame()) {
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return ERR(NO_MORE_FRAMES);
}
art::ArtMethod* method = visitor.GetMethod(); if (method->IsNative()) {
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return ERR(OPAQUE_FRAME);
} // From here we are sure to succeed. bool needs_instrument = false; // Get/create a shadow frame
art::ShadowFrame* shadow_frame =
visitor.GetOrCreateShadowFrame(&needs_instrument);
{
art::WriterMutexLock lk(self, tienv->event_info_mutex_); if (LIKELY(!shadow_frame->NeedsNotifyPop())) { // Ensure we won't miss exceptions being thrown if we get jit-compiled. We // only do this for the first NotifyPopFrame.
target->IncrementForceInterpreterCount();
// Mark shadow frame as needs_notify_pop_
shadow_frame->SetNotifyPop(true);
}
tienv->notify_frames.insert(shadow_frame);
} // Make sure can we will go to the interpreter and use the shadow frames. if (needs_instrument) {
art::FunctionClosure fc([](art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
DeoptManager::Get()->DeoptimizeThread(self);
});
target->RequestSynchronousCheckpoint(&fc);
} else {
art::Locks::thread_list_lock_->ExclusiveUnlock(self);
} return OK;
}
if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target_, &result_)) { return;
}
{
art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_); if (target_ != self && target_->GetUserCodeSuspendCount() == 0) { // We cannot be the current thread for this function.
result_ = ERR(THREAD_NOT_SUSPENDED); return;
}
}
JvmtiGlobalTLSData* tls_data = ThreadUtil::GetGlobalTLSData(target_);
constexpr art::StackVisitor::StackWalkKind kWalkKind =
art::StackVisitor::StackWalkKind::kIncludeInlinedFrames; if (tls_data != nullptr &&
tls_data->disable_pop_frame_depth != JvmtiGlobalTLSData::kNoDisallowedPopFrame &&
tls_data->disable_pop_frame_depth ==
art::StackVisitor::ComputeNumFrames(target_, kWalkKind)) {
JVMTI_LOG(WARNING, env) << "Disallowing frame pop due to in-progress class-load/prepare. "
<< "Frame at depth " << tls_data->disable_pop_frame_depth << " was "
<< "marked as un-poppable by the jvmti plugin. See b/117615146 for "
<< "more information.";
result_ = ERR(OPAQUE_FRAME); return;
} // We hold the user_code_suspension_lock_ so the target thread is staying suspended until we are // done.
std::unique_ptr<art::Context> context(art::Context::Create());
FindFrameAtDepthVisitor final_frame(target_, context.get(), 0);
FindFrameAtDepthVisitor penultimate_frame(target_, context.get(), 1);
final_frame.WalkStack();
penultimate_frame.WalkStack();
if (!final_frame.FoundFrame() || !penultimate_frame.FoundFrame()) { // Cannot do it if there is only one frame!
JVMTI_LOG(INFO, env) << "Can not pop final frame off of a stack";
result_ = ERR(NO_MORE_FRAMES); return;
}
art::ObjPtr<art::mirror::Class> ResolveFinalFrameReturnType(art::Thread* self)
REQUIRES(art::Locks::user_code_suspension_lock_)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(art::Locks::thread_list_lock_)
REQUIRES(!art::Locks::thread_suspend_count_lock_) { // Temporarily release the thread list lock to allow thread suspension and // resolve the final frame's return type. // Note: If the target thread is not `self`, holding the user code suspension // lock prevents the suspended target thread from resuming and therefore // prevents it from being unregistered, see `ThreadList::Unregister()`.
art::Locks::user_code_suspension_lock_->AssertExclusiveHeld(self);
art::Locks::thread_list_lock_->ExclusiveUnlock(self); // FIXME: What if `ArtMethod::ResolveReturnType()` throws an exception?
art::ObjPtr<art::mirror::Class> return_type = final_frame_->GetMethod()->ResolveReturnType();
art::Locks::thread_list_lock_->ExclusiveLock(self); return return_type;
}
template <> bool NonStandardExitFrames<NonStandardExitType::kForceReturn>::CheckFunctions(
jvmtiEnv* env, [[maybe_unused]] art::ArtMethod* calling, art::ArtMethod* called) { if (UNLIKELY(called->IsNative())) {
result_ = ERR(OPAQUE_FRAME);
JVMTI_LOG(INFO, env) << "Cannot force early return from " << called->PrettyMethod()
<< " because it is native."; returnfalse;
} else { returntrue;
}
}
template <> bool NonStandardExitFrames<NonStandardExitType::kPopFrame>::CheckFunctions(
jvmtiEnv* env, art::ArtMethod* calling, art::ArtMethod* called) { if (UNLIKELY(calling->IsNative() || called->IsNative())) {
result_ = ERR(OPAQUE_FRAME);
JVMTI_LOG(INFO, env) << "Cannot force early return from " << called->PrettyMethod() << " to "
<< calling->PrettyMethod() << " because at least one of them is native."; returnfalse;
} else { returntrue;
}
}
template <typename T>
jvmtiError
StackUtil::ForceEarlyReturn(jvmtiEnv* env, EventHandler* event_handler, jthread thread, T value) {
art::Thread* self = art::Thread::Current(); // We don't want to use the null == current-thread idiom since for events (that we use internally // to implement force-early-return) we instead have null == all threads. Instead just get the // current jthread if needed.
ScopedLocalRef<jthread> cur_thread(self->GetJniEnv(), nullptr); if (UNLIKELY(thread == nullptr)) {
art::ScopedObjectAccess soa(self);
cur_thread.reset(soa.AddLocalReference<jthread>(self->GetPeer()));
thread = cur_thread.get();
} // This sets up the exit events we implement early return using before we have the locks and // thanks to destructor ordering will tear them down if something goes wrong.
SetupMethodExitEvents smee(self, event_handler, thread);
ScopedNoUserCodeSuspension snucs(self);
art::ScopedObjectAccess soa(self);
NonStandardExitFrames<NonStandardExitType::kForceReturn> frames(soa, env, thread); if (frames.result_ != OK) {
smee.NotifyFailure();
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return frames.result_;
} elseif (!ValidReturnType<T>(self, frames.ResolveFinalFrameReturnType(self), value)) {
smee.NotifyFailure();
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return ERR(TYPE_MISMATCH);
} elseif (frames.final_frame_->GetForcePopFrame()) { // TODO We should really support this.
smee.NotifyFailure();
std::string thread_name;
frames.target_->GetThreadName(thread_name);
JVMTI_LOG(WARNING, env) << "PopFrame or force-return already pending on thread " << thread_name;
art::Locks::thread_list_lock_->ExclusiveUnlock(self); return ERR(OPAQUE_FRAME);
} // Tell the shadow-frame to return immediately and skip all exit events.
frames.final_frame_->SetForcePopFrame(true);
AddDelayedMethodExitEvent<T>(event_handler, frames.final_frame_, value); if (frames.created_final_frame_ || frames.created_penultimate_frame_) {
art::FunctionClosure fc([](art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_){
DeoptManager::Get()->DeoptimizeThread(self);
});
frames.target_->RequestSynchronousCheckpoint(&fc);
} else {
art::Locks::thread_list_lock_->ExclusiveUnlock(self);
} return OK;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.40Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29)
¤
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.