|  | /* Copyright (C) 2017 The Android Open Source Project | 
|  | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|  | * | 
|  | * This file implements interfaces from the file jvmti.h. This implementation | 
|  | * is licensed under the same terms as the file jvmti.h.  The | 
|  | * copyright and license information for the file jvmti.h follows. | 
|  | * | 
|  | * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. | 
|  | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|  | * | 
|  | * This code is free software; you can redistribute it and/or modify it | 
|  | * under the terms of the GNU General Public License version 2 only, as | 
|  | * published by the Free Software Foundation.  Oracle designates this | 
|  | * particular file as subject to the "Classpath" exception as provided | 
|  | * by Oracle in the LICENSE file that accompanied this code. | 
|  | * | 
|  | * This code is distributed in the hope that it will be useful, but WITHOUT | 
|  | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|  | * version 2 for more details (a copy is included in the LICENSE file that | 
|  | * accompanied this code). | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License version | 
|  | * 2 along with this work; if not, write to the Free Software Foundation, | 
|  | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | * | 
|  | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|  | * or visit www.oracle.com if you need additional information or have any | 
|  | * questions. | 
|  | */ | 
|  |  | 
|  | #include "ti_thread.h" | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  | #include <android-base/strings.h> | 
|  |  | 
|  | #include "art_field-inl.h" | 
|  | #include "art_jvmti.h" | 
|  | #include "base/mutex.h" | 
|  | #include "deopt_manager.h" | 
|  | #include "events-inl.h" | 
|  | #include "gc/collector_type.h" | 
|  | #include "gc/gc_cause.h" | 
|  | #include "gc/scoped_gc_critical_section.h" | 
|  | #include "gc/system_weak.h" | 
|  | #include "gc_root-inl.h" | 
|  | #include "jni/jni_internal.h" | 
|  | #include "metrics/reporter.h" | 
|  | #include "mirror/class.h" | 
|  | #include "mirror/object-inl.h" | 
|  | #include "mirror/string.h" | 
|  | #include "mirror/throwable.h" | 
|  | #include "nativehelper/scoped_local_ref.h" | 
|  | #include "nativehelper/scoped_utf_chars.h" | 
|  | #include "obj_ptr.h" | 
|  | #include "runtime.h" | 
|  | #include "runtime_callbacks.h" | 
|  | #include "scoped_thread_state_change-inl.h" | 
|  | #include "thread-current-inl.h" | 
|  | #include "thread_list.h" | 
|  | #include "ti_phase.h" | 
|  | #include "well_known_classes-inl.h" | 
|  |  | 
|  | namespace openjdkjvmti { | 
|  |  | 
|  | static const char* kJvmtiTlsKey = "JvmtiTlsKey"; | 
|  |  | 
|  | art::ArtField* ThreadUtil::context_class_loader_ = nullptr; | 
|  |  | 
|  | ScopedNoUserCodeSuspension::ScopedNoUserCodeSuspension(art::Thread* self) : self_(self) { | 
|  | DCHECK_EQ(self, art::Thread::Current()); | 
|  | // Loop until we both have the user_code_suspension_locK_ and don't have any pending user_code | 
|  | // suspensions. | 
|  | do { | 
|  | art::Locks::user_code_suspension_lock_->AssertNotHeld(self_); | 
|  | ThreadUtil::SuspendCheck(self_); | 
|  |  | 
|  | art::Locks::user_code_suspension_lock_->ExclusiveLock(self_); | 
|  | if (ThreadUtil::WouldSuspendForUserCodeLocked(self_)) { | 
|  | art::Locks::user_code_suspension_lock_->ExclusiveUnlock(self_); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | art::Locks::user_code_suspension_lock_->AssertHeld(self_); | 
|  |  | 
|  | return; | 
|  | } while (true); | 
|  | } | 
|  |  | 
|  | ScopedNoUserCodeSuspension::~ScopedNoUserCodeSuspension() { | 
|  | art::Locks::user_code_suspension_lock_->ExclusiveUnlock(self_); | 
|  | } | 
|  |  | 
|  | struct ThreadCallback : public art::ThreadLifecycleCallback { | 
|  | jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { | 
|  | if (self->GetPeer() == nullptr) { | 
|  | return nullptr; | 
|  | } | 
|  | return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer()); | 
|  | } | 
|  |  | 
|  | template <ArtJvmtiEvent kEvent> | 
|  | void Post(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { | 
|  | DCHECK_EQ(self, art::Thread::Current()); | 
|  | ScopedLocalRef<jthread> thread(self->GetJniEnv(), GetThreadObject(self)); | 
|  | art::ScopedThreadSuspension sts(self, art::ThreadState::kNative); | 
|  | event_handler->DispatchEvent<kEvent>(self, | 
|  | reinterpret_cast<JNIEnv*>(self->GetJniEnv()), | 
|  | thread.get()); | 
|  | } | 
|  |  | 
|  | void ThreadStart(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) { | 
|  | // Needs to be checked first because we might start these threads before we actually send the | 
|  | // VMInit event. | 
|  | if (self->IsSystemDaemon()) { | 
|  | // System daemon threads are things like the finalizer or gc thread. It would be dangerous to | 
|  | // allow agents to get in the way of these threads starting up. These threads include things | 
|  | // like the HeapTaskDaemon and the finalizer daemon. | 
|  | // | 
|  | // This event can happen during the time before VMInit or just after zygote fork. Since the | 
|  | // second is hard to distinguish we unfortunately cannot really check the state here. | 
|  | return; | 
|  | } | 
|  | if (!started) { | 
|  | // Runtime isn't started. We only expect at most the signal handler or JIT threads to be | 
|  | // started here; this includes the perfetto_hprof_listener signal handler thread for | 
|  | // perfetto_hprof, as well as the metrics background reporting thread. | 
|  | if (art::kIsDebugBuild) { | 
|  | std::string name; | 
|  | self->GetThreadName(name); | 
|  | if (name != "JDWP" && name != "Signal Catcher" && name != "perfetto_hprof_listener" && | 
|  | name != art::metrics::MetricsReporter::kBackgroundThreadName && | 
|  | !android::base::StartsWith(name, "Jit thread pool") && | 
|  | !android::base::StartsWith(name, "Heap thread pool worker thread") && | 
|  | !android::base::StartsWith(name, "Runtime worker thread")) { | 
|  | LOG(FATAL) << "Unexpected thread before start: " << name << " id: " | 
|  | << self->GetThreadId(); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  | Post<ArtJvmtiEvent::kThreadStart>(self); | 
|  | } | 
|  |  | 
|  | void ThreadDeath(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) { | 
|  | Post<ArtJvmtiEvent::kThreadEnd>(self); | 
|  | } | 
|  |  | 
|  | EventHandler* event_handler = nullptr; | 
|  | bool started = false; | 
|  | }; | 
|  |  | 
|  | ThreadCallback gThreadCallback; | 
|  |  | 
|  | void ThreadUtil::Register(EventHandler* handler) { | 
|  | art::Runtime* runtime = art::Runtime::Current(); | 
|  |  | 
|  | gThreadCallback.started = runtime->IsStarted(); | 
|  | gThreadCallback.event_handler = handler; | 
|  |  | 
|  | art::ScopedThreadStateChange stsc(art::Thread::Current(), | 
|  | art::ThreadState::kWaitingForDebuggerToAttach); | 
|  | art::ScopedSuspendAll ssa("Add thread callback"); | 
|  | runtime->GetRuntimeCallbacks()->AddThreadLifecycleCallback(&gThreadCallback); | 
|  | } | 
|  |  | 
|  | void ThreadUtil::VMInitEventSent() { | 
|  | // We should have already started. | 
|  | DCHECK(gThreadCallback.started); | 
|  | // We moved to VMInit. Report the main thread as started (it was attached early, and must not be | 
|  | // reported until Init. | 
|  | gThreadCallback.Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current()); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { | 
|  | art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart->InvokeStatic<'V'>(self); | 
|  | if (self->IsExceptionPending()) { | 
|  | LOG(WARNING) << "Exception occurred when waiting for system daemons to start: " | 
|  | << self->GetException()->Dump(); | 
|  | self->ClearException(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ThreadUtil::CacheData() { | 
|  | // We must have started since it is now safe to cache our data; | 
|  | gThreadCallback.started = true; | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::ObjPtr<art::mirror::Class> thread_class = art::WellKnownClasses::java_lang_Thread.Get(); | 
|  | CHECK(thread_class != nullptr); | 
|  | context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader", | 
|  | "Ljava/lang/ClassLoader;"); | 
|  | CHECK(context_class_loader_ != nullptr); | 
|  | // Now wait for all required system threads to come up before allowing the rest of loading to | 
|  | // continue. | 
|  | WaitForSystemDaemonStart(self); | 
|  | } | 
|  |  | 
|  | void ThreadUtil::Unregister() { | 
|  | art::ScopedThreadStateChange stsc(art::Thread::Current(), | 
|  | art::ThreadState::kWaitingForDebuggerToAttach); | 
|  | art::ScopedSuspendAll ssa("Remove thread callback"); | 
|  | art::Runtime* runtime = art::Runtime::Current(); | 
|  | runtime->GetRuntimeCallbacks()->RemoveThreadLifecycleCallback(&gThreadCallback); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::GetCurrentThread([[maybe_unused]] jvmtiEnv* env, jthread* thread_ptr) { | 
|  | art::Thread* self = art::Thread::Current(); | 
|  |  | 
|  | art::ScopedObjectAccess soa(self); | 
|  |  | 
|  | jthread thread_peer; | 
|  | if (self->IsStillStarting()) { | 
|  | thread_peer = nullptr; | 
|  | } else { | 
|  | thread_peer = soa.AddLocalReference<jthread>(self->GetPeer()); | 
|  | } | 
|  |  | 
|  | *thread_ptr = thread_peer; | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | // Get the native thread. The spec says a null object denotes the current thread. | 
|  | bool ThreadUtil::GetNativeThread(jthread thread, | 
|  | const art::ScopedObjectAccessAlreadyRunnable& soa, | 
|  | /*out*/ art::Thread** thr, | 
|  | /*out*/ jvmtiError* err) { | 
|  | art::ScopedExceptionStorage sse(soa.Self()); | 
|  | if (thread == nullptr) { | 
|  | *thr = art::Thread::Current(); | 
|  | return true; | 
|  | } | 
|  | art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread); | 
|  | if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) { | 
|  | *err = ERR(INVALID_THREAD); | 
|  | return false; | 
|  | } else { | 
|  | *thr = art::Thread::FromManagedThread(soa, thread); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool ThreadUtil::GetAliveNativeThread(jthread thread, | 
|  | const art::ScopedObjectAccessAlreadyRunnable& soa, | 
|  | /*out*/ art::Thread** thr, | 
|  | /*out*/ jvmtiError* err) { | 
|  | if (!GetNativeThread(thread, soa, thr, err)) { | 
|  | return false; | 
|  | } else if (*thr == nullptr || (*thr)->GetState() == art::ThreadState::kTerminated) { | 
|  | *err = ERR(THREAD_NOT_ALIVE); | 
|  | return false; | 
|  | } else { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) { | 
|  | if (info_ptr == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  | if (!PhaseUtil::IsLivePhase()) { | 
|  | return JVMTI_ERROR_WRONG_PHASE; | 
|  | } | 
|  |  | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock mu(self, *art::Locks::thread_list_lock_); | 
|  |  | 
|  | art::Thread* target; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | JvmtiUniquePtr<char[]> name_uptr; | 
|  | if (target != nullptr) { | 
|  | // Have a native thread object, this thread is alive. | 
|  | std::string name; | 
|  | target->GetThreadName(name); | 
|  | jvmtiError name_result; | 
|  | name_uptr = CopyString(env, name.c_str(), &name_result); | 
|  | if (name_uptr == nullptr) { | 
|  | return name_result; | 
|  | } | 
|  | info_ptr->name = name_uptr.get(); | 
|  |  | 
|  | info_ptr->priority = target->GetNativePriority(); | 
|  |  | 
|  | info_ptr->is_daemon = target->IsDaemon(); | 
|  |  | 
|  | art::ObjPtr<art::mirror::Object> peer = target->GetPeerFromOtherThread(); | 
|  |  | 
|  | // ThreadGroup. | 
|  | if (peer != nullptr) { | 
|  | art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group; | 
|  | CHECK(f != nullptr); | 
|  | art::ObjPtr<art::mirror::Object> group = f->GetObject(peer); | 
|  | info_ptr->thread_group = group == nullptr | 
|  | ? nullptr | 
|  | : soa.AddLocalReference<jthreadGroup>(group); | 
|  | } else { | 
|  | info_ptr->thread_group = nullptr; | 
|  | } | 
|  |  | 
|  | // Context classloader. | 
|  | DCHECK(context_class_loader_ != nullptr); | 
|  | art::ObjPtr<art::mirror::Object> ccl = peer != nullptr | 
|  | ? context_class_loader_->GetObject(peer) | 
|  | : nullptr; | 
|  | info_ptr->context_class_loader = ccl == nullptr | 
|  | ? nullptr | 
|  | : soa.AddLocalReference<jobject>(ccl); | 
|  | } else { | 
|  | // Only the peer. This thread has either not been started, or is dead. Read things from | 
|  | // the Java side. | 
|  | art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread); | 
|  |  | 
|  | // Name. | 
|  | { | 
|  | art::ArtField* f = art::WellKnownClasses::java_lang_Thread_name; | 
|  | CHECK(f != nullptr); | 
|  | art::ObjPtr<art::mirror::Object> name = f->GetObject(peer); | 
|  | std::string name_cpp; | 
|  | const char* name_cstr; | 
|  | if (name != nullptr) { | 
|  | name_cpp = name->AsString()->ToModifiedUtf8(); | 
|  | name_cstr = name_cpp.c_str(); | 
|  | } else { | 
|  | name_cstr = ""; | 
|  | } | 
|  | jvmtiError name_result; | 
|  | name_uptr = CopyString(env, name_cstr, &name_result); | 
|  | if (name_uptr == nullptr) { | 
|  | return name_result; | 
|  | } | 
|  | info_ptr->name = name_uptr.get(); | 
|  | } | 
|  |  | 
|  | // Priority. | 
|  | { | 
|  | art::ArtField* f = art::WellKnownClasses::java_lang_Thread_priority; | 
|  | CHECK(f != nullptr); | 
|  | info_ptr->priority = static_cast<jint>(f->GetInt(peer)); | 
|  | } | 
|  |  | 
|  | // Daemon. | 
|  | { | 
|  | art::ArtField* f = art::WellKnownClasses::java_lang_Thread_daemon; | 
|  | CHECK(f != nullptr); | 
|  | info_ptr->is_daemon = f->GetBoolean(peer) == 0 ? JNI_FALSE : JNI_TRUE; | 
|  | } | 
|  |  | 
|  | // ThreadGroup. | 
|  | { | 
|  | art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group; | 
|  | CHECK(f != nullptr); | 
|  | art::ObjPtr<art::mirror::Object> group = f->GetObject(peer); | 
|  | info_ptr->thread_group = group == nullptr | 
|  | ? nullptr | 
|  | : soa.AddLocalReference<jthreadGroup>(group); | 
|  | } | 
|  |  | 
|  | // Context classloader. | 
|  | DCHECK(context_class_loader_ != nullptr); | 
|  | art::ObjPtr<art::mirror::Object> ccl = peer != nullptr | 
|  | ? context_class_loader_->GetObject(peer) | 
|  | : nullptr; | 
|  | info_ptr->context_class_loader = ccl == nullptr | 
|  | ? nullptr | 
|  | : soa.AddLocalReference<jobject>(ccl); | 
|  | } | 
|  |  | 
|  | name_uptr.release(); | 
|  |  | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | struct InternalThreadState { | 
|  | art::Thread* native_thread; | 
|  | art::ThreadState art_state; | 
|  | int thread_user_code_suspend_count; | 
|  | }; | 
|  |  | 
|  | // Return the thread's (or current thread, if null) thread state. | 
|  | static InternalThreadState GetNativeThreadState(art::Thread* target) | 
|  | REQUIRES_SHARED(art::Locks::mutator_lock_) | 
|  | REQUIRES(art::Locks::thread_list_lock_, art::Locks::user_code_suspension_lock_) { | 
|  | InternalThreadState thread_state = {}; | 
|  | art::MutexLock tscl_mu(art::Thread::Current(), *art::Locks::thread_suspend_count_lock_); | 
|  | thread_state.native_thread = target; | 
|  | if (target == nullptr || target->IsStillStarting()) { | 
|  | thread_state.art_state = art::ThreadState::kStarting; | 
|  | thread_state.thread_user_code_suspend_count = 0; | 
|  | } else { | 
|  | thread_state.art_state = target->GetState(); | 
|  | thread_state.thread_user_code_suspend_count = target->GetUserCodeSuspendCount(); | 
|  | } | 
|  | return thread_state; | 
|  | } | 
|  |  | 
|  | static jint GetJvmtiThreadStateFromInternal(const InternalThreadState& state) { | 
|  | art::ThreadState internal_thread_state = state.art_state; | 
|  | jint jvmti_state = JVMTI_THREAD_STATE_ALIVE; | 
|  |  | 
|  | if (state.thread_user_code_suspend_count != 0) { | 
|  | // Suspended can be set with any thread state so check it here. Even if the thread isn't in | 
|  | // kSuspended state it will move to that once it hits a checkpoint so we can still set this. | 
|  | jvmti_state |= JVMTI_THREAD_STATE_SUSPENDED; | 
|  | // Note: We do not have data about the previous state. Otherwise we should load the previous | 
|  | //       state here. | 
|  | } | 
|  |  | 
|  | if (state.native_thread->IsInterrupted()) { | 
|  | // Interrupted can be set with any thread state so check it here. | 
|  | jvmti_state |= JVMTI_THREAD_STATE_INTERRUPTED; | 
|  | } | 
|  |  | 
|  | // Enumerate all the thread states and fill in the other bits. This contains the results of | 
|  | // following the decision tree in the JVMTI spec GetThreadState documentation. | 
|  | switch (internal_thread_state) { | 
|  | case art::ThreadState::kRunnable: | 
|  | case art::ThreadState::kWaitingWeakGcRootRead: | 
|  | case art::ThreadState::kSuspended: | 
|  | // These are all simply runnable. | 
|  | // kRunnable is self-explanatory. | 
|  | // kWaitingWeakGcRootRead is set during some operations with strings due to the intern-table | 
|  | // so we want to keep it marked as runnable. | 
|  | // kSuspended we don't mark since if we don't have a user_code_suspend_count then it is done | 
|  | // by the GC and not a JVMTI suspension, which means it cannot be removed by ResumeThread. | 
|  | jvmti_state |= JVMTI_THREAD_STATE_RUNNABLE; | 
|  | break; | 
|  | case art::ThreadState::kNative: | 
|  | // kNative means native and runnable. Technically THREAD_STATE_IN_NATIVE can be set with any | 
|  | // state but we don't have the information to know if it should be present for any but the | 
|  | // kNative state. | 
|  | jvmti_state |= (JVMTI_THREAD_STATE_IN_NATIVE | | 
|  | JVMTI_THREAD_STATE_RUNNABLE); | 
|  | break; | 
|  | case art::ThreadState::kBlocked: | 
|  | // Blocked is one of the top level states so it sits alone. | 
|  | jvmti_state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; | 
|  | break; | 
|  | case art::ThreadState::kWaiting: | 
|  | // Object.wait() so waiting, indefinitely, in object.wait. | 
|  | jvmti_state |= (JVMTI_THREAD_STATE_WAITING | | 
|  | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | | 
|  | JVMTI_THREAD_STATE_IN_OBJECT_WAIT); | 
|  | break; | 
|  | case art::ThreadState::kTimedWaiting: | 
|  | // Object.wait(long) so waiting, with timeout, in object.wait. | 
|  | jvmti_state |= (JVMTI_THREAD_STATE_WAITING | | 
|  | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | | 
|  | JVMTI_THREAD_STATE_IN_OBJECT_WAIT); | 
|  | break; | 
|  | case art::ThreadState::kSleeping: | 
|  | // In object.sleep. This is a timed wait caused by sleep. | 
|  | jvmti_state |= (JVMTI_THREAD_STATE_WAITING | | 
|  | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | | 
|  | JVMTI_THREAD_STATE_SLEEPING); | 
|  | break; | 
|  | // TODO We might want to print warnings if we have the debugger running while JVMTI agents are | 
|  | // attached. | 
|  | case art::ThreadState::kWaitingForDebuggerSend: | 
|  | case art::ThreadState::kWaitingForDebuggerToAttach: | 
|  | case art::ThreadState::kWaitingInMainDebuggerLoop: | 
|  | case art::ThreadState::kWaitingForDebuggerSuspension: | 
|  | case art::ThreadState::kWaitingForLockInflation: | 
|  | case art::ThreadState::kWaitingForTaskProcessor: | 
|  | case art::ThreadState::kWaitingForGcToComplete: | 
|  | case art::ThreadState::kWaitingForCheckPointsToRun: | 
|  | case art::ThreadState::kWaitingPerformingGc: | 
|  | case art::ThreadState::kWaitingForJniOnLoad: | 
|  | case art::ThreadState::kWaitingInMainSignalCatcherLoop: | 
|  | case art::ThreadState::kWaitingForSignalCatcherOutput: | 
|  | case art::ThreadState::kWaitingForDeoptimization: | 
|  | case art::ThreadState::kWaitingForMethodTracingStart: | 
|  | case art::ThreadState::kWaitingForVisitObjects: | 
|  | case art::ThreadState::kWaitingForGetObjectsAllocated: | 
|  | case art::ThreadState::kWaitingForGcThreadFlip: | 
|  | case art::ThreadState::kNativeForAbort: | 
|  | // All of these are causing the thread to wait for an indeterminate amount of time but isn't | 
|  | // caused by sleep, park, or object#wait. | 
|  | jvmti_state |= (JVMTI_THREAD_STATE_WAITING | | 
|  | JVMTI_THREAD_STATE_WAITING_INDEFINITELY); | 
|  | break; | 
|  | case art::ThreadState::kObsoleteRunnable:  // Obsolete value. | 
|  | case art::ThreadState::kStarting: | 
|  | case art::ThreadState::kTerminated: | 
|  | // We only call this if we are alive so we shouldn't see either of these states. | 
|  | LOG(FATAL) << "Should not be in state " << internal_thread_state; | 
|  | UNREACHABLE(); | 
|  | } | 
|  | // TODO: PARKED. We'll have to inspect the stack. | 
|  |  | 
|  | return jvmti_state; | 
|  | } | 
|  |  | 
|  | static jint GetJavaStateFromInternal(const InternalThreadState& state) { | 
|  | switch (state.art_state) { | 
|  | case art::ThreadState::kTerminated: | 
|  | return JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED; | 
|  |  | 
|  | case art::ThreadState::kRunnable: | 
|  | case art::ThreadState::kNative: | 
|  | case art::ThreadState::kWaitingWeakGcRootRead: | 
|  | case art::ThreadState::kSuspended: | 
|  | return JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE; | 
|  |  | 
|  | case art::ThreadState::kTimedWaiting: | 
|  | case art::ThreadState::kSleeping: | 
|  | return JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING; | 
|  |  | 
|  | case art::ThreadState::kBlocked: | 
|  | return JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED; | 
|  |  | 
|  | case art::ThreadState::kStarting: | 
|  | return JVMTI_JAVA_LANG_THREAD_STATE_NEW; | 
|  |  | 
|  | case art::ThreadState::kWaiting: | 
|  | case art::ThreadState::kWaitingForTaskProcessor: | 
|  | case art::ThreadState::kWaitingForLockInflation: | 
|  | case art::ThreadState::kWaitingForGcToComplete: | 
|  | case art::ThreadState::kWaitingPerformingGc: | 
|  | case art::ThreadState::kWaitingForCheckPointsToRun: | 
|  | case art::ThreadState::kWaitingForDebuggerSend: | 
|  | case art::ThreadState::kWaitingForDebuggerToAttach: | 
|  | case art::ThreadState::kWaitingInMainDebuggerLoop: | 
|  | case art::ThreadState::kWaitingForDebuggerSuspension: | 
|  | case art::ThreadState::kWaitingForDeoptimization: | 
|  | case art::ThreadState::kWaitingForGetObjectsAllocated: | 
|  | case art::ThreadState::kWaitingForJniOnLoad: | 
|  | case art::ThreadState::kWaitingForSignalCatcherOutput: | 
|  | case art::ThreadState::kWaitingInMainSignalCatcherLoop: | 
|  | case art::ThreadState::kWaitingForMethodTracingStart: | 
|  | case art::ThreadState::kWaitingForVisitObjects: | 
|  | case art::ThreadState::kWaitingForGcThreadFlip: | 
|  | case art::ThreadState::kNativeForAbort: | 
|  | return JVMTI_JAVA_LANG_THREAD_STATE_WAITING; | 
|  |  | 
|  | case art::ThreadState::kObsoleteRunnable: | 
|  | break;  // Obsolete value. | 
|  | } | 
|  | LOG(FATAL) << "Unreachable"; | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | // Suspends the current thread if it has any suspend requests on it. | 
|  | void ThreadUtil::SuspendCheck(art::Thread* self) { | 
|  | art::ScopedObjectAccess soa(self); | 
|  | // Really this is only needed if we are in FastJNI and actually have the mutator_lock_ already. | 
|  | self->FullSuspendCheck(); | 
|  | } | 
|  |  | 
|  | bool ThreadUtil::WouldSuspendForUserCodeLocked(art::Thread* self) { | 
|  | DCHECK(self == art::Thread::Current()); | 
|  | art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_); | 
|  | return self->GetUserCodeSuspendCount() != 0; | 
|  | } | 
|  |  | 
|  | bool ThreadUtil::WouldSuspendForUserCode(art::Thread* self) { | 
|  | DCHECK(self == art::Thread::Current()); | 
|  | art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_); | 
|  | return WouldSuspendForUserCodeLocked(self); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::GetThreadState([[maybe_unused]] jvmtiEnv* env, | 
|  | jthread thread, | 
|  | jint* thread_state_ptr) { | 
|  | if (thread_state_ptr == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  |  | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | InternalThreadState state = {}; | 
|  | { | 
|  | ScopedNoUserCodeSuspension snucs(self); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_); | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | art::Thread* target = nullptr; | 
|  | if (!GetNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } | 
|  | state = GetNativeThreadState(target); | 
|  | if (state.art_state != art::ThreadState::kStarting) { | 
|  | DCHECK(state.native_thread != nullptr); | 
|  |  | 
|  | // Translate internal thread state to JVMTI and Java state. | 
|  | jint jvmti_state = GetJvmtiThreadStateFromInternal(state); | 
|  |  | 
|  | // Java state is derived from nativeGetState. | 
|  | // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly | 
|  | //       different mask if a thread got suspended due to user-code. However, this is for | 
|  | //       consistency with the Java view. | 
|  | jint java_state = GetJavaStateFromInternal(state); | 
|  |  | 
|  | *thread_state_ptr = jvmti_state | java_state; | 
|  |  | 
|  | return ERR(NONE); | 
|  | } | 
|  | } | 
|  |  | 
|  | DCHECK_EQ(state.art_state, art::ThreadState::kStarting); | 
|  |  | 
|  | if (thread == nullptr) { | 
|  | // No native thread, and no Java thread? We must be starting up. Report as wrong phase. | 
|  | return ERR(WRONG_PHASE); | 
|  | } | 
|  |  | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::StackHandleScope<1> hs(self); | 
|  |  | 
|  | // Need to read the Java "started" field to know whether this is starting or terminated. | 
|  | art::Handle<art::mirror::Object> peer(hs.NewHandle(soa.Decode<art::mirror::Object>(thread))); | 
|  | art::ObjPtr<art::mirror::Class> thread_klass = art::WellKnownClasses::java_lang_Thread.Get(); | 
|  | if (!thread_klass->IsAssignableFrom(peer->GetClass())) { | 
|  | return ERR(INVALID_THREAD); | 
|  | } | 
|  | art::ArtField* started_field = thread_klass->FindDeclaredInstanceField("started", "Z"); | 
|  | CHECK(started_field != nullptr); | 
|  | bool started = started_field->GetBoolean(peer.Get()) != 0; | 
|  | constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW; | 
|  | constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED | | 
|  | JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED; | 
|  | *thread_state_ptr = started ? kTerminatedState : kStartedState; | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::GetAllThreads(jvmtiEnv* env, | 
|  | jint* threads_count_ptr, | 
|  | jthread** threads_ptr) { | 
|  | if (threads_count_ptr == nullptr || threads_ptr == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  |  | 
|  | art::Thread* current = art::Thread::Current(); | 
|  |  | 
|  | art::ScopedObjectAccess soa(current); | 
|  |  | 
|  | art::MutexLock mu(current, *art::Locks::thread_list_lock_); | 
|  | std::list<art::Thread*> thread_list = art::Runtime::Current()->GetThreadList()->GetList(); | 
|  |  | 
|  | std::vector<art::ObjPtr<art::mirror::Object>> peers; | 
|  |  | 
|  | for (art::Thread* thread : thread_list) { | 
|  | // Skip threads that are still starting. | 
|  | if (thread->IsStillStarting()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | art::ObjPtr<art::mirror::Object> peer = thread->GetPeerFromOtherThread(); | 
|  | if (peer != nullptr) { | 
|  | peers.push_back(peer); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (peers.empty()) { | 
|  | *threads_count_ptr = 0; | 
|  | *threads_ptr = nullptr; | 
|  | } else { | 
|  | unsigned char* data; | 
|  | jvmtiError data_result = env->Allocate(peers.size() * sizeof(jthread), &data); | 
|  | if (data_result != ERR(NONE)) { | 
|  | return data_result; | 
|  | } | 
|  | jthread* threads = reinterpret_cast<jthread*>(data); | 
|  | for (size_t i = 0; i != peers.size(); ++i) { | 
|  | threads[i] = soa.AddLocalReference<jthread>(peers[i]); | 
|  | } | 
|  |  | 
|  | *threads_count_ptr = static_cast<jint>(peers.size()); | 
|  | *threads_ptr = threads; | 
|  | } | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | static void RemoveTLSData(art::Thread* target, void* ctx) REQUIRES(art::Locks::thread_list_lock_) { | 
|  | jvmtiEnv* env = reinterpret_cast<jvmtiEnv*>(ctx); | 
|  | art::Locks::thread_list_lock_->AssertHeld(art::Thread::Current()); | 
|  | JvmtiGlobalTLSData* global_tls = ThreadUtil::GetGlobalTLSData(target); | 
|  | if (global_tls != nullptr) { | 
|  | global_tls->data.erase(env); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ThreadUtil::RemoveEnvironment(jvmtiEnv* env) { | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::MutexLock mu(self, *art::Locks::thread_list_lock_); | 
|  | art::ThreadList* list = art::Runtime::Current()->GetThreadList(); | 
|  | list->ForEach(RemoveTLSData, env); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) { | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock mu(self, *art::Locks::thread_list_lock_); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | JvmtiGlobalTLSData* global_tls = GetOrCreateGlobalTLSData(target); | 
|  |  | 
|  | global_tls->data[env] = data; | 
|  |  | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | JvmtiGlobalTLSData* ThreadUtil::GetOrCreateGlobalTLSData(art::Thread* thread) { | 
|  | JvmtiGlobalTLSData* data = GetGlobalTLSData(thread); | 
|  | if (data != nullptr) { | 
|  | return data; | 
|  | } else { | 
|  | thread->SetCustomTLS(kJvmtiTlsKey, new JvmtiGlobalTLSData); | 
|  | return GetGlobalTLSData(thread); | 
|  | } | 
|  | } | 
|  |  | 
|  | JvmtiGlobalTLSData* ThreadUtil::GetGlobalTLSData(art::Thread* thread) { | 
|  | return reinterpret_cast<JvmtiGlobalTLSData*>(thread->GetCustomTLS(kJvmtiTlsKey)); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env, | 
|  | jthread thread, | 
|  | void** data_ptr) { | 
|  | if (data_ptr == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  |  | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock mu(self, *art::Locks::thread_list_lock_); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | JvmtiGlobalTLSData* global_tls = GetGlobalTLSData(target); | 
|  | if (global_tls == nullptr) { | 
|  | *data_ptr = nullptr; | 
|  | return OK; | 
|  | } | 
|  | auto it = global_tls->data.find(env); | 
|  | if (it != global_tls->data.end()) { | 
|  | *data_ptr = const_cast<void*>(it->second); | 
|  | } else { | 
|  | *data_ptr = nullptr; | 
|  | } | 
|  |  | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | struct AgentData { | 
|  | const void* arg; | 
|  | jvmtiStartFunction proc; | 
|  | jthread thread; | 
|  | JavaVM* java_vm; | 
|  | jvmtiEnv* jvmti_env; | 
|  | jint priority; | 
|  | std::string name; | 
|  | }; | 
|  |  | 
|  | static void* AgentCallback(void* arg) { | 
|  | std::unique_ptr<AgentData> data(reinterpret_cast<AgentData*>(arg)); | 
|  | CHECK(data->thread != nullptr); | 
|  |  | 
|  | // We already have a peer. So call our special Attach function. | 
|  | art::Thread* self = art::Thread::Attach(data->name.c_str(), true, data->thread); | 
|  | CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached."; | 
|  | // The name in Attach() is only for logging. Set the thread name. This is important so | 
|  | // that the thread is no longer seen as starting up. | 
|  | { | 
|  | art::ScopedObjectAccess soa(self); | 
|  | self->SetThreadName(data->name.c_str()); | 
|  | } | 
|  |  | 
|  | // Release the peer. | 
|  | JNIEnv* env = self->GetJniEnv(); | 
|  | env->DeleteGlobalRef(data->thread); | 
|  | data->thread = nullptr; | 
|  |  | 
|  | { | 
|  | // The StartThreadBirth was called in the parent thread. We let the runtime know we are up | 
|  | // before going into the provided code. | 
|  | art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_); | 
|  | art::Runtime::Current()->EndThreadBirth(); | 
|  | } | 
|  |  | 
|  | // Run the agent code. | 
|  | data->proc(data->jvmti_env, env, const_cast<void*>(data->arg)); | 
|  |  | 
|  | // Detach the thread. | 
|  | int detach_result = data->java_vm->DetachCurrentThread(); | 
|  | CHECK_EQ(detach_result, 0); | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, | 
|  | jthread thread, | 
|  | jvmtiStartFunction proc, | 
|  | const void* arg, | 
|  | jint priority) { | 
|  | if (!PhaseUtil::IsLivePhase()) { | 
|  | return ERR(WRONG_PHASE); | 
|  | } | 
|  | if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { | 
|  | return ERR(INVALID_PRIORITY); | 
|  | } | 
|  | if (thread == nullptr) { | 
|  | return ERR(INVALID_THREAD); | 
|  | } | 
|  | art::Runtime* runtime = art::Runtime::Current(); | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | std::unique_ptr<AgentData> data; | 
|  | { | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread); | 
|  | if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) { | 
|  | return ERR(INVALID_THREAD); | 
|  | } | 
|  | if (proc == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  |  | 
|  | { | 
|  | art::MutexLock mu(soa.Self(), *art::Locks::runtime_shutdown_lock_); | 
|  | if (runtime->IsShuttingDownLocked()) { | 
|  | // The runtime is shutting down so we cannot create new threads. | 
|  | // TODO It's not fully clear from the spec what we should do here. We aren't yet in | 
|  | // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now | 
|  | // impossible. Existing agents don't seem to generally do anything with this return value so | 
|  | // it doesn't matter too much. We could do something like sending a fake ThreadStart event | 
|  | // even though code is never actually run. | 
|  | return ERR(INTERNAL); | 
|  | } | 
|  | runtime->StartThreadBirth(); | 
|  | } | 
|  |  | 
|  | data.reset(new AgentData); | 
|  | data->arg = arg; | 
|  | data->proc = proc; | 
|  | // We need a global ref for Java objects, as local refs will be invalid. | 
|  | data->thread = runtime->GetJavaVM()->AddGlobalRef(soa.Self(), othread); | 
|  | data->java_vm = runtime->GetJavaVM(); | 
|  | data->jvmti_env = jvmti_env; | 
|  | data->priority = priority; | 
|  | art::ObjPtr<art::mirror::Object> name = | 
|  | art::WellKnownClasses::java_lang_Thread_name->GetObject( | 
|  | soa.Decode<art::mirror::Object>(thread)); | 
|  | if (name == nullptr) { | 
|  | data->name = "JVMTI Agent Thread"; | 
|  | } else { | 
|  | data->name = name->AsString()->ToModifiedUtf8(); | 
|  | } | 
|  | } | 
|  |  | 
|  | pthread_t pthread; | 
|  | int pthread_create_result = pthread_create(&pthread, | 
|  | nullptr, | 
|  | &AgentCallback, | 
|  | reinterpret_cast<void*>(data.get())); | 
|  | if (pthread_create_result != 0) { | 
|  | // If the create succeeded the other thread will call EndThreadBirth. | 
|  | art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_); | 
|  | runtime->EndThreadBirth(); | 
|  | return ERR(INTERNAL); | 
|  | } | 
|  | data.release();  // NOLINT pthreads API. | 
|  |  | 
|  | return ERR(NONE); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::SuspendOther(art::Thread* self, | 
|  | jthread target_jthread) { | 
|  | // Loop since we need to bail out and try again if we would end up getting suspended while holding | 
|  | // the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we | 
|  | // release the lock, wait to get resumed and try again. | 
|  | do { | 
|  | ScopedNoUserCodeSuspension snucs(self); | 
|  | // We are not going to be suspended by user code from now on. | 
|  | { | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock thread_list_mu(self, *art::Locks::thread_list_lock_); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(target_jthread, soa, &target, &err)) { | 
|  | return err; | 
|  | } | 
|  | art::ThreadState state = target->GetState(); | 
|  | if (state == art::ThreadState::kStarting || target->IsStillStarting()) { | 
|  | return ERR(THREAD_NOT_ALIVE); | 
|  | } else { | 
|  | art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_); | 
|  | if (target->GetUserCodeSuspendCount() != 0) { | 
|  | return ERR(THREAD_SUSPENDED); | 
|  | } | 
|  | } | 
|  | } | 
|  | bool timeout = true; | 
|  | art::Thread* ret_target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer( | 
|  | target_jthread, | 
|  | art::SuspendReason::kForUserCode, | 
|  | &timeout); | 
|  | if (ret_target == nullptr && !timeout) { | 
|  | // TODO It would be good to get more information about why exactly the thread failed to | 
|  | // suspend. | 
|  | return ERR(INTERNAL); | 
|  | } else if (!timeout) { | 
|  | // we didn't time out and got a result. | 
|  | return OK; | 
|  | } | 
|  | // We timed out. Just go around and try again. | 
|  | } while (true); | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::SuspendSelf(art::Thread* self) { | 
|  | CHECK(self == art::Thread::Current()); | 
|  | { | 
|  | art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_); | 
|  | art::MutexLock thread_list_mu(self, *art::Locks::thread_suspend_count_lock_); | 
|  | if (self->GetUserCodeSuspendCount() != 0) { | 
|  | // This can only happen if we race with another thread to suspend 'self' and we lose. | 
|  | return ERR(THREAD_SUSPENDED); | 
|  | } | 
|  | // We shouldn't be able to fail this. | 
|  | if (!self->ModifySuspendCount(self, +1, nullptr, art::SuspendReason::kForUserCode)) { | 
|  | // TODO More specific error would be nice. | 
|  | return ERR(INTERNAL); | 
|  | } | 
|  | } | 
|  | // Once we have requested the suspend we actually go to sleep. We need to do this after releasing | 
|  | // the suspend_lock to make sure we can be woken up. This call gains the mutator lock causing us | 
|  | // to go to sleep until we are resumed. | 
|  | SuspendCheck(self); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::SuspendThread([[maybe_unused]] jvmtiEnv* env, jthread thread) { | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | bool target_is_self = false; | 
|  | { | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock mu(self, *art::Locks::thread_list_lock_); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } else if (target == self) { | 
|  | target_is_self = true; | 
|  | } | 
|  | } | 
|  | if (target_is_self) { | 
|  | return SuspendSelf(self); | 
|  | } else { | 
|  | return SuspendOther(self, thread); | 
|  | } | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::ResumeThread([[maybe_unused]] jvmtiEnv* env, jthread thread) { | 
|  | if (thread == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::Thread* target; | 
|  |  | 
|  | // Make sure we won't get suspended ourselves while in the middle of resuming another thread. | 
|  | 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::MutexLock tll_mu(self, *art::Locks::thread_list_lock_); | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } else if (target == self) { | 
|  | // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so | 
|  | // we can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs | 
|  | // about current state since it's all concurrent. | 
|  | return ERR(THREAD_NOT_SUSPENDED); | 
|  | } | 
|  | // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really | 
|  | // cannot tell why resume failed. | 
|  | { | 
|  | art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_); | 
|  | if (target->GetUserCodeSuspendCount() == 0) { | 
|  | return ERR(THREAD_NOT_SUSPENDED); | 
|  | } | 
|  | } | 
|  | } | 
|  | // It is okay that we don't have a thread_list_lock here since we know that the thread cannot | 
|  | // die since it is currently held suspended by a SuspendReason::kForUserCode suspend. | 
|  | DCHECK(target != self); | 
|  | if (!art::Runtime::Current()->GetThreadList()->Resume(target, | 
|  | art::SuspendReason::kForUserCode)) { | 
|  | // TODO Give a better error. | 
|  | // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure. | 
|  | return ERR(INTERNAL); | 
|  | } else { | 
|  | return OK; | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool IsCurrentThread(jthread thr) { | 
|  | if (thr == nullptr) { | 
|  | return true; | 
|  | } | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock mu(self, *art::Locks::thread_list_lock_); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err_unused = ERR(INTERNAL); | 
|  | if (ThreadUtil::GetNativeThread(thr, soa, &target, &err_unused)) { | 
|  | return target == self; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Suspends all the threads in the list at the same time. Getting this behavior is a little tricky | 
|  | // since we can have threads in the list multiple times. This generally doesn't matter unless the | 
|  | // current thread is present multiple times. In that case we need to suspend only once and either | 
|  | // return the same error code in all the other slots if it failed or return ERR(THREAD_SUSPENDED) if | 
|  | // it didn't. We also want to handle the current thread last to make the behavior of the code | 
|  | // simpler to understand. | 
|  | jvmtiError ThreadUtil::SuspendThreadList(jvmtiEnv* env, | 
|  | jint request_count, | 
|  | const jthread* threads, | 
|  | jvmtiError* results) { | 
|  | if (request_count == 0) { | 
|  | return ERR(ILLEGAL_ARGUMENT); | 
|  | } else if (results == nullptr || threads == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  | // This is the list of the indexes in 'threads' and 'results' that correspond to the currently | 
|  | // running thread. These indexes we need to handle specially since we need to only actually | 
|  | // suspend a single time. | 
|  | std::vector<jint> current_thread_indexes; | 
|  | for (jint i = 0; i < request_count; i++) { | 
|  | if (IsCurrentThread(threads[i])) { | 
|  | current_thread_indexes.push_back(i); | 
|  | } else { | 
|  | results[i] = env->SuspendThread(threads[i]); | 
|  | } | 
|  | } | 
|  | if (!current_thread_indexes.empty()) { | 
|  | jint first_current_thread_index = current_thread_indexes[0]; | 
|  | // Suspend self. | 
|  | jvmtiError res = env->SuspendThread(threads[first_current_thread_index]); | 
|  | results[first_current_thread_index] = res; | 
|  | // Fill in the rest of the error values as appropriate. | 
|  | jvmtiError other_results = (res != OK) ? res : ERR(THREAD_SUSPENDED); | 
|  | for (auto it = ++current_thread_indexes.begin(); it != current_thread_indexes.end(); ++it) { | 
|  | results[*it] = other_results; | 
|  | } | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::ResumeThreadList(jvmtiEnv* env, | 
|  | jint request_count, | 
|  | const jthread* threads, | 
|  | jvmtiError* results) { | 
|  | if (request_count == 0) { | 
|  | return ERR(ILLEGAL_ARGUMENT); | 
|  | } else if (results == nullptr || threads == nullptr) { | 
|  | return ERR(NULL_POINTER); | 
|  | } | 
|  | for (jint i = 0; i < request_count; i++) { | 
|  | results[i] = env->ResumeThread(threads[i]); | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::StopThread([[maybe_unused]] jvmtiEnv* env, | 
|  | jthread thread, | 
|  | jobject exception) { | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::StackHandleScope<1> hs(self); | 
|  | if (exception == nullptr) { | 
|  | return ERR(INVALID_OBJECT); | 
|  | } | 
|  | art::ObjPtr<art::mirror::Object> obj(soa.Decode<art::mirror::Object>(exception)); | 
|  | if (!obj->GetClass()->IsThrowableClass()) { | 
|  | return ERR(INVALID_OBJECT); | 
|  | } | 
|  | art::Handle<art::mirror::Throwable> exc(hs.NewHandle(obj->AsThrowable())); | 
|  | art::Locks::thread_list_lock_->ExclusiveLock(self); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(thread, soa, &target, &err)) { | 
|  | art::Locks::thread_list_lock_->ExclusiveUnlock(self); | 
|  | return err; | 
|  | } else if (target->GetState() == art::ThreadState::kStarting || target->IsStillStarting()) { | 
|  | art::Locks::thread_list_lock_->ExclusiveUnlock(self); | 
|  | return ERR(THREAD_NOT_ALIVE); | 
|  | } | 
|  | struct StopThreadClosure : public art::Closure { | 
|  | public: | 
|  | explicit StopThreadClosure(art::Handle<art::mirror::Throwable> except) : exception_(except) { } | 
|  |  | 
|  | void Run(art::Thread* me) override REQUIRES_SHARED(art::Locks::mutator_lock_) { | 
|  | // Make sure the thread is prepared to notice the exception. | 
|  | DeoptManager::Get()->DeoptimizeThread(me); | 
|  | me->SetAsyncException(exception_.Get()); | 
|  | // Wake up the thread if it is sleeping. | 
|  | me->Notify(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | art::Handle<art::mirror::Throwable> exception_; | 
|  | }; | 
|  | StopThreadClosure c(exc); | 
|  | // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. | 
|  | if (target->RequestSynchronousCheckpoint(&c)) { | 
|  | return OK; | 
|  | } else { | 
|  | // Something went wrong, probably the thread died. | 
|  | return ERR(THREAD_NOT_ALIVE); | 
|  | } | 
|  | } | 
|  |  | 
|  | jvmtiError ThreadUtil::InterruptThread([[maybe_unused]] jvmtiEnv* env, jthread thread) { | 
|  | art::Thread* self = art::Thread::Current(); | 
|  | art::ScopedObjectAccess soa(self); | 
|  | art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_); | 
|  | art::Thread* target = nullptr; | 
|  | jvmtiError err = ERR(INTERNAL); | 
|  | if (!GetAliveNativeThread(thread, soa, &target, &err)) { | 
|  | return err; | 
|  | } else if (target->GetState() == art::ThreadState::kStarting || target->IsStillStarting()) { | 
|  | return ERR(THREAD_NOT_ALIVE); | 
|  | } | 
|  | target->Interrupt(self); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | }  // namespace openjdkjvmti |