Mercurial > hg > graal-jvmci-8
diff src/os/solaris/vm/os_solaris.cpp @ 10405:f2110083203d
8005849: JEP 167: Event-Based JVM Tracing
Reviewed-by: acorn, coleenp, sla
Contributed-by: Karen Kinnear <karen.kinnear@oracle.com>, Bengt Rutisson <bengt.rutisson@oracle.com>, Calvin Cheung <calvin.cheung@oracle.com>, Erik Gahlin <erik.gahlin@oracle.com>, Erik Helin <erik.helin@oracle.com>, Jesper Wilhelmsson <jesper.wilhelmsson@oracle.com>, Keith McGuigan <keith.mcguigan@oracle.com>, Mattias Tobiasson <mattias.tobiasson@oracle.com>, Markus Gronlund <markus.gronlund@oracle.com>, Mikael Auno <mikael.auno@oracle.com>, Nils Eliasson <nils.eliasson@oracle.com>, Nils Loodin <nils.loodin@oracle.com>, Rickard Backman <rickard.backman@oracle.com>, Staffan Larsen <staffan.larsen@oracle.com>, Stefan Karlsson <stefan.karlsson@oracle.com>, Yekaterina Kantserova <yekaterina.kantserova@oracle.com>
author | sla |
---|---|
date | Mon, 10 Jun 2013 11:30:51 +0200 |
parents | 9ce110b1d14a |
children | 836a62f43af9 a837fa3d3f86 |
line wrap: on
line diff
--- a/src/os/solaris/vm/os_solaris.cpp Fri Jun 07 09:33:01 2013 -0700 +++ b/src/os/solaris/vm/os_solaris.cpp Mon Jun 10 11:30:51 2013 +0200 @@ -240,6 +240,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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(); }