You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
14 KiB
438 lines
14 KiB
/* Copyright (C) 2017 The Android Open Source Project
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This file implements interfaces from the file jvmti.h. This implementation
|
|
* is licensed under the same terms as the file jvmti.h. The
|
|
* copyright and license information for the file jvmti.h follows.
|
|
*
|
|
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include "ti_monitor.h"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
|
|
#include "art_jvmti.h"
|
|
#include "gc_root-inl.h"
|
|
#include "mirror/object-inl.h"
|
|
#include "monitor.h"
|
|
#include "runtime.h"
|
|
#include "scoped_thread_state_change-inl.h"
|
|
#include "thread-current-inl.h"
|
|
#include "ti_thread.h"
|
|
#include "thread.h"
|
|
#include "thread_pool.h"
|
|
|
|
namespace openjdkjvmti {
|
|
|
|
// We cannot use ART monitors, as they require the mutator lock for contention locking. We
|
|
// also cannot use pthread mutexes and condition variables (or C++11 abstractions) directly,
|
|
// as the do not have the right semantics for recursive mutexes and waiting (wait only unlocks
|
|
// the mutex once).
|
|
// So go ahead and use a wrapper that does the counting explicitly.
|
|
|
|
class JvmtiMonitor {
|
|
public:
|
|
JvmtiMonitor() : owner_(nullptr), count_(0) { }
|
|
|
|
static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) NO_THREAD_SAFETY_ANALYSIS {
|
|
// Check whether this thread holds the monitor, or nobody does.
|
|
art::Thread* owner_thread = monitor->owner_.load(std::memory_order_relaxed);
|
|
if (owner_thread != nullptr && self != owner_thread) {
|
|
return false;
|
|
}
|
|
|
|
if (monitor->count_ > 0) {
|
|
monitor->count_ = 0;
|
|
monitor->owner_.store(nullptr, std::memory_order_relaxed);
|
|
monitor->mutex_.unlock();
|
|
}
|
|
|
|
delete monitor;
|
|
return true;
|
|
}
|
|
|
|
void MonitorEnter(art::Thread* self, bool suspend) NO_THREAD_SAFETY_ANALYSIS {
|
|
// Perform a suspend-check. The spec doesn't require this but real-world agents depend on this
|
|
// behavior. We do this by performing a suspend-check then retrying if the thread is suspended
|
|
// before or after locking the internal mutex.
|
|
do {
|
|
if (suspend) {
|
|
ThreadUtil::SuspendCheck(self);
|
|
if (ThreadUtil::WouldSuspendForUserCode(self)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Check for recursive enter.
|
|
if (IsOwner(self)) {
|
|
count_++;
|
|
return;
|
|
}
|
|
|
|
// Checking for user-code suspension takes acquiring 2 art::Mutexes so we want to avoid doing
|
|
// that if possible. To avoid it we try to get the internal mutex without sleeping. If we do
|
|
// this we don't bother doing another suspend check since it can linearize after the lock.
|
|
if (mutex_.try_lock()) {
|
|
break;
|
|
} else {
|
|
// Lock with sleep. We will need to check for suspension after this to make sure that agents
|
|
// won't deadlock.
|
|
mutex_.lock();
|
|
if (!suspend || !ThreadUtil::WouldSuspendForUserCode(self)) {
|
|
break;
|
|
} else {
|
|
// We got suspended in the middle of waiting for the mutex. We should release the mutex
|
|
// and try again so we can get it while not suspended. This lets some other
|
|
// (non-suspended) thread acquire the mutex in case it's waiting to wake us up.
|
|
mutex_.unlock();
|
|
continue;
|
|
}
|
|
}
|
|
} while (true);
|
|
|
|
DCHECK(owner_.load(std::memory_order_relaxed) == nullptr);
|
|
owner_.store(self, std::memory_order_relaxed);
|
|
DCHECK_EQ(0u, count_);
|
|
count_ = 1;
|
|
}
|
|
|
|
bool MonitorExit(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS {
|
|
if (!IsOwner(self)) {
|
|
return false;
|
|
}
|
|
|
|
--count_;
|
|
if (count_ == 0u) {
|
|
owner_.store(nullptr, std::memory_order_relaxed);
|
|
mutex_.unlock();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Wait(art::Thread* self) {
|
|
auto wait_without_timeout = [&](std::unique_lock<std::mutex>& lk) {
|
|
cond_.wait(lk);
|
|
};
|
|
return Wait(self, wait_without_timeout);
|
|
}
|
|
|
|
bool Wait(art::Thread* self, uint64_t timeout_in_ms) {
|
|
auto wait_with_timeout = [&](std::unique_lock<std::mutex>& lk) {
|
|
cond_.wait_for(lk, std::chrono::milliseconds(timeout_in_ms));
|
|
};
|
|
return Wait(self, wait_with_timeout);
|
|
}
|
|
|
|
bool Notify(art::Thread* self) {
|
|
return Notify(self, [&]() { cond_.notify_one(); });
|
|
}
|
|
|
|
bool NotifyAll(art::Thread* self) {
|
|
return Notify(self, [&]() { cond_.notify_all(); });
|
|
}
|
|
|
|
private:
|
|
bool IsOwner(art::Thread* self) const {
|
|
// There's a subtle correctness argument here for a relaxed load outside the critical section.
|
|
// A thread is guaranteed to see either its own latest store or another thread's store. If a
|
|
// thread sees another thread's store than it cannot be holding the lock.
|
|
art::Thread* owner_thread = owner_.load(std::memory_order_relaxed);
|
|
return self == owner_thread;
|
|
}
|
|
|
|
template <typename T>
|
|
bool Wait(art::Thread* self, T how_to_wait) {
|
|
if (!IsOwner(self)) {
|
|
return false;
|
|
}
|
|
|
|
size_t old_count = count_;
|
|
DCHECK_GT(old_count, 0u);
|
|
|
|
count_ = 0;
|
|
owner_.store(nullptr, std::memory_order_relaxed);
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lk(mutex_, std::adopt_lock);
|
|
how_to_wait(lk);
|
|
// Here we release the mutex. We will get it back below. We first need to do a suspend-check
|
|
// without holding it however. This is done in the MonitorEnter function.
|
|
// TODO We could do this more efficiently.
|
|
// We hold the mutex_ but the overall monitor is not owned at this point.
|
|
CHECK(owner_.load(std::memory_order_relaxed) == nullptr);
|
|
DCHECK_EQ(0u, count_);
|
|
}
|
|
|
|
// Reaquire the mutex/monitor, also go to sleep if we were suspended.
|
|
// TODO Give an extension to wait without suspension as well.
|
|
MonitorEnter(self, /*suspend=*/ true);
|
|
CHECK(owner_.load(std::memory_order_relaxed) == self);
|
|
DCHECK_EQ(1u, count_);
|
|
// Reset the count.
|
|
count_ = old_count;
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool Notify(art::Thread* self, T how_to_notify) {
|
|
if (!IsOwner(self)) {
|
|
return false;
|
|
}
|
|
|
|
how_to_notify();
|
|
|
|
return true;
|
|
}
|
|
|
|
std::mutex mutex_;
|
|
std::condition_variable cond_;
|
|
std::atomic<art::Thread*> owner_;
|
|
size_t count_;
|
|
};
|
|
|
|
static jrawMonitorID EncodeMonitor(JvmtiMonitor* monitor) {
|
|
return reinterpret_cast<jrawMonitorID>(monitor);
|
|
}
|
|
|
|
static JvmtiMonitor* DecodeMonitor(jrawMonitorID id) {
|
|
return reinterpret_cast<JvmtiMonitor*>(id);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::CreateRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED,
|
|
const char* name,
|
|
jrawMonitorID* monitor_ptr) {
|
|
if (name == nullptr || monitor_ptr == nullptr) {
|
|
return ERR(NULL_POINTER);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = new JvmtiMonitor();
|
|
*monitor_ptr = EncodeMonitor(monitor);
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::DestroyRawMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
if (!JvmtiMonitor::Destroy(self, monitor)) {
|
|
return ERR(NOT_MONITOR_OWNER);
|
|
}
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::RawMonitorEnterNoSuspend(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
monitor->MonitorEnter(self, /*suspend=*/false);
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::RawMonitorEnter(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
monitor->MonitorEnter(self, /*suspend=*/true);
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::RawMonitorExit(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
if (!monitor->MonitorExit(self)) {
|
|
return ERR(NOT_MONITOR_OWNER);
|
|
}
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::RawMonitorWait(jvmtiEnv* env ATTRIBUTE_UNUSED,
|
|
jrawMonitorID id,
|
|
jlong millis) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
// What millis < 0 means is not defined in the spec. Real world agents seem to assume that it is a
|
|
// valid call though. We treat it as though it was 0 and wait indefinitely.
|
|
bool result = (millis > 0)
|
|
? monitor->Wait(self, static_cast<uint64_t>(millis))
|
|
: monitor->Wait(self);
|
|
|
|
if (!result) {
|
|
return ERR(NOT_MONITOR_OWNER);
|
|
}
|
|
|
|
// TODO: Make sure that is really what we should be checking here.
|
|
if (self->IsInterrupted()) {
|
|
return ERR(INTERRUPT);
|
|
}
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::RawMonitorNotify(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
if (!monitor->Notify(self)) {
|
|
return ERR(NOT_MONITOR_OWNER);
|
|
}
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::RawMonitorNotifyAll(jvmtiEnv* env ATTRIBUTE_UNUSED, jrawMonitorID id) {
|
|
if (id == nullptr) {
|
|
return ERR(INVALID_MONITOR);
|
|
}
|
|
|
|
JvmtiMonitor* monitor = DecodeMonitor(id);
|
|
art::Thread* self = art::Thread::Current();
|
|
|
|
if (!monitor->NotifyAll(self)) {
|
|
return ERR(NOT_MONITOR_OWNER);
|
|
}
|
|
|
|
return ERR(NONE);
|
|
}
|
|
|
|
jvmtiError MonitorUtil::GetCurrentContendedMonitor(jvmtiEnv* env ATTRIBUTE_UNUSED,
|
|
jthread thread,
|
|
jobject* monitor) {
|
|
if (monitor == nullptr) {
|
|
return ERR(NULL_POINTER);
|
|
}
|
|
art::Thread* self = art::Thread::Current();
|
|
art::ScopedObjectAccess soa(self);
|
|
art::Locks::thread_list_lock_->ExclusiveLock(self);
|
|
art::Thread* target = nullptr;
|
|
jvmtiError err = ERR(INTERNAL);
|
|
if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
|
|
art::Locks::thread_list_lock_->ExclusiveUnlock(self);
|
|
return err;
|
|
}
|
|
struct GetContendedMonitorClosure : public art::Closure {
|
|
public:
|
|
GetContendedMonitorClosure() : out_(nullptr) {}
|
|
|
|
void Run(art::Thread* target_thread) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
|
|
art::ScopedAssertNoThreadSuspension sants("GetContendedMonitorClosure::Run");
|
|
switch (target_thread->GetState()) {
|
|
// These three we are actually currently waiting on a monitor and have sent the appropriate
|
|
// events (if anyone is listening).
|
|
case art::kBlocked:
|
|
case art::kTimedWaiting:
|
|
case art::kWaiting: {
|
|
out_ = art::GcRoot<art::mirror::Object>(art::Monitor::GetContendedMonitor(target_thread));
|
|
return;
|
|
}
|
|
case art::kTerminated:
|
|
case art::kRunnable:
|
|
case art::kSleeping:
|
|
case art::kWaitingForLockInflation:
|
|
case art::kWaitingForTaskProcessor:
|
|
case art::kWaitingForGcToComplete:
|
|
case art::kWaitingForCheckPointsToRun:
|
|
case art::kWaitingPerformingGc:
|
|
case art::kWaitingForDebuggerSend:
|
|
case art::kWaitingForDebuggerToAttach:
|
|
case art::kWaitingInMainDebuggerLoop:
|
|
case art::kWaitingForDebuggerSuspension:
|
|
case art::kWaitingForJniOnLoad:
|
|
case art::kWaitingForSignalCatcherOutput:
|
|
case art::kWaitingInMainSignalCatcherLoop:
|
|
case art::kWaitingForDeoptimization:
|
|
case art::kWaitingForMethodTracingStart:
|
|
case art::kWaitingForVisitObjects:
|
|
case art::kWaitingForGetObjectsAllocated:
|
|
case art::kWaitingWeakGcRootRead:
|
|
case art::kWaitingForGcThreadFlip:
|
|
case art::kNativeForAbort:
|
|
case art::kStarting:
|
|
case art::kNative:
|
|
case art::kSuspended: {
|
|
// We aren't currently (explicitly) waiting for a monitor so just return null.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
jobject GetResult() REQUIRES_SHARED(art::Locks::mutator_lock_) {
|
|
return out_.IsNull()
|
|
? nullptr
|
|
: art::Thread::Current()->GetJniEnv()->AddLocalReference<jobject>(out_.Read());
|
|
}
|
|
|
|
private:
|
|
art::GcRoot<art::mirror::Object> out_;
|
|
};
|
|
art::ScopedAssertNoThreadSuspension sants("Performing GetCurrentContendedMonitor");
|
|
GetContendedMonitorClosure closure;
|
|
// RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution. We
|
|
// need to avoid suspending as we wait for the checkpoint to occur since we are (potentially)
|
|
// transfering a GcRoot across threads.
|
|
if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) {
|
|
return ERR(THREAD_NOT_ALIVE);
|
|
}
|
|
*monitor = closure.GetResult();
|
|
return OK;
|
|
}
|
|
|
|
} // namespace openjdkjvmti
|