mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8005849: JEP 167: Event-Based JVM Tracing
Co-authored-by: Karen Kinnear <karen.kinnear@oracle.com> Co-authored-by: Bengt Rutisson <bengt.rutisson@oracle.com> Co-authored-by: Calvin Cheung <calvin.cheung@oracle.com> Co-authored-by: Erik Gahlin <erik.gahlin@oracle.com> Co-authored-by: Erik Helin <erik.helin@oracle.com> Co-authored-by: Jesper Wilhelmsson <jesper.wilhelmsson@oracle.com> Co-authored-by: Keith McGuigan <keith.mcguigan@oracle.com> Co-authored-by: Mattias Tobiasson <mattias.tobiasson@oracle.com> Co-authored-by: Markus Gronlund <markus.gronlund@oracle.com> Co-authored-by: Mikael Auno <mikael.auno@oracle.com> Co-authored-by: Nils Eliasson <nils.eliasson@oracle.com> Co-authored-by: Nils Loodin <nils.loodin@oracle.com> Co-authored-by: Rickard Backman <rickard.backman@oracle.com> Co-authored-by: Stefan Karlsson <stefan.karlsson@oracle.com> Co-authored-by: Yekaterina Kantserova <yekaterina.kantserova@oracle.com> Reviewed-by: acorn, coleenp, sla
This commit is contained in:
parent
6b2c468c35
commit
718f3252f6
195 changed files with 7628 additions and 1484 deletions
|
@ -240,6 +240,8 @@ extern "C" {
|
|||
static int pthread_cond_default_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; }
|
||||
}
|
||||
|
||||
static void unpackTime(timespec* absTime, bool isAbsolute, jlong time);
|
||||
|
||||
// Thread Local Storage
|
||||
// This is common to all Solaris platforms so it is defined here,
|
||||
// in this common file.
|
||||
|
@ -2580,6 +2582,57 @@ void* os::user_handler() {
|
|||
return CAST_FROM_FN_PTR(void*, UserHandler);
|
||||
}
|
||||
|
||||
class Semaphore : public StackObj {
|
||||
public:
|
||||
Semaphore();
|
||||
~Semaphore();
|
||||
void signal();
|
||||
void wait();
|
||||
bool trywait();
|
||||
bool timedwait(unsigned int sec, int nsec);
|
||||
private:
|
||||
sema_t _semaphore;
|
||||
};
|
||||
|
||||
|
||||
Semaphore::Semaphore() {
|
||||
sema_init(&_semaphore, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
sema_destroy(&_semaphore);
|
||||
}
|
||||
|
||||
void Semaphore::signal() {
|
||||
sema_post(&_semaphore);
|
||||
}
|
||||
|
||||
void Semaphore::wait() {
|
||||
sema_wait(&_semaphore);
|
||||
}
|
||||
|
||||
bool Semaphore::trywait() {
|
||||
return sema_trywait(&_semaphore) == 0;
|
||||
}
|
||||
|
||||
bool Semaphore::timedwait(unsigned int sec, int nsec) {
|
||||
struct timespec ts;
|
||||
unpackTime(&ts, false, (sec * NANOSECS_PER_SEC) + nsec);
|
||||
|
||||
while (1) {
|
||||
int result = sema_timedwait(&_semaphore, &ts);
|
||||
if (result == 0) {
|
||||
return true;
|
||||
} else if (errno == EINTR) {
|
||||
continue;
|
||||
} else if (errno == ETIME) {
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
typedef void (*sa_handler_t)(int);
|
||||
typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
|
||||
|
@ -4164,6 +4217,68 @@ void os::hint_no_preempt() {
|
|||
schedctl_start(schedctl_init());
|
||||
}
|
||||
|
||||
static void resume_clear_context(OSThread *osthread) {
|
||||
osthread->set_ucontext(NULL);
|
||||
}
|
||||
|
||||
static void suspend_save_context(OSThread *osthread, ucontext_t* context) {
|
||||
osthread->set_ucontext(context);
|
||||
}
|
||||
|
||||
static Semaphore sr_semaphore;
|
||||
|
||||
void os::Solaris::SR_handler(Thread* thread, ucontext_t* uc) {
|
||||
// Save and restore errno to avoid confusing native code with EINTR
|
||||
// after sigsuspend.
|
||||
int old_errno = errno;
|
||||
|
||||
OSThread* osthread = thread->osthread();
|
||||
assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread");
|
||||
|
||||
os::SuspendResume::State current = osthread->sr.state();
|
||||
if (current == os::SuspendResume::SR_SUSPEND_REQUEST) {
|
||||
suspend_save_context(osthread, uc);
|
||||
|
||||
// attempt to switch the state, we assume we had a SUSPEND_REQUEST
|
||||
os::SuspendResume::State state = osthread->sr.suspended();
|
||||
if (state == os::SuspendResume::SR_SUSPENDED) {
|
||||
sigset_t suspend_set; // signals for sigsuspend()
|
||||
|
||||
// get current set of blocked signals and unblock resume signal
|
||||
thr_sigsetmask(SIG_BLOCK, NULL, &suspend_set);
|
||||
sigdelset(&suspend_set, os::Solaris::SIGasync());
|
||||
|
||||
sr_semaphore.signal();
|
||||
// wait here until we are resumed
|
||||
while (1) {
|
||||
sigsuspend(&suspend_set);
|
||||
|
||||
os::SuspendResume::State result = osthread->sr.running();
|
||||
if (result == os::SuspendResume::SR_RUNNING) {
|
||||
sr_semaphore.signal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (state == os::SuspendResume::SR_RUNNING) {
|
||||
// request was cancelled, continue
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
resume_clear_context(osthread);
|
||||
} else if (current == os::SuspendResume::SR_RUNNING) {
|
||||
// request was cancelled, continue
|
||||
} else if (current == os::SuspendResume::SR_WAKEUP_REQUEST) {
|
||||
// ignore
|
||||
} else {
|
||||
// ignore
|
||||
}
|
||||
|
||||
errno = old_errno;
|
||||
}
|
||||
|
||||
|
||||
void os::interrupt(Thread* thread) {
|
||||
assert(Thread::current() == thread || Threads_lock->owned_by_self(), "possibility of dangling Thread pointer");
|
||||
|
||||
|
@ -4247,6 +4362,116 @@ int os::message_box(const char* title, const char* message) {
|
|||
return buf[0] == 'y' || buf[0] == 'Y';
|
||||
}
|
||||
|
||||
static int sr_notify(OSThread* osthread) {
|
||||
int status = thr_kill(osthread->thread_id(), os::Solaris::SIGasync());
|
||||
assert_status(status == 0, status, "thr_kill");
|
||||
return status;
|
||||
}
|
||||
|
||||
// "Randomly" selected value for how long we want to spin
|
||||
// before bailing out on suspending a thread, also how often
|
||||
// we send a signal to a thread we want to resume
|
||||
static const int RANDOMLY_LARGE_INTEGER = 1000000;
|
||||
static const int RANDOMLY_LARGE_INTEGER2 = 100;
|
||||
|
||||
static bool do_suspend(OSThread* osthread) {
|
||||
assert(osthread->sr.is_running(), "thread should be running");
|
||||
assert(!sr_semaphore.trywait(), "semaphore has invalid state");
|
||||
|
||||
// mark as suspended and send signal
|
||||
if (osthread->sr.request_suspend() != os::SuspendResume::SR_SUSPEND_REQUEST) {
|
||||
// failed to switch, state wasn't running?
|
||||
ShouldNotReachHere();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sr_notify(osthread) != 0) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
// managed to send the signal and switch to SUSPEND_REQUEST, now wait for SUSPENDED
|
||||
while (true) {
|
||||
if (sr_semaphore.timedwait(0, 2000 * NANOSECS_PER_MILLISEC)) {
|
||||
break;
|
||||
} else {
|
||||
// timeout
|
||||
os::SuspendResume::State cancelled = osthread->sr.cancel_suspend();
|
||||
if (cancelled == os::SuspendResume::SR_RUNNING) {
|
||||
return false;
|
||||
} else if (cancelled == os::SuspendResume::SR_SUSPENDED) {
|
||||
// make sure that we consume the signal on the semaphore as well
|
||||
sr_semaphore.wait();
|
||||
break;
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guarantee(osthread->sr.is_suspended(), "Must be suspended");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void do_resume(OSThread* osthread) {
|
||||
assert(osthread->sr.is_suspended(), "thread should be suspended");
|
||||
assert(!sr_semaphore.trywait(), "invalid semaphore state");
|
||||
|
||||
if (osthread->sr.request_wakeup() != os::SuspendResume::SR_WAKEUP_REQUEST) {
|
||||
// failed to switch to WAKEUP_REQUEST
|
||||
ShouldNotReachHere();
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (sr_notify(osthread) == 0) {
|
||||
if (sr_semaphore.timedwait(0, 2 * NANOSECS_PER_MILLISEC)) {
|
||||
if (osthread->sr.is_running()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
guarantee(osthread->sr.is_running(), "Must be running!");
|
||||
}
|
||||
|
||||
void os::SuspendedThreadTask::internal_do_task() {
|
||||
if (do_suspend(_thread->osthread())) {
|
||||
SuspendedThreadTaskContext context(_thread, _thread->osthread()->ucontext());
|
||||
do_task(context);
|
||||
do_resume(_thread->osthread());
|
||||
}
|
||||
}
|
||||
|
||||
class PcFetcher : public os::SuspendedThreadTask {
|
||||
public:
|
||||
PcFetcher(Thread* thread) : os::SuspendedThreadTask(thread) {}
|
||||
ExtendedPC result();
|
||||
protected:
|
||||
void do_task(const os::SuspendedThreadTaskContext& context);
|
||||
private:
|
||||
ExtendedPC _epc;
|
||||
};
|
||||
|
||||
ExtendedPC PcFetcher::result() {
|
||||
guarantee(is_done(), "task is not done yet.");
|
||||
return _epc;
|
||||
}
|
||||
|
||||
void PcFetcher::do_task(const os::SuspendedThreadTaskContext& context) {
|
||||
Thread* thread = context.thread();
|
||||
OSThread* osthread = thread->osthread();
|
||||
if (osthread->ucontext() != NULL) {
|
||||
_epc = os::Solaris::ucontext_get_pc((ucontext_t *) context.ucontext());
|
||||
} else {
|
||||
// NULL context is unexpected, double-check this is the VMThread
|
||||
guarantee(thread->is_VM_thread(), "can only be called for VMThread");
|
||||
}
|
||||
}
|
||||
|
||||
// A lightweight implementation that does not suspend the target thread and
|
||||
// thus returns only a hint. Used for profiling only!
|
||||
ExtendedPC os::get_thread_pc(Thread* thread) {
|
||||
|
@ -4254,21 +4479,9 @@ ExtendedPC os::get_thread_pc(Thread* thread) {
|
|||
assert(Thread::current()->is_Watcher_thread(), "Must be watcher and own Threads_lock");
|
||||
// For now, is only used to profile the VM Thread
|
||||
assert(thread->is_VM_thread(), "Can only be called for VMThread");
|
||||
ExtendedPC epc;
|
||||
|
||||
GetThreadPC_Callback cb(ProfileVM_lock);
|
||||
OSThread *osthread = thread->osthread();
|
||||
const int time_to_wait = 400; // 400ms wait for initial response
|
||||
int status = cb.interrupt(thread, time_to_wait);
|
||||
|
||||
if (cb.is_done() ) {
|
||||
epc = cb.addr();
|
||||
} else {
|
||||
DEBUG_ONLY(tty->print_cr("Failed to get pc for thread: %d got %d status",
|
||||
osthread->thread_id(), status););
|
||||
// epc is already NULL
|
||||
}
|
||||
return epc;
|
||||
PcFetcher fetcher(thread);
|
||||
fetcher.run();
|
||||
return fetcher.result();
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue