void Thread::InitTlsEntryPoints() {
ScopedTrace trace("InitTlsEntryPoints"); // Insert a placeholder so we can easily tell if we call an unimplemented entry point.
uintptr_t* begin = reinterpret_cast<uintptr_t*>(&tlsPtr_.jni_entrypoints);
uintptr_t* end = reinterpret_cast<uintptr_t*>( reinterpret_cast<uint8_t*>(&tlsPtr_.quick_entrypoints) + sizeof(tlsPtr_.quick_entrypoints)); for (uintptr_t* it = begin; it != end; ++it) {
*it = reinterpret_cast<uintptr_t>(UnimplementedEntryPoint);
} bool monitor_jni_entry_exit = false;
PaletteShouldReportJniInvocations(&monitor_jni_entry_exit); if (monitor_jni_entry_exit) {
AtomicSetFlag(ThreadFlag::kMonitorJniEntryExit);
}
InitEntryPoints(&tlsPtr_.jni_entrypoints, &tlsPtr_.quick_entrypoints, monitor_jni_entry_exit);
}
void Thread::AssertHasDeoptimizationContext() {
CHECK(tlsPtr_.deoptimization_context_stack != nullptr)
<< "No deoptimization context for thread " << *this;
}
enum {
kPermitAvailable = 0, // Incrementing consumes the permit
kNoPermit = 1, // Incrementing marks as waiter waiting
kNoPermitWaiterWaiting = 2
};
void Thread::Park(bool is_absolute, int64_t time) {
DCHECK(this == Thread::Current()); #if ART_USE_FUTEXES // Consume the permit, or mark as waiting. This cannot cause park_state to go // outside of its valid range (0, 1, 2), because in all cases where 2 is // assigned it is set back to 1 before returning, and this method cannot run // concurrently with itself since it operates on the current thread. int old_state = tls32_.park_state_.fetch_add(1, std::memory_order_relaxed); if (old_state == kNoPermit) { // no permit was available. block thread until later.
Runtime::Current()->GetRuntimeCallbacks()->ThreadParkStart(is_absolute, time); bool timed_out = false; if (!is_absolute && time == 0) { // Thread.getState() is documented to return waiting for untimed parks.
ScopedThreadSuspension sts(this, ThreadState::kWaiting);
DCHECK_EQ(NumberOfHeldMutexes(), 0u); int result = futex(tls32_.park_state_.Address(),
FUTEX_WAIT_PRIVATE, /* sleep if val = */ kNoPermitWaiterWaiting, /* timeout */ nullptr,
nullptr, 0); // This errno check must happen before the scope is closed, to ensure that // no destructors (such as ScopedThreadSuspension) overwrite errno. if (result == -1) { switch (errno) { case EAGAIN:
FALLTHROUGH_INTENDED; case EINTR: break; // park() is allowed to spuriously return default: PLOG(FATAL) << "Failed to park";
}
}
} elseif (time > 0) { // Only actually suspend and futex_wait if we're going to wait for some // positive amount of time - the kernel will reject negative times with // EINVAL, and a zero time will just noop.
// Thread.getState() is documented to return timed wait for timed parks.
ScopedThreadSuspension sts(this, ThreadState::kTimedWaiting);
DCHECK_EQ(NumberOfHeldMutexes(), 0u);
timespec timespec; int result = 0; if (is_absolute) { // Time is millis when scheduled for an absolute time
timespec.tv_nsec = (time % 1000) * 1000000;
timespec.tv_sec = SaturatedTimeT(time / 1000); // This odd looking pattern is recommended by futex documentation to // wait until an absolute deadline, with otherwise identical behavior to // FUTEX_WAIT_PRIVATE. This also allows parkUntil() to return at the // correct time when the system clock changes.
result = futex(tls32_.park_state_.Address(),
FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME, /* sleep if val = */ kNoPermitWaiterWaiting,
×pec,
nullptr, static_cast<int>(FUTEX_BITSET_MATCH_ANY));
} else { // Time is nanos when scheduled for a relative time
timespec.tv_sec = SaturatedTimeT(time / 1000000000);
timespec.tv_nsec = time % 1000000000;
result = futex(tls32_.park_state_.Address(),
FUTEX_WAIT_PRIVATE, /* sleep if val = */ kNoPermitWaiterWaiting,
×pec,
nullptr, 0);
} // This errno check must happen before the scope is closed, to ensure that // no destructors (such as ScopedThreadSuspension) overwrite errno. if (result == -1) { switch (errno) { case ETIMEDOUT:
timed_out = true;
FALLTHROUGH_INTENDED; case EAGAIN: case EINTR: break; // park() is allowed to spuriously return default: PLOG(FATAL) << "Failed to park";
}
}
} // Mark as no longer waiting, and consume permit if there is one.
tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed); // TODO: Call to signal jvmti here
Runtime::Current()->GetRuntimeCallbacks()->ThreadParkFinished(timed_out);
} else { // the fetch_add has consumed the permit. immediately return.
DCHECK_EQ(old_state, kPermitAvailable);
} #else #pragma clang diagnostic push #pragma clang diagnostic warning "-W#warnings" #warning"LockSupport.park/unpark implemented as noops without FUTEX support." #pragma clang diagnostic pop
UNUSED(is_absolute, time);
UNIMPLEMENTED(WARNING);
sched_yield(); #endif
}
void Thread::Unpark() { #if ART_USE_FUTEXES // Set permit available; will be consumed either by fetch_add (when the thread // tries to park) or store (when the parked thread is woken up) if (tls32_.park_state_.exchange(kPermitAvailable, std::memory_order_relaxed)
== kNoPermitWaiterWaiting) { int result = futex(tls32_.park_state_.Address(),
FUTEX_WAKE_PRIVATE, /* number of waiters = */ 1,
nullptr,
nullptr, 0); if (result == -1) {
PLOG(FATAL) << "Failed to unpark";
}
} #else
UNIMPLEMENTED(WARNING); #endif
}
void Thread::PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type) {
StackedShadowFrameRecord* record = new StackedShadowFrameRecord(
sf, type, tlsPtr_.stacked_shadow_frame_record);
tlsPtr_.stacked_shadow_frame_record = record;
}
class FrameIdToShadowFrame { public: static FrameIdToShadowFrame* Create(size_t frame_id,
ShadowFrame* shadow_frame,
FrameIdToShadowFrame* next,
size_t num_vregs) { // Append a bool array at the end to keep track of what vregs are updated by the debugger.
uint8_t* memory = new uint8_t[sizeof(FrameIdToShadowFrame) + sizeof(bool) * num_vregs]; returnnew (memory) FrameIdToShadowFrame(frame_id, shadow_frame, next);
}
static FrameIdToShadowFrame* FindFrameIdToShadowFrame(FrameIdToShadowFrame* head,
size_t frame_id) {
FrameIdToShadowFrame* found = nullptr; for (FrameIdToShadowFrame* record = head; record != nullptr; record = record->GetNext()) { if (record->GetFrameId() == frame_id) { if (kIsDebugBuild) { // Check we have at most one record for this frame.
CHECK(found == nullptr) << "Multiple records for the frame " << frame_id;
found = record;
} else { return record;
}
}
} return found;
}
// Must only be called when FindDebuggerShadowFrame(frame_id) returns non-nullptr. bool* Thread::GetUpdatedVRegFlags(size_t frame_id) {
FrameIdToShadowFrame* record = FindFrameIdToShadowFrame(
tlsPtr_.frame_id_to_shadow_frame, frame_id);
CHECK(record != nullptr); return record->GetUpdatedVRegFlags();
}
ShadowFrame* Thread::FindOrCreateDebuggerShadowFrame(size_t frame_id,
uint32_t num_vregs,
ArtMethod* method,
uint32_t dex_pc) {
ShadowFrame* shadow_frame = FindDebuggerShadowFrame(frame_id); if (shadow_frame != nullptr) { return shadow_frame;
}
VLOG(deopt) << "Create pre-deopted ShadowFrame for " << ArtMethod::PrettyMethod(method);
shadow_frame = ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc);
FrameIdToShadowFrame* record = FrameIdToShadowFrame::Create(frame_id,
shadow_frame,
tlsPtr_.frame_id_to_shadow_frame,
num_vregs); for (uint32_t i = 0; i < num_vregs; i++) { // Do this to clear all references for root visitors.
shadow_frame->SetVRegReference(i, nullptr); // This flag will be changed to true if the debugger modifies the value.
record->GetUpdatedVRegFlags()[i] = false;
}
tlsPtr_.frame_id_to_shadow_frame = record; return shadow_frame;
}
TLSData* Thread::GetCustomTLS(constchar* key) {
MutexLock mu(Thread::Current(), *Locks::custom_tls_lock_); auto it = custom_tls_.find(key); return (it != custom_tls_.end()) ? it->second.get() : nullptr;
}
void Thread::SetCustomTLS(constchar* key, TLSData* data) { // We will swap the old data (which might be nullptr) with this and then delete it outside of the // custom_tls_lock_.
std::unique_ptr<TLSData> old_data(data);
{
MutexLock mu(Thread::Current(), *Locks::custom_tls_lock_);
custom_tls_.GetOrCreate(key, []() { return std::unique_ptr<TLSData>(); }).swap(old_data);
}
}
void Thread::RemoveDebuggerShadowFrameMapping(size_t frame_id) {
FrameIdToShadowFrame* head = tlsPtr_.frame_id_to_shadow_frame; if (head->GetFrameId() == frame_id) {
tlsPtr_.frame_id_to_shadow_frame = head->GetNext();
FrameIdToShadowFrame::Delete(head); return;
}
FrameIdToShadowFrame* prev = head; for (FrameIdToShadowFrame* record = head->GetNext();
record != nullptr;
prev = record, record = record->GetNext()) { if (record->GetFrameId() == frame_id) {
prev->SetNext(record->GetNext());
FrameIdToShadowFrame::Delete(record); return;
}
}
LOG(FATAL) << "No shadow frame for frame " << frame_id;
UNREACHABLE();
}
void Thread::InitAfterFork() { // One thread (us) survived the fork, but we have a new tid so we need to // update the value stashed in this Thread*.
InitTid();
}
void Thread::DeleteJPeer(JNIEnv* env) { // Make sure nothing can observe both opeer and jpeer set at the same time.
jobject old_jpeer = tlsPtr_.jpeer;
CHECK(old_jpeer != nullptr);
tlsPtr_.jpeer = nullptr;
env->DeleteGlobalRef(old_jpeer);
}
void* Thread::CreateCallback(void* arg) {
Thread* self = reinterpret_cast<Thread*>(arg);
Runtime* runtime = Runtime::Current(); if (runtime == nullptr) {
LOG(ERROR) << "Thread attaching to non-existent runtime: " << *self; return nullptr;
}
{ // TODO: pass self to MutexLock - requires self to equal Thread::Current(), which is only true // after self->Init().
MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); // Check that if we got here we cannot be shutting down (as shutdown should never have started // while threads are being born).
CHECK(!runtime->IsShuttingDownLocked()); // Note: given that the JNIEnv is created in the parent thread, the only failure point here is // a mess in InitStack. We do not have a reasonable way to recover from that, so abort // the runtime in such a case. In case this ever changes, we need to make sure here to // delete the tmp_jni_env, as we own it at this point.
CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env));
self->tlsPtr_.tmp_jni_env = nullptr;
Runtime::Current()->EndThreadBirth();
}
{
ScopedObjectAccess soa(self);
self->InitStringEntryPoints();
// Copy peer into self, deleting global reference when done.
CHECK(self->tlsPtr_.jpeer != nullptr);
self->tlsPtr_.opeer = soa.Decode<mirror::Object>(self->tlsPtr_.jpeer).Ptr();
self->tlsPtr_.current_peer = self->tlsPtr_.opeer; // Make sure nothing can observe both opeer and jpeer set at the same time.
self->DeleteJPeer(self->GetJniEnv());
self->SetThreadName(self->GetThreadName()->ToModifiedUtf8().c_str());
// Java priority is inherited at the point at which the Java thread is // created. That uses the stored `priority` value. However niceness can be changed before // starting the thread. So use the niceness value to set the actual OS priority. // The priority value stored in the peer is needed to set the priority of children of self, // but does not determine our own priority (though this distinction very rarely matters). int niceness = self->GetCachedNiceness();
self->SetNativePriority(NicenessToPriority(niceness), niceness);
// Unpark ourselves if the java peer was unparked before it started (see // b/28845097#comment49 for more information)
ArtField* unparkedField = WellKnownClasses::java_lang_Thread_unparkedBeforeStart; bool should_unpark = false;
{ // Hold the lock here, so that if another thread calls unpark before the thread starts // we don't observe the unparkedBeforeStart field before the unparker writes to it, // which could cause a lost unpark.
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
should_unpark = unparkedField->GetBoolean(self->tlsPtr_.opeer) == JNI_TRUE;
} if (should_unpark) {
self->Unpark();
}
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
ObjPtr<mirror::Object> runnable =
WellKnownClasses::java_lang_Thread_target->GetObject(receiver); // When the runnable is a VirtualThreadContext, don't run thread.run() and treat it as a virtual // thread. if (kIsVirtualThreadEnabled &&
UNLIKELY(!runnable.IsNull() &&
runnable->InstanceOf(GetClassRoot<mirror::VirtualThreadContext>()))) {
StackHandleScope<1> hs(self);
Handle<mirror::VirtualThreadContext> v_context = hs.NewHandle(
ObjPtr<mirror::VirtualThreadContext>::DownCast(runnable));
uint8_t flags = v_context->GetParkedStates() != nullptr ? VirtualThreadFlag::kUnparking : 0;
uint32_t thin_lock_id = v_context->GetMonitorThreadId();
DCHECK_GT(thin_lock_id, 0u);
MountedVirtualThreadData mounted_data((uint32_t)thin_lock_id, self->GetThreadId(), flags); bool mounted = self->TrySetMountedVirtualThreadData(&mounted_data);
DCHECK(mounted) << mounted_data;
// Invoke the Runnable.run() method to avoid holding a reference of opeer in the managed // stack.
WellKnownClasses::java_lang_Runnable_run->InvokeInterface<'V'>(self, v_context.Get());
// When a virtual thread is parked, we expect and clear the VirtualThreadParkingError used to // unwind the native stack. if (self->IsExceptionPending() && self->IsVirtualThreadParking()) {
DCHECK(self->GetException()->GetClass()->DescriptorEquals( "Ldalvik/system/VirtualThreadParkingError;"));
self->ClearException();
} bool unmounted = self->TryClearMountedVirtualThreadData();
DCHECK(unmounted) << mounted_data;
} else { // Invoke the 'run' method of our java.lang.Thread.
WellKnownClasses::java_lang_Thread_run->InvokeVirtual<'V'>(self, receiver);
}
}
// Detach and delete self.
Runtime::Current()->GetThreadList()->Unregister(self, /* should_run_callbacks= */ true);
return nullptr;
}
Thread* Thread::FromManagedThread(Thread* self, ObjPtr<mirror::Object> thread_peer) {
ArtField* f = WellKnownClasses::java_lang_Thread_nativePeer;
Thread* result = reinterpret_cast64<Thread*>(f->GetLong(thread_peer)); // Check that if we have a result it is either suspended or we hold the thread_list_lock_ // to stop it from going away. if (kIsDebugBuild) {
MutexLock mu(self, *Locks::thread_suspend_count_lock_); if (result != nullptr && !result->IsSuspended()) {
Locks::thread_list_lock_->AssertHeld(self);
}
} return result;
}
static size_t FixStackSize(size_t stack_size) { // A stack size of zero means "use the default". if (stack_size == 0) {
stack_size = Runtime::Current()->GetDefaultStackSize();
}
// Dalvik used the bionic pthread default stack size for native threads, // so include that here to support apps that expect large native stacks.
stack_size += 1 * MB;
// Under sanitization, frames of the interpreter may become bigger, both for C code as // well as the ShadowFrame. Ensure a larger minimum size. Otherwise initialization // of all core classes cannot be done in all test circumstances. if (kMemoryToolIsAvailable) {
stack_size = std::max(2 * MB, stack_size);
}
// It's not possible to request a stack smaller than the system-defined PTHREAD_STACK_MIN. if (stack_size < PTHREAD_STACK_MIN) {
stack_size = PTHREAD_STACK_MIN;
}
if (Runtime::Current()->GetImplicitStackOverflowChecks()) { // If we are going to use implicit stack checks, allocate space for the protected // region at the bottom of the stack.
stack_size += Thread::kStackOverflowImplicitCheckSize +
GetStackOverflowReservedBytes(kRuntimeQuickCodeISA);
} else { // It's likely that callers are trying to ensure they have at least a certain amount of // stack space, so we should add our reserved space on top of what they requested, rather // than implicitly take it away from them.
stack_size += GetStackOverflowReservedBytes(kRuntimeQuickCodeISA);
}
// Some systems require the stack size to be a multiple of the system page size, so round up.
stack_size = RoundUp(stack_size, gPageSize);
// Install a protected region in the stack. This is used to trigger a SIGSEGV if a stack // overflow is detected. It is located right below the stack_begin_. template <StackType stack_type>
ATTRIBUTE_NO_SANITIZE_ADDRESS void Thread::InstallImplicitProtection() {
uint8_t* pregion = GetStackBegin<stack_type>() - GetStackOverflowProtectedSize(); // Page containing current top of stack.
uint8_t* stack_top = FindStackTop<stack_type>();
// It is possible that the native stack is not mapped into memory when initially trying to // protect it so don't treat the failure as fatal. bool fatal_on_error = false; if constexpr (stack_type == StackType::kSimulated) { // The simulated stack is mapped into memory upon creation therefore it is an error if we fail // to protect it.
fatal_on_error = true;
}
// Try to directly protect the stack.
VLOG(threads) << "installing stack protected region at " << std::hex << static_cast<void*>(pregion) << " to " << static_cast<void*>(pregion + GetStackOverflowProtectedSize() - 1); if (ProtectStack<stack_type>(fatal_on_error)) { // Tell the kernel that we won't be needing these pages any more. // NB. madvise will probably write zeroes into the memory (on linux it does).
size_t unwanted_size = reinterpret_cast<uintptr_t>(stack_top) - reinterpret_cast<uintptr_t>(pregion) - gPageSize;
madvise(pregion, unwanted_size, MADV_DONTNEED); return;
}
// There is a little complexity here that deserves a special mention. On some // architectures, the stack is created using a VM_GROWSDOWN flag // to prevent memory being allocated when it's not needed. This flag makes the // kernel only allocate memory for the stack by growing down in memory. Because we // want to put an mprotected region far away from that at the stack top, we need // to make sure the pages for the stack are mapped in before we call mprotect. // // The failed mprotect in UnprotectStack is an indication of a thread with VM_GROWSDOWN // with a non-mapped stack (usually only the main thread). // // We map in the stack by reading every page from the stack bottom (highest address) // to the stack top. (We then madvise this away.) This must be done by reading from the // current stack pointer downwards. // // Accesses too far below the current machine register corresponding to the stack pointer (e.g., // ESP on x86[-32], SP on ARM) might cause a SIGSEGV (at least on x86 with newer kernels). We // thus have to move the stack pointer. We do this portably by using a recursive function with a // large stack frame size.
// (Defensively) first remove the protection on the protected region as we'll want to read // and write it. Ignore errors.
UnprotectStack<stack_type>();
VLOG(threads) << "Need to map in stack for thread at " << std::hex << static_cast<void*>(pregion);
struct RecurseDownStack { // This function has an intentionally large stack size. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wframe-larger-than="
NO_INLINE
__attribute__((no_sanitize("memtag"))) staticvoid Touch(uintptr_t target) { volatile size_t zero = 0; // Use a large local volatile array to ensure a large frame size. Do not use anything close // to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but // there is no pragma support for this. // Note: for ASAN we need to shrink the array a bit, as there's other overhead.
constexpr size_t kAsanMultiplier = #ifdef ADDRESS_SANITIZER 2u; #else 1u; #endif // Ensure that the array size is known at compile time: this is necessary to prevent Clang // from generating a stack-probing loop with `-fstack-clash-protection`. Clang generates code // that assumes that there's at least one more page available below the start of the array, // which is not always true on the last recursive call in this function, and may result in // stackoverflow (observed on riscv64, see b/480856545 for details).
constexpr size_t space_size = kMinPageSize - (kAsanMultiplier * 256); // Keep space uninitialized as it can overflow the stack otherwise (should Clang actually // auto-initialize this local variable). volatilechar space[space_size] __attribute__((uninitialized));
[[maybe_unused]] char sink = space[zero]; // Remove tag from the pointer. Nop in non-hwasan builds.
uintptr_t addr = reinterpret_cast<uintptr_t>(
__hwasan_tag_pointer != nullptr ? __hwasan_tag_pointer(space, 0) : space); if (addr >= target + kMinPageSize) {
Touch(target);
}
zero *= 2; // Try to avoid tail recursion.
} #pragma GCC diagnostic pop
};
RecurseDownStack::Touch(reinterpret_cast<uintptr_t>(pregion));
VLOG(threads) << "(again) installing stack protected region at " << std::hex << static_cast<void*>(pregion) << " to " << static_cast<void*>(pregion + GetStackOverflowProtectedSize() - 1);
// Protect the bottom of the stack to prevent read/write to it.
ProtectStack<stack_type>(/* fatal_on_error= */ true);
// Tell the kernel that we won't be needing these pages any more. // NB. madvise will probably write zeroes into the memory (on linux it does).
size_t unwanted_size = reinterpret_cast<uintptr_t>(stack_top) - reinterpret_cast<uintptr_t>(pregion) - gPageSize;
madvise(pregion, unwanted_size, MADV_DONTNEED);
}
// Atomically start the birth of the thread ensuring the runtime isn't shutting down. bool thread_start_during_shutdown = false;
{
MutexLock mu(self, *Locks::runtime_shutdown_lock_); if (runtime->IsShuttingDownLocked()) {
thread_start_during_shutdown = true;
} else {
runtime->StartThreadBirth();
}
} if (thread_start_during_shutdown) {
ScopedLocalRef<jclass> error_class(env, env->FindClass("java/lang/InternalError"));
env->ThrowNew(error_class.get(), "Thread starting during runtime shutdown"); return;
}
Thread* child_thread = new Thread(is_daemon); // Use global JNI ref to hold peer live while child thread starts.
child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer);
stack_size = FixStackSize(stack_size);
// Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing // to assign it.
SetNativePeer(env, java_peer, child_thread);
// Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and // do not have a good way to report this on the child's side.
std::string error_msg;
std::unique_ptr<JNIEnvExt> child_jni_env_ext(
JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM(), &error_msg));
if (pthread_create_result == 0) { // pthread_create started the new thread. The child is now responsible for managing the // JNIEnvExt we created. // Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization // between the threads.
child_jni_env_ext.release(); // NOLINT pthreads API. return;
}
}
// Either JNIEnvExt::Create or pthread_create(3) failed, so clean up.
{
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
} // Manually delete the global reference since Thread::Init will not have been run. Make sure // nothing can observe both opeer and jpeer set at the same time.
child_thread->DeleteJPeer(env); delete child_thread;
child_thread = nullptr; // TODO: remove from thread group?
SetNativePeer(env, java_peer, nullptr);
{
std::string msg(child_jni_env_ext.get() == nullptr ?
StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) :
StringPrintf("pthread_create (%s stack) failed: %s",
PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
ScopedObjectAccess soa(env);
soa.Self()->ThrowOutOfMemoryError(msg.c_str());
}
}
// Check whether stack_addr is the base or end of the stack. // (On Mac OS 10.7, it's the end.) int stack_variable; if (stack_addr > &stack_variable) {
*stack_base = reinterpret_cast<uint8_t*>(stack_addr) - *stack_size;
} else {
*stack_base = stack_addr;
}
// This is wrong, but there doesn't seem to be a way to get the actual value on the Mac.
pthread_attr_t attributes;
CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); #else
pthread_attr_t attributes;
CHECK_PTHREAD_CALL(pthread_getattr_np, (thread, &attributes), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__);
#ifdefined(__GLIBC__) // If we're the main thread, check whether we were run with an unlimited stack. In that case, // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection // will be broken because we'll die long before we get close to 2GB. bool is_main_thread = (::art::GetTid() == static_cast<uint32_t>(getpid())); if (is_main_thread) {
rlimit stack_limit; if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) {
PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed";
} if (stack_limit.rlim_cur == RLIM_INFINITY) {
size_t old_stack_size = *stack_size;
// Use the kernel default limit as our size, and adjust the base to match.
*stack_size = 8 * MB;
*stack_base = reinterpret_cast<uint8_t*>(*stack_base) + (old_stack_size - *stack_size);
VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")"
<< " to " << PrettySize(*stack_size)
<< " with base " << *stack_base;
}
} #endif
#endif
}
bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) { // This function does all the initialization that must be run by the native thread it applies to. // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so // we can handshake with the corresponding native thread when it's ready.) Check this native // thread hasn't been through here already...
CHECK(Thread::Current() == nullptr);
// Set pthread_self ahead of pthread_setspecific, that makes Thread::Current function, this // avoids pthread_self ever being invalid when discovered from Thread::Current().
tlsPtr_.pthread_self = pthread_self();
CHECK(is_started_);
#ifdef ART_USE_SIMULATOR if (Runtime::IsSimulatorMode()) { // Use the same stack size for the simulator stack as the native stack.
CreateSimExecutor(read_stack_size);
uint8_t* stack_begin = GetSimExecutor()->GetStackBaseInternal() - read_stack_size; if (!InitStack<StackType::kSimulated>(stack_begin,
read_stack_size,
read_guard_size)) { returnfalse;
}
} #endif
// Run the action that is acting on the peer. if (!peer_action(self)) {
runtime->GetThreadList()->Unregister(self, should_run_callbacks); // Unregister deletes self, no need to do this here. return nullptr;
}
Trace::AllocateThreadBuffer(self); if (should_run_callbacks) {
ScopedObjectAccess soa(self);
runtime->GetRuntimeCallbacks()->ThreadStart(self);
}
return self;
}
Thread* Thread::Attach(constchar* thread_name, bool as_daemon,
jobject thread_group, bool create_peer, bool should_run_callbacks) { auto create_peer_action = [&](Thread* self) { // If we're the main thread, ClassLinker won't be created until after we're attached, // so that thread needs a two-stage attach. Regular threads don't need this hack. // In the compiler, all threads need this hack, because no-one's going to be getting // a native peer! if (create_peer) {
self->CreatePeer(thread_name, as_daemon, thread_group); if (self->IsExceptionPending()) { // We cannot keep the exception around, as we're deleting self. Try to be helpful and log // the failure but do not dump the exception details. If we fail to allocate the peer, we // usually also fail to allocate an exception object and throw a pre-allocated OOME without // any useful information. If we do manage to allocate the exception object, the memory // information in the message could have been collected too late and therefore misleading.
{
ScopedObjectAccess soa(self);
LOG(ERROR) << "Exception creating thread peer: "
<< ((thread_name != nullptr) ? thread_name : "<null>");
self->ClearException();
} returnfalse;
}
} else { // These aren't necessary, but they improve diagnostics for unit tests & command-line tools. if (thread_name != nullptr) {
self->SetCachedThreadName(thread_name);
::art::SetThreadName(thread_name);
} elseif (self->GetJniEnv()->IsCheckJniEnabled()) {
LOG(WARNING) << *Thread::Current() << " attached without supplying a name";
}
} returntrue;
}; return Attach(thread_name, as_daemon, create_peer_action, should_run_callbacks);
}
MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName())); if (peer_thread_name == nullptr) { // The Thread constructor should have set the Thread.name to a // non-null value. However, because we can run without code // available (in the compiler, in tests), we manually assign the // fields the constructor should have set. if (runtime->IsActiveTransaction()) {
InitPeer<true>(tlsPtr_.opeer,
as_daemon,
thr_group.Get(),
thread_name.Get(),
thread_priority,
thread_niceness);
} else {
InitPeer<false>(tlsPtr_.opeer,
as_daemon,
thr_group.Get(),
thread_name.Get(),
thread_priority,
thread_niceness);
}
peer_thread_name.Assign(GetThreadName());
} // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null. if (peer_thread_name != nullptr) {
SetThreadName(peer_thread_name->ToModifiedUtf8().c_str());
}
}
// We cannot call Thread.init, as it will recursively ask for currentThread.
// The Thread constructor should have set the Thread.name to a // non-null value. However, because we can run without code // available (in the compiler, in tests), we manually assign the // fields the constructor should have set. if (runtime->IsActiveTransaction()) {
InitPeer<true>(peer.Get(),
as_daemon,
thr_group.Get(),
thread_name.Get(),
kNormThreadPriority,
thread_niceness);
} else {
InitPeer<false>(peer.Get(),
as_daemon,
thr_group.Get(),
thread_name.Get(),
kNormThreadPriority,
thread_niceness);
}
return peer.Get();
}
template <bool kTransactionActive> void Thread::InitPeer(ObjPtr<mirror::Object> peer, bool as_daemon,
ObjPtr<mirror::Object> thread_group,
ObjPtr<mirror::String> thread_name,
jint thread_priority,
jint thread_niceness) {
WellKnownClasses::java_lang_Thread_daemon->SetBoolean<kTransactionActive>(peer, static_cast<uint8_t>(as_daemon ? 1u : 0u));
WellKnownClasses::java_lang_Thread_group->SetObject<kTransactionActive>(peer, thread_group);
WellKnownClasses::java_lang_Thread_name->SetObject<kTransactionActive>(peer, thread_name); // Setting niceness and priority here is partially redundant. But this is unlikely to be a hot // path.
DCHECK_GE(thread_priority, kMinThreadPriority);
DCHECK_LE(thread_priority, kMaxThreadPriority);
DCHECK_GE(thread_niceness, kMinNiceness);
DCHECK_LE(thread_niceness, kMaxNiceness);
WellKnownClasses::java_lang_Thread_priority->SetInt<kTransactionActive>(peer, thread_priority);
WellKnownClasses::java_lang_Thread_niceness->SetInt<kTransactionActive>(peer, thread_niceness);
}
void Thread::SetCachedThreadName(constchar* name) {
DCHECK(name != kThreadNameDuringStartup); constchar* old_name = tlsPtr_.name.exchange(name == nullptr ? nullptr : strdup(name)); if (old_name != nullptr && old_name != kThreadNameDuringStartup) { // Deallocate it, carefully. Note that the load has to be ordered wrt the store of the xchg. for (uint32_t i = 0; UNLIKELY(tls32_.num_name_readers.load(std::memory_order_seq_cst) != 0);
++i) { static constexpr uint32_t kNumSpins = 1000; // Ugly, but keeps us from having to do anything on the reader side. if (i > kNumSpins) {
usleep(500);
}
} // We saw the reader count drop to zero since we replaced the name; old one is now safe to // deallocate.
free(const_cast<char *>(old_name));
}
}
void Thread::SetThreadName(constchar* name) {
DCHECK(this == Thread::Current() || IsSuspended()); // O.w. `this` may disappear.
SetCachedThreadName(name); if (!IsStillStarting() || this == Thread::Current()) { // The RI is documented to do this only in the this == self case, which would avoid the // IsStillStarting() issue below. We instead use a best effort approach.
::art::SetThreadName(tlsPtr_.pthread_self /* Not necessarily current thread! */, name);
} // O.w. this will normally be set when we finish starting. We can rarely fail to set the // pthread name. See TODO in IsStillStarting().
Dbg::DdmSendThreadNotification(this, CHUNK_TYPE("THNM"));
}
// The minimum stack size we can cope with is the protected region size + stack overflow check // region size + some memory for normal stack usage. // // The protected region is located at the beginning (lowest address) of the stack region. // Therefore, it starts at a page-aligned address. Its size should be a multiple of page sizes. // Typically, it is one page in size, however this varies in some configurations. // // The overflow reserved bytes is size of the stack overflow check region, located right after // the protected region, so also starts at a page-aligned address. The size is discretionary. // Typically it is 8K, but this varies in some configurations. // // The rest of the stack memory is available for normal stack usage. It is located right after // the stack overflow check region, so its starting address isn't necessarily page-aligned. The // size of the region is discretionary, however should be chosen in a way that the overall stack // size is a multiple of page sizes. Historically, it is chosen to be at least 4 KB. // // On systems with 4K page size, typically the minimum stack size will be 4+8+4 = 16K. // The thread won't be able to do much with this stack: even the GC takes between 8K and 12K.
DCHECK_ALIGNED_PARAM(static_cast<size_t>(GetStackOverflowProtectedSize()), static_cast<int32_t>(gPageSize));
size_t min_stack = GetStackOverflowProtectedSize() +
RoundUp(GetStackOverflowReservedBytes(kRuntimeQuickCodeISA) + 4 * KB, gPageSize); if (read_stack_size <= min_stack) { // Note, as we know the stack is small, avoid operations that could use a lot of stack.
LogHelper::LogLineLowStack(__PRETTY_FUNCTION__,
__LINE__,
::android::base::ERROR, "Attempt to attach a thread with a too-small stack"); returnfalse;
}
// This is included in the SIGQUIT output, but it's useful here for thread debugging.
VLOG(threads) << StringPrintf("%s stack is at %p (%s with %s guard)",
stack_type_str,
read_stack_base,
PrettySize(read_stack_size).c_str(),
PrettySize(read_guard_size).c_str());
// Set stack_end_ to the bottom of the stack saving space of stack overflows
// Install the protected region if we are doing implicit overflow checks. if (implicit_stack_check) { // The thread might have protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it.
bool Thread::TrySetMountedVirtualThreadData(MountedVirtualThreadData* e, bool spin) {
CHECK(kIsVirtualThreadEnabled);
DCHECK(this == Thread::Current());
DCHECK_EQ(GetMountedVirtualThreadData(), nullptr);
DCHECK_NE(e, nullptr);
DCHECK_EQ(e->carrier_thread_id_, GetThreadId()) << "The carrier thread must be self"; while (true) {
{
MutexLock mu(this, *Locks::thread_list_lock_); // The virtual thread is suspended by lock inflation if the count isn't 0.
ThreadList* thread_list = Runtime::Current()->GetThreadList();
uint32_t suspension_count = thread_list->GetVirtualThreadSuspendCount(e->virtual_thread_id_); if (suspension_count == 0) { if (kIsDebugBuild) {
uint32_t another_carrier_id = thread_list->GetCarrierThreadIdByVirtualThreadId(
e->virtual_thread_id_); if (another_carrier_id != ThreadList::kInvalidThreadId) { // Release the thread_list_lock_ first before the crash to allow ART dump all threads.
Locks::thread_list_lock_->Unlock(this);
LOG(FATAL) << ("A virtual thread is being mounted by a second carrier thread! ")
<< "virtual thread id : " << e->virtual_thread_id_
<< ", this carrier thread id : " << e->carrier_thread_id_
<< ", another carrier thread id : " << another_carrier_id;
UNREACHABLE();
}
}
thread_list->AddMountedVirtualThread(e);
SetMountedVirtualThreadData(e); returntrue;
}
}
if (!spin) { returnfalse;
}
// Lock inflation for locks held by this unmounted virtual thread doesn't suspend the carrier // thread. However, we may just do a suspend check on the carrier thread for the other purposes, // e.g. GC, while spinning.
{
ScopedThreadSuspension(this, ThreadState::kWaitingForLockInflation); // NOLINT
usleep(kVirtualThreadSuspendSleepUs);
}
}
}
bool Thread::TryClearMountedVirtualThreadData(bool spin) {
CHECK(kIsVirtualThreadEnabled);
DCHECK(this == Thread::Current());
MountedVirtualThreadData* e = GetMountedVirtualThreadData(); if (e == nullptr) {
DCHECK_NE(e, nullptr); returnfalse;
}
DCHECK_EQ(e->carrier_thread_id_, GetThreadId()); while (true) {
{
MutexLock mu(this, *Locks::thread_list_lock_); // The virtual thread is suspended by lock inflation if the count isn't 0.
ThreadList* thread_list = Runtime::Current()->GetThreadList();
e = GetMountedVirtualThreadData();
uint32_t suspension_count = thread_list->GetVirtualThreadSuspendCount(e->virtual_thread_id_); if (suspension_count == 0) {
thread_list->RemoveMountedVirtualThread(e);
SetMountedVirtualThreadData(nullptr); returntrue;
}
}
void Thread::ShortDump(std::ostream& os) const {
os << "Thread["; if (GetThreadId() != 0) { // If we're in kStarting, we won't have a thin lock id or tid yet.
os << GetThreadId()
<< ",tid=" << GetTid() << ',';
}
tls32_.num_name_readers.fetch_add(1, std::memory_order_seq_cst); constchar* name = tlsPtr_.name.load();
os << GetState()
<< ",Thread*=" << this
<< ",peer=" << tlsPtr_.opeer
<< ",\"" << (name == nullptr ? "null" : name) << "\""
<< "]";
tls32_.num_name_readers.fetch_sub(1/* at least memory_order_release */);
}
ObjPtr<mirror::String> Thread::GetThreadName() const { if (tlsPtr_.opeer == nullptr) { return nullptr;
}
ObjPtr<mirror::Object> name = WellKnownClasses::java_lang_Thread_name->GetObject(tlsPtr_.opeer); return name == nullptr ? nullptr : name->AsString();
}
void Thread::GetThreadName(std::string& name) const {
tls32_.num_name_readers.fetch_add(1, std::memory_order_seq_cst); // The store part of the increment has to be ordered with respect to the following load. constchar* c_name = tlsPtr_.name.load(std::memory_order_seq_cst);
name.assign(c_name == nullptr ? "<no name>" : c_name);
tls32_.num_name_readers.fetch_sub(1/* at least memory_order_release */);
}
// Attempt to rectify locks so that we dump thread list with required locks before exiting. void Thread::UnsafeLogFatalForSuspendCount(Thread* self, Thread* thread) NO_THREAD_SAFETY_ANALYSIS {
LOG(ERROR) << *thread << " suspend count already zero.";
Locks::thread_suspend_count_lock_->Unlock(self); if (!Locks::mutator_lock_->IsSharedHeld(self)) {
Locks::mutator_lock_->SharedTryLock(self); if (!Locks::mutator_lock_->IsSharedHeld(self)) {
LOG(WARNING) << "Dumping thread list without holding mutator_lock_";
}
} if (!Locks::thread_list_lock_->IsExclusiveHeld(self)) {
Locks::thread_list_lock_->TryLock(self); if (!Locks::thread_list_lock_->IsExclusiveHeld(self)) {
LOG(WARNING) << "Dumping thread list without holding thread_list_lock_";
}
}
std::ostringstream ss;
Runtime::Current()->GetThreadList()->Dump(ss);
LOG(FATAL) << ss.str();
UNREACHABLE();
}
bool Thread::PassActiveSuspendBarriers() {
DCHECK_EQ(this, Thread::Current());
DCHECK_NE(GetState(), ThreadState::kRunnable); // Grab the suspend_count lock and copy the current set of barriers. Then clear the list and the // flag. The IncrementSuspendCount function requires the lock so we prevent a race between setting // the kActiveSuspendBarrier flag and clearing it. // TODO: Consider doing this without the temporary vector. That code will be a bit // tricky, since the WrappedSuspend1Barrier may disappear once the barrier is decremented.
std::vector<AtomicInteger*> pass_barriers{};
{
MutexLock mu(this, *Locks::thread_suspend_count_lock_); if (!ReadFlag(ThreadFlag::kActiveSuspendBarrier, std::memory_order_relaxed)) { // Quick exit test: The barriers have already been claimed - this is possible as there may // be a race to claim and it doesn't matter who wins. All of the callers of this function // (except SuspendAllInternal) will first test the kActiveSuspendBarrier flag without the // lock. Here we double-check whether the barrier has been passed with the // suspend_count_lock_. returnfalse;
} if (tlsPtr_.active_suspendall_barrier != nullptr) { // We have at most one active active_suspendall_barrier. See thread.h comment.
pass_barriers.push_back(tlsPtr_.active_suspendall_barrier);
tlsPtr_.active_suspendall_barrier = nullptr;
} for (WrappedSuspend1Barrier* w = tlsPtr_.active_suspend1_barriers; w != nullptr; w = w->next_) {
CHECK_EQ(w->magic_, WrappedSuspend1Barrier::kMagic)
<< "first = " << tlsPtr_.active_suspend1_barriers << " current = " << w
<< " next = " << w->next_;
pass_barriers.push_back(&(w->barrier_));
}
tlsPtr_.active_suspend1_barriers = nullptr;
AtomicClearFlag(ThreadFlag::kActiveSuspendBarrier);
CHECK_GT(pass_barriers.size(), 0U); // Since kActiveSuspendBarrier was set. // Decrement suspend barrier(s) while we still hold the lock, since SuspendThread may // remove and deallocate suspend barriers while holding suspend_count_lock_ . // There will typically only be a single barrier to pass here. for (AtomicInteger*& barrier : pass_barriers) {
int32_t old_val = barrier->fetch_sub(1, std::memory_order_release);
CHECK_GT(old_val, 0) << "Unexpected value for PassActiveSuspendBarriers(): " << old_val; if (old_val != 1) { // We're done with it.
barrier = nullptr;
}
}
} // Finally do futex_wakes after releasing the lock. for (AtomicInteger* barrier : pass_barriers) { #if ART_USE_FUTEXES if (barrier != nullptr) {
futex(barrier->Address(), FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
} #endif
} returntrue;
}
void Thread::RunCheckpointFunction() {
DCHECK_EQ(Thread::Current(), this);
CHECK(!GetStateAndFlags(std::memory_order_relaxed).IsAnyOfFlagsSet(FlipFunctionFlags())); // Grab the suspend_count lock, get the next checkpoint and update all the checkpoint fields. If // there are no more checkpoints we will also clear the kCheckpointRequest flag.
Closure* checkpoint;
{
MutexLock mu(this, *Locks::thread_suspend_count_lock_);
checkpoint = tlsPtr_.checkpoint_function; if (!checkpoint_overflow_.empty()) { // Overflow list not empty, copy the first one out and continue.
tlsPtr_.checkpoint_function = checkpoint_overflow_.front();
checkpoint_overflow_.pop_front();
} else { // No overflow checkpoints. Clear the kCheckpointRequest flag
tlsPtr_.checkpoint_function = nullptr;
AtomicClearFlag(ThreadFlag::kCheckpointRequest);
}
} // Outside the lock, run the checkpoint function.
ScopedTrace trace("Run checkpoint function");
CHECK(checkpoint != nullptr) << "Checkpoint flag set without pending checkpoint";
checkpoint->Run(this);
}
void Thread::RunEmptyCheckpoint() { // Note: Empty checkpoint does not access the thread's stack, // so we do not need to check for the flip function.
DCHECK_EQ(Thread::Current(), this); // See mutator_gc_coord.md and b/382722942 for memory ordering discussion.
AtomicClearFlag(ThreadFlag::kEmptyCheckpointRequest, std::memory_order_release);
Runtime::Current()->GetThreadList()->EmptyCheckpointBarrier()->Pass(this);
}
bool Thread::RequestCheckpoint(Closure* function) { bool success; do {
StateAndFlags old_state_and_flags = GetStateAndFlags(std::memory_order_relaxed); if (old_state_and_flags.GetState() != ThreadState::kRunnable) { returnfalse; // Fail, thread is suspended and so can't run a checkpoint.
}
StateAndFlags new_state_and_flags = old_state_and_flags;
new_state_and_flags.SetFlag(ThreadFlag::kCheckpointRequest);
success = tls32_.state_and_flags.CompareAndSetWeakSequentiallyConsistent(
old_state_and_flags.GetValue(), new_state_and_flags.GetValue());
} while (!success); // Succeeded setting checkpoint flag, now insert the actual checkpoint. if (tlsPtr_.checkpoint_function == nullptr) {
tlsPtr_.checkpoint_function = function;
} else {
checkpoint_overflow_.push_back(function);
}
DCHECK(ReadFlag(ThreadFlag::kCheckpointRequest, std::memory_order_relaxed));
TriggerSuspend(); returntrue;
}
bool Thread::RequestEmptyCheckpoint() {
StateAndFlags old_state_and_flags = GetStateAndFlags(std::memory_order_relaxed); if (old_state_and_flags.GetState() != ThreadState::kRunnable) { // If it's not runnable, we don't need to do anything because it won't be in the middle of a // heap access (eg. the read barrier). returnfalse;
}
// We must be runnable to request a checkpoint.
DCHECK_EQ(old_state_and_flags.GetState(), ThreadState::kRunnable);
StateAndFlags new_state_and_flags = old_state_and_flags;
new_state_and_flags.SetFlag(ThreadFlag::kEmptyCheckpointRequest); bool success = tls32_.state_and_flags.CompareAndSetStrongSequentiallyConsistent(
old_state_and_flags.GetValue(), new_state_and_flags.GetValue()); if (success) {
TriggerSuspend();
} return success;
}
class BarrierClosure : public Closure { public: explicit BarrierClosure(Closure* wrapped) : wrapped_(wrapped), barrier_(0) {}
// RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. bool Thread::RequestSynchronousCheckpoint(Closure* function, ThreadState wait_state) {
Thread* self = Thread::Current(); if (this == self) {
Locks::thread_list_lock_->AssertExclusiveHeld(self); // Unlock the tll before running so that the state is the same regardless of thread.
Locks::thread_list_lock_->ExclusiveUnlock(self); // Asked to run on this thread. Just run.
function->Run(this); returntrue;
}
// The current thread is not this thread.
VerifyState();
Locks::thread_list_lock_->AssertExclusiveHeld(self); // If target "this" thread is runnable, try to schedule a checkpoint. Do some gymnastics to not // hold the suspend-count lock for too long. if (GetState() == ThreadState::kRunnable) {
BarrierClosure barrier_closure(function); bool installed = false;
{
MutexLock mu(self, *Locks::thread_suspend_count_lock_);
installed = RequestCheckpoint(&barrier_closure);
} if (installed) { // Relinquish the thread-list lock. We should not wait holding any locks. We cannot // reacquire it since we don't know if 'this' hasn't been deleted yet.
Locks::thread_list_lock_->ExclusiveUnlock(self);
ScopedThreadStateChange sts(self, wait_state); // Wait state can be kRunnable, in which case, for lock ordering purposes, it's as if we ran // the closure ourselves. This means that the target thread should not acquire a pre-mutator // lock without running the checkpoint, and the closure should not acquire a pre-mutator // lock or suspend.
barrier_closure.Wait(self, wait_state); returntrue;
} // No longer runnable. Fall-through.
}
// Target "this" thread was not runnable. Suspend it, hopefully redundantly, // but it might have become runnable in the meantime. // Although this is a thread suspension, the target thread only blocks while we run the // checkpoint, which is presumed to terminate quickly even if other threads are blocked. // Note: IncrementSuspendCount also expects the thread_list_lock to be held unless this == self.
WrappedSuspend1Barrier wrapped_barrier{};
{ bool is_suspended = false;
{
MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_); // If wait_state is kRunnable, function may not suspend. We thus never block because // we ourselves are being asked to suspend. if (UNLIKELY(wait_state != ThreadState::kRunnable && self->GetSuspendCount() != 0)) { // We are being asked to suspend while we are suspending another thread that may be // responsible for our suspension. This is likely to result in deadlock if we each // block on the suspension request. Instead we wait for the situation to change.
ThreadExitFlag target_status;
NotifyOnThreadExit(&target_status); for (int iter_count = 1; self->GetSuspendCount() != 0; ++iter_count) {
Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
Locks::thread_list_lock_->ExclusiveUnlock(self);
{
ScopedThreadStateChange sts(self, wait_state);
usleep(ThreadList::kThreadSuspendSleepUs);
}
CHECK_LT(iter_count, ThreadList::kMaxSuspendRetries);
Locks::thread_list_lock_->ExclusiveLock(self); if (target_status.HasExited()) {
Locks::thread_list_lock_->ExclusiveUnlock(self);
DCheckUnregisteredEverywhere(&target_status, &target_status); returnfalse;
}
Locks::thread_suspend_count_lock_->ExclusiveLock(self);
}
UnregisterThreadExitFlag(&target_status);
}
IncrementSuspendCount(self, nullptr, &wrapped_barrier, SuspendReason::kInternal);
VerifyState();
DCHECK_GT(GetSuspendCount(), 0); if (wait_state != ThreadState::kRunnable) {
DCHECK_EQ(self->GetSuspendCount(), 0);
} // Since we've incremented the suspend count, "this" thread can no longer disappear.
Locks::thread_list_lock_->ExclusiveUnlock(self); if (IsSuspended()) { // See the discussion in mutator_gc_coord.md and SuspendAllInternal for the race here.
RemoveFirstSuspend1Barrier(&wrapped_barrier); if (!HasActiveSuspendBarrier()) {
AtomicClearFlag(ThreadFlag::kActiveSuspendBarrier);
}
is_suspended = true;
}
} if (!is_suspended) { // This waits while holding the mutator lock. Effectively `self` becomes // impossible to suspend until `this` responds to the suspend request. // Arguably that's not making anything qualitatively worse. auto opt_fail_string = Runtime::Current()->GetThreadList()->WaitForSuspendBarrier(
self, &wrapped_barrier.barrier_); if (opt_fail_string.has_value()) {
AbortInThis("Synchronous checkpoint failed to suspend: " + opt_fail_string.value());
}
}
// Ensure that the flip function for this thread, if pending, is finished *before* // the checkpoint function is run. Otherwise, we may end up with both `to' and 'from' // space references on the stack, confusing the GC's thread-flip logic. The caller is // runnable so can't have a pending flip function.
DCHECK_EQ(self->GetState(), ThreadState::kRunnable);
DCHECK(IsSuspended());
DCHECK(!self->GetStateAndFlags(std::memory_order_relaxed).IsAnyOfFlagsSet(FlipFunctionFlags()));
EnsureFlipFunctionStarted(self, this); // Since we're runnable, and kPendingFlipFunction is set with all threads suspended, it // cannot be set again here. Thus kRunningFlipFunction is either already set after the // EnsureFlipFunctionStarted call, or will not be set before we call Run(). // See mutator_gc_coord.md for a discussion of memory ordering for thread flags. if (ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_acquire)) {
WaitForFlipFunction(self);
}
function->Run(this);
}
void Thread::SetFlipFunction(Closure* function) { // This is called with all threads suspended, except for the calling thread.
DCHECK(IsSuspended() || Thread::Current() == this);
DCHECK(function != nullptr);
DCHECK(GetFlipFunction() == nullptr);
tlsPtr_.flip_function.store(function, std::memory_order_relaxed);
DCHECK(!GetStateAndFlags(std::memory_order_relaxed).IsAnyOfFlagsSet(FlipFunctionFlags()));
AtomicSetFlag(ThreadFlag::kPendingFlipFunction, std::memory_order_release);
}
bool Thread::EnsureFlipFunctionStarted(Thread* self,
Thread* target,
StateAndFlags old_state_and_flags,
ThreadExitFlag* tef, bool* finished) { // Note: If tef is non-null, *target may have been destroyed. We have to be careful about // accessing it. That is the reason this is static and not a member function.
DCHECK(self == Current()); bool check_exited = (tef != nullptr); // Check that the thread can't unexpectedly exit while we are running.
DCHECK(self == target || check_exited ||
target->ReadFlag(ThreadFlag::kSuspendRequest, std::memory_order_relaxed) ||
Locks::thread_list_lock_->IsExclusiveHeld(self))
<< *target; bool become_runnable; auto maybe_release = [=]() NO_THREAD_SAFETY_ANALYSIS /* conditionally unlocks */ { if (check_exited) {
Locks::thread_list_lock_->Unlock(self);
}
}; auto set_finished = [=](bool value) { if (finished != nullptr) {
*finished = value;
}
};
if (check_exited) {
Locks::thread_list_lock_->Lock(self); if (tef->HasExited()) {
Locks::thread_list_lock_->Unlock(self);
set_finished(true); returnfalse;
}
}
target->VerifyState(); if (old_state_and_flags.GetValue() == 0) {
become_runnable = false; // Memory_order_relaxed is OK here, since we re-check with memory_order_acquire below before // acting on a pending flip function.
old_state_and_flags = target->GetStateAndFlags(std::memory_order_relaxed);
} else {
become_runnable = true;
DCHECK(!check_exited);
DCHECK(target == self);
DCHECK(old_state_and_flags.IsFlagSet(ThreadFlag::kPendingFlipFunction));
DCHECK(!old_state_and_flags.IsFlagSet(ThreadFlag::kSuspendRequest));
} while (true) {
DCHECK(!check_exited || (Locks::thread_list_lock_->IsExclusiveHeld(self) && !tef->HasExited())); if (!old_state_and_flags.IsFlagSet(ThreadFlag::kPendingFlipFunction)) { // Re-read kRunningFlipFunction flag with acquire ordering to ensure that if we claim // flip function has run then its execution happened-before our return. bool running_flip =
target->ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_acquire);
maybe_release();
set_finished(!running_flip); returnfalse;
}
DCHECK(!old_state_and_flags.IsFlagSet(ThreadFlag::kRunningFlipFunction));
StateAndFlags new_state_and_flags =
old_state_and_flags.WithFlag(ThreadFlag::kRunningFlipFunction)
.WithoutFlag(ThreadFlag::kPendingFlipFunction); if (become_runnable) {
DCHECK_EQ(self, target);
DCHECK_NE(self->GetState(), ThreadState::kRunnable);
new_state_and_flags = new_state_and_flags.WithState(ThreadState::kRunnable);
} if (target->tls32_.state_and_flags.CompareAndSetWeakAcquire(old_state_and_flags.GetValue(),
new_state_and_flags.GetValue())) { if (become_runnable) {
self->GetMutatorLock()->TransitionFromSuspendedToRunnable(self);
}
art::Locks::mutator_lock_->AssertSharedHeld(self);
maybe_release(); // Thread will not go away while kRunningFlipFunction is set.
target->RunFlipFunction(self); // At this point, no flip function flags should be set. It's unsafe to DCHECK that, since // the thread may now have exited.
set_finished(true); return become_runnable;
} if (become_runnable) {
DCHECK(!check_exited); // We didn't acquire thread_list_lock_ . // Let caller retry. returnfalse;
} // Again, we re-read with memory_order_acquire before acting on the flags.
old_state_and_flags = target->GetStateAndFlags(std::memory_order_relaxed);
} // Unreachable.
}
void Thread::RunFlipFunction(Thread* self) { // This function is called either by the thread running `ThreadList::FlipThreadRoots()` or when // a thread becomes runnable, after we've successfully set the kRunningFlipFunction ThreadFlag.
DCHECK(ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_relaxed));
Closure* flip_function = GetFlipFunction();
tlsPtr_.flip_function.store(nullptr, std::memory_order_relaxed);
DCHECK(flip_function != nullptr);
VerifyState();
flip_function->Run(this);
DCHECK(!ReadFlag(ThreadFlag::kPendingFlipFunction, std::memory_order_relaxed));
VerifyState();
AtomicClearFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_release); // From here on this thread may go away, and it is no longer safe to access.
// Notify all threads that are waiting for completion. // TODO: Should we create a separate mutex and condition variable instead // of piggy-backing on the `thread_suspend_count_lock_` and `resume_cond_`?
MutexLock mu(self, *Locks::thread_suspend_count_lock_);
resume_cond_->Broadcast(self);
}
void Thread::WaitForFlipFunction(Thread* self) const { // Another thread is running the flip function. Wait for it to complete. // Check the flag while holding the mutex so that we do not miss the broadcast. // Repeat the check after waiting to guard against spurious wakeups (and because // we share the `thread_suspend_count_lock_` and `resume_cond_` with other code). // Check that the thread can't unexpectedly exit while we are running.
DCHECK(self == this || ReadFlag(ThreadFlag::kSuspendRequest, std::memory_order_relaxed) ||
Locks::thread_list_lock_->IsExclusiveHeld(self));
MutexLock mu(self, *Locks::thread_suspend_count_lock_); while (true) { // See mutator_gc_coord.md for a discussion of memory ordering for thread flags. if (!ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_acquire)) { return;
} // We sometimes hold mutator lock here. OK since the flip function must complete quickly.
resume_cond_->WaitHoldingLocks(self);
}
}
void Thread::WaitForFlipFunctionTestingExited(Thread* self, ThreadExitFlag* tef) {
Locks::thread_list_lock_->Lock(self); if (tef->HasExited()) {
Locks::thread_list_lock_->Unlock(self); return;
} // We need to hold suspend_count_lock_ to avoid missed wakeups when the flip function finishes. // We need to hold thread_list_lock_ because the tef test result is only valid while we hold the // lock, and once kRunningFlipFunction is no longer set, "this" may be deallocated. Hence the // complicated locking dance.
MutexLock mu(self, *Locks::thread_suspend_count_lock_); while (true) { // See mutator_gc_coord.md for a discussion of memory ordering for thread flags. bool running_flip = ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_acquire);
Locks::thread_list_lock_->Unlock(self); // So we can wait or return. if (!running_flip) { return;
}
resume_cond_->WaitHoldingLocks(self);
Locks::thread_suspend_count_lock_->Unlock(self); // To re-lock thread_list_lock.
Locks::thread_list_lock_->Lock(self);
Locks::thread_suspend_count_lock_->Lock(self); if (tef->HasExited()) {
Locks::thread_list_lock_->Unlock(self); return;
}
}
}
void Thread::FullSuspendCheck(bool implicit) {
ScopedTrace trace(__FUNCTION__);
DCHECK(!ReadFlag(ThreadFlag::kSuspensionImmune, std::memory_order_relaxed));
DCHECK(this == Thread::Current());
VLOG(threads) << this << " self-suspending"; // Make thread appear suspended to other threads, release mutator_lock_. // Transition to suspended and back to runnable, re-acquire share on mutator_lock_.
ScopedThreadSuspension(this, ThreadState::kSuspended); // NOLINT if (implicit) { // For implicit suspend check we want to `madvise()` away // the alternate signal stack to avoid wasting memory.
MadviseAwayAlternateSignalStack();
}
VLOG(threads) << this << " self-reviving";
}
static std::string GetSchedulerGroupName(pid_t tid) { // /proc/<pid>/cgroup looks like this: // 2:devices:/ // 1:cpuacct,cpu:/ // We want the third field from the line whose second field contains the "cpu" token.
std::string cgroup_file; if (!android::base::ReadFileToString(StringPrintf("/proc/self/task/%d/cgroup", tid),
&cgroup_file)) { return"";
}
std::vector<std::string> cgroup_lines;
Split(cgroup_file, '\n', &cgroup_lines); for (size_t i = 0; i < cgroup_lines.size(); ++i) {
std::vector<std::string> cgroup_fields;
Split(cgroup_lines[i], ':', &cgroup_fields);
std::vector<std::string> cgroups;
Split(cgroup_fields[1], ',', &cgroups); for (size_t j = 0; j < cgroups.size(); ++j) { if (cgroups[j] == "cpu") { return cgroup_fields[2].substr(1); // Skip the leading slash.
}
}
} return"";
}
// Don't do this if we are aborting since the GC may have all the threads suspended. This will // cause ScopedObjectAccessUnchecked to deadlock. if (gAborting == 0 && self != nullptr && thread != nullptr) {
ScopedObjectAccessUnchecked soa(self); if (thread->GetStateAndFlags(std::memory_order_relaxed).IsAnyOfFlagsSet(FlipFunctionFlags())) {
is_flipping = true;
priority = thread->GetNativePriority(); // See below for disclaimer.
} elseif ((peer = thread->tlsPtr_.opeer) != nullptr) { // Flip is initiated with all threads suspended, and we're not suspended. // So no flip can be requested while we hold the mutator lock.
priority = NicenessToPriority(WellKnownClasses::java_lang_Thread_niceness->GetInt(peer));
is_daemon = WellKnownClasses::java_lang_Thread_daemon->GetBoolean(peer);
if (thread_group != nullptr) {
ObjPtr<mirror::Object> group_name_object =
WellKnownClasses::java_lang_ThreadGroup_name->GetObject(thread_group);
group_name = (group_name_object != nullptr)
? group_name_object->AsString()->ToModifiedUtf8()
: "<null>";
}
} else {
priority = thread->GetNativePriority(); // See below for disclaimer.
}
} elseif (thread != nullptr) { // This produces niceness translated to a Java priority, which may not match the cached Java // priority, and may have no relation to real scheduling priority if this was bumped to // real-time priority. Except in the palette-fake case, this is always what it did, so change // seems risky.
priority = thread->GetNativePriority();
} else {
errno = 0; int niceness = getpriority(PRIO_PROCESS, static_cast<id_t>(tid)); if (niceness == -1 && errno != 0) {
priority = -1; // A recognizably bogus value.
} else {
priority = NicenessToPriority(niceness);
}
}
os << " at " << m->PrettyMethod(false); if (m->IsNative()) {
os << "(Native method)";
} else { constchar* source_file(m->GetDeclaringClassSourceFile()); if (line_number == -1) { // If we failed to map to a line number, use // the dex pc as the line number and leave source file null
source_file = nullptr;
line_number = static_cast<int32_t>(dex_pc);
}
os << "(" << (source_file != nullptr ? source_file : "unavailable")
<< ":" << line_number << ")";
}
os << "\n"; // Go and visit locks. return VisitMethodResult::kContinueMethod;
}
VisitMethodResult EndMethod([[maybe_unused]] ArtMethod* m) override { return VisitMethodResult::kContinueMethod;
}
void PrintObject(ObjPtr<mirror::Object> obj, constchar* msg,
uint32_t owner_tid) REQUIRES_SHARED(Locks::mutator_lock_) { if (obj == nullptr) {
os << msg << "an unknown object";
} else { const std::string pretty_type(obj->PrettyTypeOf()); // It's often unsafe to allow lock inflation here. We may be the only runnable thread, or // this may be called from a checkpoint. We get the hashcode on a best effort basis. static constexpr int kNumRetries = 3; static constexpr int kSleepMicros = 10;
int32_t hash_code; for (int i = 0;; ++i) {
hash_code = obj->IdentityHashCodeNoInflation(); if (hash_code != 0 || i == kNumRetries) { break;
}
usleep(kSleepMicros);
} if (hash_code == 0) {
os << msg
<< StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", reinterpret_cast<intptr_t>(obj.Ptr()),
pretty_type.c_str());
} else { // - waiting on <0x608c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
os << msg << StringPrintf("<0x%08x> (a %s)", hash_code, pretty_type.c_str());
}
} if (owner_tid != ThreadList::kInvalidThreadId) {
os << " held by thread " << owner_tid;
}
os << "\n";
}
staticbool ShouldShowNativeStack(const Thread* thread)
REQUIRES_SHARED(Locks::mutator_lock_) {
ThreadState state = thread->GetState();
// In native code somewhere in the VM (one of the kWaitingFor* states)? That's interesting. if (state > ThreadState::kWaiting && state < ThreadState::kStarting) { returntrue;
}
// In an Object.wait variant or Thread.sleep? That's not interesting. if (state == ThreadState::kTimedWaiting ||
state == ThreadState::kSleeping ||
state == ThreadState::kWaiting) { returnfalse;
}
// Threads with no managed stack frames should be shown. if (!thread->HasManagedStack()) { returntrue;
}
// In some other native method? That's interesting. // We don't just check kNative because native methods will be in state kSuspended if they're // calling back into the VM, or kBlocked if they're blocked on a monitor, or one of the // thread-startup states if it's early enough in their life cycle (http://b/7432159).
ArtMethod* current_method = thread->GetCurrentMethod(nullptr); return current_method != nullptr && current_method->IsNative();
}
Thread::DumpOrder Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_locks) const { // Dumping the Java stack involves the verifier for locks. The verifier operates under the // assumption that there is no exception pending on entry. Thus, stash any pending exception. // Thread::Current() instead of this in case a thread is dumping the stack of another suspended // thread.
ScopedExceptionStorage ses(Thread::Current());
Thread::DumpOrder Thread::DumpStack(std::ostream& os,
unwindstack::AndroidLocalUnwinder& unwinder, bool dump_native_stack, bool force_dump_stack) const { // TODO: we call this code when dying but may not have suspended the thread ourself. The // IsSuspended check is therefore racy with the use for dumping (normally we inhibit // the race with the thread_suspend_count_lock_). bool dump_for_abort = (gAborting > 0); bool safe_to_dump = (this == Thread::Current() || IsSuspended()); if (!kIsDebugBuild) { // We always want to dump the stack for an abort, however, there is no point dumping another // thread's stack in debug builds where we'll hit the not suspended check in the stack walk.
safe_to_dump = (safe_to_dump || dump_for_abort);
}
DumpOrder dump_order = DumpOrder::kDefault; if (safe_to_dump || force_dump_stack) {
uint64_t nanotime = NanoTime(); // If we're currently in native code, dump that stack before dumping the managed stack. if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) {
DumpNativeStack(os, unwinder, GetTid(), " native: ");
}
dump_order = DumpJavaStack(os, /*check_suspended=*/ !force_dump_stack, /*dump_locks=*/ !force_dump_stack);
Runtime* runtime = Runtime::Current();
std::optional<uint64_t> start = runtime != nullptr ? runtime->SigQuitNanoTime() : std::nullopt; if (start.has_value()) {
os << "DumpLatencyMs: " << static_cast<float>(nanotime - start.value()) / 1000000.0 << "\n";
}
} else {
os << "Not able to dump stack of thread that isn't suspended";
} return dump_order;
}
void Thread::ThreadExitCallback(void* arg) {
Thread* self = reinterpret_cast<Thread*>(arg); if (self->tls32_.thread_exit_check_count == 0) {
LOG(WARNING) << "Native thread exiting without having called DetachCurrentThread (maybe it's " "going to use a pthread_key_create destructor?): " << *self;
CHECK(is_started_); #ifdef __BIONIC__
__get_tls()[TLS_SLOT_ART_THREAD_SELF] = self; #else
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, self), "reattach self");
Thread::self_tls_ = self; #endif
self->tls32_.thread_exit_check_count = 1;
} else {
LOG(FATAL) << "Native thread exited without calling DetachCurrentThread: " << *self;
}
}
void Thread::Startup() {
CHECK(!is_started_);
is_started_ = true;
{ // MutexLock to keep annotalysis happy. // // Note we use null for the thread because Thread::Current can // return garbage since (is_started_ == true) and // Thread::pthread_key_self_ is not yet initialized. // This was seen on glibc.
MutexLock mu(nullptr, *Locks::thread_suspend_count_lock_);
resume_cond_ = new ConditionVariable("Thread resumption condition variable",
*Locks::thread_suspend_count_lock_);
}
// Allocate a TLS slot.
CHECK_PTHREAD_CALL(pthread_key_create, (&Thread::pthread_key_self_, Thread::ThreadExitCallback), "self key");
// Double-check the TLS slot allocation. if (pthread_getspecific(pthread_key_self_) != nullptr) {
LOG(FATAL) << "Newly-created pthread TLS slot is not nullptr";
} #ifndef __BIONIC__
CHECK(Thread::self_tls_ == nullptr); #endif
}
// Finish attaching the main thread.
ScopedObjectAccess soa(Thread::Current());
soa.Self()->CreatePeer("main", false, runtime->GetMainThreadGroup());
soa.Self()->AssertNoPendingException();
runtime->RunRootClinits(soa.Self());
// The thread counts as started from now on. We need to add it to the ThreadGroup. For regular // threads, this is done in Thread.start() on the Java side.
soa.Self()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup());
soa.Self()->AssertNoPendingException();
}
static_assert((sizeof(Thread) % 4) == 0U, "art::Thread has a size which is not a multiple of 4.");
DCHECK_EQ(GetStateAndFlags(std::memory_order_relaxed).GetValue(), 0u);
StateAndFlags state_and_flags = StateAndFlags(0u).WithState(ThreadState::kNative);
tls32_.state_and_flags.store(state_and_flags.GetValue(), std::memory_order_relaxed);
tls32_.interrupted.store(false, std::memory_order_relaxed); // Initialize with no permit; if the java Thread was unparked before being // started, it will unpark itself before calling into java code.
tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed);
memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes));
std::fill(tlsPtr_.rosalloc_runs,
tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread,
gc::allocator::RosAlloc::GetDedicatedFullRun());
tlsPtr_.checkpoint_function = nullptr;
tlsPtr_.active_suspendall_barrier = nullptr;
tlsPtr_.active_suspend1_barriers = nullptr;
tlsPtr_.flip_function.store(nullptr, std::memory_order_relaxed);
tlsPtr_.thread_local_mark_stack = nullptr;
ResetTlab();
}
bool Thread::IsStillStarting() const { // You might think you can check whether the state is kStarting, but for much of thread startup, // the thread is in kNative; it might also be in kVmWait. // You might think you can check whether the peer is null, but the peer is actually created and // assigned fairly early on, and needs to be. // It turns out that the last thing to change is the thread name; that's a good proxy for "has // this thread _ever_ entered kRunnable". // TODO: I believe that SetThreadName(), ThreadGroup::GetThreads() and many jvmti functions can // call this while the thread is in the process of starting. Thus we appear to have data races // here on opeer and jpeer, and our result may be obsolete by the time we return. Aside from the // data races, it is not immediately clear whether clients are robust against this behavior. It // may make sense to acquire a per-thread lock during the transition, and have this function // REQUIRE that. `runtime_shutdown_lock_` might almost work, but is global and currently not // held long enough. return (tlsPtr_.jpeer == nullptr && tlsPtr_.opeer == nullptr) ||
(tlsPtr_.name.load() == kThreadNameDuringStartup);
}
if (tlsPtr_.jni_env != nullptr) {
{
ScopedObjectAccess soa(self);
MonitorExitVisitor visitor(self); // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
tlsPtr_.jni_env->monitors_.VisitRoots(&visitor, RootInfo(kRootVMInternal));
} // Release locally held global references which releasing may require the mutator lock. if (tlsPtr_.jpeer != nullptr) { // If pthread_create fails we don't have a jni env here.
tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
tlsPtr_.jpeer = nullptr;
} if (tlsPtr_.class_loader_override != nullptr) {
tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.class_loader_override);
tlsPtr_.class_loader_override = nullptr;
}
}
if (tlsPtr_.opeer != nullptr) {
ScopedObjectAccess soa(self); // We may need to call user-supplied managed code, do this before final clean-up.
HandleUncaughtExceptions();
RemoveFromThreadGroup();
Runtime* runtime = Runtime::Current(); if (runtime != nullptr && should_run_callbacks) {
runtime->GetRuntimeCallbacks()->ThreadDeath(self);
}
// Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone // who is waiting.
ObjPtr<mirror::Object> lock =
WellKnownClasses::java_lang_Thread_lock->GetObject(tlsPtr_.opeer); // (This conditional is only needed for tests, where Thread.lock won't have been set.) if (lock != nullptr) {
StackHandleScope<1> hs(self);
Handle<mirror::Object> h_obj(hs.NewHandle(lock));
ObjectLock<mirror::Object> locker(self, h_obj);
locker.NotifyAll();
}
if (UNLIKELY(self->GetMethodTraceBuffer() != nullptr)) {
Trace::FlushThreadBuffer(self);
}
} // Mark-stack revocation must be performed at the very end. No // checkpoint/flip-function or read-barrier should be called after this. if (gUseReadBarrier) {
Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->RevokeThreadLocalMarkStack(this);
}
}
Thread::~Thread() {
CHECK(tlsPtr_.class_loader_override == nullptr);
CHECK(tlsPtr_.jpeer == nullptr);
CHECK(tlsPtr_.opeer == nullptr); bool initialized = (tlsPtr_.jni_env != nullptr); // Did Thread::Init run? if (initialized) { delete tlsPtr_.jni_env;
tlsPtr_.jni_env = nullptr;
}
CHECK_NE(GetState(), ThreadState::kRunnable);
CHECK(!ReadFlag(ThreadFlag::kCheckpointRequest, std::memory_order_relaxed));
CHECK(!ReadFlag(ThreadFlag::kEmptyCheckpointRequest, std::memory_order_relaxed));
CHECK(!ReadFlag(ThreadFlag::kSuspensionImmune, std::memory_order_relaxed));
CHECK(tlsPtr_.checkpoint_function == nullptr);
CHECK_EQ(checkpoint_overflow_.size(), 0u); // A pending flip function request is OK. FlipThreadRoots will have been notified that we // exited, and nobody will attempt to process the request.
// Make sure we processed all deoptimization requests.
CHECK(tlsPtr_.deoptimization_context_stack == nullptr) << "Missed deoptimization";
CHECK(tlsPtr_.frame_id_to_shadow_frame == nullptr) << "Not all deoptimized frames have been consumed by the debugger.";
// We may be deleting a still born thread.
SetStateUnsafe(ThreadState::kTerminated);
delete wait_cond_; delete wait_mutex_;
if (initialized) {
CleanupCpu();
}
#ifdef ART_USE_SIMULATOR if (Runtime::Current()->GetImplicitStackOverflowChecks()) {
UnprotectStack<StackType::kSimulated>();
}
if (tlsPtr_.sim_data.sim_executor != nullptr) { delete tlsPtr_.sim_data.sim_executor;
} #endif
// If the dispatchUncaughtException threw, clear that exception too.
self->ClearException();
}
void Thread::RemoveFromThreadGroup() {
Thread* self = this;
DCHECK_EQ(self, Thread::Current()); // this.group.threadTerminated(this); // group can be null if we're in the compiler or a test.
ObjPtr<mirror::Object> group =
WellKnownClasses::java_lang_Thread_group->GetObject(tlsPtr_.opeer); if (group != nullptr) {
WellKnownClasses::java_lang_ThreadGroup_threadTerminated->InvokeVirtual<'V', 'L'>(
self, group, tlsPtr_.opeer);
}
}
using ArtMethodDexPcPair = std::pair<ArtMethod*, uint32_t>;
// Counts the stack trace depth and also fetches the first max_saved_frames frames. class FetchStackTraceVisitor : public StackVisitor { public: explicit FetchStackTraceVisitor(Thread* thread,
ArtMethodDexPcPair* saved_frames = nullptr,
size_t max_saved_frames = 0)
REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
saved_frames_(saved_frames),
max_saved_frames_(max_saved_frames) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { // We want to skip frames up to and including the exception's constructor. // Note we also skip the frame if it doesn't have a method (namely the callee // save frame)
ArtMethod* m = GetMethod(); if (skipping_ && !m->IsRuntimeMethod() &&
!GetClassRoot<mirror::Throwable>()->IsAssignableFrom(m->GetDeclaringClass())) {
skipping_ = false;
} if (!skipping_) { if (!m->IsRuntimeMethod()) { // Ignore runtime frames (in particular callee save). if (depth_ < max_saved_frames_) {
saved_frames_[depth_].first = m;
saved_frames_[depth_].second = m->IsProxyMethod() ? dex::kDexNoIndex : GetDexPc();
}
++depth_;
}
} else {
++skip_depth_;
} returntrue;
}
bool Init(uint32_t depth) REQUIRES_SHARED(Locks::mutator_lock_) ACQUIRE(Roles::uninterruptible_) { // Allocate method trace as an object array where the first element is a pointer array that // contains the ArtMethod pointers and dex PCs. The rest of the elements are the declaring // class of the ArtMethod pointers.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self_);
ObjPtr<mirror::Class> array_class =
GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker); // The first element is the methods and dex pc array, the other elements are declaring classes // for the methods to ensure classes in the stack trace don't get unloaded.
Handle<mirror::ObjectArray<mirror::Object>> trace(
hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
hs.Self(), array_class, static_cast<int32_t>(depth) + 1))); if (trace == nullptr) { // Acquire uninterruptible_ in all paths.
self_->StartAssertNoThreadSuspension("Building internal stack trace");
self_->AssertPendingOOMException(); returnfalse;
}
ObjPtr<mirror::PointerArray> methods_and_pcs =
class_linker->AllocPointerArray(self_, depth * 2); constchar* last_no_suspend_cause =
self_->StartAssertNoThreadSuspension("Building internal stack trace"); if (methods_and_pcs == nullptr) {
self_->AssertPendingOOMException(); returnfalse;
}
trace->Set</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(0, methods_and_pcs);
trace_ = trace.Get(); // If We are called from native, use non-transactional mode.
CHECK(last_no_suspend_cause == nullptr) << last_no_suspend_cause; returntrue;
}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { if (trace_ == nullptr) { returntrue; // We're probably trying to fillInStackTrace for an OutOfMemoryError.
} if (skip_depth_ > 0) {
skip_depth_--; returntrue;
}
ArtMethod* m = GetMethod(); if (m->IsRuntimeMethod()) { returntrue; // Ignore runtime frames (in particular callee save).
}
AddFrame(m, m->IsProxyMethod() ? dex::kDexNoIndex : GetDexPc()); returntrue;
}
void AddFrame(ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::PointerArray> methods_and_pcs = GetTraceMethodsAndPCs();
methods_and_pcs->SetElementPtrSize</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
count_, method, pointer_size_);
methods_and_pcs->SetElementPtrSize</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>( static_cast<uint32_t>(methods_and_pcs->GetLength()) / 2 + count_, dex_pc, pointer_size_); // Save the declaring class of the method to ensure that the declaring classes of the methods // do not get unloaded while the stack trace is live. However, this does not work for copied // methods because the declaring class of a copied method points to an interface class which // may be in a different class loader. Instead, retrieve the class loader associated with the // allocator that holds the copied method. This is much cheaper than finding the actual class.
ObjPtr<mirror::Object> keep_alive; if (UNLIKELY(method->IsCopied())) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
keep_alive = class_linker->GetHoldingClassLoaderOfCopiedMethod(self_, method);
} else {
keep_alive = method->GetDeclaringClass();
}
trace_->Set</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>( static_cast<int32_t>(count_) + 1, keep_alive);
++count_;
}
private:
Thread* const self_; // How many more frames to skip.
uint32_t skip_depth_; // Current position down stack trace.
uint32_t count_ = 0; // An object array where the first element is a pointer array that contains the `ArtMethod` // pointers on the stack and dex PCs. The rest of the elements are referencing objects // that shall keep the methods alive, namely the declaring class of the `ArtMethod` for // declared methods and the class loader for copied methods (because it's faster to find // the class loader than the actual class that holds the copied method). The `trace_[i+1]` // contains the declaring class or class loader of the `ArtMethod` of the i'th frame. // We're initializing a newly allocated trace, so we do not need to record that under // a transaction. If the transaction is aborted, the whole trace shall be unreachable.
mirror::ObjectArray<mirror::Object>* trace_ = nullptr; // For cross compilation. const PointerSize pointer_size_;
ObjPtr<mirror::ObjectArray<mirror::Object>> Thread::CreateInternalStackTrace( const ScopedObjectAccessAlreadyRunnable& soa) const { // Compute depth of stack, save frames if possible to avoid needing to recompute many.
constexpr size_t kMaxSavedFrames = 256;
std::unique_ptr<ArtMethodDexPcPair[]> saved_frames(new ArtMethodDexPcPair[kMaxSavedFrames]);
FetchStackTraceVisitor count_visitor(const_cast<Thread*>(this),
&saved_frames[0],
kMaxSavedFrames);
count_visitor.WalkStack(); const uint32_t depth = count_visitor.GetDepth(); const uint32_t skip_depth = count_visitor.GetSkipDepth();
// Build internal stack trace.
BuildInternalStackTraceVisitor build_trace_visitor(
soa.Self(), const_cast<Thread*>(this), skip_depth); if (!build_trace_visitor.Init(depth)) { return nullptr; // Allocation failed.
} // If we saved all of the frames we don't even need to do the actual stack walk. This is faster // than doing the stack walk twice. if (depth < kMaxSavedFrames) { for (size_t i = 0; i < depth; ++i) {
build_trace_visitor.AddFrame(saved_frames[i].first, saved_frames[i].second);
}
} else {
build_trace_visitor.WalkStack();
}
mirror::ObjectArray<mirror::Object>* trace = build_trace_visitor.GetInternalStackTrace(); if (kIsDebugBuild) {
ObjPtr<mirror::PointerArray> trace_methods = build_trace_visitor.GetTraceMethodsAndPCs(); // Second half of trace_methods is dex PCs. for (uint32_t i = 0; i < static_cast<uint32_t>(trace_methods->GetLength() / 2); ++i) { auto* method = trace_methods->GetElementPtrSize<ArtMethod*>(
i, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
CHECK(method != nullptr);
}
} return trace;
}
bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const { // Only count the depth since we do not pass a stack frame array as an argument.
FetchStackTraceVisitor count_visitor(const_cast<Thread*>(this));
count_visitor.WalkStack(); return count_visitor.GetDepth() == static_cast<uint32_t>(exception->GetStackDepth());
}
static ObjPtr<mirror::StackTraceElement> CreateStackTraceElement( const ScopedObjectAccessAlreadyRunnable& soa,
ArtMethod* method,
uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
int32_t line_number;
StackHandleScope<3> hs(soa.Self()); auto class_name_object(hs.NewHandle<mirror::String>(nullptr)); auto source_name_object(hs.NewHandle<mirror::String>(nullptr)); if (method->IsProxyMethod()) {
line_number = -1;
class_name_object.Assign(method->GetDeclaringClass()->GetName()); // source_name_object intentionally left null for proxy methods
} else {
line_number = method->GetLineNumFromDexPC(dex_pc); // Allocate element, potentially triggering GC // TODO: reuse class_name_object via Class::name_? constchar* descriptor = method->GetDeclaringClassDescriptor();
CHECK(descriptor != nullptr);
std::string class_name(PrettyDescriptor(descriptor));
class_name_object.Assign(
mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); if (class_name_object == nullptr) {
soa.Self()->AssertPendingOOMException(); return nullptr;
} char source_file_buffer[64]; constchar* source_file = method->GetDeclaringClassSourceFile(); if (source_file == nullptr) { // Create artificial filename based on SHA1 of the dex file.
DexFile::Sha1 hash = method->GetDeclaringClass()->GetDexFile().GetSha1();
snprintf(source_file_buffer, sizeof(source_file_buffer), "dex-id-%s", hash.ToHex().data());
source_file = source_file_buffer;
}
source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); if (source_name_object == nullptr) {
soa.Self()->AssertPendingOOMException(); return nullptr;
} if (line_number == -1) { // Make the line_number field of StackTraceElement hold the dex pc.
line_number = static_cast<int32_t>(dex_pc);
}
} constchar* method_name = method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName();
CHECK(method_name != nullptr);
Handle<mirror::String> method_name_object(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); if (method_name_object == nullptr) { return nullptr;
} return mirror::StackTraceElement::Alloc(soa.Self(),
class_name_object,
method_name_object,
source_name_object,
line_number);
}
jobjectArray Thread::InternalStackTraceToStackTraceElementArray( const ScopedObjectAccessAlreadyRunnable& soa,
jobject internal,
jobjectArray output_array, int* stack_depth) { // Decode the internal stack trace into the depth, method trace and PC trace. // Subtract one for the methods and PC trace.
int32_t depth = soa.Decode<mirror::Array>(internal)->GetLength() - 1;
DCHECK_GE(depth, 0);
if (output_array != nullptr) { // Reuse the array we were given.
result = output_array; // ...adjusting the number of frames we'll write to not exceed the array length. const int32_t traces_length =
soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(result)->GetLength();
depth = std::min(depth, traces_length);
} else { // Create java_trace array and place in local reference table
ObjPtr<mirror::ObjectArray<mirror::StackTraceElement>> java_traces =
class_linker->AllocStackTraceElementArray(soa.Self(), static_cast<size_t>(depth)); if (java_traces == nullptr) { return nullptr;
}
result = soa.AddLocalReference<jobjectArray>(java_traces);
}
if (stack_depth != nullptr) {
*stack_depth = depth;
}
for (uint32_t i = 0; i < static_cast<uint32_t>(depth); ++i) {
ObjPtr<mirror::ObjectArray<mirror::Object>> decoded_traces =
soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>(); // Methods and dex PC trace is element 0.
DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray()); const ObjPtr<mirror::PointerArray> method_trace =
ObjPtr<mirror::PointerArray>::DownCast(decoded_traces->Get(0)); // Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
i + static_cast<uint32_t>(method_trace->GetLength()) / 2, kRuntimePointerSize); const ObjPtr<mirror::StackTraceElement> obj = CreateStackTraceElement(soa, method, dex_pc); if (obj == nullptr) { return nullptr;
} // We are called from native: use non-transactional mode.
soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(result)->Set<false>( static_cast<int32_t>(i), obj);
} return result;
}
[[nodiscard]] static ObjPtr<mirror::StackFrameInfo> InitStackFrameInfo( const ScopedObjectAccessAlreadyRunnable& soa,
ClassLinker* class_linker,
Handle<mirror::StackFrameInfo> stackFrameInfo,
ArtMethod* method,
uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<4> hs(soa.Self());
int32_t line_number; auto source_name_object(hs.NewHandle<mirror::String>(nullptr)); if (method->IsProxyMethod()) {
line_number = -1; // source_name_object intentionally left null for proxy methods
} else {
line_number = method->GetLineNumFromDexPC(dex_pc); if (line_number == -1) { // Make the line_number field of StackFrameInfo hold the dex pc. // source_name_object is intentionally left null if we failed to map the dex pc to // a line number (most probably because there is no debug info). See b/30183883.
line_number = static_cast<int32_t>(dex_pc);
} else { constchar* source_file = method->GetDeclaringClassSourceFile(); if (source_file != nullptr) {
source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); if (source_name_object == nullptr) {
soa.Self()->AssertPendingOOMException(); return nullptr;
}
}
}
}
jint Thread::InternalStackTraceToStackFrameInfoArray( const ScopedObjectAccessAlreadyRunnable& soa,
jlong mode, // See java.lang.StackStreamFactory for the mode flags
jobject internal,
jint startLevel,
jint batchSize,
jint startBufferIndex,
jobjectArray output_array) { // Decode the internal stack trace into the depth, method trace and PC trace. // Subtract one for the methods and PC trace.
int32_t depth = soa.Decode<mirror::Array>(internal)->GetLength() - 1;
DCHECK_GE(depth, 0);
// The FILL_CLASS_REFS_ONLY flag is defined in AbstractStackWalker.fetchStackFrames() javadoc. bool isClassArray = (mode & FILL_CLASS_REFS_ONLY) != 0;
Handle<mirror::ObjectArray<mirror::Object>> decoded_traces =
hs.NewHandle(soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>()); // Methods and dex PC trace is element 0.
DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
Handle<mirror::PointerArray> method_trace =
hs.NewHandle(ObjPtr<mirror::PointerArray>::DownCast(decoded_traces->Get(0)));
MutableHandle<mirror::StackFrameInfo> frame = hs.NewHandle<mirror::StackFrameInfo>(nullptr);
MutableHandle<mirror::Class> clazz = hs.NewHandle<mirror::Class>(nullptr); for (uint32_t i = static_cast<uint32_t>(startLevel); i < static_cast<uint32_t>(depth); ++i) { if (endBufferIndex >= startBufferIndex + batchSize || endBufferIndex >= bufferSize) { break;
}
ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize); if (isClassArray) {
clazz.Assign(method->GetDeclaringClass());
framesOrClasses->Set(endBufferIndex, clazz.Get());
} else { // Prepare parameters for fields in StackFrameInfo
uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
i + static_cast<uint32_t>(method_trace->GetLength()) / 2, kRuntimePointerSize);
ObjPtr<mirror::Object> frameObject = framesOrClasses->Get(endBufferIndex); // If libcore didn't allocate the object, we just stop here, but it's unlikely. if (frameObject == nullptr || !frameObject->InstanceOf(sfi_class.Get())) { break;
}
frame.Assign(ObjPtr<mirror::StackFrameInfo>::DownCast(frameObject));
frame.Assign(InitStackFrameInfo(soa, class_linker, frame, method, dex_pc)); // Break if InitStackFrameInfo fails to allocate objects or assign the fields. if (frame == nullptr) { break;
}
}
++endBufferIndex;
}
return endBufferIndex;
}
jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const { // This code allocates. Do not allow it to operate with a pending exception. if (IsExceptionPending()) { return nullptr;
}
Handle<mirror::Class> h_o_array_class =
hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker));
DCHECK(h_o_array_class != nullptr); // Class roots must be already initialized.
// Make sure the AnnotatedStackTraceElement.class is initialized, b/76208924 .
class_linker->EnsureInitialized(soa.Self(),
h_aste_class, /* can_init_fields= */ true, /* can_init_parents= */ true); if (soa.Self()->IsExceptionPending()) { // This should not fail in a healthy runtime. return nullptr;
}
// Set blocked-on object. if (i == 0) { if (dumper.block_jobject_ != nullptr) {
blocked_on_field->SetObject<false>(
handle.Get(), soa.Decode<mirror::Object>(dumper.block_jobject_.get()));
}
}
// Disable public sdk checks if we need to throw exceptions. // The checks are only used in AOT compilation and may block (exception) class // initialization if it needs access to private fields (e.g. serialVersionUID). // // Since throwing an exception will EnsureInitialization and the public sdk may // block that, disable the checks. It's ok to do so, because the thrown exceptions // are not part of the application code that needs to verified.
ScopedDisablePublicSdkChecker sdpsc;
// If we couldn't allocate the exception, throw the pre-allocated out of memory exception. if (exception == nullptr) {
Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one.
SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingException()); return;
}
// Choose an appropriate constructor and set up the arguments. constchar* signature;
ScopedLocalRef<jstring> msg_string(GetJniEnv(), nullptr); if (msg != nullptr) { // Ensure we remember this and the method over the String allocation.
msg_string.reset(
soa.AddLocalReference<jstring>(mirror::String::AllocFromModifiedUtf8(this, msg))); if (UNLIKELY(msg_string.get() == nullptr)) {
CHECK(IsExceptionPending()); // OOME. return;
} if (cause.get() == nullptr) {
signature = "(Ljava/lang/String;)V";
} else {
signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V";
}
} else { if (cause.get() == nullptr) {
signature = "()V";
} else {
signature = "(Ljava/lang/Throwable;)V";
}
}
ArtMethod* exception_init_method =
exception_class->FindConstructor(signature, cl->GetImagePointerSize());
CHECK(exception_init_method != nullptr) << "No <init>" << signature << " in "
<< PrettyDescriptor(exception_class_descriptor);
if (UNLIKELY(!runtime->IsStarted())) { // Something is trying to throw an exception without a started runtime, which is the common // case in the compiler. We won't be able to invoke the constructor of the exception, so set // the exception fields directly. if (msg != nullptr) {
exception->SetDetailMessage(DecodeJObject(msg_string.get())->AsString());
} if (cause.get() != nullptr) {
exception->SetCause(DecodeJObject(cause.get())->AsThrowable());
}
ObjPtr<mirror::ObjectArray<mirror::Object>> trace = CreateInternalStackTrace(soa); if (trace != nullptr) {
exception->SetStackState(trace.Ptr());
}
SetException(exception.Get());
} else {
jvalue jv_args[2];
size_t i = 0;
#define QUICK_ENTRY_POINT_INFO(x, ...) \ if (QUICK_ENTRYPOINT_OFFSET(ptr_size, p ## x).Uint32Value() == offset) { \
os << "p"# x; \ return; \
} #include"entrypoints/quick/quick_entrypoints_list.h"
QUICK_ENTRYPOINT_LIST(QUICK_ENTRY_POINT_INFO) #undef QUICK_ENTRYPOINT_LIST #undef QUICK_ENTRY_POINT_INFO
os << offset;
}
std::unique_ptr<Context> Thread::QuickDeliverException(bool skip_method_exit_callbacks) { // Get exception from thread.
ObjPtr<mirror::Throwable> exception = GetException();
CHECK(exception != nullptr); if (exception == GetDeoptimizationException()) { // This wasn't a real exception, so just clear it here. If there was an actual exception it // will be recorded in the DeoptimizationContext and it will be restored later.
ClearException(); return Deoptimize(DeoptimizationKind::kFullFrame, /*single_frame=*/ false,
skip_method_exit_callbacks);
}
// This is a real exception: let the instrumentation know about it. Exception throw listener // could set a breakpoint or install listeners that might require a deoptimization. Hence the // deoptimization check needs to happen after calling the listener.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (instrumentation->HasExceptionThrownListeners() &&
IsExceptionThrownByCurrentMethod(exception)) { // Instrumentation may cause GC so keep the exception object safe.
StackHandleScope<1> hs(this);
HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
instrumentation->ExceptionThrownEvent(this, exception);
} // Does instrumentation need to deoptimize the stack or otherwise go to interpreter for something? // Note: we do this *after* reporting the exception to instrumentation in case it now requires // deoptimization. It may happen if a debugger is attached and requests new events (single-step, // breakpoint, ...) when the exception is reported. // Frame pop can be requested on a method unwind callback which requires a deopt. We could // potentially check after each unwind callback to see if a frame pop was requested and deopt if // needed. Since this is a debug only feature and this path is only taken when an exception is // thrown, it is not performance critical and we keep it simple by just deopting if method exit // listeners are installed and frame pop feature is supported. bool needs_deopt =
instrumentation->HasMethodExitListeners() && Runtime::Current()->AreNonStandardExitsEnabled(); // parkVirtualInternal throws an exception when parking a virtual thread. It's not a // deoptimization request. bool is_parking_vthread = this->IsVirtualThreadParking(); if (!is_parking_vthread &&
(Dbg::IsForcedInterpreterNeededForException(this) || IsForceInterpreter() || needs_deopt)) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack(); if (visitor.GetCurrentQuickFrame() != nullptr) { if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.GetOuterMethod(), visitor.caller_pc)) { // method_type shouldn't matter due to exception handling. const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault; // Save the exception into the deoptimization context so it can be restored // before entering the interpreter.
PushDeoptimizationContext(
JValue(), /* is_reference= */ false,
exception, /* from_code= */ false,
method_type); return Deoptimize(DeoptimizationKind::kFullFrame, /*single_frame=*/ false,
skip_method_exit_callbacks);
} else {
LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
<< visitor.caller->PrettyMethod();
}
} else { // This is either top of call stack, or shadow frame.
DCHECK(visitor.caller == nullptr || visitor.IsShadowFrame());
}
}
// Don't leave exception visible while we try to find the handler, which may cause class // resolution.
ClearException();
QuickExceptionHandler exception_handler(this, false);
exception_handler.FindCatch(exception, skip_method_exit_callbacks); if (exception_handler.GetClearException()) { // Exception was cleared as part of delivery.
DCHECK(!IsExceptionPending());
} else { // Exception was put back with a throw location.
DCHECK(IsExceptionPending()); // Check the to-space invariant on the re-installed exception (if applicable).
ReadBarrier::MaybeAssertToSpaceInvariant(GetException());
} return exception_handler.PrepareLongJump();
}
std::unique_ptr<Context> Thread::Deoptimize(DeoptimizationKind kind, bool single_frame, bool skip_method_exit_callbacks) {
Runtime::Current()->IncrementDeoptimizationCount(kind); if (VLOG_IS_ON(deopt)) { if (single_frame) { // Deopt logging will be in DeoptimizeSingleFrame. It is there to take advantage of the // specialized visitor that will show whether a method is Quick or Shadow.
} else {
LOG(INFO) << "Deopting:";
Dump(LOG_STREAM(INFO));
}
}
AssertHasDeoptimizationContext();
QuickExceptionHandler exception_handler(this, true); if (single_frame) {
exception_handler.DeoptimizeSingleFrame(kind);
} else {
exception_handler.DeoptimizeStack(skip_method_exit_callbacks);
} if (exception_handler.IsFullFragmentDone()) { return exception_handler.PrepareLongJump(/*smash_caller_saves=*/ true);
} else {
exception_handler.DeoptimizePartialFragmentFixup(); // We cannot smash the caller-saves, as we need the ArtMethod in a parameter register that would // be caller-saved. This has the downside that we cannot track incorrect register usage down the // line. return exception_handler.PrepareLongJump(/*smash_caller_saves=*/ false);
}
}
ArtMethod* Thread::GetCurrentMethod(uint32_t* dex_pc_out, bool check_suspended, bool abort_on_error) const { // Note: this visitor may return with a method set, but dex_pc_ being DexFile:kDexNoIndex. This is // so we don't abort in a special situation (thinlocked monitor) when dumping the Java // stack.
ArtMethod* method = nullptr;
uint32_t dex_pc = dex::kDexNoIndex;
StackVisitor::WalkStack(
[&](const StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = visitor->GetMethod(); if (m->IsRuntimeMethod()) { // Continue if this is a runtime method. returntrue;
}
method = m;
dex_pc = visitor->GetDexPc(abort_on_error); returnfalse;
}, const_cast<Thread*>(this), /* context= */ nullptr,
StackVisitor::StackWalkKind::kIncludeInlinedFrames,
check_suspended);
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor). template <typename RootVisitor, bool kPrecise = false> class ReferenceMapVisitor : public StackVisitor { public:
ReferenceMapVisitor(Thread* thread, Context* context, RootVisitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) // We are visiting the references in compiled frames, so we do not need // to know the inlined frames.
: StackVisitor(thread, context, StackVisitor::StackWalkKind::kSkipInlinedFrames),
visitor_(visitor),
visit_declaring_class_(!Runtime::Current()->GetHeap()->IsPerformingUffdCompaction()) {}
void VisitShadowFrame(ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = shadow_frame->GetMethod();
VisitDeclaringClass(m);
DCHECK(m != nullptr);
size_t num_regs = shadow_frame->NumberOfVRegs(); // handle scope for JNI or References for interpreter. for (size_t reg = 0; reg < num_regs; ++reg) {
mirror::Object* ref = shadow_frame->GetVRegReference(reg); if (ref != nullptr) {
mirror::Object* new_ref = ref;
visitor_(&new_ref, reg, this); if (new_ref != ref) {
shadow_frame->SetVRegReference(reg, new_ref);
}
}
} // Mark lock count map required for structured locking checks.
shadow_frame->GetLockCountData().VisitMonitors(visitor_, /* vreg= */ -1, this);
}
private: // Visiting the declaring class is necessary so that we don't unload the class of a method that // is executing. We need to ensure that the code stays mapped. NO_THREAD_SAFETY_ANALYSIS since // the threads do not all hold the heap bitmap lock for parallel GC. void VisitDeclaringClass(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_)
NO_THREAD_SAFETY_ANALYSIS { if (!visit_declaring_class_) { return;
}
ObjPtr<mirror::Class> klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>(); // klass can be null for runtime methods. if (klass != nullptr) { if (kVerifyImageObjectsMarked) {
gc::Heap* const heap = Runtime::Current()->GetHeap();
gc::space::ContinuousSpace* space = heap->FindContinuousSpaceFromObject(klass, /*fail_ok=*/true); if (space != nullptr && space->IsImageSpace()) { bool failed = false; if (!space->GetLiveBitmap()->Test(klass.Ptr())) {
failed = true;
LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image " << *space;
} elseif (!heap->GetLiveBitmap()->Test(klass.Ptr())) {
failed = true;
LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image through live bitmap " << *space;
} if (failed) {
GetThread()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
space->AsImageSpace()->DumpSections(LOG_STREAM(FATAL_WITHOUT_ABORT));
LOG(FATAL_WITHOUT_ABORT) << "Method@" << method->GetDexMethodIndex() << ":" << method
<< " klass@" << klass.Ptr(); // Pretty info last in case it crashes.
LOG(FATAL) << "Method " << method->PrettyMethod() << " klass "
<< klass->PrettyClass();
}
}
}
mirror::Object* new_ref = klass.Ptr();
visitor_(&new_ref, /* vreg= */ JavaFrameRootInfo::kMethodDeclaringClass, this); if (new_ref != klass) {
method->CASDeclaringClass(klass.Ptr(), new_ref->AsClass());
}
}
}
void VisitNterpFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod** cur_quick_frame = GetCurrentQuickFrame();
StackReference<mirror::Object>* vreg_ref_base = reinterpret_cast<StackReference<mirror::Object>*>(NterpGetReferenceArray(cur_quick_frame));
StackReference<mirror::Object>* vreg_int_base = reinterpret_cast<StackReference<mirror::Object>*>(NterpGetRegistersArray(cur_quick_frame));
CodeItemDataAccessor accessor((*cur_quick_frame)->DexInstructionData()); const uint16_t num_regs = accessor.RegistersSize(); // An nterp frame has two arrays: a dex register array and a reference array // that shadows the dex register array but only containing references // (non-reference dex registers have nulls). See nterp_helpers.cc. for (size_t reg = 0; reg < num_regs; ++reg) {
StackReference<mirror::Object>* ref_addr = vreg_ref_base + reg;
mirror::Object* ref = ref_addr->AsMirrorPtr(); if (ref != nullptr) {
mirror::Object* new_ref = ref;
visitor_(&new_ref, reg, this); if (new_ref != ref) {
ref_addr->Assign(new_ref);
StackReference<mirror::Object>* int_addr = vreg_int_base + reg;
int_addr->Assign(new_ref);
}
}
}
}
if (m->IsNative()) { // TODO: Spill the `this` reference in the AOT-compiled String.charAt() // slow-path for throwing SIOOBE, so that we can remove this carve-out. if (UNLIKELY(m->IsIntrinsic()) && m->GetIntrinsic() == Intrinsics::kStringCharAt) { // The String.charAt() method is AOT-compiled with an intrinsic implementation // instead of a JNI stub. It has a slow path that constructs a runtime frame // for throwing SIOOBE and in that path we do not get the `this` pointer // spilled on the stack, so there is nothing to visit. We can distinguish // this from the GenericJni path by checking that the PC is in the boot image // (PC shall be known thanks to the runtime frame for throwing SIOOBE). // Note that JIT does not emit that intrinic implementation. constvoid* pc = reinterpret_cast<constvoid*>(GetCurrentQuickFramePc()); if (pc != nullptr && Runtime::Current()->GetHeap()->IsInBootImageOatFile(pc)) { return;
}
} // Native methods spill their arguments to the reserved vregs in the caller's frame // and use pointers to these stack references as jobject, jclass, jarray, etc. // Note: We can come here for a @CriticalNative method when it needs to resolve the // target native function but there would be no references to visit below. const size_t frame_size = GetCurrentQuickFrameInfo().FrameSizeInBytes(); const size_t method_pointer_size = static_cast<size_t>(kRuntimePointerSize);
uint32_t* current_vreg = reinterpret_cast<uint32_t*>( reinterpret_cast<uint8_t*>(cur_quick_frame) + frame_size + method_pointer_size); auto visit = [&]() REQUIRES_SHARED(Locks::mutator_lock_) { auto* ref_addr = reinterpret_cast<StackReference<mirror::Object>*>(current_vreg);
mirror::Object* ref = ref_addr->AsMirrorPtr(); if (ref != nullptr) {
mirror::Object* new_ref = ref;
visitor_(&new_ref, /* vreg= */ JavaFrameRootInfo::kNativeReferenceArgument, this); if (ref != new_ref) {
ref_addr->Assign(new_ref);
}
}
}; constchar* shorty = m->GetShorty(); if (!m->IsStatic()) {
visit();
current_vreg += 1u;
} for (shorty += 1u; *shorty != 0; ++shorty) { switch (*shorty) { case'D': case'J':
current_vreg += 2u; break; case'L':
visit();
FALLTHROUGH_INTENDED; default:
current_vreg += 1u; break;
}
}
} elseif (!m->IsRuntimeMethod() && (!m->IsProxyMethod() || m->IsConstructor())) { // Process register map (which native, runtime and proxy methods don't have) const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
DCHECK(method_header->IsOptimized());
StackReference<mirror::Object>* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>(cur_quick_frame);
uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
CodeInfo code_info = kPrecise
? CodeInfo(method_header) // We will need dex register maps.
: CodeInfo::DecodeGcMasksOnly(method_header);
StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
DCHECK(map.IsValid());
T vreg_info(m, code_info, map, visitor_);
// Visit stack entries that hold pointers.
BitMemoryRegion stack_mask = code_info.GetStackMaskOf(map); for (size_t i = 0; i < stack_mask.size_in_bits(); ++i) { if (stack_mask.LoadBit(i)) {
StackReference<mirror::Object>* ref_addr = vreg_base + i;
mirror::Object* ref = ref_addr->AsMirrorPtr(); if (ref != nullptr) {
mirror::Object* new_ref = ref;
vreg_info.VisitStack(&new_ref, i, this); if (ref != new_ref) {
ref_addr->Assign(new_ref);
}
}
}
} // Visit callee-save registers that hold pointers.
uint32_t register_mask = code_info.GetRegisterMaskOf(map); for (uint32_t i = 0; i < BitSizeOf<uint32_t>(); ++i) { if (register_mask & (1 << i)) {
mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(i)); if (kIsDebugBuild && ref_addr == nullptr) {
std::string thread_name;
GetThread()->GetThreadName(thread_name);
LOG(FATAL_WITHOUT_ABORT) << "On thread " << thread_name;
DescribeStack(GetThread());
LOG(FATAL) << "Found an unsaved callee-save register " << i << " (null GPRAddress) "
<< "set in register_mask=" << register_mask << " at " << DescribeLocation();
} if (*ref_addr != nullptr) {
vreg_info.VisitRegister(ref_addr, i, this);
}
}
}
} elseif (!m->IsRuntimeMethod() && m->IsProxyMethod()) { // If this is a proxy method, visit its reference arguments.
DCHECK(!m->IsStatic());
DCHECK(!m->IsNative());
std::vector<StackReference<mirror::Object>*> ref_addrs =
GetProxyReferenceArguments(cur_quick_frame); for (StackReference<mirror::Object>* ref_addr : ref_addrs) {
mirror::Object* ref = ref_addr->AsMirrorPtr(); if (ref != nullptr) {
mirror::Object* new_ref = ref;
visitor_(&new_ref, /* vreg= */ JavaFrameRootInfo::kProxyReferenceArgument, this); if (ref != new_ref) {
ref_addr->Assign(new_ref);
}
}
}
}
}
// TODO: If necessary, we should consider caching a reverse map instead of the linear // lookups for each location. void FindWithType(const size_t index, const DexRegisterLocation::Kind kind,
mirror::Object** ref, const StackVisitor* stack_visitor)
REQUIRES_SHARED(Locks::mutator_lock_) { if (dex_register_map.empty() && number_of_dex_registers != 0) { // It is possible to see optimized code that isn't compiled with // debuggable even in debuggable runtimes. For ex: zygote frames.
DCHECK(!code_info.IsDebuggable());
visitor(ref, JavaFrameRootInfo::kImpreciseVreg, stack_visitor); return;
}
bool found = false; for (size_t dex_reg = 0; dex_reg != number_of_dex_registers; ++dex_reg) {
DexRegisterLocation location = dex_register_map[dex_reg]; if (location.GetKind() == kind && static_cast<size_t>(location.GetValue()) == index) {
visitor(ref, dex_reg, stack_visitor);
found = true;
}
}
if (!found) { // If nothing found, report with unknown.
visitor(ref, JavaFrameRootInfo::kUnknownVreg, stack_visitor);
}
}
// Restore the exception that was pending before deoptimization then interpret the // deoptimized frames. if (pending_exception != nullptr) {
SetException(pending_exception);
}
ShadowFrame* shadow_frame = MaybePopDeoptimizedStackedShadowFrame(); // We may not have a shadow frame if we deoptimized at the return of the // quick_to_interpreter_bridge which got directly called by art_quick_invoke_stub. if (shadow_frame != nullptr) {
SetTopOfShadowStack(shadow_frame);
interpreter::EnterInterpreterFromDeoptimize(this,
shadow_frame,
result,
from_code,
method_type);
}
}
void Thread::SetAsyncException(ObjPtr<mirror::Throwable> new_exception) {
CHECK(new_exception != nullptr);
Runtime::Current()->SetAsyncExceptionsThrown(); if (kIsDebugBuild) { // Make sure we are in a checkpoint.
MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_);
CHECK(this == Thread::Current() || GetSuspendCount() >= 1)
<< "It doesn't look like this was called in a checkpoint! this: "
<< this << " count: " << GetSuspendCount();
}
tlsPtr_.async_exception = new_exception.Ptr();
}
mirror::Object* Thread::GetPeerFromOtherThread() {
Thread* self = Thread::Current(); if (this == self) { // We often call this on every thread, including ourselves. return GetPeer();
} // If "this" thread is not suspended, it could disappear.
DCHECK(IsSuspended()) << *this;
DCHECK(tlsPtr_.jpeer == nullptr); // Some JVMTI code may unfortunately hold thread_list_lock_, but if it does, it should hold the // mutator lock in exclusive mode, and we should not have a pending flip function. if (kIsDebugBuild && Locks::thread_list_lock_->IsExclusiveHeld(self)) {
Locks::mutator_lock_->AssertExclusiveHeld(self);
CHECK(!ReadFlag(ThreadFlag::kPendingFlipFunction, std::memory_order_relaxed));
} // Ensure that opeer is not obsolete.
EnsureFlipFunctionStarted(self, this); if (ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_acquire)) { // Does not release mutator lock. Hence no new flip requests can be issued.
WaitForFlipFunction(self);
} return tlsPtr_.opeer;
}
mirror::Object* Thread::LockedGetPeerFromOtherThread(ThreadExitFlag* tef) {
DCHECK(tlsPtr_.jpeer == nullptr);
Thread* self = Thread::Current();
Locks::thread_list_lock_->AssertHeld(self); // memory_order_relaxed is OK here, because we recheck it later with acquire order. if (ReadFlag(ThreadFlag::kPendingFlipFunction, std::memory_order_relaxed)) { // It is unsafe to call EnsureFlipFunctionStarted with thread_list_lock_. Thus we temporarily // release it, taking care to handle the case in which "this" thread disapppears while we no // longer hold it.
Locks::thread_list_lock_->Unlock(self);
EnsureFlipFunctionStarted(self, this, StateAndFlags(0), tef);
Locks::thread_list_lock_->Lock(self); if (tef->HasExited()) { return nullptr;
}
} if (ReadFlag(ThreadFlag::kRunningFlipFunction, std::memory_order_acquire)) { // Does not release mutator lock. Hence no new flip requests can be issued.
WaitForFlipFunction(self);
} return tlsPtr_.opeer;
}
// Use PaletteSchedSetPriority on host for testing. This should set canSetPriority to false, // but not crash. static constexpr bool kUseFakeOnHost = false;
staticbool canSetPriority = true; // If false, we skip attempting to set OS priority.
// Android S, does more than setting niceness in PaletteSchedSetPriority, making it unsafe to use // that in Zygote, and making it desirable (for risk minimization, at least) to actually call it // when expected. inlinebool NeedSWorkaround() { staticbool needSWorkaround = false; static std::once_flag sWorkaroundInitialized;
std::call_once(sWorkaroundInitialized, []() { if (kIsTargetAndroid) { if (android::base::GetIntProperty("ro.build.version.sdk", 0) <= 32) {
needSWorkaround = true;
}
} elseif (!kUseFakeOnHost) { // Priority setting is often restricted on host. Just fake it, as for S. // TODO: Fuchsia may require attention here.
needSWorkaround = true;
canSetPriority = false;
}
}); return needSWorkaround;
}
// Return an int array result, so that result[i] is the native priority, really // "niceness" corresponding to Java priority i. result[0] is unused. int* Thread::GetPriorityMap() { staticint priorityMap[kMaxThreadPriority + 1]; static std::once_flag priorityMapInitialized; // For Android S, PaletteSchedSetPriority is unsafe in the zygote, since it leaves a file // descriptor open, which must crash zygote. We could possibly postpone discovering the map, but // that adds a few dozen system calls in each child. We instead simply assume the historical map // that shipped with Android S. // Deviating from this is questionable anyway, since a lot of Android code at all levels sets // niceness directly without going through the Palette API. On Android S, we continue to use // PaletteSchedSetPriority to set Java priorities, but we cache the niceness values given here, // and return those. Thus actual thread niceness values will not reflect those here, but pure // Java behavior should be consistent. This is similar to what happens when framework code // alters thread priorities externally, so we should be OK. staticint traditional_priority_map[] = {0/*unused*/, 19, 16, 13, 10, 0, -2, -4, -5, -6, -8}; constchar* failure_msg = nullptr;
if (NeedSWorkaround()) { staticbool warned = false; if (!warned) {
LOG(WARNING) << "Using default priority map due to SDK version";
warned = true;
} return traditional_priority_map;
}
std::call_once(priorityMapInitialized, [&pm = priorityMap, &failure_msg]() { // CHECKs in this function should be avoided. Dump calls will invoke this recursively. #define CHECK_DEFERRED_ABORT(pred, msg) \ if (!(pred)) { \
failure_msg = (msg); \ return; \
} bool need_fake = false; // Saw an anomaly requiring us to fake the map? bool success = true; bool saw_difference = true; // Do we map to different niceness values? // PaletteMapPriority always yields nontrivial mapping. for (int p = kMinThreadPriority; p <= kMaxThreadPriority; ++p) {
palette_status_t result = PaletteMapPriority(p, &pm[p]); if (result == PALETTE_STATUS_NOT_SUPPORTED) {
success = false; break;
}
CHECK_DEFERRED_ABORT(result == PALETTE_STATUS_OK, "Bad PALLETTE_STATUS");
} if (!success) { // Discover the map the hard way.
int32_t me = static_cast<int32_t>(::art::GetTid()); bool map_consistent;
errno = 0; int orig_niceness = getpriority(PRIO_PROCESS, 0/* self */);
CHECK_DEFERRED_ABORT(orig_niceness != -1 || errno == 0, "getpriority() failed");
constexpr int kMaxIters = 10; int iters = 0; do {
map_consistent = true;
++iters;
saw_difference = false;
CHECK_DEFERRED_ABORT(iters <= kMaxIters, "iters > kMaxIters"); // Start checking from higher priorities, since that is most likely to fail, and we may // have trouble undoing the damage if we don't detect the problem immediately. for (int p = kMaxThreadPriority; p >= kMinThreadPriority; --p) { int ret = PaletteSchedSetPriority(me, p); if (ret == PALETTE_STATUS_OK) {
errno = 0;
pm[p] = getpriority(PRIO_PROCESS, 0/* self */); // If we always get the same value, we're dealing with a fake, and need to fake a // consistent result here. if (!saw_difference && pm[p] != pm[kMaxThreadPriority]) { if (p == kMaxThreadPriority - 1) {
saw_difference = true;
} else { // We saw several identical values, which is wrong. // Force a complete pass with checking.
map_consistent = false; break;
}
}
CHECK_DEFERRED_ABORT(pm[p] != -1 || errno == 0, "2nd getpriority() failed");
VLOG(threads) << "Niceness[" << p << "] = " << pm[p]; if (saw_difference) { // With a non-fake PaletteSchedSetPriority the map should be strictly monotonically // decreasing. if (p < kMaxThreadPriority && pm[p] <= pm[p + 1]) { // Maybe somebody else mucked with our priority? Start over.
map_consistent = false; break;
}
}
} else {
need_fake = true; break;
}
}
} while (!map_consistent && !need_fake); int ret = setpriority(PRIO_PROCESS, static_cast<id_t>(me), orig_niceness);
CHECK_DEFERRED_ABORT(ret == 0, "setpriority() failed");
} if (!saw_difference || need_fake) { // Palette calls don't impact getpriority(), as with the traditional // PaletteSetSchedPriority fake on host.
canSetPriority = false;
LOG(WARNING) << "Failed to retrieve monotonic priority map : faking it"; // Make the map monotonic, so we can map priority to niceness and back without losing // information. for (int p = kMinThreadPriority; p <= kMaxThreadPriority; ++p) {
pm[p] = 5 - p;
}
}
std::ostringstream priority_map_string; for (int p = kMinThreadPriority; p <= kMaxThreadPriority; ++p) { if (p != kMinThreadPriority) {
priority_map_string << ", ";
}
priority_map_string << priorityMap[p];
}
LOG(INFO) << "Priority-to-niceness mapping: " << priority_map_string.str(); #undef CHECK_DEFERRED_ABORT
}); if (failure_msg != nullptr) { // Calls to GetPriorityMap() during dumping must return plausible values. for (int p = kMinThreadPriority; p <= kMaxThreadPriority; ++p) {
priorityMap[p] = 5 - p;
}
LOG(FATAL) << failure_msg;
} return priorityMap;
}
// Many niceness values don't correspond to a priority. Find and return a close one. int Thread::NicenessToPriority(int niceness) {
DCHECK_GE(niceness, kMinNiceness);
DCHECK_LE(niceness, kMaxNiceness); int* pm = GetPriorityMap(); int* bound = std::lower_bound(pm + kMinThreadPriority,
pm + kMaxThreadPriority + 1,
niceness,
std::greater() /* niceness decreases */); if (bound > pm + kMaxThreadPriority) { return kMaxThreadPriority;
} if (bound == pm + kMinThreadPriority) { return kMinThreadPriority;
} // The closest is either bound[0] or bound[-1].
DCHECK_LE(bound[0], niceness);
DCHECK_GT(bound[-1], niceness); // Resolve ties towards the higher priority. This usually maps system daemon priority to Java // normal priority, which is the traditional behavior we test for. returnstatic_cast<int>((niceness - bound[0] > bound[-1] - niceness) ? bound - pm - 1
: bound - pm);
}
int Thread::SetNativeNiceness(int niceness) { int ret = setpriority(PRIO_PROCESS, static_cast<id_t>(GetTid()), niceness); if (ret == 0) { return0;
}
LOG(WARNING) << "Cannot set niceness to " << niceness; // TODO: With PaletteMapPriority we may want to do more here. return errno;
}
int Thread::GetNativeNiceness() const {
errno = 0; int niceness = getpriority(PRIO_PROCESS, static_cast<id_t>(GetTid())); if (niceness == -1 && errno != 0) {
LOG(gAborting == 0 ? FATAL_WITHOUT_ABORT : ERROR)
<< "getpriority() in GetNativeNiceness() failed: " << strerror(errno); // This may mean the world is badly broken. Tread carefully, producing as much information as // possible before crashing, one way or another.
LOG(gAborting == 0 ? FATAL_WITHOUT_ABORT : ERROR) << "\ttid: " << GetTid();
std::string name;
GetThreadName(name);
LOG(gAborting == 0 ? FATAL : ERROR) << "\tthread name: " << name;
niceness = 19; // A valid result that will hopefully stand out.
} return niceness;
}
void Thread::SetNativePriority(int new_priority, int new_niceness) { if (kIsDebugBuild && Thread::Current() == this && GetPeer() != nullptr &&
GetCachedNiceness() != new_niceness) { // We do this in some tests, but it should not normally happen. // We test only for self == this, to avoid middle-of-thread-flip issues.
LOG(VERBOSE) << "Setting priority to unexpected value " << new_niceness;
} if (canSetPriority) { if (UNLIKELY(NeedSWorkaround())) {
palette_status_t status = PaletteSchedSetPriority(GetTid(), new_priority);
CHECK(status == PALETTE_STATUS_OK || status == PALETTE_STATUS_CHECK_ERRNO);
} else {
SetNativeNiceness(new_niceness);
}
}
}
void Thread::SetNativePriority(int new_priority) { int n = PriorityToNiceness(new_priority);
SetNativePriority(new_priority, n);
}
void Thread::AbortInThis(const std::string& message) {
std::string thread_name;
Thread::Current()->GetThreadName(thread_name);
LOG(ERROR) << message;
LOG(ERROR) << "Aborting culprit thread";
Runtime::Current()->SetAbortMessage(("Caused " + thread_name + " failure : " + message).c_str()); // Unlike Runtime::Abort() we do not fflush(nullptr), since we want to send the signal with as // little delay as possible. int res = pthread_kill(tlsPtr_.pthread_self, SIGABRT); if (res != 0) {
LOG(ERROR) << "pthread_kill failed with " << res << " " << strerror(res) << " target was "
<< tls32_.tid;
} else { // Wait for our process to be aborted.
sleep(10/* seconds */);
} // The process should have died long before we got here. Never return.
LOG(FATAL) << "Failed to abort in culprit thread: " << message;
UNREACHABLE();
}
int Thread::GetCachedNiceness() const { // TODO: Possibly consider inlining again. A straightforward move to thread-inl.h requires // additional includes there to get GetInt() defined, which then result in build failures for // other uses of that file.
DCHECK_EQ(this, Thread::Current());
mirror::Object* peer = GetPeer(); if (peer == nullptr) { return0;
} // Respects the fact that the `niceness` field is volatile. return WellKnownClasses::java_lang_Thread_niceness->GetInt(peer);
}
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.