| duke@0 | /*
|
| trims@1772 | * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
|
| duke@0 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
| duke@0 | *
|
| duke@0 | * This code is free software; you can redistribute it and/or modify it
|
| duke@0 | * under the terms of the GNU General Public License version 2 only, as
|
| duke@0 | * published by the Free Software Foundation.
|
| duke@0 | *
|
| duke@0 | * This code is distributed in the hope that it will be useful, but WITHOUT
|
| duke@0 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
| duke@0 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
| duke@0 | * version 2 for more details (a copy is included in the LICENSE file that
|
| duke@0 | * accompanied this code).
|
| duke@0 | *
|
| duke@0 | * You should have received a copy of the GNU General Public License version
|
| duke@0 | * 2 along with this work; if not, write to the Free Software Foundation,
|
| duke@0 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
| duke@0 | *
|
| trims@1772 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
| trims@1772 | * or visit www.oracle.com if you need additional information or have any
|
| trims@1772 | * questions.
|
| duke@0 | *
|
| duke@0 | */
|
| duke@0 |
|
| duke@0 | // do not include precompiled header file
|
| duke@0 | # include "incls/_os_solaris.cpp.incl"
|
| duke@0 |
|
| duke@0 | // put OS-includes here
|
| duke@0 | # include <dlfcn.h>
|
| duke@0 | # include <errno.h>
|
| duke@0 | # include <link.h>
|
| duke@0 | # include <poll.h>
|
| duke@0 | # include <pthread.h>
|
| duke@0 | # include <pwd.h>
|
| duke@0 | # include <schedctl.h>
|
| duke@0 | # include <setjmp.h>
|
| duke@0 | # include <signal.h>
|
| duke@0 | # include <stdio.h>
|
| duke@0 | # include <alloca.h>
|
| duke@0 | # include <sys/filio.h>
|
| duke@0 | # include <sys/ipc.h>
|
| duke@0 | # include <sys/lwp.h>
|
| duke@0 | # include <sys/machelf.h> // for elf Sym structure used by dladdr1
|
| duke@0 | # include <sys/mman.h>
|
| duke@0 | # include <sys/processor.h>
|
| duke@0 | # include <sys/procset.h>
|
| duke@0 | # include <sys/pset.h>
|
| duke@0 | # include <sys/resource.h>
|
| duke@0 | # include <sys/shm.h>
|
| duke@0 | # include <sys/socket.h>
|
| duke@0 | # include <sys/stat.h>
|
| duke@0 | # include <sys/systeminfo.h>
|
| duke@0 | # include <sys/time.h>
|
| duke@0 | # include <sys/times.h>
|
| duke@0 | # include <sys/types.h>
|
| duke@0 | # include <sys/wait.h>
|
| duke@0 | # include <sys/utsname.h>
|
| duke@0 | # include <thread.h>
|
| duke@0 | # include <unistd.h>
|
| duke@0 | # include <sys/priocntl.h>
|
| duke@0 | # include <sys/rtpriocntl.h>
|
| duke@0 | # include <sys/tspriocntl.h>
|
| duke@0 | # include <sys/iapriocntl.h>
|
| duke@0 | # include <sys/loadavg.h>
|
| duke@0 | # include <string.h>
|
| duke@0 |
|
| duke@0 | # define _STRUCTURED_PROC 1 // this gets us the new structured proc interfaces of 5.6 & later
|
| duke@0 | # include <sys/procfs.h> // see comment in <sys/procfs.h>
|
| duke@0 |
|
| duke@0 | #define MAX_PATH (2 * K)
|
| duke@0 |
|
| duke@0 | // for timer info max values which include all bits
|
| duke@0 | #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF)
|
| duke@0 |
|
| duke@0 | #ifdef _GNU_SOURCE
|
| duke@0 | // See bug #6514594
|
| duke@0 | extern "C" int madvise(caddr_t, size_t, int);
|
| duke@0 | extern "C" int memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg,
|
| duke@0 | int attr, int mask);
|
| duke@0 | #endif //_GNU_SOURCE
|
| duke@0 |
|
| duke@0 | /*
|
| duke@0 | MPSS Changes Start.
|
| duke@0 | The JVM binary needs to be built and run on pre-Solaris 9
|
| duke@0 | systems, but the constants needed by MPSS are only in Solaris 9
|
| duke@0 | header files. They are textually replicated here to allow
|
| duke@0 | building on earlier systems. Once building on Solaris 8 is
|
| duke@0 | no longer a requirement, these #defines can be replaced by ordinary
|
| duke@0 | system .h inclusion.
|
| duke@0 |
|
| duke@0 | In earlier versions of the JDK and Solaris, we used ISM for large pages.
|
| duke@0 | But ISM requires shared memory to achieve this and thus has many caveats.
|
| duke@0 | MPSS is a fully transparent and is a cleaner way to get large pages.
|
| duke@0 | Although we still require keeping ISM for backward compatiblitiy as well as
|
| duke@0 | giving the opportunity to use large pages on older systems it is
|
| duke@0 | recommended that MPSS be used for Solaris 9 and above.
|
| duke@0 |
|
| duke@0 | */
|
| duke@0 |
|
| duke@0 | #ifndef MC_HAT_ADVISE
|
| duke@0 |
|
| duke@0 | struct memcntl_mha {
|
| duke@0 | uint_t mha_cmd; /* command(s) */
|
| duke@0 | uint_t mha_flags;
|
| duke@0 | size_t mha_pagesize;
|
| duke@0 | };
|
| duke@0 | #define MC_HAT_ADVISE 7 /* advise hat map size */
|
| duke@0 | #define MHA_MAPSIZE_VA 0x1 /* set preferred page size */
|
| duke@0 | #define MAP_ALIGN 0x200 /* addr specifies alignment */
|
| duke@0 |
|
| duke@0 | #endif
|
| duke@0 | // MPSS Changes End.
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Here are some liblgrp types from sys/lgrp_user.h to be able to
|
| duke@0 | // compile on older systems without this header file.
|
| duke@0 |
|
| duke@0 | #ifndef MADV_ACCESS_LWP
|
| duke@0 | # define MADV_ACCESS_LWP 7 /* next LWP to access heavily */
|
| duke@0 | #endif
|
| duke@0 | #ifndef MADV_ACCESS_MANY
|
| duke@0 | # define MADV_ACCESS_MANY 8 /* many processes to access heavily */
|
| duke@0 | #endif
|
| duke@0 |
|
| iveresov@201 | #ifndef LGRP_RSRC_CPU
|
| iveresov@201 | # define LGRP_RSRC_CPU 0 /* CPU resources */
|
| iveresov@201 | #endif
|
| iveresov@201 | #ifndef LGRP_RSRC_MEM
|
| iveresov@201 | # define LGRP_RSRC_MEM 1 /* memory resources */
|
| iveresov@201 | #endif
|
| iveresov@201 |
|
| duke@0 | // Some more macros from sys/mman.h that are not present in Solaris 8.
|
| duke@0 |
|
| duke@0 | #ifndef MAX_MEMINFO_CNT
|
| duke@0 | /*
|
| duke@0 | * info_req request type definitions for meminfo
|
| duke@0 | * request types starting with MEMINFO_V are used for Virtual addresses
|
| duke@0 | * and should not be mixed with MEMINFO_PLGRP which is targeted for Physical
|
| duke@0 | * addresses
|
| duke@0 | */
|
| duke@0 | # define MEMINFO_SHIFT 16
|
| duke@0 | # define MEMINFO_MASK (0xFF << MEMINFO_SHIFT)
|
| duke@0 | # define MEMINFO_VPHYSICAL (0x01 << MEMINFO_SHIFT) /* get physical addr */
|
| duke@0 | # define MEMINFO_VLGRP (0x02 << MEMINFO_SHIFT) /* get lgroup */
|
| duke@0 | # define MEMINFO_VPAGESIZE (0x03 << MEMINFO_SHIFT) /* size of phys page */
|
| duke@0 | # define MEMINFO_VREPLCNT (0x04 << MEMINFO_SHIFT) /* no. of replica */
|
| duke@0 | # define MEMINFO_VREPL (0x05 << MEMINFO_SHIFT) /* physical replica */
|
| duke@0 | # define MEMINFO_VREPL_LGRP (0x06 << MEMINFO_SHIFT) /* lgrp of replica */
|
| duke@0 | # define MEMINFO_PLGRP (0x07 << MEMINFO_SHIFT) /* lgroup for paddr */
|
| duke@0 |
|
| duke@0 | /* maximum number of addresses meminfo() can process at a time */
|
| duke@0 | # define MAX_MEMINFO_CNT 256
|
| duke@0 |
|
| duke@0 | /* maximum number of request types */
|
| duke@0 | # define MAX_MEMINFO_REQ 31
|
| duke@0 | #endif
|
| duke@0 |
|
| duke@0 | // see thr_setprio(3T) for the basis of these numbers
|
| duke@0 | #define MinimumPriority 0
|
| duke@0 | #define NormalPriority 64
|
| duke@0 | #define MaximumPriority 127
|
| duke@0 |
|
| duke@0 | // Values for ThreadPriorityPolicy == 1
|
| duke@0 | int prio_policy1[MaxPriority+1] = { -99999, 0, 16, 32, 48, 64,
|
| duke@0 | 80, 96, 112, 124, 127 };
|
| duke@0 |
|
| duke@0 | // System parameters used internally
|
| duke@0 | static clock_t clock_tics_per_sec = 100;
|
| duke@0 |
|
| duke@0 | // For diagnostics to print a message once. see run_periodic_checks
|
| duke@0 | static bool check_addr0_done = false;
|
| duke@0 | static sigset_t check_signal_done;
|
| duke@0 | static bool check_signals = true;
|
| duke@0 |
|
| duke@0 | address os::Solaris::handler_start; // start pc of thr_sighndlrinfo
|
| duke@0 | address os::Solaris::handler_end; // end pc of thr_sighndlrinfo
|
| duke@0 |
|
| duke@0 | address os::Solaris::_main_stack_base = NULL; // 4352906 workaround
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // "default" initializers for missing libc APIs
|
| duke@0 | extern "C" {
|
| duke@0 | static int lwp_mutex_init(mutex_t *mx, int scope, void *arg) { memset(mx, 0, sizeof(mutex_t)); return 0; }
|
| duke@0 | static int lwp_mutex_destroy(mutex_t *mx) { return 0; }
|
| duke@0 |
|
| duke@0 | static int lwp_cond_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; }
|
| duke@0 | static int lwp_cond_destroy(cond_t *cv) { return 0; }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // "default" initializers for pthread-based synchronization
|
| duke@0 | extern "C" {
|
| duke@0 | static int pthread_mutex_default_init(mutex_t *mx, int scope, void *arg) { memset(mx, 0, sizeof(mutex_t)); return 0; }
|
| duke@0 | static int pthread_cond_default_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Thread Local Storage
|
| duke@0 | // This is common to all Solaris platforms so it is defined here,
|
| duke@0 | // in this common file.
|
| duke@0 | // The declarations are in the os_cpu threadLS*.hpp files.
|
| duke@0 | //
|
| duke@0 | // Static member initialization for TLS
|
| duke@0 | Thread* ThreadLocalStorage::_get_thread_cache[ThreadLocalStorage::_pd_cache_size] = {NULL};
|
| duke@0 |
|
| duke@0 | #ifndef PRODUCT
|
| duke@0 | #define _PCT(n,d) ((100.0*(double)(n))/(double)(d))
|
| duke@0 |
|
| duke@0 | int ThreadLocalStorage::_tcacheHit = 0;
|
| duke@0 | int ThreadLocalStorage::_tcacheMiss = 0;
|
| duke@0 |
|
| duke@0 | void ThreadLocalStorage::print_statistics() {
|
| duke@0 | int total = _tcacheMiss+_tcacheHit;
|
| duke@0 | tty->print_cr("Thread cache hits %d misses %d total %d percent %f\n",
|
| duke@0 | _tcacheHit, _tcacheMiss, total, _PCT(_tcacheHit, total));
|
| duke@0 | }
|
| duke@0 | #undef _PCT
|
| duke@0 | #endif // PRODUCT
|
| duke@0 |
|
| duke@0 | Thread* ThreadLocalStorage::get_thread_via_cache_slowly(uintptr_t raw_id,
|
| duke@0 | int index) {
|
| duke@0 | Thread *thread = get_thread_slow();
|
| duke@0 | if (thread != NULL) {
|
| duke@0 | address sp = os::current_stack_pointer();
|
| duke@0 | guarantee(thread->_stack_base == NULL ||
|
| duke@0 | (sp <= thread->_stack_base &&
|
| duke@0 | sp >= thread->_stack_base - thread->_stack_size) ||
|
| duke@0 | is_error_reported(),
|
| duke@0 | "sp must be inside of selected thread stack");
|
| duke@0 |
|
| duke@0 | thread->_self_raw_id = raw_id; // mark for quick retrieval
|
| duke@0 | _get_thread_cache[ index ] = thread;
|
| duke@0 | }
|
| duke@0 | return thread;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | static const double all_zero[ sizeof(Thread) / sizeof(double) + 1 ] = {0};
|
| duke@0 | #define NO_CACHED_THREAD ((Thread*)all_zero)
|
| duke@0 |
|
| duke@0 | void ThreadLocalStorage::pd_set_thread(Thread* thread) {
|
| duke@0 |
|
| duke@0 | // Store the new value before updating the cache to prevent a race
|
| duke@0 | // between get_thread_via_cache_slowly() and this store operation.
|
| duke@0 | os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread);
|
| duke@0 |
|
| duke@0 | // Update thread cache with new thread if setting on thread create,
|
| duke@0 | // or NO_CACHED_THREAD (zeroed) thread if resetting thread on exit.
|
| duke@0 | uintptr_t raw = pd_raw_thread_id();
|
| duke@0 | int ix = pd_cache_index(raw);
|
| duke@0 | _get_thread_cache[ix] = thread == NULL ? NO_CACHED_THREAD : thread;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void ThreadLocalStorage::pd_init() {
|
| duke@0 | for (int i = 0; i < _pd_cache_size; i++) {
|
| duke@0 | _get_thread_cache[i] = NO_CACHED_THREAD;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Invalidate all the caches (happens to be the same as pd_init).
|
| duke@0 | void ThreadLocalStorage::pd_invalidate_all() { pd_init(); }
|
| duke@0 |
|
| duke@0 | #undef NO_CACHED_THREAD
|
| duke@0 |
|
| duke@0 | // END Thread Local Storage
|
| duke@0 |
|
| duke@0 | static inline size_t adjust_stack_size(address base, size_t size) {
|
| duke@0 | if ((ssize_t)size < 0) {
|
| duke@0 | // 4759953: Compensate for ridiculous stack size.
|
| duke@0 | size = max_intx;
|
| duke@0 | }
|
| duke@0 | if (size > (size_t)base) {
|
| duke@0 | // 4812466: Make sure size doesn't allow the stack to wrap the address space.
|
| duke@0 | size = (size_t)base;
|
| duke@0 | }
|
| duke@0 | return size;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static inline stack_t get_stack_info() {
|
| duke@0 | stack_t st;
|
| duke@0 | int retval = thr_stksegment(&st);
|
| duke@0 | st.ss_size = adjust_stack_size((address)st.ss_sp, st.ss_size);
|
| duke@0 | assert(retval == 0, "incorrect return value from thr_stksegment");
|
| duke@0 | assert((address)&st < (address)st.ss_sp, "Invalid stack base returned");
|
| duke@0 | assert((address)&st > (address)st.ss_sp-st.ss_size, "Invalid stack size returned");
|
| duke@0 | return st;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | address os::current_stack_base() {
|
| duke@0 | int r = thr_main() ;
|
| duke@0 | guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ;
|
| duke@0 | bool is_primordial_thread = r;
|
| duke@0 |
|
| duke@0 | // Workaround 4352906, avoid calls to thr_stksegment by
|
| duke@0 | // thr_main after the first one (it looks like we trash
|
| duke@0 | // some data, causing the value for ss_sp to be incorrect).
|
| duke@0 | if (!is_primordial_thread || os::Solaris::_main_stack_base == NULL) {
|
| duke@0 | stack_t st = get_stack_info();
|
| duke@0 | if (is_primordial_thread) {
|
| duke@0 | // cache initial value of stack base
|
| duke@0 | os::Solaris::_main_stack_base = (address)st.ss_sp;
|
| duke@0 | }
|
| duke@0 | return (address)st.ss_sp;
|
| duke@0 | } else {
|
| duke@0 | guarantee(os::Solaris::_main_stack_base != NULL, "Attempt to use null cached stack base");
|
| duke@0 | return os::Solaris::_main_stack_base;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | size_t os::current_stack_size() {
|
| duke@0 | size_t size;
|
| duke@0 |
|
| duke@0 | int r = thr_main() ;
|
| duke@0 | guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ;
|
| duke@0 | if(!r) {
|
| duke@0 | size = get_stack_info().ss_size;
|
| duke@0 | } else {
|
| duke@0 | struct rlimit limits;
|
| duke@0 | getrlimit(RLIMIT_STACK, &limits);
|
| duke@0 | size = adjust_stack_size(os::Solaris::_main_stack_base, (size_t)limits.rlim_cur);
|
| duke@0 | }
|
| duke@0 | // base may not be page aligned
|
| duke@0 | address base = current_stack_base();
|
| duke@0 | address bottom = (address)align_size_up((intptr_t)(base - size), os::vm_page_size());;
|
| duke@0 | return (size_t)(base - bottom);
|
| ysr@678 | }
|
| ysr@678 |
|
| ysr@678 | struct tm* os::localtime_pd(const time_t* clock, struct tm* res) {
|
| ysr@678 | return localtime_r(clock, res);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // interruptible infrastructure
|
| duke@0 |
|
| duke@0 | // setup_interruptible saves the thread state before going into an
|
| duke@0 | // interruptible system call.
|
| duke@0 | // The saved state is used to restore the thread to
|
| duke@0 | // its former state whether or not an interrupt is received.
|
| duke@0 | // Used by classloader os::read
|
| duke@0 | // hpi calls skip this layer and stay in _thread_in_native
|
| duke@0 |
|
| duke@0 | void os::Solaris::setup_interruptible(JavaThread* thread) {
|
| duke@0 |
|
| duke@0 | JavaThreadState thread_state = thread->thread_state();
|
| duke@0 |
|
| duke@0 | assert(thread_state != _thread_blocked, "Coming from the wrong thread");
|
| duke@0 | assert(thread_state != _thread_in_native, "Native threads skip setup_interruptible");
|
| duke@0 | OSThread* osthread = thread->osthread();
|
| duke@0 | osthread->set_saved_interrupt_thread_state(thread_state);
|
| duke@0 | thread->frame_anchor()->make_walkable(thread);
|
| duke@0 | ThreadStateTransition::transition(thread, thread_state, _thread_blocked);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Version of setup_interruptible() for threads that are already in
|
| duke@0 | // _thread_blocked. Used by os_sleep().
|
| duke@0 | void os::Solaris::setup_interruptible_already_blocked(JavaThread* thread) {
|
| duke@0 | thread->frame_anchor()->make_walkable(thread);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | JavaThread* os::Solaris::setup_interruptible() {
|
| duke@0 | JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread();
|
| duke@0 | setup_interruptible(thread);
|
| duke@0 | return thread;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::try_enable_extended_io() {
|
| duke@0 | typedef int (*enable_extended_FILE_stdio_t)(int, int);
|
| duke@0 |
|
| duke@0 | if (!UseExtendedFileIO) {
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | enable_extended_FILE_stdio_t enabler =
|
| duke@0 | (enable_extended_FILE_stdio_t) dlsym(RTLD_DEFAULT,
|
| duke@0 | "enable_extended_FILE_stdio");
|
| duke@0 | if (enabler) {
|
| duke@0 | enabler(-1, -1);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | #ifdef ASSERT
|
| duke@0 |
|
| duke@0 | JavaThread* os::Solaris::setup_interruptible_native() {
|
| duke@0 | JavaThread* thread = (JavaThread*)ThreadLocalStorage::thread();
|
| duke@0 | JavaThreadState thread_state = thread->thread_state();
|
| duke@0 | assert(thread_state == _thread_in_native, "Assumed thread_in_native");
|
| duke@0 | return thread;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::cleanup_interruptible_native(JavaThread* thread) {
|
| duke@0 | JavaThreadState thread_state = thread->thread_state();
|
| duke@0 | assert(thread_state == _thread_in_native, "Assumed thread_in_native");
|
| duke@0 | }
|
| duke@0 | #endif
|
| duke@0 |
|
| duke@0 | // cleanup_interruptible reverses the effects of setup_interruptible
|
| duke@0 | // setup_interruptible_already_blocked() does not need any cleanup.
|
| duke@0 |
|
| duke@0 | void os::Solaris::cleanup_interruptible(JavaThread* thread) {
|
| duke@0 | OSThread* osthread = thread->osthread();
|
| duke@0 |
|
| duke@0 | ThreadStateTransition::transition(thread, _thread_blocked, osthread->saved_interrupt_thread_state());
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // I/O interruption related counters called in _INTERRUPTIBLE
|
| duke@0 |
|
| duke@0 | void os::Solaris::bump_interrupted_before_count() {
|
| duke@0 | RuntimeService::record_interrupted_before_count();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::bump_interrupted_during_count() {
|
| duke@0 | RuntimeService::record_interrupted_during_count();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static int _processors_online = 0;
|
| duke@0 |
|
| duke@0 | jint os::Solaris::_os_thread_limit = 0;
|
| duke@0 | volatile jint os::Solaris::_os_thread_count = 0;
|
| duke@0 |
|
| duke@0 | julong os::available_memory() {
|
| duke@0 | return Solaris::available_memory();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | julong os::Solaris::available_memory() {
|
| duke@0 | return (julong)sysconf(_SC_AVPHYS_PAGES) * os::vm_page_size();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | julong os::Solaris::_physical_memory = 0;
|
| duke@0 |
|
| duke@0 | julong os::physical_memory() {
|
| duke@0 | return Solaris::physical_memory();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | julong os::allocatable_physical_memory(julong size) {
|
| duke@0 | #ifdef _LP64
|
| duke@0 | return size;
|
| duke@0 | #else
|
| duke@0 | julong result = MIN2(size, (julong)3835*M);
|
| duke@0 | if (!is_allocatable(result)) {
|
| duke@0 | // Memory allocations will be aligned but the alignment
|
| duke@0 | // is not known at this point. Alignments will
|
| duke@0 | // be at most to LargePageSizeInBytes. Protect
|
| duke@0 | // allocations from alignments up to illegal
|
| duke@0 | // values. If at this point 2G is illegal.
|
| duke@0 | julong reasonable_size = (julong)2*G - 2 * LargePageSizeInBytes;
|
| duke@0 | result = MIN2(size, reasonable_size);
|
| duke@0 | }
|
| duke@0 | return result;
|
| duke@0 | #endif
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static hrtime_t first_hrtime = 0;
|
| duke@0 | static const hrtime_t hrtime_hz = 1000*1000*1000;
|
| duke@0 | const int LOCK_BUSY = 1;
|
| duke@0 | const int LOCK_FREE = 0;
|
| duke@0 | const int LOCK_INVALID = -1;
|
| duke@0 | static volatile hrtime_t max_hrtime = 0;
|
| duke@0 | static volatile int max_hrtime_lock = LOCK_FREE; // Update counter with LSB as lock-in-progress
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | void os::Solaris::initialize_system_info() {
|
| phh@1352 | set_processor_count(sysconf(_SC_NPROCESSORS_CONF));
|
| duke@0 | _processors_online = sysconf (_SC_NPROCESSORS_ONLN);
|
| duke@0 | _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | int os::active_processor_count() {
|
| duke@0 | int online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
| duke@0 | pid_t pid = getpid();
|
| duke@0 | psetid_t pset = PS_NONE;
|
| xlu@444 | // Are we running in a processor set or is there any processor set around?
|
| duke@0 | if (pset_bind(PS_QUERY, P_PID, pid, &pset) == 0) {
|
| xlu@444 | uint_t pset_cpus;
|
| xlu@444 | // Query the number of cpus available to us.
|
| xlu@444 | if (pset_info(pset, NULL, &pset_cpus, NULL) == 0) {
|
| xlu@444 | assert(pset_cpus > 0 && pset_cpus <= online_cpus, "sanity check");
|
| xlu@444 | _processors_online = pset_cpus;
|
| xlu@444 | return pset_cpus;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | // Otherwise return number of online cpus
|
| duke@0 | return online_cpus;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static bool find_processors_in_pset(psetid_t pset,
|
| duke@0 | processorid_t** id_array,
|
| duke@0 | uint_t* id_length) {
|
| duke@0 | bool result = false;
|
| duke@0 | // Find the number of processors in the processor set.
|
| duke@0 | if (pset_info(pset, NULL, id_length, NULL) == 0) {
|
| duke@0 | // Make up an array to hold their ids.
|
| duke@0 | *id_array = NEW_C_HEAP_ARRAY(processorid_t, *id_length);
|
| duke@0 | // Fill in the array with their processor ids.
|
| duke@0 | if (pset_info(pset, NULL, id_length, *id_array) == 0) {
|
| duke@0 | result = true;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | return result;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Callers of find_processors_online() must tolerate imprecise results --
|
| duke@0 | // the system configuration can change asynchronously because of DR
|
| duke@0 | // or explicit psradm operations.
|
| duke@0 | //
|
| duke@0 | // We also need to take care that the loop (below) terminates as the
|
| duke@0 | // number of processors online can change between the _SC_NPROCESSORS_ONLN
|
| duke@0 | // request and the loop that builds the list of processor ids. Unfortunately
|
| duke@0 | // there's no reliable way to determine the maximum valid processor id,
|
| duke@0 | // so we use a manifest constant, MAX_PROCESSOR_ID, instead. See p_online
|
| duke@0 | // man pages, which claim the processor id set is "sparse, but
|
| duke@0 | // not too sparse". MAX_PROCESSOR_ID is used to ensure that we eventually
|
| duke@0 | // exit the loop.
|
| duke@0 | //
|
| duke@0 | // In the future we'll be able to use sysconf(_SC_CPUID_MAX), but that's
|
| duke@0 | // not available on S8.0.
|
| duke@0 |
|
| duke@0 | static bool find_processors_online(processorid_t** id_array,
|
| duke@0 | uint* id_length) {
|
| duke@0 | const processorid_t MAX_PROCESSOR_ID = 100000 ;
|
| duke@0 | // Find the number of processors online.
|
| duke@0 | *id_length = sysconf(_SC_NPROCESSORS_ONLN);
|
| duke@0 | // Make up an array to hold their ids.
|
| duke@0 | *id_array = NEW_C_HEAP_ARRAY(processorid_t, *id_length);
|
| duke@0 | // Processors need not be numbered consecutively.
|
| duke@0 | long found = 0;
|
| duke@0 | processorid_t next = 0;
|
| duke@0 | while (found < *id_length && next < MAX_PROCESSOR_ID) {
|
| duke@0 | processor_info_t info;
|
| duke@0 | if (processor_info(next, &info) == 0) {
|
| duke@0 | // NB, PI_NOINTR processors are effectively online ...
|
| duke@0 | if (info.pi_state == P_ONLINE || info.pi_state == P_NOINTR) {
|
| duke@0 | (*id_array)[found] = next;
|
| duke@0 | found += 1;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | next += 1;
|
| duke@0 | }
|
| duke@0 | if (found < *id_length) {
|
| duke@0 | // The loop above didn't identify the expected number of processors.
|
| duke@0 | // We could always retry the operation, calling sysconf(_SC_NPROCESSORS_ONLN)
|
| duke@0 | // and re-running the loop, above, but there's no guarantee of progress
|
| duke@0 | // if the system configuration is in flux. Instead, we just return what
|
| duke@0 | // we've got. Note that in the worst case find_processors_online() could
|
| duke@0 | // return an empty set. (As a fall-back in the case of the empty set we
|
| duke@0 | // could just return the ID of the current processor).
|
| duke@0 | *id_length = found ;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static bool assign_distribution(processorid_t* id_array,
|
| duke@0 | uint id_length,
|
| duke@0 | uint* distribution,
|
| duke@0 | uint distribution_length) {
|
| duke@0 | // We assume we can assign processorid_t's to uint's.
|
| duke@0 | assert(sizeof(processorid_t) == sizeof(uint),
|
| duke@0 | "can't convert processorid_t to uint");
|
| duke@0 | // Quick check to see if we won't succeed.
|
| duke@0 | if (id_length < distribution_length) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | // Assign processor ids to the distribution.
|
| duke@0 | // Try to shuffle processors to distribute work across boards,
|
| duke@0 | // assuming 4 processors per board.
|
| duke@0 | const uint processors_per_board = ProcessDistributionStride;
|
| duke@0 | // Find the maximum processor id.
|
| duke@0 | processorid_t max_id = 0;
|
| duke@0 | for (uint m = 0; m < id_length; m += 1) {
|
| duke@0 | max_id = MAX2(max_id, id_array[m]);
|
| duke@0 | }
|
| duke@0 | // The next id, to limit loops.
|
| duke@0 | const processorid_t limit_id = max_id + 1;
|
| duke@0 | // Make up markers for available processors.
|
| duke@0 | bool* available_id = NEW_C_HEAP_ARRAY(bool, limit_id);
|
| duke@0 | for (uint c = 0; c < limit_id; c += 1) {
|
| duke@0 | available_id[c] = false;
|
| duke@0 | }
|
| duke@0 | for (uint a = 0; a < id_length; a += 1) {
|
| duke@0 | available_id[id_array[a]] = true;
|
| duke@0 | }
|
| duke@0 | // Step by "boards", then by "slot", copying to "assigned".
|
| duke@0 | // NEEDS_CLEANUP: The assignment of processors should be stateful,
|
| duke@0 | // remembering which processors have been assigned by
|
| duke@0 | // previous calls, etc., so as to distribute several
|
| duke@0 | // independent calls of this method. What we'd like is
|
| duke@0 | // It would be nice to have an API that let us ask
|
| duke@0 | // how many processes are bound to a processor,
|
| duke@0 | // but we don't have that, either.
|
| duke@0 | // In the short term, "board" is static so that
|
| duke@0 | // subsequent distributions don't all start at board 0.
|
| duke@0 | static uint board = 0;
|
| duke@0 | uint assigned = 0;
|
| duke@0 | // Until we've found enough processors ....
|
| duke@0 | while (assigned < distribution_length) {
|
| duke@0 | // ... find the next available processor in the board.
|
| duke@0 | for (uint slot = 0; slot < processors_per_board; slot += 1) {
|
| duke@0 | uint try_id = board * processors_per_board + slot;
|
| duke@0 | if ((try_id < limit_id) && (available_id[try_id] == true)) {
|
| duke@0 | distribution[assigned] = try_id;
|
| duke@0 | available_id[try_id] = false;
|
| duke@0 | assigned += 1;
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | board += 1;
|
| duke@0 | if (board * processors_per_board + 0 >= limit_id) {
|
| duke@0 | board = 0;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | if (available_id != NULL) {
|
| duke@0 | FREE_C_HEAP_ARRAY(bool, available_id);
|
| duke@0 | }
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::distribute_processes(uint length, uint* distribution) {
|
| duke@0 | bool result = false;
|
| duke@0 | // Find the processor id's of all the available CPUs.
|
| duke@0 | processorid_t* id_array = NULL;
|
| duke@0 | uint id_length = 0;
|
| duke@0 | // There are some races between querying information and using it,
|
| duke@0 | // since processor sets can change dynamically.
|
| duke@0 | psetid_t pset = PS_NONE;
|
| duke@0 | // Are we running in a processor set?
|
| duke@0 | if ((pset_bind(PS_QUERY, P_PID, P_MYID, &pset) == 0) && pset != PS_NONE) {
|
| duke@0 | result = find_processors_in_pset(pset, &id_array, &id_length);
|
| duke@0 | } else {
|
| duke@0 | result = find_processors_online(&id_array, &id_length);
|
| duke@0 | }
|
| duke@0 | if (result == true) {
|
| duke@0 | if (id_length >= length) {
|
| duke@0 | result = assign_distribution(id_array, id_length, distribution, length);
|
| duke@0 | } else {
|
| duke@0 | result = false;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | if (id_array != NULL) {
|
| duke@0 | FREE_C_HEAP_ARRAY(processorid_t, id_array);
|
| duke@0 | }
|
| duke@0 | return result;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::bind_to_processor(uint processor_id) {
|
| duke@0 | // We assume that a processorid_t can be stored in a uint.
|
| duke@0 | assert(sizeof(uint) == sizeof(processorid_t),
|
| duke@0 | "can't convert uint to processorid_t");
|
| duke@0 | int bind_result =
|
| duke@0 | processor_bind(P_LWPID, // bind LWP.
|
| duke@0 | P_MYID, // bind current LWP.
|
| duke@0 | (processorid_t) processor_id, // id.
|
| duke@0 | NULL); // don't return old binding.
|
| duke@0 | return (bind_result == 0);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::getenv(const char* name, char* buffer, int len) {
|
| duke@0 | char* val = ::getenv( name );
|
| duke@0 | if ( val == NULL
|
| duke@0 | || strlen(val) + 1 > len ) {
|
| duke@0 | if (len > 0) buffer[0] = 0; // return a null string
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | strcpy( buffer, val );
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Return true if user is running as root.
|
| duke@0 |
|
| duke@0 | bool os::have_special_privileges() {
|
| duke@0 | static bool init = false;
|
| duke@0 | static bool privileges = false;
|
| duke@0 | if (!init) {
|
| duke@0 | privileges = (getuid() != geteuid()) || (getgid() != getegid());
|
| duke@0 | init = true;
|
| duke@0 | }
|
| duke@0 | return privileges;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | void os::init_system_properties_values() {
|
| duke@0 | char arch[12];
|
| duke@0 | sysinfo(SI_ARCHITECTURE, arch, sizeof(arch));
|
| duke@0 |
|
| duke@0 | // The next steps are taken in the product version:
|
| duke@0 | //
|
| duke@0 | // Obtain the JAVA_HOME value from the location of libjvm[_g].so.
|
| duke@0 | // This library should be located at:
|
| duke@0 | // <JAVA_HOME>/jre/lib/<arch>/{client|server}/libjvm[_g].so.
|
| duke@0 | //
|
| duke@0 | // If "/jre/lib/" appears at the right place in the path, then we
|
| duke@0 | // assume libjvm[_g].so is installed in a JDK and we use this path.
|
| duke@0 | //
|
| duke@0 | // Otherwise exit with message: "Could not create the Java virtual machine."
|
| duke@0 | //
|
| duke@0 | // The following extra steps are taken in the debugging version:
|
| duke@0 | //
|
| duke@0 | // If "/jre/lib/" does NOT appear at the right place in the path
|
| duke@0 | // instead of exit check for $JAVA_HOME environment variable.
|
| duke@0 | //
|
| duke@0 | // If it is defined and we are able to locate $JAVA_HOME/jre/lib/<arch>,
|
| duke@0 | // then we append a fake suffix "hotspot/libjvm[_g].so" to this path so
|
| duke@0 | // it looks like libjvm[_g].so is installed there
|
| duke@0 | // <JAVA_HOME>/jre/lib/<arch>/hotspot/libjvm[_g].so.
|
| duke@0 | //
|
| duke@0 | // Otherwise exit.
|
| duke@0 | //
|
| duke@0 | // Important note: if the location of libjvm.so changes this
|
| duke@0 | // code needs to be changed accordingly.
|
| duke@0 |
|
| duke@0 | // The next few definitions allow the code to be verbatim:
|
| duke@0 | #define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n))
|
| duke@0 | #define free(p) FREE_C_HEAP_ARRAY(char, p)
|
| duke@0 | #define getenv(n) ::getenv(n)
|
| duke@0 |
|
| duke@0 | #define EXTENSIONS_DIR "/lib/ext"
|
| duke@0 | #define ENDORSED_DIR "/lib/endorsed"
|
| duke@0 | #define COMMON_DIR "/usr/jdk/packages"
|
| duke@0 |
|
| duke@0 | {
|
| duke@0 | /* sysclasspath, java_home, dll_dir */
|
| duke@0 | {
|
| duke@0 | char *home_path;
|
| duke@0 | char *dll_path;
|
| duke@0 | char *pslash;
|
| duke@0 | char buf[MAXPATHLEN];
|
| duke@0 | os::jvm_path(buf, sizeof(buf));
|
| duke@0 |
|
| duke@0 | // Found the full path to libjvm.so.
|
| duke@0 | // Now cut the path to <java_home>/jre if we can.
|
| duke@0 | *(strrchr(buf, '/')) = '\0'; /* get rid of /libjvm.so */
|
| duke@0 | pslash = strrchr(buf, '/');
|
| duke@0 | if (pslash != NULL)
|
| duke@0 | *pslash = '\0'; /* get rid of /{client|server|hotspot} */
|
| duke@0 | dll_path = malloc(strlen(buf) + 1);
|
| duke@0 | if (dll_path == NULL)
|
| duke@0 | return;
|
| duke@0 | strcpy(dll_path, buf);
|
| duke@0 | Arguments::set_dll_dir(dll_path);
|
| duke@0 |
|
| duke@0 | if (pslash != NULL) {
|
| duke@0 | pslash = strrchr(buf, '/');
|
| duke@0 | if (pslash != NULL) {
|
| duke@0 | *pslash = '\0'; /* get rid of /<arch> */
|
| duke@0 | pslash = strrchr(buf, '/');
|
| duke@0 | if (pslash != NULL)
|
| duke@0 | *pslash = '\0'; /* get rid of /lib */
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | home_path = malloc(strlen(buf) + 1);
|
| duke@0 | if (home_path == NULL)
|
| duke@0 | return;
|
| duke@0 | strcpy(home_path, buf);
|
| duke@0 | Arguments::set_java_home(home_path);
|
| duke@0 |
|
| duke@0 | if (!set_boot_path('/', ':'))
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | /*
|
| duke@0 | * Where to look for native libraries
|
| duke@0 | */
|
| duke@0 | {
|
| duke@0 | // Use dlinfo() to determine the correct java.library.path.
|
| duke@0 | //
|
| duke@0 | // If we're launched by the Java launcher, and the user
|
| duke@0 | // does not set java.library.path explicitly on the commandline,
|
| duke@0 | // the Java launcher sets LD_LIBRARY_PATH for us and unsets
|
| duke@0 | // LD_LIBRARY_PATH_32 and LD_LIBRARY_PATH_64. In this case
|
| duke@0 | // dlinfo returns LD_LIBRARY_PATH + crle settings (including
|
| duke@0 | // /usr/lib), which is exactly what we want.
|
| duke@0 | //
|
| duke@0 | // If the user does set java.library.path, it completely
|
| duke@0 | // overwrites this setting, and always has.
|
| duke@0 | //
|
| duke@0 | // If we're not launched by the Java launcher, we may
|
| duke@0 | // get here with any/all of the LD_LIBRARY_PATH[_32|64]
|
| duke@0 | // settings. Again, dlinfo does exactly what we want.
|
| duke@0 |
|
| duke@0 | Dl_serinfo _info, *info = &_info;
|
| duke@0 | Dl_serpath *path;
|
| duke@0 | char* library_path;
|
| duke@0 | char *common_path;
|
| duke@0 | int i;
|
| duke@0 |
|
| duke@0 | // determine search path count and required buffer size
|
| duke@0 | if (dlinfo(RTLD_SELF, RTLD_DI_SERINFOSIZE, (void *)info) == -1) {
|
| duke@0 | vm_exit_during_initialization("dlinfo SERINFOSIZE request", dlerror());
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // allocate new buffer and initialize
|
| duke@0 | info = (Dl_serinfo*)malloc(_info.dls_size);
|
| duke@0 | if (info == NULL) {
|
| duke@0 | vm_exit_out_of_memory(_info.dls_size,
|
| duke@0 | "init_system_properties_values info");
|
| duke@0 | }
|
| duke@0 | info->dls_size = _info.dls_size;
|
| duke@0 | info->dls_cnt = _info.dls_cnt;
|
| duke@0 |
|
| duke@0 | // obtain search path information
|
| duke@0 | if (dlinfo(RTLD_SELF, RTLD_DI_SERINFO, (void *)info) == -1) {
|
| duke@0 | free(info);
|
| duke@0 | vm_exit_during_initialization("dlinfo SERINFO request", dlerror());
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | path = &info->dls_serpath[0];
|
| duke@0 |
|
| duke@0 | // Note: Due to a legacy implementation, most of the library path
|
| duke@0 | // is set in the launcher. This was to accomodate linking restrictions
|
| duke@0 | // on legacy Solaris implementations (which are no longer supported).
|
| duke@0 | // Eventually, all the library path setting will be done here.
|
| duke@0 | //
|
| duke@0 | // However, to prevent the proliferation of improperly built native
|
| duke@0 | // libraries, the new path component /usr/jdk/packages is added here.
|
| duke@0 |
|
| duke@0 | // Determine the actual CPU architecture.
|
| duke@0 | char cpu_arch[12];
|
| duke@0 | sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof(cpu_arch));
|
| duke@0 | #ifdef _LP64
|
| duke@0 | // If we are a 64-bit vm, perform the following translations:
|
| duke@0 | // sparc -> sparcv9
|
| duke@0 | // i386 -> amd64
|
| duke@0 | if (strcmp(cpu_arch, "sparc") == 0)
|
| duke@0 | strcat(cpu_arch, "v9");
|
| duke@0 | else if (strcmp(cpu_arch, "i386") == 0)
|
| duke@0 | strcpy(cpu_arch, "amd64");
|
| duke@0 | #endif
|
| duke@0 |
|
| duke@0 | // Construct the invariant part of ld_library_path. Note that the
|
| duke@0 | // space for the colon and the trailing null are provided by the
|
| duke@0 | // nulls included by the sizeof operator.
|
| duke@0 | size_t bufsize = sizeof(COMMON_DIR) + sizeof("/lib/") + strlen(cpu_arch);
|
| duke@0 | common_path = malloc(bufsize);
|
| duke@0 | if (common_path == NULL) {
|
| duke@0 | free(info);
|
| duke@0 | vm_exit_out_of_memory(bufsize,
|
| duke@0 | "init_system_properties_values common_path");
|
| duke@0 | }
|
| duke@0 | sprintf(common_path, COMMON_DIR "/lib/%s", cpu_arch);
|
| duke@0 |
|
| duke@0 | // struct size is more than sufficient for the path components obtained
|
| duke@0 | // through the dlinfo() call, so only add additional space for the path
|
| duke@0 | // components explicitly added here.
|
| duke@0 | bufsize = info->dls_size + strlen(common_path);
|
| duke@0 | library_path = malloc(bufsize);
|
| duke@0 | if (library_path == NULL) {
|
| duke@0 | free(info);
|
| duke@0 | free(common_path);
|
| duke@0 | vm_exit_out_of_memory(bufsize,
|
| duke@0 | "init_system_properties_values library_path");
|
| duke@0 | }
|
| duke@0 | library_path[0] = '\0';
|
| duke@0 |
|
| duke@0 | // Construct the desired Java library path from the linker's library
|
| duke@0 | // search path.
|
| duke@0 | //
|
| duke@0 | // For compatibility, it is optimal that we insert the additional path
|
| duke@0 | // components specific to the Java VM after those components specified
|
| duke@0 | // in LD_LIBRARY_PATH (if any) but before those added by the ld.so
|
| duke@0 | // infrastructure.
|
| duke@0 | if (info->dls_cnt == 0) { // Not sure this can happen, but allow for it
|
| duke@0 | strcpy(library_path, common_path);
|
| duke@0 | } else {
|
| duke@0 | int inserted = 0;
|
| duke@0 | for (i = 0; i < info->dls_cnt; i++, path++) {
|
| duke@0 | uint_t flags = path->dls_flags & LA_SER_MASK;
|
| duke@0 | if (((flags & LA_SER_LIBPATH) == 0) && !inserted) {
|
| duke@0 | strcat(library_path, common_path);
|
| duke@0 | strcat(library_path, os::path_separator());
|
| duke@0 | inserted = 1;
|
| duke@0 | }
|
| duke@0 | strcat(library_path, path->dls_name);
|
| duke@0 | strcat(library_path, os::path_separator());
|
| duke@0 | }
|
| duke@0 | // eliminate trailing path separator
|
| duke@0 | library_path[strlen(library_path)-1] = '\0';
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // happens before argument parsing - can't use a trace flag
|
| duke@0 | // tty->print_raw("init_system_properties_values: native lib path: ");
|
| duke@0 | // tty->print_raw_cr(library_path);
|
| duke@0 |
|
| duke@0 | // callee copies into its own buffer
|
| duke@0 | Arguments::set_library_path(library_path);
|
| duke@0 |
|
| duke@0 | free(common_path);
|
| duke@0 | free(library_path);
|
| duke@0 | free(info);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | /*
|
| duke@0 | * Extensions directories.
|
| duke@0 | *
|
| duke@0 | * Note that the space for the colon and the trailing null are provided
|
| duke@0 | * by the nulls included by the sizeof operator (so actually one byte more
|
| duke@0 | * than necessary is allocated).
|
| duke@0 | */
|
| duke@0 | {
|
| duke@0 | char *buf = (char *) malloc(strlen(Arguments::get_java_home()) +
|
| duke@0 | sizeof(EXTENSIONS_DIR) + sizeof(COMMON_DIR) +
|
| duke@0 | sizeof(EXTENSIONS_DIR));
|
| duke@0 | sprintf(buf, "%s" EXTENSIONS_DIR ":" COMMON_DIR EXTENSIONS_DIR,
|
| duke@0 | Arguments::get_java_home());
|
| duke@0 | Arguments::set_ext_dirs(buf);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | /* Endorsed standards default directory. */
|
| duke@0 | {
|
| duke@0 | char * buf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR));
|
| duke@0 | sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home());
|
| duke@0 | Arguments::set_endorsed_dirs(buf);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | #undef malloc
|
| duke@0 | #undef free
|
| duke@0 | #undef getenv
|
| duke@0 | #undef EXTENSIONS_DIR
|
| duke@0 | #undef ENDORSED_DIR
|
| duke@0 | #undef COMMON_DIR
|
| duke@0 |
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::breakpoint() {
|
| duke@0 | BREAKPOINT;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::obsolete_option(const JavaVMOption *option)
|
| duke@0 | {
|
| duke@0 | if (!strncmp(option->optionString, "-Xt", 3)) {
|
| duke@0 | return true;
|
| duke@0 | } else if (!strncmp(option->optionString, "-Xtm", 4)) {
|
| duke@0 | return true;
|
| duke@0 | } else if (!strncmp(option->optionString, "-Xverifyheap", 12)) {
|
| duke@0 | return true;
|
| duke@0 | } else if (!strncmp(option->optionString, "-Xmaxjitcodesize", 16)) {
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::Solaris::valid_stack_address(Thread* thread, address sp) {
|
| duke@0 | address stackStart = (address)thread->stack_base();
|
| duke@0 | address stackEnd = (address)(stackStart - (address)thread->stack_size());
|
| duke@0 | if (sp < stackStart && sp >= stackEnd ) return true;
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | extern "C" void breakpoint() {
|
| duke@0 | // use debugger to set breakpoint here
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Returns an estimate of the current stack pointer. Result must be guaranteed to
|
| duke@0 | // point into the calling threads stack, and be no lower than the current stack
|
| duke@0 | // pointer.
|
| duke@0 | address os::current_stack_pointer() {
|
| duke@0 | volatile int dummy;
|
| duke@0 | address sp = (address)&dummy + 8; // %%%% need to confirm if this is right
|
| duke@0 | return sp;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static thread_t main_thread;
|
| duke@0 |
|
| duke@0 | // Thread start routine for all new Java threads
|
| duke@0 | extern "C" void* java_start(void* thread_addr) {
|
| duke@0 | // Try to randomize the cache line index of hot stack frames.
|
| duke@0 | // This helps when threads of the same stack traces evict each other's
|
| duke@0 | // cache lines. The threads can be either from the same JVM instance, or
|
| duke@0 | // from different JVM instances. The benefit is especially true for
|
| duke@0 | // processors with hyperthreading technology.
|
| duke@0 | static int counter = 0;
|
| duke@0 | int pid = os::current_process_id();
|
| duke@0 | alloca(((pid ^ counter++) & 7) * 128);
|
| duke@0 |
|
| duke@0 | int prio;
|
| duke@0 | Thread* thread = (Thread*)thread_addr;
|
| duke@0 | OSThread* osthr = thread->osthread();
|
| duke@0 |
|
| duke@0 | osthr->set_lwp_id( _lwp_self() ); // Store lwp in case we are bound
|
| duke@0 | thread->_schedctl = (void *) schedctl_init () ;
|
| duke@0 |
|
| duke@0 | if (UseNUMA) {
|
| duke@0 | int lgrp_id = os::numa_get_group_id();
|
| duke@0 | if (lgrp_id != -1) {
|
| duke@0 | thread->set_lgrp_id(lgrp_id);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // If the creator called set priority before we started,
|
| duke@0 | // we need to call set priority now that we have an lwp.
|
| duke@0 | // Get the priority from libthread and set the priority
|
| duke@0 | // for the new Solaris lwp.
|
| duke@0 | if ( osthr->thread_id() != -1 ) {
|
| duke@0 | if ( UseThreadPriorities ) {
|
| duke@0 | thr_getprio(osthr->thread_id(), &prio);
|
| duke@0 | if (ThreadPriorityVerbose) {
|
| duke@0 | tty->print_cr("Starting Thread " INTPTR_FORMAT ", LWP is " INTPTR_FORMAT ", setting priority: %d\n",
|
| duke@0 | osthr->thread_id(), osthr->lwp_id(), prio );
|
| duke@0 | }
|
| duke@0 | os::set_native_priority(thread, prio);
|
| duke@0 | }
|
| duke@0 | } else if (ThreadPriorityVerbose) {
|
| duke@0 | warning("Can't set priority in _start routine, thread id hasn't been set\n");
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | assert(osthr->get_state() == RUNNABLE, "invalid os thread state");
|
| duke@0 |
|
| duke@0 | // initialize signal mask for this thread
|
| duke@0 | os::Solaris::hotspot_sigmask(thread);
|
| duke@0 |
|
| duke@0 | thread->run();
|
| duke@0 |
|
| duke@0 | // One less thread is executing
|
| duke@0 | // When the VMThread gets here, the main thread may have already exited
|
| duke@0 | // which frees the CodeHeap containing the Atomic::dec code
|
| duke@0 | if (thread != VMThread::vm_thread() && VMThread::vm_thread() != NULL) {
|
| duke@0 | Atomic::dec(&os::Solaris::_os_thread_count);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (UseDetachedThreads) {
|
| duke@0 | thr_exit(NULL);
|
| duke@0 | ShouldNotReachHere();
|
| duke@0 | }
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static OSThread* create_os_thread(Thread* thread, thread_t thread_id) {
|
| duke@0 | // Allocate the OSThread object
|
| duke@0 | OSThread* osthread = new OSThread(NULL, NULL);
|
| duke@0 | if (osthread == NULL) return NULL;
|
| duke@0 |
|
| duke@0 | // Store info on the Solaris thread into the OSThread
|
| duke@0 | osthread->set_thread_id(thread_id);
|
| duke@0 | osthread->set_lwp_id(_lwp_self());
|
| duke@0 | thread->_schedctl = (void *) schedctl_init () ;
|
| duke@0 |
|
| duke@0 | if (UseNUMA) {
|
| duke@0 | int lgrp_id = os::numa_get_group_id();
|
| duke@0 | if (lgrp_id != -1) {
|
| duke@0 | thread->set_lgrp_id(lgrp_id);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if ( ThreadPriorityVerbose ) {
|
| duke@0 | tty->print_cr("In create_os_thread, Thread " INTPTR_FORMAT ", LWP is " INTPTR_FORMAT "\n",
|
| duke@0 | osthread->thread_id(), osthread->lwp_id() );
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Initial thread state is INITIALIZED, not SUSPENDED
|
| duke@0 | osthread->set_state(INITIALIZED);
|
| duke@0 |
|
| duke@0 | return osthread;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::hotspot_sigmask(Thread* thread) {
|
| duke@0 |
|
| duke@0 | //Save caller's signal mask
|
| duke@0 | sigset_t sigmask;
|
| duke@0 | thr_sigsetmask(SIG_SETMASK, NULL, &sigmask);
|
| duke@0 | OSThread *osthread = thread->osthread();
|
| duke@0 | osthread->set_caller_sigmask(sigmask);
|
| duke@0 |
|
| duke@0 | thr_sigsetmask(SIG_UNBLOCK, os::Solaris::unblocked_signals(), NULL);
|
| duke@0 | if (!ReduceSignalUsage) {
|
| duke@0 | if (thread->is_VM_thread()) {
|
| duke@0 | // Only the VM thread handles BREAK_SIGNAL ...
|
| duke@0 | thr_sigsetmask(SIG_UNBLOCK, vm_signals(), NULL);
|
| duke@0 | } else {
|
| duke@0 | // ... all other threads block BREAK_SIGNAL
|
| duke@0 | assert(!sigismember(vm_signals(), SIGINT), "SIGINT should not be blocked");
|
| duke@0 | thr_sigsetmask(SIG_BLOCK, vm_signals(), NULL);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::create_attached_thread(JavaThread* thread) {
|
| duke@0 | #ifdef ASSERT
|
| duke@0 | thread->verify_not_published();
|
| duke@0 | #endif
|
| duke@0 | OSThread* osthread = create_os_thread(thread, thr_self());
|
| duke@0 | if (osthread == NULL) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Initial thread state is RUNNABLE
|
| duke@0 | osthread->set_state(RUNNABLE);
|
| duke@0 | thread->set_osthread(osthread);
|
| duke@0 |
|
| duke@0 | // initialize signal mask for this thread
|
| duke@0 | // and save the caller's signal mask
|
| duke@0 | os::Solaris::hotspot_sigmask(thread);
|
| duke@0 |
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::create_main_thread(JavaThread* thread) {
|
| duke@0 | #ifdef ASSERT
|
| duke@0 | thread->verify_not_published();
|
| duke@0 | #endif
|
| duke@0 | if (_starting_thread == NULL) {
|
| duke@0 | _starting_thread = create_os_thread(thread, main_thread);
|
| duke@0 | if (_starting_thread == NULL) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // The primodial thread is runnable from the start
|
| duke@0 | _starting_thread->set_state(RUNNABLE);
|
| duke@0 |
|
| duke@0 | thread->set_osthread(_starting_thread);
|
| duke@0 |
|
| duke@0 | // initialize signal mask for this thread
|
| duke@0 | // and save the caller's signal mask
|
| duke@0 | os::Solaris::hotspot_sigmask(thread);
|
| duke@0 |
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // _T2_libthread is true if we believe we are running with the newer
|
| duke@0 | // SunSoft lwp/libthread.so (2.8 patch, 2.9 default)
|
| duke@0 | bool os::Solaris::_T2_libthread = false;
|
| duke@0 |
|
| duke@0 | bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
|
| duke@0 | // Allocate the OSThread object
|
| duke@0 | OSThread* osthread = new OSThread(NULL, NULL);
|
| duke@0 | if (osthread == NULL) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if ( ThreadPriorityVerbose ) {
|
| duke@0 | char *thrtyp;
|
| duke@0 | switch ( thr_type ) {
|
| duke@0 | case vm_thread:
|
| duke@0 | thrtyp = (char *)"vm";
|
| duke@0 | break;
|
| duke@0 | case cgc_thread:
|
| duke@0 | thrtyp = (char *)"cgc";
|
| duke@0 | break;
|
| duke@0 | case pgc_thread:
|
| duke@0 | thrtyp = (char *)"pgc";
|
| duke@0 | break;
|
| duke@0 | case java_thread:
|
| duke@0 | thrtyp = (char *)"java";
|
| duke@0 | break;
|
| duke@0 | case compiler_thread:
|
| duke@0 | thrtyp = (char *)"compiler";
|
| duke@0 | break;
|
| duke@0 | case watcher_thread:
|
| duke@0 | thrtyp = (char *)"watcher";
|
| duke@0 | break;
|
| duke@0 | default:
|
| duke@0 | thrtyp = (char *)"unknown";
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 | tty->print_cr("In create_thread, creating a %s thread\n", thrtyp);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Calculate stack size if it's not specified by caller.
|
| duke@0 | if (stack_size == 0) {
|
| duke@0 | // The default stack size 1M (2M for LP64).
|
| duke@0 | stack_size = (BytesPerWord >> 2) * K * K;
|
| duke@0 |
|
| duke@0 | switch (thr_type) {
|
| duke@0 | case os::java_thread:
|
| duke@0 | // Java threads use ThreadStackSize which default value can be changed with the flag -Xss
|
| duke@0 | if (JavaThread::stack_size_at_create() > 0) stack_size = JavaThread::stack_size_at_create();
|
| duke@0 | break;
|
| duke@0 | case os::compiler_thread:
|
| duke@0 | if (CompilerThreadStackSize > 0) {
|
| duke@0 | stack_size = (size_t)(CompilerThreadStackSize * K);
|
| duke@0 | break;
|
| duke@0 | } // else fall through:
|
| duke@0 | // use VMThreadStackSize if CompilerThreadStackSize is not defined
|
| duke@0 | case os::vm_thread:
|
| duke@0 | case os::pgc_thread:
|
| duke@0 | case os::cgc_thread:
|
| duke@0 | case os::watcher_thread:
|
| duke@0 | if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | stack_size = MAX2(stack_size, os::Solaris::min_stack_allowed);
|
| duke@0 |
|
| duke@0 | // Initial state is ALLOCATED but not INITIALIZED
|
| duke@0 | osthread->set_state(ALLOCATED);
|
| duke@0 |
|
| duke@0 | if (os::Solaris::_os_thread_count > os::Solaris::_os_thread_limit) {
|
| duke@0 | // We got lots of threads. Check if we still have some address space left.
|
| duke@0 | // Need to be at least 5Mb of unreserved address space. We do check by
|
| duke@0 | // trying to reserve some.
|
| duke@0 | const size_t VirtualMemoryBangSize = 20*K*K;
|
| duke@0 | char* mem = os::reserve_memory(VirtualMemoryBangSize);
|
| duke@0 | if (mem == NULL) {
|
| duke@0 | delete osthread;
|
| duke@0 | return false;
|
| duke@0 | } else {
|
| duke@0 | // Release the memory again
|
| duke@0 | os::release_memory(mem, VirtualMemoryBangSize);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Setup osthread because the child thread may need it.
|
| duke@0 | thread->set_osthread(osthread);
|
| duke@0 |
|
| duke@0 | // Create the Solaris thread
|
| duke@0 | // explicit THR_BOUND for T2_libthread case in case
|
| duke@0 | // that assumption is not accurate, but our alternate signal stack
|
| duke@0 | // handling is based on it which must have bound threads
|
| duke@0 | thread_t tid = 0;
|
| duke@0 | long flags = (UseDetachedThreads ? THR_DETACHED : 0) | THR_SUSPENDED
|
| duke@0 | | ((UseBoundThreads || os::Solaris::T2_libthread() ||
|
| duke@0 | (thr_type == vm_thread) ||
|
| duke@0 | (thr_type == cgc_thread) ||
|
| duke@0 | (thr_type == pgc_thread) ||
|
| duke@0 | (thr_type == compiler_thread && BackgroundCompilation)) ?
|
| duke@0 | THR_BOUND : 0);
|
| duke@0 | int status;
|
| duke@0 |
|
| duke@0 | // 4376845 -- libthread/kernel don't provide enough LWPs to utilize all CPUs.
|
| duke@0 | //
|
| duke@0 | // On multiprocessors systems, libthread sometimes under-provisions our
|
| duke@0 | // process with LWPs. On a 30-way systems, for instance, we could have
|
| duke@0 | // 50 user-level threads in ready state and only 2 or 3 LWPs assigned
|
| duke@0 | // to our process. This can result in under utilization of PEs.
|
| duke@0 | // I suspect the problem is related to libthread's LWP
|
| duke@0 | // pool management and to the kernel's SIGBLOCKING "last LWP parked"
|
| duke@0 | // upcall policy.
|
| duke@0 | //
|
| duke@0 | // The following code is palliative -- it attempts to ensure that our
|
| duke@0 | // process has sufficient LWPs to take advantage of multiple PEs.
|
| duke@0 | // Proper long-term cures include using user-level threads bound to LWPs
|
| duke@0 | // (THR_BOUND) or using LWP-based synchronization. Note that there is a
|
| duke@0 | // slight timing window with respect to sampling _os_thread_count, but
|
| duke@0 | // the race is benign. Also, we should periodically recompute
|
| duke@0 | // _processors_online as the min of SC_NPROCESSORS_ONLN and the
|
| duke@0 | // the number of PEs in our partition. You might be tempted to use
|
| duke@0 | // THR_NEW_LWP here, but I'd recommend against it as that could
|
| duke@0 | // result in undesirable growth of the libthread's LWP pool.
|
| duke@0 | // The fix below isn't sufficient; for instance, it doesn't take into count
|
| duke@0 | // LWPs parked on IO. It does, however, help certain CPU-bound benchmarks.
|
| duke@0 | //
|
| duke@0 | // Some pathologies this scheme doesn't handle:
|
| duke@0 | // * Threads can block, releasing the LWPs. The LWPs can age out.
|
| duke@0 | // When a large number of threads become ready again there aren't
|
| duke@0 | // enough LWPs available to service them. This can occur when the
|
| duke@0 | // number of ready threads oscillates.
|
| duke@0 | // * LWPs/Threads park on IO, thus taking the LWP out of circulation.
|
| duke@0 | //
|
| duke@0 | // Finally, we should call thr_setconcurrency() periodically to refresh
|
| duke@0 | // the LWP pool and thwart the LWP age-out mechanism.
|
| duke@0 | // The "+3" term provides a little slop -- we want to slightly overprovision.
|
| duke@0 |
|
| duke@0 | if (AdjustConcurrency && os::Solaris::_os_thread_count < (_processors_online+3)) {
|
| duke@0 | if (!(flags & THR_BOUND)) {
|
| duke@0 | thr_setconcurrency (os::Solaris::_os_thread_count); // avoid starvation
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | // Although this doesn't hurt, we should warn of undefined behavior
|
| duke@0 | // when using unbound T1 threads with schedctl(). This should never
|
| duke@0 | // happen, as the compiler and VM threads are always created bound
|
| duke@0 | DEBUG_ONLY(
|
| duke@0 | if ((VMThreadHintNoPreempt || CompilerThreadHintNoPreempt) &&
|
| duke@0 | (!os::Solaris::T2_libthread() && (!(flags & THR_BOUND))) &&
|
| duke@0 | ((thr_type == vm_thread) || (thr_type == cgc_thread) ||
|
| duke@0 | (thr_type == pgc_thread) || (thr_type == compiler_thread && BackgroundCompilation))) {
|
| duke@0 | warning("schedctl behavior undefined when Compiler/VM/GC Threads are Unbound");
|
| duke@0 | }
|
| duke@0 | );
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Mark that we don't have an lwp or thread id yet.
|
| duke@0 | // In case we attempt to set the priority before the thread starts.
|
| duke@0 | osthread->set_lwp_id(-1);
|
| duke@0 | osthread->set_thread_id(-1);
|
| duke@0 |
|
| duke@0 | status = thr_create(NULL, stack_size, java_start, thread, flags, &tid);
|
| duke@0 | if (status != 0) {
|
| duke@0 | if (PrintMiscellaneous && (Verbose || WizardMode)) {
|
| duke@0 | perror("os::create_thread");
|
| duke@0 | }
|
| duke@0 | thread->set_osthread(NULL);
|
| duke@0 | // Need to clean up stuff we've allocated so far
|
| duke@0 | delete osthread;
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | Atomic::inc(&os::Solaris::_os_thread_count);
|
| duke@0 |
|
| duke@0 | // Store info on the Solaris thread into the OSThread
|
| duke@0 | osthread->set_thread_id(tid);
|
| duke@0 |
|
| duke@0 | // Remember that we created this thread so we can set priority on it
|
| duke@0 | osthread->set_vm_created();
|
| duke@0 |
|
| duke@0 | // Set the default thread priority otherwise use NormalPriority
|
| duke@0 |
|
| duke@0 | if ( UseThreadPriorities ) {
|
| duke@0 | thr_setprio(tid, (DefaultThreadPriority == -1) ?
|
| duke@0 | java_to_os_priority[NormPriority] :
|
| duke@0 | DefaultThreadPriority);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Initial thread state is INITIALIZED, not SUSPENDED
|
| duke@0 | osthread->set_state(INITIALIZED);
|
| duke@0 |
|
| duke@0 | // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | /* defined for >= Solaris 10. This allows builds on earlier versions
|
| duke@0 | * of Solaris to take advantage of the newly reserved Solaris JVM signals
|
| duke@0 | * With SIGJVM1, SIGJVM2, INTERRUPT_SIGNAL is SIGJVM1, ASYNC_SIGNAL is SIGJVM2
|
| duke@0 | * and -XX:+UseAltSigs does nothing since these should have no conflict
|
| duke@0 | */
|
| duke@0 | #if !defined(SIGJVM1)
|
| duke@0 | #define SIGJVM1 39
|
| duke@0 | #define SIGJVM2 40
|
| duke@0 | #endif
|
| duke@0 |
|
| duke@0 | debug_only(static bool signal_sets_initialized = false);
|
| duke@0 | static sigset_t unblocked_sigs, vm_sigs, allowdebug_blocked_sigs;
|
| duke@0 | int os::Solaris::_SIGinterrupt = INTERRUPT_SIGNAL;
|
| duke@0 | int os::Solaris::_SIGasync = ASYNC_SIGNAL;
|
| duke@0 |
|
| duke@0 | bool os::Solaris::is_sig_ignored(int sig) {
|
| duke@0 | struct sigaction oact;
|
| duke@0 | sigaction(sig, (struct sigaction*)NULL, &oact);
|
| duke@0 | void* ohlr = oact.sa_sigaction ? CAST_FROM_FN_PTR(void*, oact.sa_sigaction)
|
| duke@0 | : CAST_FROM_FN_PTR(void*, oact.sa_handler);
|
| duke@0 | if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN))
|
| duke@0 | return true;
|
| duke@0 | else
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Note: SIGRTMIN is a macro that calls sysconf() so it will
|
| duke@0 | // dynamically detect SIGRTMIN value for the system at runtime, not buildtime
|
| duke@0 | static bool isJVM1available() {
|
| duke@0 | return SIGJVM1 < SIGRTMIN;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::signal_sets_init() {
|
| duke@0 | // Should also have an assertion stating we are still single-threaded.
|
| duke@0 | assert(!signal_sets_initialized, "Already initialized");
|
| duke@0 | // Fill in signals that are necessarily unblocked for all threads in
|
| duke@0 | // the VM. Currently, we unblock the following signals:
|
| duke@0 | // SHUTDOWN{1,2,3}_SIGNAL: for shutdown hooks support (unless over-ridden
|
| duke@0 | // by -Xrs (=ReduceSignalUsage));
|
| duke@0 | // BREAK_SIGNAL which is unblocked only by the VM thread and blocked by all
|
| duke@0 | // other threads. The "ReduceSignalUsage" boolean tells us not to alter
|
| duke@0 | // the dispositions or masks wrt these signals.
|
| duke@0 | // Programs embedding the VM that want to use the above signals for their
|
| duke@0 | // own purposes must, at this time, use the "-Xrs" option to prevent
|
| duke@0 | // interference with shutdown hooks and BREAK_SIGNAL thread dumping.
|
| duke@0 | // (See bug 4345157, and other related bugs).
|
| duke@0 | // In reality, though, unblocking these signals is really a nop, since
|
| duke@0 | // these signals are not blocked by default.
|
| duke@0 | sigemptyset(&unblocked_sigs);
|
| duke@0 | sigemptyset(&allowdebug_blocked_sigs);
|
| duke@0 | sigaddset(&unblocked_sigs, SIGILL);
|
| duke@0 | sigaddset(&unblocked_sigs, SIGSEGV);
|
| duke@0 | sigaddset(&unblocked_sigs, SIGBUS);
|
| duke@0 | sigaddset(&unblocked_sigs, SIGFPE);
|
| duke@0 |
|
| duke@0 | if (isJVM1available) {
|
| duke@0 | os::Solaris::set_SIGinterrupt(SIGJVM1);
|
| duke@0 | os::Solaris::set_SIGasync(SIGJVM2);
|
| duke@0 | } else if (UseAltSigs) {
|
| duke@0 | os::Solaris::set_SIGinterrupt(ALT_INTERRUPT_SIGNAL);
|
| duke@0 | os::Solaris::set_SIGasync(ALT_ASYNC_SIGNAL);
|
| duke@0 | } else {
|
| duke@0 | os::Solaris::set_SIGinterrupt(INTERRUPT_SIGNAL);
|
| duke@0 | os::Solaris::set_SIGasync(ASYNC_SIGNAL);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | sigaddset(&unblocked_sigs, os::Solaris::SIGinterrupt());
|
| duke@0 | sigaddset(&unblocked_sigs, os::Solaris::SIGasync());
|
| duke@0 |
|
| duke@0 | if (!ReduceSignalUsage) {
|
| duke@0 | if (!os::Solaris::is_sig_ignored(SHUTDOWN1_SIGNAL)) {
|
| duke@0 | sigaddset(&unblocked_sigs, SHUTDOWN1_SIGNAL);
|
| duke@0 | sigaddset(&allowdebug_blocked_sigs, SHUTDOWN1_SIGNAL);
|
| duke@0 | }
|
| duke@0 | if (!os::Solaris::is_sig_ignored(SHUTDOWN2_SIGNAL)) {
|
| duke@0 | sigaddset(&unblocked_sigs, SHUTDOWN2_SIGNAL);
|
| duke@0 | sigaddset(&allowdebug_blocked_sigs, SHUTDOWN2_SIGNAL);
|
| duke@0 | }
|
| duke@0 | if (!os::Solaris::is_sig_ignored(SHUTDOWN3_SIGNAL)) {
|
| duke@0 | sigaddset(&unblocked_sigs, SHUTDOWN3_SIGNAL);
|
| duke@0 | sigaddset(&allowdebug_blocked_sigs, SHUTDOWN3_SIGNAL);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | // Fill in signals that are blocked by all but the VM thread.
|
| duke@0 | sigemptyset(&vm_sigs);
|
| duke@0 | if (!ReduceSignalUsage)
|
| duke@0 | sigaddset(&vm_sigs, BREAK_SIGNAL);
|
| duke@0 | debug_only(signal_sets_initialized = true);
|
| duke@0 |
|
| duke@0 | // For diagnostics only used in run_periodic_checks
|
| duke@0 | sigemptyset(&check_signal_done);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // These are signals that are unblocked while a thread is running Java.
|
| duke@0 | // (For some reason, they get blocked by default.)
|
| duke@0 | sigset_t* os::Solaris::unblocked_signals() {
|
| duke@0 | assert(signal_sets_initialized, "Not initialized");
|
| duke@0 | return &unblocked_sigs;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // These are the signals that are blocked while a (non-VM) thread is
|
| duke@0 | // running Java. Only the VM thread handles these signals.
|
| duke@0 | sigset_t* os::Solaris::vm_signals() {
|
| duke@0 | assert(signal_sets_initialized, "Not initialized");
|
| duke@0 | return &vm_sigs;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // These are signals that are blocked during cond_wait to allow debugger in
|
| duke@0 | sigset_t* os::Solaris::allowdebug_blocked_signals() {
|
| duke@0 | assert(signal_sets_initialized, "Not initialized");
|
| duke@0 | return &allowdebug_blocked_sigs;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // First crack at OS-specific initialization, from inside the new thread.
|
| duke@0 | void os::initialize_thread() {
|
| duke@0 | int r = thr_main() ;
|
| duke@0 | guarantee (r == 0 || r == 1, "CR6501650 or CR6493689") ;
|
| duke@0 | if (r) {
|
| duke@0 | JavaThread* jt = (JavaThread *)Thread::current();
|
| duke@0 | assert(jt != NULL,"Sanity check");
|
| duke@0 | size_t stack_size;
|
| duke@0 | address base = jt->stack_base();
|
| duke@0 | if (Arguments::created_by_java_launcher()) {
|
| duke@0 | // Use 2MB to allow for Solaris 7 64 bit mode.
|
| duke@0 | stack_size = JavaThread::stack_size_at_create() == 0
|
| duke@0 | ? 2048*K : JavaThread::stack_size_at_create();
|
| duke@0 |
|
| duke@0 | // There are rare cases when we may have already used more than
|
| duke@0 | // the basic stack size allotment before this method is invoked.
|
| duke@0 | // Attempt to allow for a normally sized java_stack.
|
| duke@0 | size_t current_stack_offset = (size_t)(base - (address)&stack_size);
|
| duke@0 | stack_size += ReservedSpace::page_align_size_down(current_stack_offset);
|
| duke@0 | } else {
|
| duke@0 | // 6269555: If we were not created by a Java launcher, i.e. if we are
|
| duke@0 | // running embedded in a native application, treat the primordial thread
|
| duke@0 | // as much like a native attached thread as possible. This means using
|
| duke@0 | // the current stack size from thr_stksegment(), unless it is too large
|
| duke@0 | // to reliably setup guard pages. A reasonable max size is 8MB.
|
| duke@0 | size_t current_size = current_stack_size();
|
| duke@0 | // This should never happen, but just in case....
|
| duke@0 | if (current_size == 0) current_size = 2 * K * K;
|
| duke@0 | stack_size = current_size > (8 * K * K) ? (8 * K * K) : current_size;
|
| duke@0 | }
|
| duke@0 | address bottom = (address)align_size_up((intptr_t)(base - stack_size), os::vm_page_size());;
|
| duke@0 | stack_size = (size_t)(base - bottom);
|
| duke@0 |
|
| duke@0 | assert(stack_size > 0, "Stack size calculation problem");
|
| duke@0 |
|
| duke@0 | if (stack_size > jt->stack_size()) {
|
| duke@0 | NOT_PRODUCT(
|
| duke@0 | struct rlimit limits;
|
| duke@0 | getrlimit(RLIMIT_STACK, &limits);
|
| duke@0 | size_t size = adjust_stack_size(base, (size_t)limits.rlim_cur);
|
| duke@0 | assert(size >= jt->stack_size(), "Stack size problem in main thread");
|
| duke@0 | )
|
| duke@0 | tty->print_cr(
|
| duke@0 | "Stack size of %d Kb exceeds current limit of %d Kb.\n"
|
| duke@0 | "(Stack sizes are rounded up to a multiple of the system page size.)\n"
|
| duke@0 | "See limit(1) to increase the stack size limit.",
|
| duke@0 | stack_size / K, jt->stack_size() / K);
|
| duke@0 | vm_exit(1);
|
| duke@0 | }
|
| duke@0 | assert(jt->stack_size() >= stack_size,
|
| duke@0 | "Attempt to map more stack than was allocated");
|
| duke@0 | jt->set_stack_size(stack_size);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // 5/22/01: Right now alternate signal stacks do not handle
|
| duke@0 | // throwing stack overflow exceptions, see bug 4463178
|
| duke@0 | // Until a fix is found for this, T2 will NOT imply alternate signal
|
| duke@0 | // stacks.
|
| duke@0 | // If using T2 libthread threads, install an alternate signal stack.
|
| duke@0 | // Because alternate stacks associate with LWPs on Solaris,
|
| duke@0 | // see sigaltstack(2), if using UNBOUND threads, or if UseBoundThreads
|
| duke@0 | // we prefer to explicitly stack bang.
|
| duke@0 | // If not using T2 libthread, but using UseBoundThreads any threads
|
| duke@0 | // (primordial thread, jni_attachCurrentThread) we do not create,
|
| duke@0 | // probably are not bound, therefore they can not have an alternate
|
| duke@0 | // signal stack. Since our stack banging code is generated and
|
| duke@0 | // is shared across threads, all threads must be bound to allow
|
| duke@0 | // using alternate signal stacks. The alternative is to interpose
|
| duke@0 | // on _lwp_create to associate an alt sig stack with each LWP,
|
| duke@0 | // and this could be a problem when the JVM is embedded.
|
| duke@0 | // We would prefer to use alternate signal stacks with T2
|
| duke@0 | // Since there is currently no accurate way to detect T2
|
| duke@0 | // we do not. Assuming T2 when running T1 causes sig 11s or assertions
|
| duke@0 | // on installing alternate signal stacks
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // 05/09/03: removed alternate signal stack support for Solaris
|
| duke@0 | // The alternate signal stack mechanism is no longer needed to
|
| duke@0 | // handle stack overflow. This is now handled by allocating
|
| duke@0 | // guard pages (red zone) and stackbanging.
|
| duke@0 | // Initially the alternate signal stack mechanism was removed because
|
| duke@0 | // it did not work with T1 llibthread. Alternate
|
| duke@0 | // signal stacks MUST have all threads bound to lwps. Applications
|
| duke@0 | // can create their own threads and attach them without their being
|
| duke@0 | // bound under T1. This is frequently the case for the primordial thread.
|
| duke@0 | // If we were ever to reenable this mechanism we would need to
|
| duke@0 | // use the dynamic check for T2 libthread.
|
| duke@0 |
|
| duke@0 | os::Solaris::init_thread_fpu_state();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Free Solaris resources related to the OSThread
|
| duke@0 | void os::free_thread(OSThread* osthread) {
|
| duke@0 | assert(osthread != NULL, "os::free_thread but osthread not set");
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // We are told to free resources of the argument thread,
|
| duke@0 | // but we can only really operate on the current thread.
|
| duke@0 | // The main thread must take the VMThread down synchronously
|
| duke@0 | // before the main thread exits and frees up CodeHeap
|
| duke@0 | guarantee((Thread::current()->osthread() == osthread
|
| duke@0 | || (osthread == VMThread::vm_thread()->osthread())), "os::free_thread but not current thread");
|
| duke@0 | if (Thread::current()->osthread() == osthread) {
|
| duke@0 | // Restore caller's signal mask
|
| duke@0 | sigset_t sigmask = osthread->caller_sigmask();
|
| duke@0 | thr_sigsetmask(SIG_SETMASK, &sigmask, NULL);
|
| duke@0 | }
|
| duke@0 | delete osthread;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::pd_start_thread(Thread* thread) {
|
| duke@0 | int status = thr_continue(thread->osthread()->thread_id());
|
| duke@0 | assert_status(status == 0, status, "thr_continue failed");
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | intx os::current_thread_id() {
|
| duke@0 | return (intx)thr_self();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static pid_t _initial_pid = 0;
|
| duke@0 |
|
| duke@0 | int os::current_process_id() {
|
| duke@0 | return (int)(_initial_pid ? _initial_pid : getpid());
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | int os::allocate_thread_local_storage() {
|
| duke@0 | // %%% in Win32 this allocates a memory segment pointed to by a
|
| duke@0 | // register. Dan Stein can implement a similar feature in
|
| duke@0 | // Solaris. Alternatively, the VM can do the same thing
|
| duke@0 | // explicitly: malloc some storage and keep the pointer in a
|
| duke@0 | // register (which is part of the thread's context) (or keep it
|
| duke@0 | // in TLS).
|
| duke@0 | // %%% In current versions of Solaris, thr_self and TSD can
|
| duke@0 | // be accessed via short sequences of displaced indirections.
|
| duke@0 | // The value of thr_self is available as %g7(36).
|
| duke@0 | // The value of thr_getspecific(k) is stored in %g7(12)(4)(k*4-4),
|
| duke@0 | // assuming that the current thread already has a value bound to k.
|
| duke@0 | // It may be worth experimenting with such access patterns,
|
| duke@0 | // and later having the parameters formally exported from a Solaris
|
| duke@0 | // interface. I think, however, that it will be faster to
|
| duke@0 | // maintain the invariant that %g2 always contains the
|
| duke@0 | // JavaThread in Java code, and have stubs simply
|
| duke@0 | // treat %g2 as a caller-save register, preserving it in a %lN.
|
| duke@0 | thread_key_t tk;
|
| duke@0 | if (thr_keycreate( &tk, NULL ) )
|
| jcoomes@1700 | fatal(err_msg("os::allocate_thread_local_storage: thr_keycreate failed "
|
| jcoomes@1700 | "(%s)", strerror(errno)));
|
| duke@0 | return int(tk);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::free_thread_local_storage(int index) {
|
| duke@0 | // %%% don't think we need anything here
|
| duke@0 | // if ( pthread_key_delete((pthread_key_t) tk) )
|
| duke@0 | // fatal("os::free_thread_local_storage: pthread_key_delete failed");
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | #define SMALLINT 32 // libthread allocate for tsd_common is a version specific
|
| duke@0 | // small number - point is NO swap space available
|
| duke@0 | void os::thread_local_storage_at_put(int index, void* value) {
|
| duke@0 | // %%% this is used only in threadLocalStorage.cpp
|
| duke@0 | if (thr_setspecific((thread_key_t)index, value)) {
|
| duke@0 | if (errno == ENOMEM) {
|
| duke@0 | vm_exit_out_of_memory(SMALLINT, "thr_setspecific: out of swap space");
|
| duke@0 | } else {
|
| jcoomes@1700 | fatal(err_msg("os::thread_local_storage_at_put: thr_setspecific failed "
|
| jcoomes@1700 | "(%s)", strerror(errno)));
|
| duke@0 | }
|
| duke@0 | } else {
|
| duke@0 | ThreadLocalStorage::set_thread_in_slot ((Thread *) value) ;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // This function could be called before TLS is initialized, for example, when
|
| duke@0 | // VM receives an async signal or when VM causes a fatal error during
|
| duke@0 | // initialization. Return NULL if thr_getspecific() fails.
|
| duke@0 | void* os::thread_local_storage_at(int index) {
|
| duke@0 | // %%% this is used only in threadLocalStorage.cpp
|
| duke@0 | void* r = NULL;
|
| duke@0 | return thr_getspecific((thread_key_t)index, &r) != 0 ? NULL : r;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | const int NANOSECS_PER_MILLISECS = 1000000;
|
| duke@0 | // gethrtime can move backwards if read from one cpu and then a different cpu
|
| duke@0 | // getTimeNanos is guaranteed to not move backward on Solaris
|
| duke@0 | // local spinloop created as faster for a CAS on an int than
|
| duke@0 | // a CAS on a 64bit jlong. Also Atomic::cmpxchg for jlong is not
|
| duke@0 | // supported on sparc v8 or pre supports_cx8 intel boxes.
|
| duke@0 | // oldgetTimeNanos for systems which do not support CAS on 64bit jlong
|
| duke@0 | // i.e. sparc v8 and pre supports_cx8 (i486) intel boxes
|
| duke@0 | inline hrtime_t oldgetTimeNanos() {
|
| duke@0 | int gotlock = LOCK_INVALID;
|
| duke@0 | hrtime_t newtime = gethrtime();
|
| duke@0 |
|
| duke@0 | for (;;) {
|
| duke@0 | // grab lock for max_hrtime
|
| duke@0 | int curlock = max_hrtime_lock;
|
| duke@0 | if (curlock & LOCK_BUSY) continue;
|
| duke@0 | if (gotlock = Atomic::cmpxchg(LOCK_BUSY, &max_hrtime_lock, LOCK_FREE) != LOCK_FREE) continue;
|
| duke@0 | if (newtime > max_hrtime) {
|
| duke@0 | max_hrtime = newtime;
|
| duke@0 | } else {
|
| duke@0 | newtime = max_hrtime;
|
| duke@0 | }
|
| duke@0 | // release lock
|
| duke@0 | max_hrtime_lock = LOCK_FREE;
|
| duke@0 | return newtime;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | // gethrtime can move backwards if read from one cpu and then a different cpu
|
| duke@0 | // getTimeNanos is guaranteed to not move backward on Solaris
|
| duke@0 | inline hrtime_t getTimeNanos() {
|
| duke@0 | if (VM_Version::supports_cx8()) {
|
| xlu@557 | const hrtime_t now = gethrtime();
|
| kvn@1017 | // Use atomic long load since 32-bit x86 uses 2 registers to keep long.
|
| kvn@1017 | const hrtime_t prev = Atomic::load((volatile jlong*)&max_hrtime);
|
| xlu@557 | if (now <= prev) return prev; // same or retrograde time;
|
| xlu@557 | const hrtime_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&max_hrtime, prev);
|
| xlu@557 | assert(obsv >= prev, "invariant"); // Monotonicity
|
| xlu@557 | // If the CAS succeeded then we're done and return "now".
|
| xlu@557 | // If the CAS failed and the observed value "obs" is >= now then
|
| xlu@557 | // we should return "obs". If the CAS failed and now > obs > prv then
|
| xlu@557 | // some other thread raced this thread and installed a new value, in which case
|
| xlu@557 | // we could either (a) retry the entire operation, (b) retry trying to install now
|
| xlu@557 | // or (c) just return obs. We use (c). No loop is required although in some cases
|
| xlu@557 | // we might discard a higher "now" value in deference to a slightly lower but freshly
|
| xlu@557 | // installed obs value. That's entirely benign -- it admits no new orderings compared
|
| xlu@557 | // to (a) or (b) -- and greatly reduces coherence traffic.
|
| xlu@557 | // We might also condition (c) on the magnitude of the delta between obs and now.
|
| xlu@557 | // Avoiding excessive CAS operations to hot RW locations is critical.
|
| xlu@557 | // See http://blogs.sun.com/dave/entry/cas_and_cache_trivia_invalidate
|
| xlu@557 | return (prev == obsv) ? now : obsv ;
|
| duke@0 | } else {
|
| duke@0 | return oldgetTimeNanos();
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Time since start-up in seconds to a fine granularity.
|
| duke@0 | // Used by VMSelfDestructTimer and the MemProfiler.
|
| duke@0 | double os::elapsedTime() {
|
| duke@0 | return (double)(getTimeNanos() - first_hrtime) / (double)hrtime_hz;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | jlong os::elapsed_counter() {
|
| duke@0 | return (jlong)(getTimeNanos() - first_hrtime);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | jlong os::elapsed_frequency() {
|
| duke@0 | return hrtime_hz;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Return the real, user, and system times in seconds from an
|
| duke@0 | // arbitrary fixed point in the past.
|
| duke@0 | bool os::getTimesSecs(double* process_real_time,
|
| duke@0 | double* process_user_time,
|
| duke@0 | double* process_system_time) {
|
| duke@0 | struct tms ticks;
|
| duke@0 | clock_t real_ticks = times(&ticks);
|
| duke@0 |
|
| duke@0 | if (real_ticks == (clock_t) (-1)) {
|
| duke@0 | return false;
|
| duke@0 | } else {
|
| duke@0 | double ticks_per_second = (double) clock_tics_per_sec;
|
| duke@0 | *process_user_time = ((double) ticks.tms_utime) / ticks_per_second;
|
| duke@0 | *process_system_time = ((double) ticks.tms_stime) / ticks_per_second;
|
| duke@0 | // For consistency return the real time from getTimeNanos()
|
| duke@0 | // converted to seconds.
|
| duke@0 | *process_real_time = ((double) getTimeNanos()) / ((double) NANOUNITS);
|
| duke@0 |
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| ysr@397 | bool os::supports_vtime() { return true; }
|
| ysr@397 |
|
| ysr@397 | bool os::enable_vtime() {
|
| ysr@397 | int fd = open("/proc/self/ctl", O_WRONLY);
|
| ysr@397 | if (fd == -1)
|
| ysr@397 | return false;
|
| ysr@397 |
|
| ysr@397 | long cmd[] = { PCSET, PR_MSACCT };
|
| ysr@397 | int res = write(fd, cmd, sizeof(long) * 2);
|
| ysr@397 | close(fd);
|
| ysr@397 | if (res != sizeof(long) * 2)
|
| ysr@397 | return false;
|
| ysr@397 |
|
| ysr@397 | return true;
|
| ysr@397 | }
|
| ysr@397 |
|
| ysr@397 | bool os::vtime_enabled() {
|
| ysr@397 | int fd = open("/proc/self/status", O_RDONLY);
|
| ysr@397 | if (fd == -1)
|
| ysr@397 | return false;
|
| ysr@397 |
|
| ysr@397 | pstatus_t status;
|
| ysr@397 | int res = read(fd, (void*) &status, sizeof(pstatus_t));
|
| ysr@397 | close(fd);
|
| ysr@397 | if (res != sizeof(pstatus_t))
|
| ysr@397 | return false;
|
| ysr@397 |
|
| ysr@397 | return status.pr_flags & PR_MSACCT;
|
| ysr@397 | }
|
| ysr@397 |
|
| ysr@397 | double os::elapsedVTime() {
|
| ysr@397 | return (double)gethrvtime() / (double)hrtime_hz;
|
| ysr@397 | }
|
| ysr@397 |
|
| duke@0 | // Used internally for comparisons only
|
| duke@0 | // getTimeMillis guaranteed to not move backwards on Solaris
|
| duke@0 | jlong getTimeMillis() {
|
| duke@0 | jlong nanotime = getTimeNanos();
|
| duke@0 | return (jlong)(nanotime / NANOSECS_PER_MILLISECS);
|
| duke@0 | }
|
| duke@0 |
|
| sbohne@119 | // Must return millis since Jan 1 1970 for JVM_CurrentTimeMillis
|
| sbohne@119 | jlong os::javaTimeMillis() {
|
| duke@0 | timeval t;
|
| duke@0 | if (gettimeofday( &t, NULL) == -1)
|
| jcoomes@1700 | fatal(err_msg("os::javaTimeMillis: gettimeofday (%s)", strerror(errno)));
|
| duke@0 | return jlong(t.tv_sec) * 1000 + jlong(t.tv_usec) / 1000;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | jlong os::javaTimeNanos() {
|
| duke@0 | return (jlong)getTimeNanos();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) {
|
| duke@0 | info_ptr->max_value = ALL_64_BITS; // gethrtime() uses all 64 bits
|
| duke@0 | info_ptr->may_skip_backward = false; // not subject to resetting or drifting
|
| duke@0 | info_ptr->may_skip_forward = false; // not subject to resetting or drifting
|
| duke@0 | info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | char * os::local_time_string(char *buf, size_t buflen) {
|
| duke@0 | struct tm t;
|
| duke@0 | time_t long_time;
|
| duke@0 | time(&long_time);
|
| duke@0 | localtime_r(&long_time, &t);
|
| duke@0 | jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d",
|
| duke@0 | t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
|
| duke@0 | t.tm_hour, t.tm_min, t.tm_sec);
|
| duke@0 | return buf;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Note: os::shutdown() might be called very early during initialization, or
|
| duke@0 | // called from signal handler. Before adding something to os::shutdown(), make
|
| duke@0 | // sure it is async-safe and can handle partially initialized VM.
|
| duke@0 | void os::shutdown() {
|
| duke@0 |
|
| duke@0 | // allow PerfMemory to attempt cleanup of any persistent resources
|
| duke@0 | perfMemory_exit();
|
| duke@0 |
|
| duke@0 | // needs to remove object in file system
|
| duke@0 | AttachListener::abort();
|
| duke@0 |
|
| duke@0 | // flush buffered output, finish log files
|
| duke@0 | ostream_abort();
|
| duke@0 |
|
| duke@0 | // Check for abort hook
|
| duke@0 | abort_hook_t abort_hook = Arguments::abort_hook();
|
| duke@0 | if (abort_hook != NULL) {
|
| duke@0 | abort_hook();
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Note: os::abort() might be called very early during initialization, or
|
| duke@0 | // called from signal handler. Before adding something to os::abort(), make
|
| duke@0 | // sure it is async-safe and can handle partially initialized VM.
|
| duke@0 | void os::abort(bool dump_core) {
|
| duke@0 | os::shutdown();
|
| duke@0 | if (dump_core) {
|
| duke@0 | #ifndef PRODUCT
|
| duke@0 | fdStream out(defaultStream::output_fd());
|
| duke@0 | out.print_raw("Current thread is ");
|
| duke@0 | char buf[16];
|
| duke@0 | jio_snprintf(buf, sizeof(buf), UINTX_FORMAT, os::current_thread_id());
|
| duke@0 | out.print_raw_cr(buf);
|
| duke@0 | out.print_raw_cr("Dumping core ...");
|
| duke@0 | #endif
|
| duke@0 | ::abort(); // dump core (for debugging)
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | ::exit(1);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Die immediately, no exit hook, no abort hook, no cleanup.
|
| duke@0 | void os::die() {
|
| duke@0 | _exit(-1);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // unused
|
| duke@0 | void os::set_error_file(const char *logfile) {}
|
| duke@0 |
|
| duke@0 | // DLL functions
|
| duke@0 |
|
| duke@0 | const char* os::dll_file_extension() { return ".so"; }
|
| duke@0 |
|
| coleenp@1999 | // This must be hard coded because it's the system's temporary
|
| coleenp@1999 | // directory not the java application's temp directory, ala java.io.tmpdir.
|
| coleenp@1999 | const char* os::get_temp_directory() { return "/tmp"; }
|
| duke@0 |
|
| phh@819 | static bool file_exists(const char* filename) {
|
| phh@819 | struct stat statbuf;
|
| phh@819 | if (filename == NULL || strlen(filename) == 0) {
|
| phh@819 | return false;
|
| phh@819 | }
|
| phh@819 | return os::stat(filename, &statbuf) == 0;
|
| phh@819 | }
|
| phh@819 |
|
| phh@819 | void os::dll_build_name(char* buffer, size_t buflen,
|
| phh@819 | const char* pname, const char* fname) {
|
| phh@819 | // Copied from libhpi
|
| kamg@299 | const size_t pnamelen = pname ? strlen(pname) : 0;
|
| kamg@299 |
|
| phh@819 | // Quietly truncate on buffer overflow. Should be an error.
|
| kamg@299 | if (pnamelen + strlen(fname) + 10 > (size_t) buflen) {
|
| bobv@1892 | *buffer = '\0';
|
| bobv@1892 | return;
|
| kamg@299 | }
|
| kamg@299 |
|
| kamg@299 | if (pnamelen == 0) {
|
| phh@819 | snprintf(buffer, buflen, "lib%s.so", fname);
|
| phh@819 | } else if (strchr(pname, *os::path_separator()) != NULL) {
|
| phh@819 | int n;
|
| phh@819 | char** pelements = split_path(pname, &n);
|
| phh@819 | for (int i = 0 ; i < n ; i++) {
|
| phh@819 | // really shouldn't be NULL but what the heck, check can't hurt
|
| phh@819 | if (pelements[i] == NULL || strlen(pelements[i]) == 0) {
|
| phh@819 | continue; // skip the empty path values
|
| phh@819 | }
|
| phh@819 | snprintf(buffer, buflen, "%s/lib%s.so", pelements[i], fname);
|
| phh@819 | if (file_exists(buffer)) {
|
| phh@819 | break;
|
| phh@819 | }
|
| phh@819 | }
|
| phh@819 | // release the storage
|
| phh@819 | for (int i = 0 ; i < n ; i++) {
|
| phh@819 | if (pelements[i] != NULL) {
|
| phh@819 | FREE_C_HEAP_ARRAY(char, pelements[i]);
|
| phh@819 | }
|
| phh@819 | }
|
| phh@819 | if (pelements != NULL) {
|
| phh@819 | FREE_C_HEAP_ARRAY(char*, pelements);
|
| phh@819 | }
|
| kamg@299 | } else {
|
| phh@819 | snprintf(buffer, buflen, "%s/lib%s.so", pname, fname);
|
| kamg@299 | }
|
| kamg@299 | }
|
| kamg@299 |
|
| duke@0 | const char* os::get_current_directory(char *buf, int buflen) {
|
| duke@0 | return getcwd(buf, buflen);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // check if addr is inside libjvm[_g].so
|
| duke@0 | bool os::address_is_in_vm(address addr) {
|
| duke@0 | static address libjvm_base_addr;
|
| duke@0 | Dl_info dlinfo;
|
| duke@0 |
|
| duke@0 | if (libjvm_base_addr == NULL) {
|
| duke@0 | dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo);
|
| duke@0 | libjvm_base_addr = (address)dlinfo.dli_fbase;
|
| duke@0 | assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm");
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (dladdr((void *)addr, &dlinfo)) {
|
| duke@0 | if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | typedef int (*dladdr1_func_type) (void *, Dl_info *, void **, int);
|
| duke@0 | static dladdr1_func_type dladdr1_func = NULL;
|
| duke@0 |
|
| duke@0 | bool os::dll_address_to_function_name(address addr, char *buf,
|
| duke@0 | int buflen, int * offset) {
|
| duke@0 | Dl_info dlinfo;
|
| duke@0 |
|
| duke@0 | // dladdr1_func was initialized in os::init()
|
| duke@0 | if (dladdr1_func){
|
| duke@0 | // yes, we have dladdr1
|
| duke@0 |
|
| duke@0 | // Support for dladdr1 is checked at runtime; it may be
|
| duke@0 | // available even if the vm is built on a machine that does
|
| duke@0 | // not have dladdr1 support. Make sure there is a value for
|
| duke@0 | // RTLD_DL_SYMENT.
|
| duke@0 | #ifndef RTLD_DL_SYMENT
|
| duke@0 | #define RTLD_DL_SYMENT 1
|
| duke@0 | #endif
|
| duke@0 | Sym * info;
|
| duke@0 | if (dladdr1_func((void *)addr, &dlinfo, (void **)&info,
|
| duke@0 | RTLD_DL_SYMENT)) {
|
| duke@0 | if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname);
|
| duke@0 | if (offset) *offset = addr - (address)dlinfo.dli_saddr;
|
| duke@0 |
|
| duke@0 | // check if the returned symbol really covers addr
|
| duke@0 | return ((char *)dlinfo.dli_saddr + info->st_size > (char *)addr);
|
| duke@0 | } else {
|
| duke@0 | if (buf) buf[0] = '\0';
|
| duke@0 | if (offset) *offset = -1;
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | } else {
|
| duke@0 | // no, only dladdr is available
|
| duke@0 | if(dladdr((void *)addr, &dlinfo)) {
|
| duke@0 | if (buf) jio_snprintf(buf, buflen, dlinfo.dli_sname);
|
| duke@0 | if (offset) *offset = addr - (address)dlinfo.dli_saddr;
|
| duke@0 | return true;
|
| duke@0 | } else {
|
| duke@0 | if (buf) buf[0] = '\0';
|
| duke@0 | if (offset) *offset = -1;
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::dll_address_to_library_name(address addr, char* buf,
|
| duke@0 | int buflen, int* offset) {
|
| duke@0 | Dl_info dlinfo;
|
| duke@0 |
|
| duke@0 | if (dladdr((void*)addr, &dlinfo)){
|
| duke@0 | if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname);
|
| duke@0 | if (offset) *offset = addr - (address)dlinfo.dli_fbase;
|
| duke@0 | return true;
|
| duke@0 | } else {
|
| duke@0 | if (buf) buf[0] = '\0';
|
| duke@0 | if (offset) *offset = -1;
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Prints the names and full paths of all opened dynamic libraries
|
| duke@0 | // for current process
|
| duke@0 | void os::print_dll_info(outputStream * st) {
|
| duke@0 | Dl_info dli;
|
| duke@0 | void *handle;
|
| duke@0 | Link_map *map;
|
| duke@0 | Link_map *p;
|
| duke@0 |
|
| duke@0 | st->print_cr("Dynamic libraries:"); st->flush();
|
| duke@0 |
|
| duke@0 | if (!dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli)) {
|
| duke@0 | st->print_cr("Error: Cannot print dynamic libraries.");
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 | handle = dlopen(dli.dli_fname, RTLD_LAZY);
|
| duke@0 | if (handle == NULL) {
|
| duke@0 | st->print_cr("Error: Cannot print dynamic libraries.");
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 | dlinfo(handle, RTLD_DI_LINKMAP, &map);
|
| duke@0 | if (map == NULL) {
|
| duke@0 | st->print_cr("Error: Cannot print dynamic libraries.");
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | while (map->l_prev != NULL)
|
| duke@0 | map = map->l_prev;
|
| duke@0 |
|
| duke@0 | while (map != NULL) {
|
| duke@0 | st->print_cr(PTR_FORMAT " \t%s", map->l_addr, map->l_name);
|
| duke@0 | map = map->l_next;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | dlclose(handle);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Loads .dll/.so and
|
| duke@0 | // in case of error it checks if .dll/.so was built for the
|
| duke@0 | // same architecture as Hotspot is running on
|
| duke@0 |
|
| duke@0 | void * os::dll_load(const char *filename, char *ebuf, int ebuflen)
|
| duke@0 | {
|
| duke@0 | void * result= ::dlopen(filename, RTLD_LAZY);
|
| duke@0 | if (result != NULL) {
|
| duke@0 | // Successful loading
|
| duke@0 | return result;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | Elf32_Ehdr elf_head;
|
| duke@0 |
|
| duke@0 | // Read system error message into ebuf
|
| duke@0 | // It may or may not be overwritten below
|
| duke@0 | ::strncpy(ebuf, ::dlerror(), ebuflen-1);
|
| duke@0 | ebuf[ebuflen-1]='\0';
|
| duke@0 | int diag_msg_max_length=ebuflen-strlen(ebuf);
|
| duke@0 | char* diag_msg_buf=ebuf+strlen(ebuf);
|
| duke@0 |
|
| duke@0 | if (diag_msg_max_length==0) {
|
| duke@0 | // No more space in ebuf for additional diagnostics message
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | int file_descriptor= ::open(filename, O_RDONLY | O_NONBLOCK);
|
| duke@0 |
|
| duke@0 | if (file_descriptor < 0) {
|
| duke@0 | // Can't open library, report dlerror() message
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool failed_to_read_elf_head=
|
| duke@0 | (sizeof(elf_head)!=
|
| duke@0 | (::read(file_descriptor, &elf_head,sizeof(elf_head)))) ;
|
| duke@0 |
|
| duke@0 | ::close(file_descriptor);
|
| duke@0 | if (failed_to_read_elf_head) {
|
| duke@0 | // file i/o error - report dlerror() msg
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | typedef struct {
|
| duke@0 | Elf32_Half code; // Actual value as defined in elf.h
|
| duke@0 | Elf32_Half compat_class; // Compatibility of archs at VM's sense
|
| duke@0 | char elf_class; // 32 or 64 bit
|
| duke@0 | char endianess; // MSB or LSB
|
| duke@0 | char* name; // String representation
|
| duke@0 | } arch_t;
|
| duke@0 |
|
| duke@0 | static const arch_t arch_array[]={
|
| duke@0 | {EM_386, EM_386, ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"},
|
| duke@0 | {EM_486, EM_386, ELFCLASS32, ELFDATA2LSB, (char*)"IA 32"},
|
| duke@0 | {EM_IA_64, EM_IA_64, ELFCLASS64, ELFDATA2LSB, (char*)"IA 64"},
|
| duke@0 | {EM_X86_64, EM_X86_64, ELFCLASS64, ELFDATA2LSB, (char*)"AMD 64"},
|
| duke@0 | {EM_SPARC, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"},
|
| duke@0 | {EM_SPARC32PLUS, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"},
|
| duke@0 | {EM_SPARCV9, EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"},
|
| duke@0 | {EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"},
|
| bobv@1892 | {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"},
|
| bobv@1892 | {EM_ARM, EM_ARM, ELFCLASS32, ELFDATA2LSB, (char*)"ARM 32"}
|
| duke@0 | };
|
| duke@0 |
|
| duke@0 | #if (defined IA32)
|
| duke@0 | static Elf32_Half running_arch_code=EM_386;
|
| duke@0 | #elif (defined AMD64)
|
| duke@0 | static Elf32_Half running_arch_code=EM_X86_64;
|
| duke@0 | #elif (defined IA64)
|
| duke@0 | static Elf32_Half running_arch_code=EM_IA_64;
|
| duke@0 | #elif (defined __sparc) && (defined _LP64)
|
| duke@0 | static Elf32_Half running_arch_code=EM_SPARCV9;
|
| duke@0 | #elif (defined __sparc) && (!defined _LP64)
|
| duke@0 | static Elf32_Half running_arch_code=EM_SPARC;
|
| duke@0 | #elif (defined __powerpc64__)
|
| duke@0 | static Elf32_Half running_arch_code=EM_PPC64;
|
| duke@0 | #elif (defined __powerpc__)
|
| duke@0 | static Elf32_Half running_arch_code=EM_PPC;
|
| bobv@1892 | #elif (defined ARM)
|
| bobv@1892 | static Elf32_Half running_arch_code=EM_ARM;
|
| duke@0 | #else
|
| duke@0 | #error Method os::dll_load requires that one of following is defined:\
|
| bobv@1892 | IA32, AMD64, IA64, __sparc, __powerpc__, ARM, ARM
|
| duke@0 | #endif
|
| duke@0 |
|
| duke@0 | // Identify compatability class for VM's architecture and library's architecture
|
| duke@0 | // Obtain string descriptions for architectures
|
| duke@0 |
|
| duke@0 | arch_t lib_arch={elf_head.e_machine,0,elf_head.e_ident[EI_CLASS], elf_head.e_ident[EI_DATA], NULL};
|
| duke@0 | int running_arch_index=-1;
|
| duke@0 |
|
| duke@0 | for (unsigned int i=0 ; i < ARRAY_SIZE(arch_array) ; i++ ) {
|
| duke@0 | if (running_arch_code == arch_array[i].code) {
|
| duke@0 | running_arch_index = i;
|
| duke@0 | }
|
| duke@0 | if (lib_arch.code == arch_array[i].code) {
|
| duke@0 | lib_arch.compat_class = arch_array[i].compat_class;
|
| duke@0 | lib_arch.name = arch_array[i].name;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | assert(running_arch_index != -1,
|
| duke@0 | "Didn't find running architecture code (running_arch_code) in arch_array");
|
| duke@0 | if (running_arch_index == -1) {
|
| duke@0 | // Even though running architecture detection failed
|
| duke@0 | // we may still continue with reporting dlerror() message
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (lib_arch.endianess != arch_array[running_arch_index].endianess) {
|
| duke@0 | ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: endianness mismatch)");
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (lib_arch.elf_class != arch_array[running_arch_index].elf_class) {
|
| duke@0 | ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: architecture word width mismatch)");
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (lib_arch.compat_class != arch_array[running_arch_index].compat_class) {
|
| duke@0 | if ( lib_arch.name!=NULL ) {
|
| duke@0 | ::snprintf(diag_msg_buf, diag_msg_max_length-1,
|
| duke@0 | " (Possible cause: can't load %s-bit .so on a %s-bit platform)",
|
| duke@0 | lib_arch.name, arch_array[running_arch_index].name);
|
| duke@0 | } else {
|
| duke@0 | ::snprintf(diag_msg_buf, diag_msg_max_length-1,
|
| duke@0 | " (Possible cause: can't load this .so (machine code=0x%x) on a %s-bit platform)",
|
| duke@0 | lib_arch.code,
|
| duke@0 | arch_array[running_arch_index].name);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| kamg@299 | void* os::dll_lookup(void* handle, const char* name) {
|
| kamg@299 | return dlsym(handle, name);
|
| kamg@299 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | bool _print_ascii_file(const char* filename, outputStream* st) {
|
| duke@0 | int fd = open(filename, O_RDONLY);
|
| duke@0 | if (fd == -1) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | char buf[32];
|
| duke@0 | int bytes;
|
| duke@0 | while ((bytes = read(fd, buf, sizeof(buf))) > 0) {
|
| duke@0 | st->print_raw(buf, bytes);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | close(fd);
|
| duke@0 |
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::print_os_info(outputStream* st) {
|
| duke@0 | st->print("OS:");
|
| duke@0 |
|
| duke@0 | if (!_print_ascii_file("/etc/release", st)) {
|
| duke@0 | st->print("Solaris");
|
| duke@0 | }
|
| duke@0 | st->cr();
|
| duke@0 |
|
| duke@0 | // kernel
|
| duke@0 | st->print("uname:");
|
| duke@0 | struct utsname name;
|
| duke@0 | uname(&name);
|
| duke@0 | st->print(name.sysname); st->print(" ");
|
| duke@0 | st->print(name.release); st->print(" ");
|
| duke@0 | st->print(name.version); st->print(" ");
|
| duke@0 | st->print(name.machine);
|
| duke@0 |
|
| duke@0 | // libthread
|
| duke@0 | if (os::Solaris::T2_libthread()) st->print(" (T2 libthread)");
|
| duke@0 | else st->print(" (T1 libthread)");
|
| duke@0 | st->cr();
|
| duke@0 |
|
| duke@0 | // rlimit
|
| duke@0 | st->print("rlimit:");
|
| duke@0 | struct rlimit rlim;
|
| duke@0 |
|
| duke@0 | st->print(" STACK ");
|
| duke@0 | getrlimit(RLIMIT_STACK, &rlim);
|
| duke@0 | if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity");
|
| duke@0 | else st->print("%uk", rlim.rlim_cur >> 10);
|
| duke@0 |
|
| duke@0 | st->print(", CORE ");
|
| duke@0 | getrlimit(RLIMIT_CORE, &rlim);
|
| duke@0 | if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity");
|
| duke@0 | else st->print("%uk", rlim.rlim_cur >> 10);
|
| duke@0 |
|
| duke@0 | st->print(", NOFILE ");
|
| duke@0 | getrlimit(RLIMIT_NOFILE, &rlim);
|
| duke@0 | if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity");
|
| duke@0 | else st->print("%d", rlim.rlim_cur);
|
| duke@0 |
|
| duke@0 | st->print(", AS ");
|
| duke@0 | getrlimit(RLIMIT_AS, &rlim);
|
| duke@0 | if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity");
|
| duke@0 | else st->print("%uk", rlim.rlim_cur >> 10);
|
| duke@0 | st->cr();
|
| duke@0 |
|
| duke@0 | // load average
|
| duke@0 | st->print("load average:");
|
| duke@0 | double loadavg[3];
|
| duke@0 | os::loadavg(loadavg, 3);
|
| duke@0 | st->print("%0.02f %0.02f %0.02f", loadavg[0], loadavg[1], loadavg[2]);
|
| duke@0 | st->cr();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | static bool check_addr0(outputStream* st) {
|
| duke@0 | jboolean status = false;
|
| duke@0 | int fd = open("/proc/self/map",O_RDONLY);
|
| duke@0 | if (fd >= 0) {
|
| duke@0 | prmap_t p;
|
| duke@0 | while(read(fd, &p, sizeof(p)) > 0) {
|
| duke@0 | if (p.pr_vaddr == 0x0) {
|
| duke@0 | st->print("Warning: Address: 0x%x, Size: %dK, ",p.pr_vaddr, p.pr_size/1024, p.pr_mapname);
|
| duke@0 | st->print("Mapped file: %s, ", p.pr_mapname[0] == '\0' ? "None" : p.pr_mapname);
|
| duke@0 | st->print("Access:");
|
| duke@0 | st->print("%s",(p.pr_mflags & MA_READ) ? "r" : "-");
|
| duke@0 | st->print("%s",(p.pr_mflags & MA_WRITE) ? "w" : "-");
|
| duke@0 | st->print("%s",(p.pr_mflags & MA_EXEC) ? "x" : "-");
|
| duke@0 | st->cr();
|
| duke@0 | status = true;
|
| duke@0 | }
|
| duke@0 | close(fd);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | return status;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::print_memory_info(outputStream* st) {
|
| duke@0 | st->print("Memory:");
|
| duke@0 | st->print(" %dk page", os::vm_page_size()>>10);
|
| duke@0 | st->print(", physical " UINT64_FORMAT "k", os::physical_memory()>>10);
|
| duke@0 | st->print("(" UINT64_FORMAT "k free)", os::available_memory() >> 10);
|
| duke@0 | st->cr();
|
| duke@0 | (void) check_addr0(st);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Taken from /usr/include/sys/machsig.h Supposed to be architecture specific
|
| duke@0 | // but they're the same for all the solaris architectures that we support.
|
| duke@0 | const char *ill_names[] = { "ILL0", "ILL_ILLOPC", "ILL_ILLOPN", "ILL_ILLADR",
|
| duke@0 | "ILL_ILLTRP", "ILL_PRVOPC", "ILL_PRVREG",
|
| duke@0 | "ILL_COPROC", "ILL_BADSTK" };
|
| duke@0 |
|
| duke@0 | const char *fpe_names[] = { "FPE0", "FPE_INTDIV", "FPE_INTOVF", "FPE_FLTDIV",
|
| duke@0 | "FPE_FLTOVF", "FPE_FLTUND", "FPE_FLTRES",
|
| duke@0 | "FPE_FLTINV", "FPE_FLTSUB" };
|
| duke@0 |
|
| duke@0 | const char *segv_names[] = { "SEGV0", "SEGV_MAPERR", "SEGV_ACCERR" };
|
| duke@0 |
|
| duke@0 | const char *bus_names[] = { "BUS0", "BUS_ADRALN", "BUS_ADRERR", "BUS_OBJERR" };
|
| duke@0 |
|
| duke@0 | void os::print_siginfo(outputStream* st, void* siginfo) {
|
| duke@0 | st->print("siginfo:");
|
| duke@0 |
|
| duke@0 | const int buflen = 100;
|
| duke@0 | char buf[buflen];
|
| duke@0 | siginfo_t *si = (siginfo_t*)siginfo;
|
| duke@0 | st->print("si_signo=%s: ", os::exception_name(si->si_signo, buf, buflen));
|
| duke@0 | char *err = strerror(si->si_errno);
|
| duke@0 | if (si->si_errno != 0 && err != NULL) {
|
| duke@0 | st->print("si_errno=%s", err);
|
| duke@0 | } else {
|
| duke@0 | st->print("si_errno=%d", si->si_errno);
|
| duke@0 | }
|
| duke@0 | const int c = si->si_code;
|
| duke@0 | assert(c > 0, "unexpected si_code");
|
| duke@0 | switch (si->si_signo) {
|
| duke@0 | case SIGILL:
|
| duke@0 | st->print(", si_code=%d (%s)", c, c > 8 ? "" : ill_names[c]);
|
| duke@0 | st->print(", si_addr=" PTR_FORMAT, si->si_addr);
|
| duke@0 | break;
|
| duke@0 | case SIGFPE:
|
| duke@0 | st->print(", si_code=%d (%s)", c, c > 9 ? "" : fpe_names[c]);
|
| duke@0 | st->print(", si_addr=" PTR_FORMAT, si->si_addr);
|
| duke@0 | break;
|
| duke@0 | case SIGSEGV:
|
| duke@0 | st->print(", si_code=%d (%s)", c, c > 2 ? "" : segv_names[c]);
|
| duke@0 | st->print(", si_addr=" PTR_FORMAT, si->si_addr);
|
| duke@0 | break;
|
| duke@0 | case SIGBUS:
|
| duke@0 | st->print(", si_code=%d (%s)", c, c > 3 ? "" : bus_names[c]);
|
| duke@0 | st->print(", si_addr=" PTR_FORMAT, si->si_addr);
|
| duke@0 | break;
|
| duke@0 | default:
|
| duke@0 | st->print(", si_code=%d", si->si_code);
|
| duke@0 | // no si_addr
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if ((si->si_signo == SIGBUS || si->si_signo == SIGSEGV) &&
|
| duke@0 | UseSharedSpaces) {
|
| duke@0 | FileMapInfo* mapinfo = FileMapInfo::current_info();
|
| duke@0 | if (mapinfo->is_in_shared_space(si->si_addr)) {
|
| duke@0 | st->print("\n\nError accessing class data sharing archive." \
|
| duke@0 | " Mapped file inaccessible during execution, " \
|
| duke@0 | " possible disk/network problem.");
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | st->cr();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Moved from whole group, because we need them here for diagnostic
|
| duke@0 | // prints.
|
| duke@0 | #define OLDMAXSIGNUM 32
|
| duke@0 | static int Maxsignum = 0;
|
| duke@0 | static int *ourSigFlags = NULL;
|
| duke@0 |
|
| duke@0 | extern "C" void sigINTRHandler(int, siginfo_t*, void*);
|
| duke@0 |
|
| duke@0 | int os::Solaris::get_our_sigflags(int sig) {
|
| duke@0 | assert(ourSigFlags!=NULL, "signal data structure not initialized");
|
| duke@0 | assert(sig > 0 && sig < Maxsignum, "vm signal out of expected range");
|
| duke@0 | return ourSigFlags[sig];
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::set_our_sigflags(int sig, int flags) {
|
| duke@0 | assert(ourSigFlags!=NULL, "signal data structure not initialized");
|
| duke@0 | assert(sig > 0 && sig < Maxsignum, "vm signal out of expected range");
|
| duke@0 | ourSigFlags[sig] = flags;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | static const char* get_signal_handler_name(address handler,
|
| duke@0 | char* buf, int buflen) {
|
| duke@0 | int offset;
|
| duke@0 | bool found = os::dll_address_to_library_name(handler, buf, buflen, &offset);
|
| duke@0 | if (found) {
|
| duke@0 | // skip directory names
|
| duke@0 | const char *p1, *p2;
|
| duke@0 | p1 = buf;
|
| duke@0 | size_t len = strlen(os::file_separator());
|
| duke@0 | while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len;
|
| duke@0 | jio_snprintf(buf, buflen, "%s+0x%x", p1, offset);
|
| duke@0 | } else {
|
| duke@0 | jio_snprintf(buf, buflen, PTR_FORMAT, handler);
|
| duke@0 | }
|
| duke@0 | return buf;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static void print_signal_handler(outputStream* st, int sig,
|
| duke@0 | char* buf, size_t buflen) {
|
| duke@0 | struct sigaction sa;
|
| duke@0 |
|
| duke@0 | sigaction(sig, NULL, &sa);
|
| duke@0 |
|
| duke@0 | st->print("%s: ", os::exception_name(sig, buf, buflen));
|
| duke@0 |
|
| duke@0 | address handler = (sa.sa_flags & SA_SIGINFO)
|
| duke@0 | ? CAST_FROM_FN_PTR(address, sa.sa_sigaction)
|
| duke@0 | : CAST_FROM_FN_PTR(address, sa.sa_handler);
|
| duke@0 |
|
| duke@0 | if (handler == CAST_FROM_FN_PTR(address, SIG_DFL)) {
|
| duke@0 | st->print("SIG_DFL");
|
| duke@0 | } else if (handler == CAST_FROM_FN_PTR(address, SIG_IGN)) {
|
| duke@0 | st->print("SIG_IGN");
|
| duke@0 | } else {
|
| duke@0 | st->print("[%s]", get_signal_handler_name(handler, buf, buflen));
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | st->print(", sa_mask[0]=" PTR32_FORMAT, *(uint32_t*)&sa.sa_mask);
|
| duke@0 |
|
| duke@0 | address rh = VMError::get_resetted_sighandler(sig);
|
| duke@0 | // May be, handler was resetted by VMError?
|
| duke@0 | if(rh != NULL) {
|
| duke@0 | handler = rh;
|
| duke@0 | sa.sa_flags = VMError::get_resetted_sigflags(sig);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | st->print(", sa_flags=" PTR32_FORMAT, sa.sa_flags);
|
| duke@0 |
|
| duke@0 | // Check: is it our handler?
|
| duke@0 | if(handler == CAST_FROM_FN_PTR(address, signalHandler) ||
|
| duke@0 | handler == CAST_FROM_FN_PTR(address, sigINTRHandler)) {
|
| duke@0 | // It is our signal handler
|
| duke@0 | // check for flags
|
| duke@0 | if(sa.sa_flags != os::Solaris::get_our_sigflags(sig)) {
|
| duke@0 | st->print(
|
| duke@0 | ", flags was changed from " PTR32_FORMAT ", consider using jsig library",
|
| duke@0 | os::Solaris::get_our_sigflags(sig));
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | st->cr();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) {
|
| duke@0 | st->print_cr("Signal Handlers:");
|
| duke@0 | print_signal_handler(st, SIGSEGV, buf, buflen);
|
| duke@0 | print_signal_handler(st, SIGBUS , buf, buflen);
|
| duke@0 | print_signal_handler(st, SIGFPE , buf, buflen);
|
| duke@0 | print_signal_handler(st, SIGPIPE, buf, buflen);
|
| duke@0 | print_signal_handler(st, SIGXFSZ, buf, buflen);
|
| duke@0 | print_signal_handler(st, SIGILL , buf, buflen);
|
| duke@0 | print_signal_handler(st, INTERRUPT_SIGNAL, buf, buflen);
|
| duke@0 | print_signal_handler(st, ASYNC_SIGNAL, buf, buflen);
|
| duke@0 | print_signal_handler(st, BREAK_SIGNAL, buf, buflen);
|
| duke@0 | print_signal_handler(st, SHUTDOWN1_SIGNAL , buf, buflen);
|
| duke@0 | print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen);
|
| duke@0 | print_signal_handler(st, SHUTDOWN3_SIGNAL, buf, buflen);
|
| duke@0 | print_signal_handler(st, os::Solaris::SIGinterrupt(), buf, buflen);
|
| duke@0 | print_signal_handler(st, os::Solaris::SIGasync(), buf, buflen);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static char saved_jvm_path[MAXPATHLEN] = { 0 };
|
| duke@0 |
|
| duke@0 | // Find the full path to the current module, libjvm.so or libjvm_g.so
|
| duke@0 | void os::jvm_path(char *buf, jint buflen) {
|
| duke@0 | // Error checking.
|
| duke@0 | if (buflen < MAXPATHLEN) {
|
| duke@0 | assert(false, "must use a large-enough buffer");
|
| duke@0 | buf[0] = '\0';
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 | // Lazy resolve the path to current module.
|
| duke@0 | if (saved_jvm_path[0] != 0) {
|
| duke@0 | strcpy(buf, saved_jvm_path);
|
| duke@0 | return;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | Dl_info dlinfo;
|
| duke@0 | int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
|
| duke@0 | assert(ret != 0, "cannot locate libjvm");
|
| duke@0 | realpath((char *)dlinfo.dli_fname, buf);
|
| duke@0 |
|
| duke@0 | if (strcmp(Arguments::sun_java_launcher(), "gamma") == 0) {
|
| duke@0 | // Support for the gamma launcher. Typical value for buf is
|
| duke@0 | // "<JAVA_HOME>/jre/lib/<arch>/<vmtype>/libjvm.so". If "/jre/lib/" appears at
|
| duke@0 | // the right place in the string, then assume we are installed in a JDK and
|
| duke@0 | // we're done. Otherwise, check for a JAVA_HOME environment variable and fix
|
| duke@0 | // up the path so it looks like libjvm.so is installed there (append a
|
| duke@0 | // fake suffix hotspot/libjvm.so).
|
| duke@0 | const char *p = buf + strlen(buf) - 1;
|
| duke@0 | for (int count = 0; p > buf && count < 5; ++count) {
|
| duke@0 | for (--p; p > buf && *p != '/'; --p)
|
| duke@0 | /* empty */ ;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (strncmp(p, "/jre/lib/", 9) != 0) {
|
| duke@0 | // Look for JAVA_HOME in the environment.
|
| duke@0 | char* java_home_var = ::getenv("JAVA_HOME");
|
| duke@0 | if (java_home_var != NULL && java_home_var[0] != 0) {
|
| duke@0 | char cpu_arch[12];
|
| mchung@1839 | char* jrelib_p;
|
| mchung@1839 | int len;
|
| duke@0 | sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof(cpu_arch));
|
| duke@0 | #ifdef _LP64
|
| duke@0 | // If we are on sparc running a 64-bit vm, look in jre/lib/sparcv9.
|
| duke@0 | if (strcmp(cpu_arch, "sparc") == 0) {
|
| duke@0 | strcat(cpu_arch, "v9");
|
| duke@0 | } else if (strcmp(cpu_arch, "i386") == 0) {
|
| duke@0 | strcpy(cpu_arch, "amd64");
|
| duke@0 | }
|
| duke@0 | #endif
|
| duke@0 | // Check the current module name "libjvm.so" or "libjvm_g.so".
|
| duke@0 | p = strrchr(buf, '/');
|
| duke@0 | assert(strstr(p, "/libjvm") == p, "invalid library name");
|
| duke@0 | p = strstr(p, "_g") ? "_g" : "";
|
| duke@0 |
|
| duke@0 | realpath(java_home_var, buf);
|
| mchung@1839 | // determine if this is a legacy image or modules image
|
| mchung@1839 | // modules image doesn't have "jre" subdirectory
|
| mchung@1839 | len = strlen(buf);
|
| mchung@1839 | jrelib_p = buf + len;
|
| mchung@1839 | snprintf(jrelib_p, buflen-len, "/jre/lib/%s", cpu_arch);
|
| mchung@1839 | if (0 != access(buf, F_OK)) {
|
| mchung@1839 | snprintf(jrelib_p, buflen-len, "/lib/%s", cpu_arch);
|
| mchung@1839 | }
|
| mchung@1839 |
|
| duke@0 | if (0 == access(buf, F_OK)) {
|
| duke@0 | // Use current module name "libjvm[_g].so" instead of
|
| duke@0 | // "libjvm"debug_only("_g")".so" since for fastdebug version
|
| duke@0 | // we should have "libjvm.so" but debug_only("_g") adds "_g"!
|
| duke@0 | // It is used when we are choosing the HPI library's name
|
| duke@0 | // "libhpi[_g].so" in hpi::initialize_get_interface().
|
| mchung@1839 | len = strlen(buf);
|
| mchung@1839 | snprintf(buf + len, buflen-len, "/hotspot/libjvm%s.so", p);
|
| duke@0 | } else {
|
| duke@0 | // Go back to path of .so
|
| duke@0 | realpath((char *)dlinfo.dli_fname, buf);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | strcpy(saved_jvm_path, buf);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | void os::print_jni_name_prefix_on(outputStream* st, int args_size) {
|
| duke@0 | // no prefix required, not even "_"
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | void os::print_jni_name_suffix_on(outputStream* st, int args_size) {
|
| duke@0 | // no suffix required
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // sun.misc.Signal
|
| duke@0 |
|
| duke@0 | extern "C" {
|
| duke@0 | static void UserHandler(int sig, void *siginfo, void *context) {
|
| duke@0 | // Ctrl-C is pressed during error reporting, likely because the error
|
| duke@0 | // handler fails to abort. Let VM die immediately.
|
| duke@0 | if (sig == SIGINT && is_error_reported()) {
|
| duke@0 | os::die();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | os::signal_notify(sig);
|
| duke@0 | // We do not need to reinstate the signal handler each time...
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void* os::user_handler() {
|
| duke@0 | return CAST_FROM_FN_PTR(void*, UserHandler);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | extern "C" {
|
| duke@0 | typedef void (*sa_handler_t)(int);
|
| duke@0 | typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void* os::signal(int signal_number, void* handler) {
|
| duke@0 | struct sigaction sigAct, oldSigAct;
|
| duke@0 | sigfillset(&(sigAct.sa_mask));
|
| duke@0 | sigAct.sa_flags = SA_RESTART & ~SA_RESETHAND;
|
| duke@0 | sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler);
|
| duke@0 |
|
| duke@0 | if (sigaction(signal_number, &sigAct, &oldSigAct))
|
| duke@0 | // -1 means registration failed
|
| duke@0 | return (void *)-1;
|
| duke@0 |
|
| duke@0 | return CAST_FROM_FN_PTR(void*, oldSigAct.sa_handler);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::signal_raise(int signal_number) {
|
| duke@0 | raise(signal_number);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | /*
|
| duke@0 | * The following code is moved from os.cpp for making this
|
| duke@0 | * code platform specific, which it is by its very nature.
|
| duke@0 | */
|
| duke@0 |
|
| duke@0 | // a counter for each possible signal value
|
| duke@0 | static int Sigexit = 0;
|
| duke@0 | static int Maxlibjsigsigs;
|
| duke@0 | static jint *pending_signals = NULL;
|
| duke@0 | static int *preinstalled_sigs = NULL;
|
| duke@0 | static struct sigaction *chainedsigactions = NULL;
|
| duke@0 | static sema_t sig_sem;
|
| duke@0 | typedef int (*version_getting_t)();
|
| duke@0 | version_getting_t os::Solaris::get_libjsig_version = NULL;
|
| duke@0 | static int libjsigversion = NULL;
|
| duke@0 |
|
| duke@0 | int os::sigexitnum_pd() {
|
| duke@0 | assert(Sigexit > 0, "signal memory not yet initialized");
|
| duke@0 | return Sigexit;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::Solaris::init_signal_mem() {
|
| duke@0 | // Initialize signal structures
|
| duke@0 | Maxsignum = SIGRTMAX;
|
| duke@0 | Sigexit = Maxsignum+1;
|
| duke@0 | assert(Maxsignum >0, "Unable to obtain max signal number");
|
| duke@0 |
|
| duke@0 | Maxlibjsigsigs = Maxsignum;
|
| duke@0 |
|
| duke@0 | // pending_signals has one int per signal
|
| duke@0 | // The additional signal is for SIGEXIT - exit signal to signal_thread
|
| duke@0 | pending_signals = (jint *)os::malloc(sizeof(jint) * (Sigexit+1));
|
| duke@0 | memset(pending_signals, 0, (sizeof(jint) * (Sigexit+1)));
|
| duke@0 |
|
| duke@0 | if (UseSignalChaining) {
|
| duke@0 | chainedsigactions = (struct sigaction *)malloc(sizeof(struct sigaction)
|
| duke@0 | * (Maxsignum + 1));
|
| duke@0 | memset(chainedsigactions, 0, (sizeof(struct sigaction) * (Maxsignum + 1)));
|
| duke@0 | preinstalled_sigs = (int *)os::malloc(sizeof(int) * (Maxsignum + 1));
|
| duke@0 | memset(preinstalled_sigs, 0, (sizeof(int) * (Maxsignum + 1)));
|
| duke@0 | }
|
| duke@0 | ourSigFlags = (int*)malloc(sizeof(int) * (Maxsignum + 1 ));
|
| duke@0 | memset(ourSigFlags, 0, sizeof(int) * (Maxsignum + 1));
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::signal_init_pd() {
|
| duke@0 | int ret;
|
| duke@0 |
|
| duke@0 | ret = ::sema_init(&sig_sem, 0, NULL, NULL);
|
| duke@0 | assert(ret == 0, "sema_init() failed");
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | void os::signal_notify(int signal_number) {
|
| duke@0 | int ret;
|
| duke@0 |
|
| duke@0 | Atomic::inc(&pending_signals[signal_number]);
|
| duke@0 | ret = ::sema_post(&sig_sem);
|
| duke@0 | assert(ret == 0, "sema_post() failed");
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static int check_pending_signals(bool wait_for_signal) {
|
| duke@0 | int ret;
|
| duke@0 | while (true) {
|
| duke@0 | for (int i = 0; i < Sigexit + 1; i++) {
|
| duke@0 | jint n = pending_signals[i];
|
| duke@0 | if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) {
|
| duke@0 | return i;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | if (!wait_for_signal) {
|
| duke@0 | return -1;
|
| duke@0 | }
|
| duke@0 | JavaThread *thread = JavaThread::current();
|
| duke@0 | ThreadBlockInVM tbivm(thread);
|
| duke@0 |
|
| duke@0 | bool threadIsSuspended;
|
| duke@0 | do {
|
| duke@0 | thread->set_suspend_equivalent();
|
| duke@0 | // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
|
| duke@0 | while((ret = ::sema_wait(&sig_sem)) == EINTR)
|
| duke@0 | ;
|
| duke@0 | assert(ret == 0, "sema_wait() failed");
|
| duke@0 |
|
| duke@0 | // were we externally suspended while we were waiting?
|
| duke@0 | threadIsSuspended = thread->handle_special_suspend_equivalent_condition();
|
| duke@0 | if (threadIsSuspended) {
|
| duke@0 | //
|
| duke@0 | // The semaphore has been incremented, but while we were waiting
|
| duke@0 | // another thread suspended us. We don't want to continue running
|
| duke@0 | // while suspended because that would surprise the thread that
|
| duke@0 | // suspended us.
|
| duke@0 | //
|
| duke@0 | ret = ::sema_post(&sig_sem);
|
| duke@0 | assert(ret == 0, "sema_post() failed");
|
| duke@0 |
|
| duke@0 | thread->java_suspend_self();
|
| duke@0 | }
|
| duke@0 | } while (threadIsSuspended);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | int os::signal_lookup() {
|
| duke@0 | return check_pending_signals(false);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | int os::signal_wait() {
|
| duke@0 | return check_pending_signals(true);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | ////////////////////////////////////////////////////////////////////////////////
|
| duke@0 | // Virtual Memory
|
| duke@0 |
|
| duke@0 | static int page_size = -1;
|
| duke@0 |
|
| duke@0 | // The mmap MAP_ALIGN flag is supported on Solaris 9 and later. init_2() will
|
| duke@0 | // clear this var if support is not available.
|
| duke@0 | static bool has_map_align = true;
|
| duke@0 |
|
| duke@0 | int os::vm_page_size() {
|
| duke@0 | assert(page_size != -1, "must call os::init");
|
| duke@0 | return page_size;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Solaris allocates memory by pages.
|
| duke@0 | int os::vm_allocation_granularity() {
|
| duke@0 | assert(page_size != -1, "must call os::init");
|
| duke@0 | return page_size;
|
| duke@0 | }
|
| duke@0 |
|
| coleenp@783 | bool os::commit_memory(char* addr, size_t bytes, bool exec) {
|
| coleenp@783 | int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE;
|
| duke@0 | size_t size = bytes;
|
| duke@0 | return
|
| coleenp@783 | NULL != Solaris::mmap_chunk(addr, size, MAP_PRIVATE|MAP_FIXED, prot);
|
| coleenp@783 | }
|
| coleenp@783 |
|
| coleenp@783 | bool os::commit_memory(char* addr, size_t bytes, size_t alignment_hint,
|
| coleenp@783 | bool exec) {
|
| coleenp@783 | if (commit_memory(addr, bytes, exec)) {
|
| duke@0 | if (UseMPSS && alignment_hint > (size_t)vm_page_size()) {
|
| duke@0 | // If the large page size has been set and the VM
|
| duke@0 | // is using large pages, use the large page size
|
| duke@0 | // if it is smaller than the alignment hint. This is
|
| duke@0 | // a case where the VM wants to use a larger alignment size
|
| duke@0 | // for its own reasons but still want to use large pages
|
| duke@0 | // (which is what matters to setting the mpss range.
|
| duke@0 | size_t page_size = 0;
|
| duke@0 | if (large_page_size() < alignment_hint) {
|
| duke@0 | assert(UseLargePages, "Expected to be here for large page use only");
|
| duke@0 | page_size = large_page_size();
|
| duke@0 | } else {
|
| duke@0 | // If the alignment hint is less than the large page
|
| duke@0 | // size, the VM wants a particular alignment (thus the hint)
|
| duke@0 | // for internal reasons. Try to set the mpss range using
|
| duke@0 | // the alignment_hint.
|
| duke@0 | page_size = alignment_hint;
|
| duke@0 | }
|
| duke@0 | // Since this is a hint, ignore any failures.
|
| duke@0 | (void)Solaris::set_mpss_range(addr, bytes, page_size);
|
| duke@0 | }
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Uncommit the pages in a specified region.
|
| duke@0 | void os::free_memory(char* addr, size_t bytes) {
|
| duke@0 | if (madvise(addr, bytes, MADV_FREE) < 0) {
|
| duke@0 | debug_only(warning("MADV_FREE failed."));
|
| duke@0 | return;
|
| duke@0 | }
|
| coleenp@1621 | }
|
| coleenp@1621 |
|
| coleenp@1621 | bool os::create_stack_guard_pages(char* addr, size_t size) {
|
| coleenp@1621 | return os::commit_memory(addr, size);
|
| coleenp@1621 | }
|
| coleenp@1621 |
|
| coleenp@1621 | bool os::remove_stack_guard_pages(char* addr, size_t size) {
|
| coleenp@1621 | return os::uncommit_memory(addr, size);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Change the page size in a given range.
|
| duke@0 | void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) {
|
| duke@0 | assert((intptr_t)addr % alignment_hint == 0, "Address should be aligned.");
|
| duke@0 | assert((intptr_t)(addr + bytes) % alignment_hint == 0, "End should be aligned.");
|
| duke@0 | Solaris::set_mpss_range(addr, bytes, alignment_hint);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Tell the OS to make the range local to the first-touching LWP
|
| iveresov@198 | void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) {
|
| duke@0 | assert((intptr_t)addr % os::vm_page_size() == 0, "Address should be page-aligned.");
|
| duke@0 | if (madvise(addr, bytes, MADV_ACCESS_LWP) < 0) {
|
| duke@0 | debug_only(warning("MADV_ACCESS_LWP failed."));
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Tell the OS that this range would be accessed from different LWPs.
|
| duke@0 | void os::numa_make_global(char *addr, size_t bytes) {
|
| duke@0 | assert((intptr_t)addr % os::vm_page_size() == 0, "Address should be page-aligned.");
|
| duke@0 | if (madvise(addr, bytes, MADV_ACCESS_MANY) < 0) {
|
| duke@0 | debug_only(warning("MADV_ACCESS_MANY failed."));
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Get the number of the locality groups.
|
| duke@0 | size_t os::numa_get_groups_num() {
|
| duke@0 | size_t n = Solaris::lgrp_nlgrps(Solaris::lgrp_cookie());
|
| duke@0 | return n != -1 ? n : 1;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Get a list of leaf locality groups. A leaf lgroup is group that
|
| duke@0 | // doesn't have any children. Typical leaf group is a CPU or a CPU/memory
|
| duke@0 | // board. An LWP is assigned to one of these groups upon creation.
|
| duke@0 | size_t os::numa_get_leaf_groups(int *ids, size_t size) {
|
| duke@0 | if ((ids[0] = Solaris::lgrp_root(Solaris::lgrp_cookie())) == -1) {
|
| duke@0 | ids[0] = 0;
|
| duke@0 | return 1;
|
| duke@0 | }
|
| duke@0 | int result_size = 0, top = 1, bottom = 0, cur = 0;
|
| duke@0 | for (int k = 0; k < size; k++) {
|
| duke@0 | int r = Solaris::lgrp_children(Solaris::lgrp_cookie(), ids[cur],
|
| duke@0 | (Solaris::lgrp_id_t*)&ids[top], size - top);
|
| duke@0 | if (r == -1) {
|
| duke@0 | ids[0] = 0;
|
| duke@0 | return 1;
|
| duke@0 | }
|
| duke@0 | if (!r) {
|
| iveresov@201 | // That's a leaf node.
|
| duke@0 | assert (bottom <= cur, "Sanity check");
|
| iveresov@201 | // Check if the node has memory
|
| iveresov@201 | if (Solaris::lgrp_resources(Solaris::lgrp_cookie(), ids[cur],
|
| iveresov@201 | NULL, 0, LGRP_RSRC_MEM) > 0) {
|
| iveresov@201 | ids[bottom++] = ids[cur];
|
| iveresov@201 | }
|
| duke@0 | }
|
| duke@0 | top += r;
|
| duke@0 | cur++;
|
| iveresov@325 | }
|
| iveresov@325 | if (bottom == 0) {
|
| iveresov@325 | // Handle a situation, when the OS reports no memory available.
|
| iveresov@325 | // Assume UMA architecture.
|
| iveresov@325 | ids[0] = 0;
|
| iveresov@325 | return 1;
|
| duke@0 | }
|
| duke@0 | return bottom;
|
| duke@0 | }
|
| duke@0 |
|
| ysr@397 | // Detect the topology change. Typically happens during CPU plugging-unplugging.
|
| duke@0 | bool os::numa_topology_changed() {
|
| duke@0 | int is_stale = Solaris::lgrp_cookie_stale(Solaris::lgrp_cookie());
|
| duke@0 | if (is_stale != -1 && is_stale) {
|
| duke@0 | Solaris::lgrp_fini(Solaris::lgrp_cookie());
|
| duke@0 | Solaris::lgrp_cookie_t c = Solaris::lgrp_init(Solaris::LGRP_VIEW_CALLER);
|
| duke@0 | assert(c != 0, "Failure to initialize LGRP API");
|
| duke@0 | Solaris::set_lgrp_cookie(c);
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Get the group id of the current LWP.
|
| duke@0 | int os::numa_get_group_id() {
|
| iveresov@201 | int lgrp_id = Solaris::lgrp_home(P_LWPID, P_MYID);
|
| duke@0 | if (lgrp_id == -1) {
|
| duke@0 | return 0;
|
| duke@0 | }
|
| iveresov@201 | const int size = os::numa_get_groups_num();
|
| iveresov@201 | int *ids = (int*)alloca(size * sizeof(int));
|
| iveresov@201 |
|
| iveresov@201 | // Get the ids of all lgroups with memory; r is the count.
|
| iveresov@201 | int r = Solaris::lgrp_resources(Solaris::lgrp_cookie(), lgrp_id,
|
| iveresov@201 | (Solaris::lgrp_id_t*)ids, size, LGRP_RSRC_MEM);
|
| iveresov@201 | if (r <= 0) {
|
| iveresov@201 | return 0;
|
| iveresov@201 | }
|
| iveresov@201 | return ids[os::random() % r];
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Request information about the page.
|
| duke@0 | bool os::get_page_info(char *start, page_info* info) {
|
| duke@0 | const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
|
| duke@0 | uint64_t addr = (uintptr_t)start;
|
| duke@0 | uint64_t outdata[2];
|
| duke@0 | uint_t validity = 0;
|
| duke@0 |
|
| duke@0 | if (os::Solaris::meminfo(&addr, 1, info_types, 2, outdata, &validity) < 0) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | info->size = 0;
|
| duke@0 | info->lgrp_id = -1;
|
| duke@0 |
|
| duke@0 | if ((validity & 1) != 0) {
|
| duke@0 | if ((validity & 2) != 0) {
|
| duke@0 | info->lgrp_id = outdata[0];
|
| duke@0 | }
|
| duke@0 | if ((validity & 4) != 0) {
|
| duke@0 | info->size = outdata[1];
|
| duke@0 | }
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Scan the pages from start to end until a page different than
|
| duke@0 | // the one described in the info parameter is encountered.
|
| duke@0 | char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) {
|
| duke@0 | const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
|
| duke@0 | const size_t types = sizeof(info_types) / sizeof(info_types[0]);
|
| duke@0 | uint64_t addrs[MAX_MEMINFO_CNT], outdata[types * MAX_MEMINFO_CNT];
|
| duke@0 | uint_t validity[MAX_MEMINFO_CNT];
|
| duke@0 |
|
| duke@0 | size_t page_size = MAX2((size_t)os::vm_page_size(), page_expected->size);
|
| duke@0 | uint64_t p = (uint64_t)start;
|
| duke@0 | while (p < (uint64_t)end) {
|
| duke@0 | addrs[0] = p;
|
| duke@0 | size_t addrs_count = 1;
|
| duke@0 | while (addrs_count < MAX_MEMINFO_CNT && addrs[addrs_count - 1] < (uint64_t)end) {
|
| duke@0 | addrs[addrs_count] = addrs[addrs_count - 1] + page_size;
|
| duke@0 | addrs_count++;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (os::Solaris::meminfo(addrs, addrs_count, info_types, types, outdata, validity) < 0) {
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | size_t i = 0;
|
| duke@0 | for (; i < addrs_count; i++) {
|
| duke@0 | if ((validity[i] & 1) != 0) {
|
| duke@0 | if ((validity[i] & 4) != 0) {
|
| duke@0 | if (outdata[types * i + 1] != page_expected->size) {
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 | } else
|
| duke@0 | if (page_expected->size != 0) {
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if ((validity[i] & 2) != 0 && page_expected->lgrp_id > 0) {
|
| duke@0 | if (outdata[types * i] != page_expected->lgrp_id) {
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | } else {
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (i != addrs_count) {
|
| duke@0 | if ((validity[i] & 2) != 0) {
|
| duke@0 | page_found->lgrp_id = outdata[types * i];
|
| duke@0 | } else {
|
| duke@0 | page_found->lgrp_id = -1;
|
| duke@0 | }
|
| duke@0 | if ((validity[i] & 4) != 0) {
|
| duke@0 | page_found->size = outdata[types * i + 1];
|
| duke@0 | } else {
|
| duke@0 | page_found->size = 0;
|
| duke@0 | }
|
| duke@0 | return (char*)addrs[i];
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | p = addrs[addrs_count - 1] + page_size;
|
| duke@0 | }
|
| duke@0 | return end;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::uncommit_memory(char* addr, size_t bytes) {
|
| duke@0 | size_t size = bytes;
|
| duke@0 | // Map uncommitted pages PROT_NONE so we fail early if we touch an
|
| duke@0 | // uncommitted page. Otherwise, the read/write might succeed if we
|
| duke@0 | // have enough swap space to back the physical page.
|
| duke@0 | return
|
| duke@0 | NULL != Solaris::mmap_chunk(addr, size,
|
| duke@0 | MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE,
|
| duke@0 | PROT_NONE);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | char* os::Solaris::mmap_chunk(char *addr, size_t size, int flags, int prot) {
|
| duke@0 | char *b = (char *)mmap(addr, size, prot, flags, os::Solaris::_dev_zero_fd, 0);
|
| duke@0 |
|
| duke@0 | if (b == MAP_FAILED) {
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 | return b;
|
| duke@0 | }
|
| duke@0 |
|
| sbohne@118 | char* os::Solaris::anon_mmap(char* requested_addr, size_t bytes, size_t alignment_hint, bool fixed) {
|
| sbohne@118 | char* addr = requested_addr;
|
| sbohne@118 | int flags = MAP_PRIVATE | MAP_NORESERVE;
|
| sbohne@118 |
|
| sbohne@118 | assert(!(fixed && (alignment_hint > 0)), "alignment hint meaningless with fixed mmap");
|
| sbohne@118 |
|
| sbohne@118 | if (fixed) {
|
| sbohne@118 | flags |= MAP_FIXED;
|
| sbohne@118 | } else if (has_map_align && (alignment_hint > (size_t) vm_page_size())) {
|
| duke@0 | flags |= MAP_ALIGN;
|
| duke@0 | addr = (char*) alignment_hint;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Map uncommitted pages PROT_NONE so we fail early if we touch an
|
| duke@0 | // uncommitted page. Otherwise, the read/write might succeed if we
|
| duke@0 | // have enough swap space to back the physical page.
|
| sbohne@118 | return mmap_chunk(addr, bytes, flags, PROT_NONE);
|
| sbohne@118 | }
|
| sbohne@118 |
|
| sbohne@118 | char* os::reserve_memory(size_t bytes, char* requested_addr, size_t alignment_hint) {
|
| sbohne@118 | char* addr = Solaris::anon_mmap(requested_addr, bytes, alignment_hint, (requested_addr != NULL));
|
| duke@0 |
|
| duke@0 | guarantee(requested_addr == NULL || requested_addr == addr,
|
| duke@0 | "OS failed to return requested mmap address.");
|
| duke@0 | return addr;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Reserve memory at an arbitrary address, only if that area is
|
| duke@0 | // available (and not reserved for something else).
|
| duke@0 |
|
| duke@0 | char* os::attempt_reserve_memory_at(size_t bytes, char* requested_addr) {
|
| duke@0 | const int max_tries = 10;
|
| duke@0 | char* base[max_tries];
|
| duke@0 | size_t size[max_tries];
|
| duke@0 |
|
| duke@0 | // Solaris adds a gap between mmap'ed regions. The size of the gap
|
| duke@0 | // is dependent on the requested size and the MMU. Our initial gap
|
| duke@0 | // value here is just a guess and will be corrected later.
|
| duke@0 | bool had_top_overlap = false;
|
| duke@0 | bool have_adjusted_gap = false;
|
| duke@0 | size_t gap = 0x400000;
|
| duke@0 |
|
| duke@0 | // Assert only that the size is a multiple of the page size, since
|
| duke@0 | // that's all that mmap requires, and since that's all we really know
|
| duke@0 | // about at this low abstraction level. If we need higher alignment,
|
| duke@0 | // we can either pass an alignment to this method or verify alignment
|
| duke@0 | // in one of the methods further up the call chain. See bug 5044738.
|
| duke@0 | assert(bytes % os::vm_page_size() == 0, "reserving unexpected size block");
|
| duke@0 |
|
| sbohne@118 | // Since snv_84, Solaris attempts to honor the address hint - see 5003415.
|
| sbohne@118 | // Give it a try, if the kernel honors the hint we can return immediately.
|
| sbohne@118 | char* addr = Solaris::anon_mmap(requested_addr, bytes, 0, false);
|
| sbohne@118 | volatile int err = errno;
|
| sbohne@118 | if (addr == requested_addr) {
|
| sbohne@118 | return addr;
|
| sbohne@118 | } else if (addr != NULL) {
|
| sbohne@118 | unmap_memory(addr, bytes);
|
| sbohne@118 | }
|
| sbohne@118 |
|
| sbohne@118 | if (PrintMiscellaneous && Verbose) {
|
| sbohne@118 | char buf[256];
|
| sbohne@118 | buf[0] = '\0';
|
| sbohne@118 | if (addr == NULL) {
|
| sbohne@118 | jio_snprintf(buf, sizeof(buf), ": %s", strerror(err));
|
| sbohne@118 | }
|
| sbohne@118 | warning("attempt_reserve_memory_at: couldn't reserve %d bytes at "
|
| sbohne@118 | PTR_FORMAT ": reserve_memory_helper returned " PTR_FORMAT
|
| sbohne@118 | "%s", bytes, requested_addr, addr, buf);
|
| sbohne@118 | }
|
| sbohne@118 |
|
| sbohne@118 | // Address hint method didn't work. Fall back to the old method.
|
| sbohne@118 | // In theory, once SNV becomes our oldest supported platform, this
|
| sbohne@118 | // code will no longer be needed.
|
| sbohne@118 | //
|
| duke@0 | // Repeatedly allocate blocks until the block is allocated at the
|
| duke@0 | // right spot. Give up after max_tries.
|
| duke@0 | int i;
|
| duke@0 | for (i = 0; i < max_tries; ++i) {
|
| duke@0 | base[i] = reserve_memory(bytes);
|
| duke@0 |
|
| duke@0 | if (base[i] != NULL) {
|
| duke@0 | // Is this the block we wanted?
|
| duke@0 | if (base[i] == requested_addr) {
|
| duke@0 | size[i] = bytes;
|
| duke@0 | break;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // check that the gap value is right
|
| duke@0 | if (had_top_overlap && !have_adjusted_gap) {
|
| duke@0 | size_t actual_gap = base[i-1] - base[i] - bytes;
|
| duke@0 | if (gap != actual_gap) {
|
| duke@0 | // adjust the gap value and retry the last 2 allocations
|
| duke@0 | assert(i > 0, "gap adjustment code problem");
|
| duke@0 | have_adjusted_gap = true; // adjust the gap only once, just in case
|
| duke@0 | gap = actual_gap;
|
| duke@0 | if (PrintMiscellaneous && Verbose) {
|
| duke@0 | warning("attempt_reserve_memory_at: adjusted gap to 0x%lx", gap);
|
| duke@0 | }
|
| duke@0 | unmap_memory(base[i], bytes);
|
| duke@0 | unmap_memory(base[i-1], size[i-1]);
|
| duke@0 | i-=2;
|
| duke@0 | continue;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Does this overlap the block we wanted? Give back the overlapped
|
| duke@0 | // parts and try again.
|
| duke@0 | //
|
| duke@0 | // There is still a bug in this code: if top_overlap == bytes,
|
| duke@0 | // the overlap is offset from requested region by the value of gap.
|
| duke@0 | // In this case giving back the overlapped part will not work,
|
| duke@0 | // because we'll give back the entire block at base[i] and
|
| duke@0 | // therefore the subsequent allocation will not generate a new gap.
|
| duke@0 | // This could be fixed with a new algorithm that used larger
|
| duke@0 | // or variable size chunks to find the requested region -
|
| duke@0 | // but such a change would introduce additional complications.
|
| duke@0 | // It's rare enough that the planets align for this bug,
|
| duke@0 | // so we'll just wait for a fix for 6204603/5003415 which
|
| duke@0 | // will provide a mmap flag to allow us to avoid this business.
|
| duke@0 |
|
| duke@0 | size_t top_overlap = requested_addr + (bytes + gap) - base[i];
|
| duke@0 | if (top_overlap >= 0 && top_overlap < bytes) {
|
| duke@0 | had_top_overlap = true;
|
| duke@0 | unmap_memory(base[i], top_overlap);
|
| duke@0 | base[i] += top_overlap;
|
| duke@0 | size[i] = bytes - top_overlap;
|
| duke@0 | } else {
|
| duke@0 | size_t bottom_overlap = base[i] + bytes - requested_addr;
|
| duke@0 | if (bottom_overlap >= 0 && bottom_overlap < bytes) {
|
| duke@0 | if (PrintMiscellaneous && Verbose && bottom_overlap == 0) {
|
| duke@0 | warning("attempt_reserve_memory_at: possible alignment bug");
|
| duke@0 | }
|
| duke@0 | unmap_memory(requested_addr, bottom_overlap);
|
| duke@0 | size[i] = bytes - bottom_overlap;
|
| duke@0 | } else {
|
| duke@0 | size[i] = bytes;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Give back the unused reserved pieces.
|
| duke@0 |
|
| duke@0 | for (int j = 0; j < i; ++j) {
|
| duke@0 | if (base[j] != NULL) {
|
| duke@0 | unmap_memory(base[j], size[j]);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | return (i < max_tries) ? requested_addr : NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::release_memory(char* addr, size_t bytes) {
|
| duke@0 | size_t size = bytes;
|
| duke@0 | return munmap(addr, size) == 0;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static bool solaris_mprotect(char* addr, size_t bytes, int prot) {
|
| duke@0 | assert(addr == (char*)align_size_down((uintptr_t)addr, os::vm_page_size()),
|
| duke@0 | "addr must be page aligned");
|
| duke@0 | int retVal = mprotect(addr, bytes, prot);
|
| duke@0 | return retVal == 0;
|
| duke@0 | }
|
| duke@0 |
|
| coleenp@295 | // Protect memory (Used to pass readonly pages through
|
| duke@0 | // JNI GetArray<type>Elements with empty arrays.)
|
| coleenp@533 | // Also, used for serialization page and for compressed oops null pointer
|
| coleenp@533 | // checking.
|
| coleenp@295 | bool os::protect_memory(char* addr, size_t bytes, ProtType prot,
|
| coleenp@295 | bool is_committed) {
|
| coleenp@295 | unsigned int p = 0;
|
| coleenp@295 | switch (prot) {
|
| coleenp@295 | case MEM_PROT_NONE: p = PROT_NONE; break;
|
| coleenp@295 | case MEM_PROT_READ: p = PROT_READ; break;
|
| coleenp@295 | case MEM_PROT_RW: p = PROT_READ|PROT_WRITE; break;
|
| coleenp@295 | case MEM_PROT_RWX: p = PROT_READ|PROT_WRITE|PROT_EXEC; break;
|
| coleenp@295 | default:
|
| coleenp@295 | ShouldNotReachHere();
|
| coleenp@295 | }
|
| coleenp@295 | // is_committed is unused.
|
| coleenp@295 | return solaris_mprotect(addr, bytes, p);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // guard_memory and unguard_memory only happens within stack guard pages.
|
| duke@0 | // Since ISM pertains only to the heap, guard and unguard memory should not
|
| duke@0 | /// happen with an ISM region.
|
| duke@0 | bool os::guard_memory(char* addr, size_t bytes) {
|
| duke@0 | return solaris_mprotect(addr, bytes, PROT_NONE);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::unguard_memory(char* addr, size_t bytes) {
|
| coleenp@533 | return solaris_mprotect(addr, bytes, PROT_READ|PROT_WRITE);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Large page support
|
| duke@0 |
|
| duke@0 | // UseLargePages is the master flag to enable/disable large page memory.
|
| duke@0 | // UseMPSS and UseISM are supported for compatibility reasons. Their combined
|
| duke@0 | // effects can be described in the following table:
|
| duke@0 | //
|
| duke@0 | // UseLargePages UseMPSS UseISM
|
| duke@0 | // false * * => UseLargePages is the master switch, turning
|
| duke@0 | // it off will turn off both UseMPSS and
|
| duke@0 | // UseISM. VM will not use large page memory
|
| duke@0 | // regardless the settings of UseMPSS/UseISM.
|
| duke@0 | // true false false => Unless future Solaris provides other
|
| duke@0 | // mechanism to use large page memory, this
|
| duke@0 | // combination is equivalent to -UseLargePages,
|
| duke@0 | // VM will not use large page memory
|
| duke@0 | // true true false => JVM will use MPSS for large page memory.
|
| duke@0 | // This is the default behavior.
|
| duke@0 | // true false true => JVM will use ISM for large page memory.
|
| duke@0 | // true true true => JVM will use ISM if it is available.
|
| duke@0 | // Otherwise, JVM will fall back to MPSS.
|
| duke@0 | // Becaues ISM is now available on all
|
| duke@0 | // supported Solaris versions, this combination
|
| duke@0 | // is equivalent to +UseISM -UseMPSS.
|
| duke@0 |
|
| duke@0 | typedef int (*getpagesizes_func_type) (size_t[], int);
|
| duke@0 | static size_t _large_page_size = 0;
|
| duke@0 |
|
| duke@0 | bool os::Solaris::ism_sanity_check(bool warn, size_t * page_size) {
|
| duke@0 | // x86 uses either 2M or 4M page, depending on whether PAE (Physical Address
|
| duke@0 | // Extensions) mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. Sparc
|
| duke@0 | // can support multiple page sizes.
|
| duke@0 |
|
| duke@0 | // Don't bother to probe page size because getpagesizes() comes with MPSS.
|
| duke@0 | // ISM is only recommended on old Solaris where there is no MPSS support.
|
| duke@0 | // Simply choose a conservative value as default.
|
| duke@0 | *page_size = LargePageSizeInBytes ? LargePageSizeInBytes :
|
| bobv@1892 | SPARC_ONLY(4 * M) IA32_ONLY(4 * M) AMD64_ONLY(2 * M)
|
| bobv@1892 | ARM_ONLY(2 * M);
|
| duke@0 |
|
| duke@0 | // ISM is available on all supported Solaris versions
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Insertion sort for small arrays (descending order).
|
| duke@0 | static void insertion_sort_descending(size_t* array, int len) {
|
| duke@0 | for (int i = 0; i < len; i++) {
|
| duke@0 | size_t val = array[i];
|
| duke@0 | for (size_t key = i; key > 0 && array[key - 1] < val; --key) {
|
| duke@0 | size_t tmp = array[key];
|
| duke@0 | array[key] = array[key - 1];
|
| duke@0 | array[key - 1] = tmp;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::Solaris::mpss_sanity_check(bool warn, size_t * page_size) {
|
| duke@0 | getpagesizes_func_type getpagesizes_func =
|
| duke@0 | CAST_TO_FN_PTR(getpagesizes_func_type, dlsym(RTLD_DEFAULT, "getpagesizes"));
|
| duke@0 | if (getpagesizes_func == NULL) {
|
| duke@0 | if (warn) {
|
| duke@0 | warning("MPSS is not supported by the operating system.");
|
| duke@0 | }
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | const unsigned int usable_count = VM_Version::page_size_count();
|
| duke@0 | if (usable_count == 1) {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Fill the array of page sizes.
|
| duke@0 | int n = getpagesizes_func(_page_sizes, page_sizes_max);
|
| duke@0 | assert(n > 0, "Solaris bug?");
|
| duke@0 | if (n == page_sizes_max) {
|
| duke@0 | // Add a sentinel value (necessary only if the array was completely filled
|
| duke@0 | // since it is static (zeroed at initialization)).
|
| duke@0 | _page_sizes[--n] = 0;
|
| duke@0 | DEBUG_ONLY(warning("increase the size of the os::_page_sizes array.");)
|
| duke@0 | }
|
| duke@0 | assert(_page_sizes[n] == 0, "missing sentinel");
|
| duke@0 |
|
| duke@0 | if (n == 1) return false; // Only one page size available.
|
| duke@0 |
|
| duke@0 | // Skip sizes larger than 4M (or LargePageSizeInBytes if it was set) and
|
| duke@0 | // select up to usable_count elements. First sort the array, find the first
|
| duke@0 | // acceptable value, then copy the usable sizes to the top of the array and
|
| duke@0 | // trim the rest. Make sure to include the default page size :-).
|
| duke@0 | //
|
| duke@0 | // A better policy could get rid of the 4M limit by taking the sizes of the
|
| duke@0 | // important VM memory regions (java heap and possibly the code cache) into
|
| duke@0 | // account.
|
| duke@0 | insertion_sort_descending(_page_sizes, n);
|
| duke@0 | const size_t size_limit =
|
| duke@0 | FLAG_IS_DEFAULT(LargePageSizeInBytes) ? 4 * M : LargePageSizeInBytes;
|
| duke@0 | int beg;
|
| duke@0 | for (beg = 0; beg < n && _page_sizes[beg] > size_limit; ++beg) /* empty */ ;
|
| duke@0 | const int end = MIN2((int)usable_count, n) - 1;
|
| duke@0 | for (int cur = 0; cur < end; ++cur, ++beg) {
|
| duke@0 | _page_sizes[cur] = _page_sizes[beg];
|
| duke@0 | }
|
| duke@0 | _page_sizes[end] = vm_page_size();
|
| duke@0 | _page_sizes[end + 1] = 0;
|
| duke@0 |
|
| duke@0 | if (_page_sizes[end] > _page_sizes[end - 1]) {
|
| duke@0 | // Default page size is not the smallest; sort again.
|
| duke@0 | insertion_sort_descending(_page_sizes, end + 1);
|
| duke@0 | }
|
| duke@0 | *page_size = _page_sizes[0];
|
| duke@0 |
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::large_page_init() {
|
| duke@0 | if (!UseLargePages) {
|
| duke@0 | UseISM = false;
|
| duke@0 | UseMPSS = false;
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // print a warning if any large page related flag is specified on command line
|
| duke@0 | bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) ||
|
| duke@0 | !FLAG_IS_DEFAULT(UseISM) ||
|
| duke@0 | !FLAG_IS_DEFAULT(UseMPSS) ||
|
| duke@0 | !FLAG_IS_DEFAULT(LargePageSizeInBytes);
|
| duke@0 | UseISM = UseISM &&
|
| duke@0 | Solaris::ism_sanity_check(warn_on_failure, &_large_page_size);
|
| duke@0 | if (UseISM) {
|
| duke@0 | // ISM disables MPSS to be compatible with old JDK behavior
|
| duke@0 | UseMPSS = false;
|
| jcoomes@137 | _page_sizes[0] = _large_page_size;
|
| jcoomes@137 | _page_sizes[1] = vm_page_size();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | UseMPSS = UseMPSS &&
|
| duke@0 | Solaris::mpss_sanity_check(warn_on_failure, &_large_page_size);
|
| duke@0 |
|
| duke@0 | UseLargePages = UseISM || UseMPSS;
|
| duke@0 | return UseLargePages;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::Solaris::set_mpss_range(caddr_t start, size_t bytes, size_t align) {
|
| duke@0 | // Signal to OS that we want large pages for addresses
|
| duke@0 | // from addr, addr + bytes
|
| duke@0 | struct memcntl_mha mpss_struct;
|
| duke@0 | mpss_struct.mha_cmd = MHA_MAPSIZE_VA;
|
| duke@0 | mpss_struct.mha_pagesize = align;
|
| duke@0 | mpss_struct.mha_flags = 0;
|
| duke@0 | if (memcntl(start, bytes, MC_HAT_ADVISE,
|
| duke@0 | (caddr_t) &mpss_struct, 0, 0) < 0) {
|
| duke@0 | debug_only(warning("Attempt to use MPSS failed."));
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | return true;
|
| duke@0 | }
|
| duke@0 |
|
| coleenp@783 | char* os::reserve_memory_special(size_t bytes, char* addr, bool exec) {
|
| coleenp@783 | // "exec" is passed in but not used. Creating the shared image for
|
| coleenp@783 | // the code cache doesn't have an SHM_X executable permission to check.
|
| duke@0 | assert(UseLargePages && UseISM, "only for ISM large pages");
|
| duke@0 |
|
| duke@0 | size_t size = bytes;
|
| duke@0 | char* retAddr = NULL;
|
| duke@0 | int shmid;
|
| duke@0 | key_t ismKey;
|
| duke@0 |
|
| duke@0 | bool warn_on_failure = UseISM &&
|
| duke@0 | (!FLAG_IS_DEFAULT(UseLargePages) ||
|
| duke@0 | !FLAG_IS_DEFAULT(UseISM) ||
|
| duke@0 | !FLAG_IS_DEFAULT(LargePageSizeInBytes)
|
| duke@0 | );
|
| duke@0 | char msg[128];
|
| duke@0 |
|
| duke@0 | ismKey = IPC_PRIVATE;
|
| duke@0 |
|
| duke@0 | // Create a large shared memory region to attach to based on size.
|
| duke@0 | // Currently, size is the total size of the heap
|
| duke@0 | shmid = shmget(ismKey, size, SHM_R | SHM_W | IPC_CREAT);
|
| duke@0 | if (shmid == -1){
|
| duke@0 | if (warn_on_failure) {
|
| duke@0 | jio_snprintf(msg, sizeof(msg), "Failed to reserve shared memory (errno = %d).", errno);
|
| duke@0 | warning(msg);
|
| duke@0 | }
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Attach to the region
|
| duke@0 | retAddr = (char *) shmat(shmid, 0, SHM_SHARE_MMU | SHM_R | SHM_W);
|
| duke@0 | int err = errno;
|
| duke@0 |
|
| duke@0 | // Remove shmid. If shmat() is successful, the actual shared memory segment
|
| duke@0 | // will be deleted when it's detached by shmdt() or when the process
|
| duke@0 | // terminates. If shmat() is not successful this will remove the shared
|
| duke@0 | // segment immediately.
|
| duke@0 | shmctl(shmid, IPC_RMID, NULL);
|
| duke@0 |
|
| duke@0 | if (retAddr == (char *) -1) {
|
| duke@0 | if (warn_on_failure) {
|
| duke@0 | jio_snprintf(msg, sizeof(msg), "Failed to attach shared memory (errno = %d).", err);
|
| duke@0 | warning(msg);
|
| duke@0 | }
|
| duke@0 | return NULL;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | return retAddr;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | bool os::release_memory_special(char* base, size_t bytes) {
|
| duke@0 | // detaching the SHM segment will also delete it, see reserve_memory_special()
|
| duke@0 | int rslt = shmdt(base);
|
| duke@0 | return rslt == 0;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | size_t os::large_page_size() {
|
| duke@0 | return _large_page_size;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // MPSS allows application to commit large page memory on demand; with ISM
|
| duke@0 | // the entire memory region must be allocated as shared memory.
|
| duke@0 | bool os::can_commit_large_page_memory() {
|
| jcoomes@137 | return UseISM ? false : true;
|
| jcoomes@137 | }
|
| jcoomes@137 |
|
| jcoomes@137 | bool os::can_execute_large_page_memory() {
|
| duke@0 | return UseISM ? false : true;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | static int os_sleep(jlong millis, bool interruptible) {
|
| duke@0 | const jlong limit = INT_MAX;
|
| duke@0 | jlong prevtime;
|
| duke@0 | int res;
|
| duke@0 |
|
| duke@0 | while (millis > limit) {
|
| duke@0 | if ((res = os_sleep(limit, interruptible)) != OS_OK)
|
| duke@0 | return res;
|
| duke@0 | millis -= limit;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Restart interrupted polls with new parameters until the proper delay
|
| duke@0 | // has been completed.
|
| duke@0 |
|
| duke@0 | prevtime = getTimeMillis();
|
| duke@0 |
|
| duke@0 | while (millis > 0) {
|
| duke@0 | jlong newtime;
|
| duke@0 |
|
| duke@0 | if (!interruptible) {
|
| duke@0 | // Following assert fails for os::yield_all:
|
| duke@0 | // assert(!thread->is_Java_thread(), "must not be java thread");
|
| duke@0 | res = poll(NULL, 0, millis);
|
| duke@0 | } else {
|
| duke@0 | JavaThread *jt = JavaThread::current();
|
| duke@0 |
|
| duke@0 | INTERRUPTIBLE_NORESTART_VM_ALWAYS(poll(NULL, 0, millis), res, jt,
|
| duke@0 | os::Solaris::clear_interrupted);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // INTERRUPTIBLE_NORESTART_VM_ALWAYS returns res == OS_INTRPT for
|
| duke@0 | // thread.Interrupt.
|
| duke@0 |
|
| duke@0 | if((res == OS_ERR) && (errno == EINTR)) {
|
| duke@0 | newtime = getTimeMillis();
|
| duke@0 | assert(newtime >= prevtime, "time moving backwards");
|
| duke@0 | /* Doing prevtime and newtime in microseconds doesn't help precision,
|
| duke@0 | and trying to round up to avoid lost milliseconds can result in a
|
| duke@0 | too-short delay. */
|
| duke@0 | millis -= newtime - prevtime;
|
| duke@0 | if(millis <= 0)
|
| duke@0 | return OS_OK;
|
| duke@0 | prevtime = newtime;
|
| duke@0 | } else
|
| duke@0 | return res;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | return OS_OK;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Read calls from inside the vm need to perform state transitions
|
| duke@0 | size_t os::read(int fd, void *buf, unsigned int nBytes) {
|
| duke@0 | INTERRUPTIBLE_RETURN_INT_VM(::read(fd, buf, nBytes), os::Solaris::clear_interrupted);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | int os::sleep(Thread* thread, jlong millis, bool interruptible) {
|
| duke@0 | assert(thread == Thread::current(), "thread consistency check");
|
| duke@0 |
|
| duke@0 | // TODO-FIXME: this should be removed.
|
| duke@0 | // On Solaris machines (especially 2.5.1) we found that sometimes the VM gets into a live lock
|
| duke@0 | // situation with a JavaThread being starved out of a lwp. The kernel doesn't seem to generate
|
| duke@0 | // a SIGWAITING signal which would enable the threads library to create a new lwp for the starving
|
| duke@0 | // thread. We suspect that because the Watcher thread keeps waking up at periodic intervals the kernel
|
| duke@0 | // is fooled into believing that the system is making progress. In the code below we block the
|
| duke@0 | // the watcher thread while safepoint is in progress so that it would not appear as though the
|
| duke@0 | // system is making progress.
|
| duke@0 | if (!Solaris::T2_libthread() &&
|
| duke@0 | thread->is_Watcher_thread() && SafepointSynchronize::is_synchronizing() && !Arguments::has_profile()) {
|
| duke@0 | // We now try to acquire the threads lock. Since this lock is held by the VM thread during
|
| duke@0 | // the entire safepoint, the watcher thread will line up here during the safepoint.
|
| duke@0 | Threads_lock->lock_without_safepoint_check();
|
| duke@0 | Threads_lock->unlock();
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (thread->is_Java_thread()) {
|
| duke@0 | // This is a JavaThread so we honor the _thread_blocked protocol
|
| duke@0 | // even for sleeps of 0 milliseconds. This was originally done
|
| duke@0 | // as a workaround for bug 4338139. However, now we also do it
|
| duke@0 | // to honor the suspend-equivalent protocol.
|
| duke@0 |
|
| duke@0 | JavaThread *jt = (JavaThread *) thread;
|
| duke@0 | ThreadBlockInVM tbivm(jt);
|
| duke@0 |
|
| duke@0 | jt->set_suspend_equivalent();
|
| duke@0 | // cleared by handle_special_suspend_equivalent_condition() or
|
| duke@0 | // java_suspend_self() via check_and_wait_while_suspended()
|
| duke@0 |
|
| duke@0 | int ret_code;
|
| duke@0 | if (millis <= 0) {
|
| duke@0 | thr_yield();
|
| duke@0 | ret_code = 0;
|
| duke@0 | } else {
|
| duke@0 | // The original sleep() implementation did not create an
|
| duke@0 | // OSThreadWaitState helper for sleeps of 0 milliseconds.
|
| duke@0 | // I'm preserving that decision for now.
|
| duke@0 | OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);
|
| duke@0 |
|
| duke@0 | ret_code = os_sleep(millis, interruptible);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // were we externally suspended while we were waiting?
|
| duke@0 | jt->check_and_wait_while_suspended();
|
| duke@0 |
|
| duke@0 | return ret_code;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // non-JavaThread from this point on:
|
| duke@0 |
|
| duke@0 | if (millis <= 0) {
|
| duke@0 | thr_yield();
|
| duke@0 | return 0;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
|
| duke@0 |
|
| duke@0 | return os_sleep(millis, interruptible);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | int os::naked_sleep() {
|
| duke@0 | // %% make the sleep time an integer flag. for now use 1 millisec.
|
| duke@0 | return os_sleep(1, false);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Sleep forever; naked call to OS-specific sleep; use with CAUTION
|
| duke@0 | void os::infinite_sleep() {
|
| duke@0 | while (true) { // sleep forever ...
|
| duke@0 | ::sleep(100); // ... 100 seconds at a time
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Used to convert frequent JVM_Yield() to nops
|
| duke@0 | bool os::dont_yield() {
|
| duke@0 | if (DontYieldALot) {
|
| duke@0 | static hrtime_t last_time = 0;
|
| duke@0 | hrtime_t diff = getTimeNanos() - last_time;
|
| duke@0 |
|
| duke@0 | if (diff < DontYieldALotInterval * 1000000)
|
| duke@0 | return true;
|
| duke@0 |
|
| duke@0 | last_time += diff;
|
| duke@0 |
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | else {
|
| duke@0 | return false;
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Caveat: Solaris os::yield() causes a thread-state transition whereas
|
| duke@0 | // the linux and win32 implementations do not. This should be checked.
|
| duke@0 |
|
| duke@0 | void os::yield() {
|
| duke@0 | // Yields to all threads with same or greater priority
|
| duke@0 | os::sleep(Thread::current(), 0, false);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Note that yield semantics are defined by the scheduling class to which
|
| duke@0 | // the thread currently belongs. Typically, yield will _not yield to
|
| duke@0 | // other equal or higher priority threads that reside on the dispatch queues
|
| duke@0 | // of other CPUs.
|
| duke@0 |
|
| duke@0 | os::YieldResult os::NakedYield() { thr_yield(); return os::YIELD_UNKNOWN; }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // On Solaris we found that yield_all doesn't always yield to all other threads.
|
| duke@0 | // There have been cases where there is a thread ready to execute but it doesn't
|
| duke@0 | // get an lwp as the VM thread continues to spin with sleeps of 1 millisecond.
|
| duke@0 | // The 1 millisecond wait doesn't seem long enough for the kernel to issue a
|
| duke@0 | // SIGWAITING signal which will cause a new lwp to be created. So we count the
|
| duke@0 | // number of times yield_all is called in the one loop and increase the sleep
|
| duke@0 | // time after 8 attempts. If this fails too we increase the concurrency level
|
| duke@0 | // so that the starving thread would get an lwp
|
| duke@0 |
|
| duke@0 | void os::yield_all(int attempts) {
|
| duke@0 | // Yields to all threads, including threads with lower priorities
|
| duke@0 | if (attempts == 0) {
|
| duke@0 | os::sleep(Thread::current(), 1, false);
|
| duke@0 | } else {
|
| duke@0 | int iterations = attempts % 30;
|
| duke@0 | if (iterations == 0 && !os::Solaris::T2_libthread()) {
|
| duke@0 | // thr_setconcurrency and _getconcurrency make sense only under T1.
|
| duke@0 | int noofLWPS = thr_getconcurrency();
|
| duke@0 | if (noofLWPS < (Threads::number_of_threads() + 2)) {
|
| duke@0 | thr_setconcurrency(thr_getconcurrency() + 1);
|
| duke@0 | }
|
| duke@0 | } else if (iterations < 25) {
|
| duke@0 | os::sleep(Thread::current(), 1, false);
|
| duke@0 | } else {
|
| duke@0 | os::sleep(Thread::current(), 10, false);
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | // Called from the tight loops to possibly influence time-sharing heuristics
|
| duke@0 | void os::loop_breaker(int attempts) {
|
| duke@0 | os::yield_all(attempts);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Interface for setting lwp priorities. If we are using T2 libthread,
|
| duke@0 | // which forces the use of BoundThreads or we manually set UseBoundThreads,
|
| duke@0 | // all of our threads will be assigned to real lwp's. Using the thr_setprio
|
| duke@0 | // function is meaningless in this mode so we must adjust the real lwp's priority
|
| duke@0 | // The routines below implement the getting and setting of lwp priorities.
|
| duke@0 | //
|
| duke@0 | // Note: There are three priority scales used on Solaris. Java priotities
|
| duke@0 | // which range from 1 to 10, libthread "thr_setprio" scale which range
|
| duke@0 | // from 0 to 127, and the current scheduling class of the process we
|
| duke@0 | // are running in. This is typically from -60 to +60.
|
| duke@0 | // The setting of the lwp priorities in done after a call to thr_setprio
|
| duke@0 | // so Java priorities are mapped to libthread priorities and we map from
|
| duke@0 | // the latter to lwp priorities. We don't keep priorities stored in
|
| duke@0 | // Java priorities since some of our worker threads want to set priorities
|
| duke@0 | // higher than all Java threads.
|
| duke@0 | //
|
| duke@0 | // For related information:
|
| duke@0 | // (1) man -s 2 priocntl
|
| duke@0 | // (2) man -s 4 priocntl
|
| duke@0 | // (3) man dispadmin
|
| duke@0 | // = librt.so
|
| duke@0 | // = libthread/common/rtsched.c - thrp_setlwpprio().
|
| duke@0 | // = ps -cL <pid> ... to validate priority.
|
| duke@0 | // = sched_get_priority_min and _max
|
| duke@0 | // pthread_create
|
| duke@0 | // sched_setparam
|
| duke@0 | // pthread_setschedparam
|
| duke@0 | //
|
| duke@0 | // Assumptions:
|
| duke@0 | // + We assume that all threads in the process belong to the same
|
| duke@0 | // scheduling class. IE. an homogenous process.
|
| duke@0 | // + Must be root or in IA group to change change "interactive" attribute.
|
| duke@0 | // Priocntl() will fail silently. The only indication of failure is when
|
| duke@0 | // we read-back the value and notice that it hasn't changed.
|
| duke@0 | // + Interactive threads enter the runq at the head, non-interactive at the tail.
|
| duke@0 | // + For RT, change timeslice as well. Invariant:
|
| duke@0 | // constant "priority integral"
|
| duke@0 | // Konst == TimeSlice * (60-Priority)
|
| duke@0 | // Given a priority, compute appropriate timeslice.
|
| duke@0 | // + Higher numerical values have higher priority.
|
| duke@0 |
|
| duke@0 | // sched class attributes
|
| duke@0 | typedef struct {
|
| duke@0 | int schedPolicy; // classID
|
| duke@0 | int maxPrio;
|
| duke@0 | int minPrio;
|
| duke@0 | } SchedInfo;
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | static SchedInfo tsLimits, iaLimits, rtLimits;
|
| duke@0 |
|
| duke@0 | #ifdef ASSERT
|
| duke@0 | static int ReadBackValidate = 1;
|
| duke@0 | #endif
|
| duke@0 | static int myClass = 0;
|
| duke@0 | static int myMin = 0;
|
| duke@0 | static int myMax = 0;
|
| duke@0 | static int myCur = 0;
|
| duke@0 | static bool priocntl_enable = false;
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Call the version of priocntl suitable for all supported versions
|
| duke@0 | // of Solaris. We need to call through this wrapper so that we can
|
| duke@0 | // build on Solaris 9 and run on Solaris 8, 9 and 10.
|
| duke@0 | //
|
| duke@0 | // This code should be removed if we ever stop supporting Solaris 8
|
| duke@0 | // and earlier releases.
|
| duke@0 |
|
| duke@0 | static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg);
|
| duke@0 | typedef long (*priocntl_type)(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg);
|
| duke@0 | static priocntl_type priocntl_ptr = priocntl_stub;
|
| duke@0 |
|
| duke@0 | // Stub to set the value of the real pointer, and then call the real
|
| duke@0 | // function.
|
| duke@0 |
|
| duke@0 | static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg) {
|
| duke@0 | // Try Solaris 8- name only.
|
| duke@0 | priocntl_type tmp = (priocntl_type)dlsym(RTLD_DEFAULT, "__priocntl");
|
| duke@0 | guarantee(tmp != NULL, "priocntl function not found.");
|
| duke@0 | priocntl_ptr = tmp;
|
| duke@0 | return (*priocntl_ptr)(PC_VERSION, idtype, id, cmd, arg);
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // lwp_priocntl_init
|
| duke@0 | //
|
| duke@0 | // Try to determine the priority scale for our process.
|
| duke@0 | //
|
| duke@0 | // Return errno or 0 if OK.
|
| duke@0 | //
|
| duke@0 | static
|
| duke@0 | int lwp_priocntl_init ()
|
| duke@0 | {
|
| duke@0 | int rslt;
|
| duke@0 | pcinfo_t ClassInfo;
|
| duke@0 | pcparms_t ParmInfo;
|
| duke@0 | int i;
|
| duke@0 |
|
| duke@0 | if (!UseThreadPriorities) return 0;
|
| duke@0 |
|
| duke@0 | // We are using Bound threads, we need to determine our priority ranges
|
| duke@0 | if (os::Solaris::T2_libthread() || UseBoundThreads) {
|
| duke@0 | // If ThreadPriorityPolicy is 1, switch tables
|
| duke@0 | if (ThreadPriorityPolicy == 1) {
|
| duke@0 | for (i = 0 ; i < MaxPriority+1; i++)
|
| duke@0 | os::java_to_os_priority[i] = prio_policy1[i];
|
| duke@0 | }
|
| duke@0 | }
|
| duke@0 | // Not using Bound Threads, set to ThreadPolicy 1
|
| duke@0 | else {
|
| duke@0 | for ( i = 0 ; i < MaxPriority+1; i++ ) {
|
| duke@0 | os::java_to_os_priority[i] = prio_policy1[i];
|
| duke@0 | }
|
| duke@0 | return 0;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Get IDs for a set of well-known scheduling classes.
|
| duke@0 | // TODO-FIXME: GETCLINFO returns the current # of classes in the
|
| duke@0 | // the system. We should have a loop that iterates over the
|
| duke@0 | // classID values, which are known to be "small" integers.
|
| duke@0 |
|
| duke@0 | strcpy(ClassInfo.pc_clname, "TS");
|
| duke@0 | ClassInfo.pc_cid = -1;
|
| duke@0 | rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
|
| duke@0 | if (rslt < 0) return errno;
|
| duke@0 | assert(ClassInfo.pc_cid != -1, "cid for TS class is -1");
|
| duke@0 | tsLimits.schedPolicy = ClassInfo.pc_cid;
|
| duke@0 | tsLimits.maxPrio = ((tsinfo_t*)ClassInfo.pc_clinfo)->ts_maxupri;
|
| duke@0 | tsLimits.minPrio = -tsLimits.maxPrio;
|
| duke@0 |
|
| duke@0 | strcpy(ClassInfo.pc_clname, "IA");
|
| duke@0 | ClassInfo.pc_cid = -1;
|
| duke@0 | rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
|
| duke@0 | if (rslt < 0) return errno;
|
| duke@0 | assert(ClassInfo.pc_cid != -1, "cid for IA class is -1");
|
| duke@0 | iaLimits.schedPolicy = ClassInfo.pc_cid;
|
| duke@0 | iaLimits.maxPrio = ((iainfo_t*)ClassInfo.pc_clinfo)->ia_maxupri;
|
| duke@0 | iaLimits.minPrio = -iaLimits.maxPrio;
|
| duke@0 |
|
| duke@0 | strcpy(ClassInfo.pc_clname, "RT");
|
| duke@0 | ClassInfo.pc_cid = -1;
|
| duke@0 | rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo);
|
| duke@0 | if (rslt < 0) return errno;
|
| duke@0 | assert(ClassInfo.pc_cid != -1, "cid for RT class is -1");
|
| duke@0 | rtLimits.schedPolicy = ClassInfo.pc_cid;
|
| duke@0 | rtLimits.maxPrio = ((rtinfo_t*)ClassInfo.pc_clinfo)->rt_maxpri;
|
| duke@0 | rtLimits.minPrio = 0;
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // Query our "current" scheduling class.
|
| duke@0 | // This will normally be IA,TS or, rarely, RT.
|
| duke@0 | memset (&ParmInfo, 0, sizeof(ParmInfo));
|
| duke@0 | ParmInfo.pc_cid = PC_CLNULL;
|
| duke@0 | rslt = (*priocntl_ptr) (PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo );
|
| duke@0 | if ( rslt < 0 ) return errno;
|
| duke@0 | myClass = ParmInfo.pc_cid;
|
| duke@0 |
|
| duke@0 | // We now know our scheduling classId, get specific information
|
| duke@0 | // the class.
|
| duke@0 | ClassInfo.pc_cid = myClass;
|
| duke@0 | ClassInfo.pc_clname[0] = 0;
|
| duke@0 | rslt = (*priocntl_ptr) (PC_VERSION, (idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo );
|
| duke@0 | if ( rslt < 0 ) return errno;
|
| duke@0 |
|
| duke@0 | if (ThreadPriorityVerbose)
|
| duke@0 | tty->print_cr ("lwp_priocntl_init: Class=%d(%s)...", myClass, ClassInfo.pc_clname);
|
| duke@0 |
|
| duke@0 | memset(&ParmInfo, 0, sizeof(pcparms_t));
|
| duke@0 | ParmInfo.pc_cid = PC_CLNULL;
|
| duke@0 | rslt = (*priocntl_ptr)(PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo);
|
| duke@0 | if (rslt < 0) return errno;
|
| duke@0 |
|
| duke@0 | if (ParmInfo.pc_cid == rtLimits.schedPolicy) {
|
| duke@0 | myMin = rtLimits.minPrio;
|
| duke@0 | myMax = rtLimits.maxPrio;
|
| duke@0 | } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) {
|
| duke@0 | iaparms_t *iaInfo = (iaparms_t*)ParmInfo.pc_clparms;
|
| duke@0 | myMin = iaLimits.minPrio;
|
| duke@0 | myMax = iaLimits.maxPrio;
|
| duke@0 | myMax = MIN2(myMax, (int)iaInfo->ia_uprilim); // clamp - restrict
|
| duke@0 | } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) {
|
| duke@0 | tsparms_t *tsInfo = (tsparms_t*)ParmInfo.pc_clparms;
|
| duke@0 | myMin = tsLimits.minPrio;
|
| duke@0 | myMax = tsLimits.maxPrio;
|
| duke@0 | myMax = MIN2(myMax, (int)tsInfo->ts_uprilim); // clamp - restrict
|
| duke@0 | } else {
|
| duke@0 | // No clue - punt
|
| duke@0 | if (ThreadPriorityVerbose)
|
| duke@0 | tty->print_cr ("Unknown scheduling class: %s ... \n", ClassInfo.pc_clname);
|
| duke@0 | return EINVAL; // no clue, punt
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | if (ThreadPriorityVerbose)
|
| duke@0 | tty->print_cr ("Thread priority Range: [%d..%d]\n", myMin, myMax);
|
| duke@0 |
|
| duke@0 | priocntl_enable = true; // Enable changing priorities
|
| duke@0 | return 0;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 | #define IAPRI(x) ((iaparms_t *)((x).pc_clparms))
|
| duke@0 | #define RTPRI(x) ((rtparms_t *)((x).pc_clparms))
|
| duke@0 | #define TSPRI(x) ((tsparms_t *)((x).pc_clparms))
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // scale_to_lwp_priority
|
| duke@0 | //
|
| duke@0 | // Convert from the libthread "thr_setprio" scale to our current
|
| duke@0 | // lwp scheduling class scale.
|
| duke@0 | //
|
| duke@0 | static
|
| duke@0 | int scale_to_lwp_priority (int rMin, int rMax, int x)
|
| duke@0 | {
|
| duke@0 | int v;
|
| duke@0 |
|
| duke@0 | if (x == 127) return rMax; // avoid round-down
|
| duke@0 | v = (((x*(rMax-rMin)))/128)+rMin;
|
| duke@0 | return v;
|
| duke@0 | }
|
| duke@0 |
|
| duke@0 |
|
| duke@0 | // set_lwp_priority
|
| duke@0 | //
|
| duke@0 | // Set the priority of the lwp. This call should only be made
|
| duke@0 | // when using bound threads (T2 threads are bound by default).
|
| duke@0 | //
|
| duke@0 | int set_lwp_priority (int ThreadID, int lwpid, int newPrio )
|
| duke@0 | {
|
| duke@0 | int rslt;
|
| duke@0 | int Actual, Expected, prv;
|
| duke@0 | pcparms_t ParmInfo; // for GET-SET
|
| duke@0 | #ifdef ASSERT
|
| duke@0 | pcparms_t ReadBack; |