simonis@6465: /* simonis@6465: * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. simonis@6465: * Copyright 2012, 2013 SAP AG. All rights reserved. simonis@6465: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. simonis@6465: * simonis@6465: * This code is free software; you can redistribute it and/or modify it simonis@6465: * under the terms of the GNU General Public License version 2 only, as simonis@6465: * published by the Free Software Foundation. simonis@6465: * simonis@6465: * This code is distributed in the hope that it will be useful, but WITHOUT simonis@6465: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or simonis@6465: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License simonis@6465: * version 2 for more details (a copy is included in the LICENSE file that simonis@6465: * accompanied this code). simonis@6465: * simonis@6465: * You should have received a copy of the GNU General Public License version simonis@6465: * 2 along with this work; if not, write to the Free Software Foundation, simonis@6465: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. simonis@6465: * simonis@6465: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA simonis@6465: * or visit www.oracle.com if you need additional information or have any simonis@6465: * questions. simonis@6465: * simonis@6465: */ simonis@6465: simonis@6465: // According to the AIX OS doc #pragma alloca must be used simonis@6465: // with C++ compiler before referencing the function alloca() simonis@6465: #pragma alloca simonis@6465: simonis@6465: // no precompiled headers simonis@6465: #include "classfile/classLoader.hpp" simonis@6465: #include "classfile/systemDictionary.hpp" simonis@6465: #include "classfile/vmSymbols.hpp" simonis@6465: #include "code/icBuffer.hpp" simonis@6465: #include "code/vtableStubs.hpp" simonis@6465: #include "compiler/compileBroker.hpp" simonis@6465: #include "interpreter/interpreter.hpp" simonis@6465: #include "jvm_aix.h" simonis@6465: #include "libperfstat_aix.hpp" simonis@6465: #include "loadlib_aix.hpp" simonis@6465: #include "memory/allocation.inline.hpp" simonis@6465: #include "memory/filemap.hpp" simonis@6465: #include "mutex_aix.inline.hpp" simonis@6465: #include "oops/oop.inline.hpp" simonis@6465: #include "os_share_aix.hpp" simonis@6465: #include "porting_aix.hpp" simonis@6465: #include "prims/jniFastGetField.hpp" simonis@6465: #include "prims/jvm.h" simonis@6465: #include "prims/jvm_misc.hpp" simonis@6465: #include "runtime/arguments.hpp" simonis@6465: #include "runtime/extendedPC.hpp" simonis@6465: #include "runtime/globals.hpp" simonis@6465: #include "runtime/interfaceSupport.hpp" simonis@6465: #include "runtime/java.hpp" simonis@6465: #include "runtime/javaCalls.hpp" simonis@6465: #include "runtime/mutexLocker.hpp" simonis@6465: #include "runtime/objectMonitor.hpp" simonis@6465: #include "runtime/osThread.hpp" simonis@6465: #include "runtime/perfMemory.hpp" simonis@6465: #include "runtime/sharedRuntime.hpp" simonis@6465: #include "runtime/statSampler.hpp" simonis@6465: #include "runtime/stubRoutines.hpp" simonis@6465: #include "runtime/threadCritical.hpp" simonis@6465: #include "runtime/timer.hpp" simonis@6465: #include "services/attachListener.hpp" simonis@6465: #include "services/runtimeService.hpp" simonis@6465: #include "thread_aix.inline.hpp" simonis@6465: #include "utilities/decoder.hpp" simonis@6465: #include "utilities/defaultStream.hpp" simonis@6465: #include "utilities/events.hpp" simonis@6465: #include "utilities/growableArray.hpp" simonis@6465: #include "utilities/vmError.hpp" simonis@6465: #ifdef TARGET_ARCH_ppc simonis@6465: # include "assembler_ppc.inline.hpp" simonis@6465: # include "nativeInst_ppc.hpp" simonis@6465: #endif simonis@6465: #ifdef COMPILER1 simonis@6465: #include "c1/c1_Runtime1.hpp" simonis@6465: #endif simonis@6465: #ifdef COMPILER2 simonis@6465: #include "opto/runtime.hpp" simonis@6465: #endif simonis@6465: simonis@6465: // put OS-includes here (sorted alphabetically) simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: #include simonis@6465: simonis@6465: // Add missing declarations (should be in procinfo.h but isn't until AIX 6.1). simonis@6465: #if !defined(_AIXVERSION_610) simonis@6465: extern "C" { simonis@6465: int getthrds64(pid_t ProcessIdentifier, simonis@6465: struct thrdentry64* ThreadBuffer, simonis@6465: int ThreadSize, simonis@6465: tid64_t* IndexPointer, simonis@6465: int Count); simonis@6465: } simonis@6465: #endif simonis@6465: simonis@6465: // Excerpts from systemcfg.h definitions newer than AIX 5.3 simonis@6465: #ifndef PV_7 simonis@6465: # define PV_7 0x200000 // Power PC 7 simonis@6465: # define PV_7_Compat 0x208000 // Power PC 7 simonis@6465: #endif simonis@6465: simonis@6465: #define MAX_PATH (2 * K) simonis@6465: simonis@6465: // for timer info max values which include all bits simonis@6465: #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) simonis@6465: // for multipage initialization error analysis (in 'g_multipage_error') simonis@6465: #define ERROR_MP_OS_TOO_OLD 100 simonis@6465: #define ERROR_MP_EXTSHM_ACTIVE 101 simonis@6465: #define ERROR_MP_VMGETINFO_FAILED 102 simonis@6465: #define ERROR_MP_VMGETINFO_CLAIMS_NO_SUPPORT_FOR_64K 103 simonis@6465: simonis@6465: // the semantics in this file are thus that codeptr_t is a *real code ptr* simonis@6465: // This means that any function taking codeptr_t as arguments will assume simonis@6465: // a real codeptr and won't handle function descriptors (eg getFuncName), simonis@6465: // whereas functions taking address as args will deal with function simonis@6465: // descriptors (eg os::dll_address_to_library_name) simonis@6465: typedef unsigned int* codeptr_t; simonis@6465: simonis@6465: // typedefs for stackslots, stack pointers, pointers to op codes simonis@6465: typedef unsigned long stackslot_t; simonis@6465: typedef stackslot_t* stackptr_t; simonis@6465: simonis@6465: // query dimensions of the stack of the calling thread simonis@6465: static void query_stack_dimensions(address* p_stack_base, size_t* p_stack_size); simonis@6465: simonis@6465: // function to check a given stack pointer against given stack limits simonis@6465: inline bool is_valid_stackpointer(stackptr_t sp, stackptr_t stack_base, size_t stack_size) { simonis@6465: if (((uintptr_t)sp) & 0x7) { simonis@6465: return false; simonis@6465: } simonis@6465: if (sp > stack_base) { simonis@6465: return false; simonis@6465: } simonis@6465: if (sp < (stackptr_t) ((address)stack_base - stack_size)) { simonis@6465: return false; simonis@6465: } simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // returns true if function is a valid codepointer simonis@6465: inline bool is_valid_codepointer(codeptr_t p) { simonis@6465: if (!p) { simonis@6465: return false; simonis@6465: } simonis@6465: if (((uintptr_t)p) & 0x3) { simonis@6465: return false; simonis@6465: } simonis@6465: if (LoadedLibraries::find_for_text_address((address)p) == NULL) { simonis@6465: return false; simonis@6465: } simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // macro to check a given stack pointer against given stack limits and to die if test fails simonis@6465: #define CHECK_STACK_PTR(sp, stack_base, stack_size) { \ simonis@6465: guarantee(is_valid_stackpointer((stackptr_t)(sp), (stackptr_t)(stack_base), stack_size), "Stack Pointer Invalid"); \ simonis@6465: } simonis@6465: simonis@6465: // macro to check the current stack pointer against given stacklimits simonis@6465: #define CHECK_CURRENT_STACK_PTR(stack_base, stack_size) { \ simonis@6465: address sp; \ simonis@6465: sp = os::current_stack_pointer(); \ simonis@6465: CHECK_STACK_PTR(sp, stack_base, stack_size); \ simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // global variables (for a description see os_aix.hpp) simonis@6465: simonis@6465: julong os::Aix::_physical_memory = 0; simonis@6465: pthread_t os::Aix::_main_thread = ((pthread_t)0); simonis@6465: int os::Aix::_page_size = -1; simonis@6465: int os::Aix::_on_pase = -1; simonis@6465: int os::Aix::_os_version = -1; simonis@6465: int os::Aix::_stack_page_size = -1; simonis@6465: size_t os::Aix::_shm_default_page_size = -1; simonis@6465: int os::Aix::_can_use_64K_pages = -1; simonis@6465: int os::Aix::_can_use_16M_pages = -1; simonis@6465: int os::Aix::_xpg_sus_mode = -1; simonis@6465: int os::Aix::_extshm = -1; simonis@6465: int os::Aix::_logical_cpus = -1; simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // local variables simonis@6465: simonis@6465: static int g_multipage_error = -1; // error analysis for multipage initialization simonis@6465: static jlong initial_time_count = 0; simonis@6465: static int clock_tics_per_sec = 100; simonis@6465: static sigset_t check_signal_done; // For diagnostics to print a message once (see run_periodic_checks) simonis@6465: static bool check_signals = true; simonis@6465: static pid_t _initial_pid = 0; simonis@6465: static int SR_signum = SIGUSR2; // Signal used to suspend/resume a thread (must be > SIGSEGV, see 4355769) simonis@6465: static sigset_t SR_sigset; simonis@6465: static pthread_mutex_t dl_mutex; // Used to protect dlsym() calls */ simonis@6465: simonis@6465: julong os::available_memory() { simonis@6465: return Aix::available_memory(); simonis@6465: } simonis@6465: simonis@6465: julong os::Aix::available_memory() { simonis@6471: os::Aix::meminfo_t mi; simonis@6471: if (os::Aix::get_meminfo(&mi)) { simonis@6471: return mi.real_free; simonis@6471: } else { simonis@6471: return 0xFFFFFFFFFFFFFFFFLL; simonis@6471: } simonis@6465: } simonis@6465: simonis@6465: julong os::physical_memory() { simonis@6465: return Aix::physical_memory(); simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // environment support simonis@6465: simonis@6465: bool os::getenv(const char* name, char* buf, int len) { simonis@6465: const char* val = ::getenv(name); simonis@6465: if (val != NULL && strlen(val) < (size_t)len) { simonis@6465: strcpy(buf, val); simonis@6465: return true; simonis@6465: } simonis@6465: if (len > 0) buf[0] = 0; // return a null string simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // Return true if user is running as root. simonis@6465: simonis@6465: bool os::have_special_privileges() { simonis@6465: static bool init = false; simonis@6465: static bool privileges = false; simonis@6465: if (!init) { simonis@6465: privileges = (getuid() != geteuid()) || (getgid() != getegid()); simonis@6465: init = true; simonis@6465: } simonis@6465: return privileges; simonis@6465: } simonis@6465: simonis@6465: // Helper function, emulates disclaim64 using multiple 32bit disclaims simonis@6465: // because we cannot use disclaim64() on AS/400 and old AIX releases. simonis@6465: static bool my_disclaim64(char* addr, size_t size) { simonis@6465: simonis@6465: if (size == 0) { simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // Maximum size 32bit disclaim() accepts. (Theoretically 4GB, but I just do not trust that.) simonis@6465: const unsigned int maxDisclaimSize = 0x80000000; simonis@6465: simonis@6465: const unsigned int numFullDisclaimsNeeded = (size / maxDisclaimSize); simonis@6465: const unsigned int lastDisclaimSize = (size % maxDisclaimSize); simonis@6465: simonis@6465: char* p = addr; simonis@6465: simonis@6465: for (int i = 0; i < numFullDisclaimsNeeded; i ++) { simonis@6465: if (::disclaim(p, maxDisclaimSize, DISCLAIM_ZEROMEM) != 0) { simonis@6465: //if (Verbose) simonis@6465: fprintf(stderr, "Cannot disclaim %p - %p (errno %d)\n", p, p + maxDisclaimSize, errno); simonis@6465: return false; simonis@6465: } simonis@6465: p += maxDisclaimSize; simonis@6465: } simonis@6465: simonis@6465: if (lastDisclaimSize > 0) { simonis@6465: if (::disclaim(p, lastDisclaimSize, DISCLAIM_ZEROMEM) != 0) { simonis@6465: //if (Verbose) simonis@6465: fprintf(stderr, "Cannot disclaim %p - %p (errno %d)\n", p, p + lastDisclaimSize, errno); simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // Cpu architecture string simonis@6465: #if defined(PPC32) simonis@6465: static char cpu_arch[] = "ppc"; simonis@6465: #elif defined(PPC64) simonis@6465: static char cpu_arch[] = "ppc64"; simonis@6465: #else simonis@6465: #error Add appropriate cpu_arch setting simonis@6465: #endif simonis@6465: simonis@6465: simonis@6465: // Given an address, returns the size of the page backing that address. simonis@6465: size_t os::Aix::query_pagesize(void* addr) { simonis@6465: simonis@6465: vm_page_info pi; simonis@6465: pi.addr = (uint64_t)addr; simonis@6465: if (::vmgetinfo(&pi, VM_PAGE_INFO, sizeof(pi)) == 0) { simonis@6465: return pi.pagesize; simonis@6465: } else { simonis@6465: fprintf(stderr, "vmgetinfo failed to retrieve page size for address %p (errno %d).\n", addr, errno); simonis@6465: assert(false, "vmgetinfo failed to retrieve page size"); simonis@6465: return SIZE_4K; simonis@6465: } simonis@6465: simonis@6465: } simonis@6465: simonis@6465: // Returns the kernel thread id of the currently running thread. simonis@6465: pid_t os::Aix::gettid() { simonis@6465: return (pid_t) thread_self(); simonis@6465: } simonis@6465: simonis@6465: void os::Aix::initialize_system_info() { simonis@6465: simonis@6465: // get the number of online(logical) cpus instead of configured simonis@6465: os::_processor_count = sysconf(_SC_NPROCESSORS_ONLN); simonis@6465: assert(_processor_count > 0, "_processor_count must be > 0"); simonis@6465: simonis@6465: // retrieve total physical storage simonis@6465: os::Aix::meminfo_t mi; simonis@6465: if (!os::Aix::get_meminfo(&mi)) { simonis@6465: fprintf(stderr, "os::Aix::get_meminfo failed.\n"); fflush(stderr); simonis@6465: assert(false, "os::Aix::get_meminfo failed."); simonis@6465: } simonis@6465: _physical_memory = (julong) mi.real_total; simonis@6465: } simonis@6465: simonis@6465: // Helper function for tracing page sizes. simonis@6465: static const char* describe_pagesize(size_t pagesize) { simonis@6465: switch (pagesize) { simonis@6465: case SIZE_4K : return "4K"; simonis@6465: case SIZE_64K: return "64K"; simonis@6465: case SIZE_16M: return "16M"; simonis@6465: case SIZE_16G: return "16G"; simonis@6465: default: simonis@6465: assert(false, "surprise"); simonis@6465: return "??"; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // Retrieve information about multipage size support. Will initialize simonis@6465: // Aix::_page_size, Aix::_stack_page_size, Aix::_can_use_64K_pages, simonis@6465: // Aix::_can_use_16M_pages. simonis@6465: // Must be called before calling os::large_page_init(). simonis@6465: void os::Aix::query_multipage_support() { simonis@6465: simonis@6465: guarantee(_page_size == -1 && simonis@6465: _stack_page_size == -1 && simonis@6465: _can_use_64K_pages == -1 && simonis@6465: _can_use_16M_pages == -1 && simonis@6465: g_multipage_error == -1, simonis@6465: "do not call twice"); simonis@6465: simonis@6465: _page_size = ::sysconf(_SC_PAGESIZE); simonis@6465: simonis@6465: // This really would surprise me. simonis@6465: assert(_page_size == SIZE_4K, "surprise!"); simonis@6465: simonis@6465: simonis@6465: // query default data page size (default page size for C-Heap, pthread stacks and .bss). simonis@6465: // Default data page size is influenced either by linker options (-bdatapsize) simonis@6465: // or by environment variable LDR_CNTRL (suboption DATAPSIZE). If none is given, simonis@6465: // default should be 4K. simonis@6465: size_t data_page_size = SIZE_4K; simonis@6465: { simonis@6465: void* p = ::malloc(SIZE_16M); simonis@6465: data_page_size = os::Aix::query_pagesize(p); simonis@6465: ::free(p); simonis@6465: } simonis@6465: simonis@6465: // query default shm page size (LDR_CNTRL SHMPSIZE) simonis@6465: { simonis@6465: const int shmid = ::shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR | S_IWUSR); simonis@6465: guarantee(shmid != -1, "shmget failed"); simonis@6465: void* p = ::shmat(shmid, NULL, 0); simonis@6465: ::shmctl(shmid, IPC_RMID, NULL); simonis@6465: guarantee(p != (void*) -1, "shmat failed"); simonis@6465: _shm_default_page_size = os::Aix::query_pagesize(p); simonis@6465: ::shmdt(p); simonis@6465: } simonis@6465: simonis@6465: // before querying the stack page size, make sure we are not running as primordial simonis@6465: // thread (because primordial thread's stack may have different page size than simonis@6465: // pthread thread stacks). Running a VM on the primordial thread won't work for a simonis@6465: // number of reasons so we may just as well guarantee it here simonis@6465: guarantee(!os::Aix::is_primordial_thread(), "Must not be called for primordial thread"); simonis@6465: simonis@6465: // query stack page size simonis@6465: { simonis@6465: int dummy = 0; simonis@6465: _stack_page_size = os::Aix::query_pagesize(&dummy); simonis@6465: // everything else would surprise me and should be looked into simonis@6465: guarantee(_stack_page_size == SIZE_4K || _stack_page_size == SIZE_64K, "Wrong page size"); simonis@6465: // also, just for completeness: pthread stacks are allocated from C heap, so simonis@6465: // stack page size should be the same as data page size simonis@6465: guarantee(_stack_page_size == data_page_size, "stack page size should be the same as data page size"); simonis@6465: } simonis@6465: simonis@6465: // EXTSHM is bad: among other things, it prevents setting pagesize dynamically simonis@6465: // for system V shm. simonis@6465: if (Aix::extshm()) { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "EXTSHM is active - will disable large page support.\n" simonis@6465: "Please make sure EXTSHM is OFF for large page support.\n"); simonis@6465: } simonis@6465: g_multipage_error = ERROR_MP_EXTSHM_ACTIVE; simonis@6465: _can_use_64K_pages = _can_use_16M_pages = 0; simonis@6465: goto query_multipage_support_end; simonis@6465: } simonis@6465: simonis@6465: // now check which page sizes the OS claims it supports, and of those, which actually can be used. simonis@6465: { simonis@6465: const int MAX_PAGE_SIZES = 4; simonis@6465: psize_t sizes[MAX_PAGE_SIZES]; simonis@6465: const int num_psizes = ::vmgetinfo(sizes, VMINFO_GETPSIZES, MAX_PAGE_SIZES); simonis@6465: if (num_psizes == -1) { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "vmgetinfo(VMINFO_GETPSIZES) failed (errno: %d)\n", errno); simonis@6465: fprintf(stderr, "disabling multipage support.\n"); simonis@6465: } simonis@6465: g_multipage_error = ERROR_MP_VMGETINFO_FAILED; simonis@6465: _can_use_64K_pages = _can_use_16M_pages = 0; simonis@6465: goto query_multipage_support_end; simonis@6465: } simonis@6465: guarantee(num_psizes > 0, "vmgetinfo(.., VMINFO_GETPSIZES, ...) failed."); simonis@6465: assert(num_psizes <= MAX_PAGE_SIZES, "Surprise! more than 4 page sizes?"); simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "vmgetinfo(.., VMINFO_GETPSIZES, ...) returns %d supported page sizes: ", num_psizes); simonis@6465: for (int i = 0; i < num_psizes; i ++) { simonis@6465: fprintf(stderr, " %s ", describe_pagesize(sizes[i])); simonis@6465: } simonis@6465: fprintf(stderr, " .\n"); simonis@6465: } simonis@6465: simonis@6465: // Can we use 64K, 16M pages? simonis@6465: _can_use_64K_pages = 0; simonis@6465: _can_use_16M_pages = 0; simonis@6465: for (int i = 0; i < num_psizes; i ++) { simonis@6465: if (sizes[i] == SIZE_64K) { simonis@6465: _can_use_64K_pages = 1; simonis@6465: } else if (sizes[i] == SIZE_16M) { simonis@6465: _can_use_16M_pages = 1; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: if (!_can_use_64K_pages) { simonis@6465: g_multipage_error = ERROR_MP_VMGETINFO_CLAIMS_NO_SUPPORT_FOR_64K; simonis@6465: } simonis@6465: simonis@6465: // Double-check for 16M pages: Even if AIX claims to be able to use 16M pages, simonis@6465: // there must be an actual 16M page pool, and we must run with enough rights. simonis@6465: if (_can_use_16M_pages) { simonis@6465: const int shmid = ::shmget(IPC_PRIVATE, SIZE_16M, IPC_CREAT | S_IRUSR | S_IWUSR); simonis@6465: guarantee(shmid != -1, "shmget failed"); simonis@6465: struct shmid_ds shm_buf = { 0 }; simonis@6465: shm_buf.shm_pagesize = SIZE_16M; simonis@6465: const bool can_set_pagesize = ::shmctl(shmid, SHM_PAGESIZE, &shm_buf) == 0 ? true : false; simonis@6465: const int en = errno; simonis@6465: ::shmctl(shmid, IPC_RMID, NULL); simonis@6465: if (!can_set_pagesize) { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "Failed to allocate even one misely 16M page. shmctl failed with %d (%s).\n" simonis@6465: "Will deactivate 16M support.\n", en, strerror(en)); simonis@6465: } simonis@6465: _can_use_16M_pages = 0; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: } // end: check which pages can be used for shared memory simonis@6465: simonis@6465: query_multipage_support_end: simonis@6465: simonis@6465: guarantee(_page_size != -1 && simonis@6465: _stack_page_size != -1 && simonis@6465: _can_use_64K_pages != -1 && simonis@6465: _can_use_16M_pages != -1, "Page sizes not properly initialized"); simonis@6465: simonis@6465: if (_can_use_64K_pages) { simonis@6465: g_multipage_error = 0; simonis@6465: } simonis@6465: simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "Data page size (C-Heap, bss, etc): %s\n", describe_pagesize(data_page_size)); simonis@6465: fprintf(stderr, "Thread stack page size (pthread): %s\n", describe_pagesize(_stack_page_size)); simonis@6465: fprintf(stderr, "Default shared memory page size: %s\n", describe_pagesize(_shm_default_page_size)); simonis@6465: fprintf(stderr, "Can use 64K pages dynamically with shared meory: %s\n", (_can_use_64K_pages ? "yes" :"no")); simonis@6465: fprintf(stderr, "Can use 16M pages dynamically with shared memory: %s\n", (_can_use_16M_pages ? "yes" :"no")); simonis@6465: fprintf(stderr, "Multipage error details: %d\n", g_multipage_error); simonis@6465: } simonis@6465: simonis@6465: } // end os::Aix::query_multipage_support() simonis@6465: simonis@6465: simonis@6465: // The code for this method was initially derived from the version in os_linux.cpp simonis@6465: void os::init_system_properties_values() { simonis@6465: // The next few definitions allow the code to be verbatim: simonis@6465: #define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n), mtInternal) simonis@6465: #define DEFAULT_LIBPATH "/usr/lib:/lib" simonis@6465: #define EXTENSIONS_DIR "/lib/ext" simonis@6465: #define ENDORSED_DIR "/lib/endorsed" simonis@6465: simonis@6465: // sysclasspath, java_home, dll_dir simonis@6465: char *home_path; simonis@6465: char *dll_path; simonis@6465: char *pslash; simonis@6465: char buf[MAXPATHLEN]; simonis@6465: os::jvm_path(buf, sizeof(buf)); simonis@6465: simonis@6465: // Found the full path to libjvm.so. simonis@6465: // Now cut the path to /jre if we can. simonis@6465: *(strrchr(buf, '/')) = '\0'; // get rid of /libjvm.so simonis@6465: pslash = strrchr(buf, '/'); simonis@6465: if (pslash != NULL) { simonis@6465: *pslash = '\0'; // get rid of /{client|server|hotspot} simonis@6465: } simonis@6465: simonis@6465: dll_path = malloc(strlen(buf) + 1); simonis@6465: strcpy(dll_path, buf); simonis@6465: Arguments::set_dll_dir(dll_path); simonis@6465: simonis@6465: if (pslash != NULL) { simonis@6465: pslash = strrchr(buf, '/'); simonis@6465: if (pslash != NULL) { simonis@6465: *pslash = '\0'; // get rid of / simonis@6465: pslash = strrchr(buf, '/'); simonis@6465: if (pslash != NULL) { simonis@6465: *pslash = '\0'; // get rid of /lib simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: home_path = malloc(strlen(buf) + 1); simonis@6465: strcpy(home_path, buf); simonis@6465: Arguments::set_java_home(home_path); simonis@6465: simonis@6465: if (!set_boot_path('/', ':')) return; simonis@6465: simonis@6465: // Where to look for native libraries simonis@6465: simonis@6465: // On Aix we get the user setting of LIBPATH simonis@6465: // Eventually, all the library path setting will be done here. simonis@6465: char *ld_library_path; simonis@6465: simonis@6465: // Construct the invariant part of ld_library_path. simonis@6465: ld_library_path = (char *) malloc(sizeof(DEFAULT_LIBPATH)); simonis@6465: sprintf(ld_library_path, DEFAULT_LIBPATH); simonis@6465: simonis@6465: // Get the user setting of LIBPATH, and prepended it. simonis@6465: char *v = ::getenv("LIBPATH"); simonis@6465: if (v == NULL) { simonis@6465: v = ""; simonis@6465: } simonis@6465: simonis@6465: char *t = ld_library_path; simonis@6465: // That's +1 for the colon and +1 for the trailing '\0' simonis@6465: ld_library_path = (char *) malloc(strlen(v) + 1 + strlen(t) + 1); simonis@6465: sprintf(ld_library_path, "%s:%s", v, t); simonis@6465: simonis@6465: Arguments::set_library_path(ld_library_path); simonis@6465: simonis@6465: // Extensions directories simonis@6465: char* cbuf = malloc(strlen(Arguments::get_java_home()) + sizeof(EXTENSIONS_DIR)); simonis@6465: sprintf(cbuf, "%s" EXTENSIONS_DIR, Arguments::get_java_home()); simonis@6465: Arguments::set_ext_dirs(cbuf); simonis@6465: simonis@6465: // Endorsed standards default directory. simonis@6465: cbuf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); simonis@6465: sprintf(cbuf, "%s" ENDORSED_DIR, Arguments::get_java_home()); simonis@6465: Arguments::set_endorsed_dirs(cbuf); simonis@6465: simonis@6465: #undef malloc simonis@6465: #undef DEFAULT_LIBPATH simonis@6465: #undef EXTENSIONS_DIR simonis@6465: #undef ENDORSED_DIR simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // breakpoint support simonis@6465: simonis@6465: void os::breakpoint() { simonis@6465: BREAKPOINT; simonis@6465: } simonis@6465: simonis@6465: extern "C" void breakpoint() { simonis@6465: // use debugger to set breakpoint here simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // signal support simonis@6465: simonis@6465: debug_only(static bool signal_sets_initialized = false); simonis@6465: static sigset_t unblocked_sigs, vm_sigs, allowdebug_blocked_sigs; simonis@6465: simonis@6465: bool os::Aix::is_sig_ignored(int sig) { simonis@6465: struct sigaction oact; simonis@6465: sigaction(sig, (struct sigaction*)NULL, &oact); simonis@6465: void* ohlr = oact.sa_sigaction ? CAST_FROM_FN_PTR(void*, oact.sa_sigaction) simonis@6465: : CAST_FROM_FN_PTR(void*, oact.sa_handler); simonis@6465: if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN)) simonis@6465: return true; simonis@6465: else simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: void os::Aix::signal_sets_init() { simonis@6465: // Should also have an assertion stating we are still single-threaded. simonis@6465: assert(!signal_sets_initialized, "Already initialized"); simonis@6465: // Fill in signals that are necessarily unblocked for all threads in simonis@6465: // the VM. Currently, we unblock the following signals: simonis@6465: // SHUTDOWN{1,2,3}_SIGNAL: for shutdown hooks support (unless over-ridden simonis@6465: // by -Xrs (=ReduceSignalUsage)); simonis@6465: // BREAK_SIGNAL which is unblocked only by the VM thread and blocked by all simonis@6465: // other threads. The "ReduceSignalUsage" boolean tells us not to alter simonis@6465: // the dispositions or masks wrt these signals. simonis@6465: // Programs embedding the VM that want to use the above signals for their simonis@6465: // own purposes must, at this time, use the "-Xrs" option to prevent simonis@6465: // interference with shutdown hooks and BREAK_SIGNAL thread dumping. simonis@6465: // (See bug 4345157, and other related bugs). simonis@6465: // In reality, though, unblocking these signals is really a nop, since simonis@6465: // these signals are not blocked by default. simonis@6465: sigemptyset(&unblocked_sigs); simonis@6465: sigemptyset(&allowdebug_blocked_sigs); simonis@6465: sigaddset(&unblocked_sigs, SIGILL); simonis@6465: sigaddset(&unblocked_sigs, SIGSEGV); simonis@6465: sigaddset(&unblocked_sigs, SIGBUS); simonis@6465: sigaddset(&unblocked_sigs, SIGFPE); simonis@6465: sigaddset(&unblocked_sigs, SIGTRAP); simonis@6465: sigaddset(&unblocked_sigs, SIGDANGER); simonis@6465: sigaddset(&unblocked_sigs, SR_signum); simonis@6465: simonis@6465: if (!ReduceSignalUsage) { simonis@6465: if (!os::Aix::is_sig_ignored(SHUTDOWN1_SIGNAL)) { simonis@6465: sigaddset(&unblocked_sigs, SHUTDOWN1_SIGNAL); simonis@6465: sigaddset(&allowdebug_blocked_sigs, SHUTDOWN1_SIGNAL); simonis@6465: } simonis@6465: if (!os::Aix::is_sig_ignored(SHUTDOWN2_SIGNAL)) { simonis@6465: sigaddset(&unblocked_sigs, SHUTDOWN2_SIGNAL); simonis@6465: sigaddset(&allowdebug_blocked_sigs, SHUTDOWN2_SIGNAL); simonis@6465: } simonis@6465: if (!os::Aix::is_sig_ignored(SHUTDOWN3_SIGNAL)) { simonis@6465: sigaddset(&unblocked_sigs, SHUTDOWN3_SIGNAL); simonis@6465: sigaddset(&allowdebug_blocked_sigs, SHUTDOWN3_SIGNAL); simonis@6465: } simonis@6465: } simonis@6465: // Fill in signals that are blocked by all but the VM thread. simonis@6465: sigemptyset(&vm_sigs); simonis@6465: if (!ReduceSignalUsage) simonis@6465: sigaddset(&vm_sigs, BREAK_SIGNAL); simonis@6465: debug_only(signal_sets_initialized = true); simonis@6465: } simonis@6465: simonis@6465: // These are signals that are unblocked while a thread is running Java. simonis@6465: // (For some reason, they get blocked by default.) simonis@6465: sigset_t* os::Aix::unblocked_signals() { simonis@6465: assert(signal_sets_initialized, "Not initialized"); simonis@6465: return &unblocked_sigs; simonis@6465: } simonis@6465: simonis@6465: // These are the signals that are blocked while a (non-VM) thread is simonis@6465: // running Java. Only the VM thread handles these signals. simonis@6465: sigset_t* os::Aix::vm_signals() { simonis@6465: assert(signal_sets_initialized, "Not initialized"); simonis@6465: return &vm_sigs; simonis@6465: } simonis@6465: simonis@6465: // These are signals that are blocked during cond_wait to allow debugger in simonis@6465: sigset_t* os::Aix::allowdebug_blocked_signals() { simonis@6465: assert(signal_sets_initialized, "Not initialized"); simonis@6465: return &allowdebug_blocked_sigs; simonis@6465: } simonis@6465: simonis@6465: void os::Aix::hotspot_sigmask(Thread* thread) { simonis@6465: simonis@6465: //Save caller's signal mask before setting VM signal mask simonis@6465: sigset_t caller_sigmask; simonis@6465: pthread_sigmask(SIG_BLOCK, NULL, &caller_sigmask); simonis@6465: simonis@6465: OSThread* osthread = thread->osthread(); simonis@6465: osthread->set_caller_sigmask(caller_sigmask); simonis@6465: simonis@6465: pthread_sigmask(SIG_UNBLOCK, os::Aix::unblocked_signals(), NULL); simonis@6465: simonis@6465: if (!ReduceSignalUsage) { simonis@6465: if (thread->is_VM_thread()) { simonis@6465: // Only the VM thread handles BREAK_SIGNAL ... simonis@6465: pthread_sigmask(SIG_UNBLOCK, vm_signals(), NULL); simonis@6465: } else { simonis@6465: // ... all other threads block BREAK_SIGNAL simonis@6465: pthread_sigmask(SIG_BLOCK, vm_signals(), NULL); simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // retrieve memory information. simonis@6465: // Returns false if something went wrong; simonis@6465: // content of pmi undefined in this case. simonis@6465: bool os::Aix::get_meminfo(meminfo_t* pmi) { simonis@6465: simonis@6465: assert(pmi, "get_meminfo: invalid parameter"); simonis@6465: simonis@6465: memset(pmi, 0, sizeof(meminfo_t)); simonis@6465: simonis@6465: if (os::Aix::on_pase()) { simonis@6465: simonis@6465: Unimplemented(); simonis@6465: return false; simonis@6465: simonis@6465: } else { simonis@6465: simonis@6465: // On AIX, I use the (dynamically loaded) perfstat library to retrieve memory statistics simonis@6465: // See: simonis@6465: // http://publib.boulder.ibm.com/infocenter/systems/index.jsp simonis@6465: // ?topic=/com.ibm.aix.basetechref/doc/basetrf1/perfstat_memtot.htm simonis@6465: // http://publib.boulder.ibm.com/infocenter/systems/index.jsp simonis@6465: // ?topic=/com.ibm.aix.files/doc/aixfiles/libperfstat.h.htm simonis@6465: simonis@6465: perfstat_memory_total_t psmt; simonis@6465: memset (&psmt, '\0', sizeof(psmt)); simonis@6465: const int rc = libperfstat::perfstat_memory_total(NULL, &psmt, sizeof(psmt), 1); simonis@6465: if (rc == -1) { simonis@6465: fprintf(stderr, "perfstat_memory_total() failed (errno=%d)\n", errno); simonis@6465: assert(0, "perfstat_memory_total() failed"); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: assert(rc == 1, "perfstat_memory_total() - weird return code"); simonis@6465: simonis@6465: // excerpt from simonis@6465: // http://publib.boulder.ibm.com/infocenter/systems/index.jsp simonis@6465: // ?topic=/com.ibm.aix.files/doc/aixfiles/libperfstat.h.htm simonis@6465: // The fields of perfstat_memory_total_t: simonis@6465: // u_longlong_t virt_total Total virtual memory (in 4 KB pages). simonis@6465: // u_longlong_t real_total Total real memory (in 4 KB pages). simonis@6465: // u_longlong_t real_free Free real memory (in 4 KB pages). simonis@6465: // u_longlong_t pgsp_total Total paging space (in 4 KB pages). simonis@6465: // u_longlong_t pgsp_free Free paging space (in 4 KB pages). simonis@6465: simonis@6465: pmi->virt_total = psmt.virt_total * 4096; simonis@6465: pmi->real_total = psmt.real_total * 4096; simonis@6465: pmi->real_free = psmt.real_free * 4096; simonis@6465: pmi->pgsp_total = psmt.pgsp_total * 4096; simonis@6465: pmi->pgsp_free = psmt.pgsp_free * 4096; simonis@6465: simonis@6465: return true; simonis@6465: simonis@6465: } simonis@6465: } // end os::Aix::get_meminfo simonis@6465: simonis@6465: // Retrieve global cpu information. simonis@6465: // Returns false if something went wrong; simonis@6465: // the content of pci is undefined in this case. simonis@6465: bool os::Aix::get_cpuinfo(cpuinfo_t* pci) { simonis@6465: assert(pci, "get_cpuinfo: invalid parameter"); simonis@6465: memset(pci, 0, sizeof(cpuinfo_t)); simonis@6465: simonis@6465: perfstat_cpu_total_t psct; simonis@6465: memset (&psct, '\0', sizeof(psct)); simonis@6465: simonis@6465: if (-1 == libperfstat::perfstat_cpu_total(NULL, &psct, sizeof(perfstat_cpu_total_t), 1)) { simonis@6465: fprintf(stderr, "perfstat_cpu_total() failed (errno=%d)\n", errno); simonis@6465: assert(0, "perfstat_cpu_total() failed"); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // global cpu information simonis@6465: strcpy (pci->description, psct.description); simonis@6465: pci->processorHZ = psct.processorHZ; simonis@6465: pci->ncpus = psct.ncpus; simonis@6465: os::Aix::_logical_cpus = psct.ncpus; simonis@6465: for (int i = 0; i < 3; i++) { simonis@6465: pci->loadavg[i] = (double) psct.loadavg[i] / (1 << SBITS); simonis@6465: } simonis@6465: simonis@6465: // get the processor version from _system_configuration simonis@6465: switch (_system_configuration.version) { simonis@6465: case PV_7: simonis@6465: strcpy(pci->version, "Power PC 7"); simonis@6465: break; simonis@6465: case PV_6_1: simonis@6465: strcpy(pci->version, "Power PC 6 DD1.x"); simonis@6465: break; simonis@6465: case PV_6: simonis@6465: strcpy(pci->version, "Power PC 6"); simonis@6465: break; simonis@6465: case PV_5: simonis@6465: strcpy(pci->version, "Power PC 5"); simonis@6465: break; simonis@6465: case PV_5_2: simonis@6465: strcpy(pci->version, "Power PC 5_2"); simonis@6465: break; simonis@6465: case PV_5_3: simonis@6465: strcpy(pci->version, "Power PC 5_3"); simonis@6465: break; simonis@6465: case PV_5_Compat: simonis@6465: strcpy(pci->version, "PV_5_Compat"); simonis@6465: break; simonis@6465: case PV_6_Compat: simonis@6465: strcpy(pci->version, "PV_6_Compat"); simonis@6465: break; simonis@6465: case PV_7_Compat: simonis@6465: strcpy(pci->version, "PV_7_Compat"); simonis@6465: break; simonis@6465: default: simonis@6465: strcpy(pci->version, "unknown"); simonis@6465: } simonis@6465: simonis@6465: return true; simonis@6465: simonis@6465: } //end os::Aix::get_cpuinfo simonis@6465: simonis@6465: ////////////////////////////////////////////////////////////////////////////// simonis@6465: // detecting pthread library simonis@6465: simonis@6465: void os::Aix::libpthread_init() { simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: ////////////////////////////////////////////////////////////////////////////// simonis@6465: // create new thread simonis@6465: simonis@6465: // Thread start routine for all newly created threads simonis@6465: static void *java_start(Thread *thread) { simonis@6465: simonis@6465: // find out my own stack dimensions simonis@6465: { simonis@6465: // actually, this should do exactly the same as thread->record_stack_base_and_size... simonis@6465: address base = 0; simonis@6465: size_t size = 0; simonis@6465: query_stack_dimensions(&base, &size); simonis@6465: thread->set_stack_base(base); simonis@6465: thread->set_stack_size(size); simonis@6465: } simonis@6465: simonis@6465: // Do some sanity checks. simonis@6465: CHECK_CURRENT_STACK_PTR(thread->stack_base(), thread->stack_size()); simonis@6465: simonis@6465: // Try to randomize the cache line index of hot stack frames. simonis@6465: // This helps when threads of the same stack traces evict each other's simonis@6465: // cache lines. The threads can be either from the same JVM instance, or simonis@6465: // from different JVM instances. The benefit is especially true for simonis@6465: // processors with hyperthreading technology. simonis@6465: simonis@6465: static int counter = 0; simonis@6465: int pid = os::current_process_id(); simonis@6465: alloca(((pid ^ counter++) & 7) * 128); simonis@6465: simonis@6465: ThreadLocalStorage::set_thread(thread); simonis@6465: simonis@6465: OSThread* osthread = thread->osthread(); simonis@6465: simonis@6465: // thread_id is kernel thread id (similar to Solaris LWP id) simonis@6465: osthread->set_thread_id(os::Aix::gettid()); simonis@6465: simonis@6465: // initialize signal mask for this thread simonis@6465: os::Aix::hotspot_sigmask(thread); simonis@6465: simonis@6465: // initialize floating point control register simonis@6465: os::Aix::init_thread_fpu_state(); simonis@6465: simonis@6465: assert(osthread->get_state() == RUNNABLE, "invalid os thread state"); simonis@6465: simonis@6465: // call one more level start routine simonis@6465: thread->run(); simonis@6465: simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { simonis@6465: simonis@6465: // We want the whole function to be synchronized. simonis@6465: ThreadCritical cs; simonis@6465: simonis@6465: assert(thread->osthread() == NULL, "caller responsible"); simonis@6465: simonis@6465: // Allocate the OSThread object simonis@6465: OSThread* osthread = new OSThread(NULL, NULL); simonis@6465: if (osthread == NULL) { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // set the correct thread state simonis@6465: osthread->set_thread_type(thr_type); simonis@6465: simonis@6465: // Initial state is ALLOCATED but not INITIALIZED simonis@6465: osthread->set_state(ALLOCATED); simonis@6465: simonis@6465: thread->set_osthread(osthread); simonis@6465: simonis@6465: // init thread attributes simonis@6465: pthread_attr_t attr; simonis@6465: pthread_attr_init(&attr); simonis@6465: guarantee(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0, "???"); simonis@6465: simonis@6465: // Make sure we run in 1:1 kernel-user-thread mode. simonis@6465: if (os::Aix::on_aix()) { simonis@6465: guarantee(pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0, "???"); simonis@6465: guarantee(pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0, "???"); simonis@6465: } // end: aix simonis@6465: simonis@6465: // Start in suspended state, and in os::thread_start, wake the thread up. simonis@6465: guarantee(pthread_attr_setsuspendstate_np(&attr, PTHREAD_CREATE_SUSPENDED_NP) == 0, "???"); simonis@6465: simonis@6465: // calculate stack size if it's not specified by caller simonis@6465: if (os::Aix::supports_variable_stack_size()) { simonis@6465: if (stack_size == 0) { simonis@6465: stack_size = os::Aix::default_stack_size(thr_type); simonis@6465: simonis@6465: switch (thr_type) { simonis@6465: case os::java_thread: simonis@6465: // Java threads use ThreadStackSize whose default value can be changed with the flag -Xss. simonis@6465: assert(JavaThread::stack_size_at_create() > 0, "this should be set"); simonis@6465: stack_size = JavaThread::stack_size_at_create(); simonis@6465: break; simonis@6465: case os::compiler_thread: simonis@6465: if (CompilerThreadStackSize > 0) { simonis@6465: stack_size = (size_t)(CompilerThreadStackSize * K); simonis@6465: break; simonis@6465: } // else fall through: simonis@6465: // use VMThreadStackSize if CompilerThreadStackSize is not defined simonis@6465: case os::vm_thread: simonis@6465: case os::pgc_thread: simonis@6465: case os::cgc_thread: simonis@6465: case os::watcher_thread: simonis@6465: if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); simonis@6465: break; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: stack_size = MAX2(stack_size, os::Aix::min_stack_allowed); simonis@6465: pthread_attr_setstacksize(&attr, stack_size); simonis@6465: } //else let thread_create() pick the default value (96 K on AIX) simonis@6465: simonis@6465: pthread_t tid; simonis@6465: int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); simonis@6465: simonis@6465: pthread_attr_destroy(&attr); simonis@6465: simonis@6465: if (ret != 0) { simonis@6465: if (PrintMiscellaneous && (Verbose || WizardMode)) { simonis@6465: perror("pthread_create()"); simonis@6465: } simonis@6465: // Need to clean up stuff we've allocated so far simonis@6465: thread->set_osthread(NULL); simonis@6465: delete osthread; simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // Store pthread info into the OSThread simonis@6465: osthread->set_pthread_id(tid); simonis@6465: simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: ///////////////////////////////////////////////////////////////////////////// simonis@6465: // attach existing thread simonis@6465: simonis@6465: // bootstrap the main thread simonis@6465: bool os::create_main_thread(JavaThread* thread) { simonis@6465: assert(os::Aix::_main_thread == pthread_self(), "should be called inside main thread"); simonis@6465: return create_attached_thread(thread); simonis@6465: } simonis@6465: simonis@6465: bool os::create_attached_thread(JavaThread* thread) { simonis@6465: #ifdef ASSERT simonis@6465: thread->verify_not_published(); simonis@6465: #endif simonis@6465: simonis@6465: // Allocate the OSThread object simonis@6465: OSThread* osthread = new OSThread(NULL, NULL); simonis@6465: simonis@6465: if (osthread == NULL) { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // Store pthread info into the OSThread simonis@6465: osthread->set_thread_id(os::Aix::gettid()); simonis@6465: osthread->set_pthread_id(::pthread_self()); simonis@6465: simonis@6465: // initialize floating point control register simonis@6465: os::Aix::init_thread_fpu_state(); simonis@6465: simonis@6465: // some sanity checks simonis@6465: CHECK_CURRENT_STACK_PTR(thread->stack_base(), thread->stack_size()); simonis@6465: simonis@6465: // Initial thread state is RUNNABLE simonis@6465: osthread->set_state(RUNNABLE); simonis@6465: simonis@6465: thread->set_osthread(osthread); simonis@6465: simonis@6465: if (UseNUMA) { simonis@6465: int lgrp_id = os::numa_get_group_id(); simonis@6465: if (lgrp_id != -1) { simonis@6465: thread->set_lgrp_id(lgrp_id); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // initialize signal mask for this thread simonis@6465: // and save the caller's signal mask simonis@6465: os::Aix::hotspot_sigmask(thread); simonis@6465: simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: void os::pd_start_thread(Thread* thread) { simonis@6465: int status = pthread_continue_np(thread->osthread()->pthread_id()); simonis@6465: assert(status == 0, "thr_continue failed"); simonis@6465: } simonis@6465: simonis@6465: // Free OS resources related to the OSThread simonis@6465: void os::free_thread(OSThread* osthread) { simonis@6465: assert(osthread != NULL, "osthread not set"); simonis@6465: simonis@6465: if (Thread::current()->osthread() == osthread) { simonis@6465: // Restore caller's signal mask simonis@6465: sigset_t sigmask = osthread->caller_sigmask(); simonis@6465: pthread_sigmask(SIG_SETMASK, &sigmask, NULL); simonis@6465: } simonis@6465: simonis@6465: delete osthread; simonis@6465: } simonis@6465: simonis@6465: ////////////////////////////////////////////////////////////////////////////// simonis@6465: // thread local storage simonis@6465: simonis@6465: int os::allocate_thread_local_storage() { simonis@6465: pthread_key_t key; simonis@6465: int rslt = pthread_key_create(&key, NULL); simonis@6465: assert(rslt == 0, "cannot allocate thread local storage"); simonis@6465: return (int)key; simonis@6465: } simonis@6465: simonis@6465: // Note: This is currently not used by VM, as we don't destroy TLS key simonis@6465: // on VM exit. simonis@6465: void os::free_thread_local_storage(int index) { simonis@6465: int rslt = pthread_key_delete((pthread_key_t)index); simonis@6465: assert(rslt == 0, "invalid index"); simonis@6465: } simonis@6465: simonis@6465: void os::thread_local_storage_at_put(int index, void* value) { simonis@6465: int rslt = pthread_setspecific((pthread_key_t)index, value); simonis@6465: assert(rslt == 0, "pthread_setspecific failed"); simonis@6465: } simonis@6465: simonis@6465: extern "C" Thread* get_thread() { simonis@6465: return ThreadLocalStorage::thread(); simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // time support simonis@6465: simonis@6465: // Time since start-up in seconds to a fine granularity. simonis@6465: // Used by VMSelfDestructTimer and the MemProfiler. simonis@6465: double os::elapsedTime() { simonis@6465: return (double)(os::elapsed_counter()) * 0.000001; simonis@6465: } simonis@6465: simonis@6465: jlong os::elapsed_counter() { simonis@6465: timeval time; simonis@6465: int status = gettimeofday(&time, NULL); simonis@6465: return jlong(time.tv_sec) * 1000 * 1000 + jlong(time.tv_usec) - initial_time_count; simonis@6465: } simonis@6465: simonis@6465: jlong os::elapsed_frequency() { simonis@6465: return (1000 * 1000); simonis@6465: } simonis@6465: simonis@6465: // For now, we say that linux does not support vtime. I have no idea simonis@6465: // whether it can actually be made to (DLD, 9/13/05). simonis@6465: simonis@6465: bool os::supports_vtime() { return false; } simonis@6465: bool os::enable_vtime() { return false; } simonis@6465: bool os::vtime_enabled() { return false; } simonis@6465: double os::elapsedVTime() { simonis@6465: // better than nothing, but not much simonis@6465: return elapsedTime(); simonis@6465: } simonis@6465: simonis@6465: jlong os::javaTimeMillis() { simonis@6465: timeval time; simonis@6465: int status = gettimeofday(&time, NULL); simonis@6465: assert(status != -1, "aix error at gettimeofday()"); simonis@6465: return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); simonis@6465: } simonis@6465: simonis@6465: // We need to manually declare mread_real_time, simonis@6465: // because IBM didn't provide a prototype in time.h. simonis@6465: // (they probably only ever tested in C, not C++) simonis@6465: extern "C" simonis@6465: int mread_real_time(timebasestruct_t *t, size_t size_of_timebasestruct_t); simonis@6465: simonis@6465: jlong os::javaTimeNanos() { simonis@6465: if (os::Aix::on_pase()) { simonis@6465: Unimplemented(); simonis@6465: return 0; simonis@6465: } simonis@6465: else { simonis@6465: // On AIX use the precision of processors real time clock simonis@6465: // or time base registers. simonis@6465: timebasestruct_t time; simonis@6465: int rc; simonis@6465: simonis@6465: // If the CPU has a time register, it will be used and simonis@6465: // we have to convert to real time first. After convertion we have following data: simonis@6465: // time.tb_high [seconds since 00:00:00 UTC on 1.1.1970] simonis@6465: // time.tb_low [nanoseconds after the last full second above] simonis@6465: // We better use mread_real_time here instead of read_real_time simonis@6465: // to ensure that we will get a monotonic increasing time. simonis@6465: if (mread_real_time(&time, TIMEBASE_SZ) != RTC_POWER) { simonis@6465: rc = time_base_to_time(&time, TIMEBASE_SZ); simonis@6465: assert(rc != -1, "aix error at time_base_to_time()"); simonis@6465: } simonis@6465: return jlong(time.tb_high) * (1000 * 1000 * 1000) + jlong(time.tb_low); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { simonis@6465: { simonis@6465: // gettimeofday - based on time in seconds since the Epoch thus does not wrap simonis@6465: info_ptr->max_value = ALL_64_BITS; simonis@6465: simonis@6465: // gettimeofday is a real time clock so it skips simonis@6465: info_ptr->may_skip_backward = true; simonis@6465: info_ptr->may_skip_forward = true; simonis@6465: } simonis@6465: simonis@6465: info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time simonis@6465: } simonis@6465: simonis@6465: // Return the real, user, and system times in seconds from an simonis@6465: // arbitrary fixed point in the past. simonis@6465: bool os::getTimesSecs(double* process_real_time, simonis@6465: double* process_user_time, simonis@6465: double* process_system_time) { simonis@6465: Unimplemented(); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: simonis@6465: char * os::local_time_string(char *buf, size_t buflen) { simonis@6465: struct tm t; simonis@6465: time_t long_time; simonis@6465: time(&long_time); simonis@6465: localtime_r(&long_time, &t); simonis@6465: jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d", simonis@6465: t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, simonis@6465: t.tm_hour, t.tm_min, t.tm_sec); simonis@6465: return buf; simonis@6465: } simonis@6465: simonis@6465: struct tm* os::localtime_pd(const time_t* clock, struct tm* res) { simonis@6465: return localtime_r(clock, res); simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // runtime exit support simonis@6465: simonis@6465: // Note: os::shutdown() might be called very early during initialization, or simonis@6465: // called from signal handler. Before adding something to os::shutdown(), make simonis@6465: // sure it is async-safe and can handle partially initialized VM. simonis@6465: void os::shutdown() { simonis@6465: simonis@6465: // allow PerfMemory to attempt cleanup of any persistent resources simonis@6465: perfMemory_exit(); simonis@6465: simonis@6465: // needs to remove object in file system simonis@6465: AttachListener::abort(); simonis@6465: simonis@6465: // flush buffered output, finish log files simonis@6465: ostream_abort(); simonis@6465: simonis@6465: // Check for abort hook simonis@6465: abort_hook_t abort_hook = Arguments::abort_hook(); simonis@6465: if (abort_hook != NULL) { simonis@6465: abort_hook(); simonis@6465: } simonis@6465: simonis@6465: } simonis@6465: simonis@6465: // Note: os::abort() might be called very early during initialization, or simonis@6465: // called from signal handler. Before adding something to os::abort(), make simonis@6465: // sure it is async-safe and can handle partially initialized VM. simonis@6465: void os::abort(bool dump_core) { simonis@6465: os::shutdown(); simonis@6465: if (dump_core) { simonis@6465: #ifndef PRODUCT simonis@6465: fdStream out(defaultStream::output_fd()); simonis@6465: out.print_raw("Current thread is "); simonis@6465: char buf[16]; simonis@6465: jio_snprintf(buf, sizeof(buf), UINTX_FORMAT, os::current_thread_id()); simonis@6465: out.print_raw_cr(buf); simonis@6465: out.print_raw_cr("Dumping core ..."); simonis@6465: #endif simonis@6465: ::abort(); // dump core simonis@6465: } simonis@6465: simonis@6465: ::exit(1); simonis@6465: } simonis@6465: simonis@6465: // Die immediately, no exit hook, no abort hook, no cleanup. simonis@6465: void os::die() { simonis@6465: ::abort(); simonis@6465: } simonis@6465: simonis@6465: // Unused on Aix for now. simonis@6465: void os::set_error_file(const char *logfile) {} simonis@6465: simonis@6465: simonis@6465: // This method is a copy of JDK's sysGetLastErrorString simonis@6465: // from src/solaris/hpi/src/system_md.c simonis@6465: simonis@6465: size_t os::lasterror(char *buf, size_t len) { simonis@6465: simonis@6465: if (errno == 0) return 0; simonis@6465: simonis@6465: const char *s = ::strerror(errno); simonis@6465: size_t n = ::strlen(s); simonis@6465: if (n >= len) { simonis@6465: n = len - 1; simonis@6465: } simonis@6465: ::strncpy(buf, s, n); simonis@6465: buf[n] = '\0'; simonis@6465: return n; simonis@6465: } simonis@6465: simonis@6465: intx os::current_thread_id() { return (intx)pthread_self(); } simonis@6465: int os::current_process_id() { simonis@6465: simonis@6465: // This implementation returns a unique pid, the pid of the simonis@6465: // launcher thread that starts the vm 'process'. simonis@6465: simonis@6465: // Under POSIX, getpid() returns the same pid as the simonis@6465: // launcher thread rather than a unique pid per thread. simonis@6465: // Use gettid() if you want the old pre NPTL behaviour. simonis@6465: simonis@6465: // if you are looking for the result of a call to getpid() that simonis@6465: // returns a unique pid for the calling thread, then look at the simonis@6465: // OSThread::thread_id() method in osThread_linux.hpp file simonis@6465: simonis@6465: return (int)(_initial_pid ? _initial_pid : getpid()); simonis@6465: } simonis@6465: simonis@6465: // DLL functions simonis@6465: simonis@6465: const char* os::dll_file_extension() { return ".so"; } simonis@6465: simonis@6465: // This must be hard coded because it's the system's temporary simonis@6465: // directory not the java application's temp directory, ala java.io.tmpdir. simonis@6465: const char* os::get_temp_directory() { return "/tmp"; } simonis@6465: simonis@6465: static bool file_exists(const char* filename) { simonis@6465: struct stat statbuf; simonis@6465: if (filename == NULL || strlen(filename) == 0) { simonis@6465: return false; simonis@6465: } simonis@6465: return os::stat(filename, &statbuf) == 0; simonis@6465: } simonis@6465: simonis@6465: bool os::dll_build_name(char* buffer, size_t buflen, simonis@6465: const char* pname, const char* fname) { simonis@6465: bool retval = false; simonis@6465: // Copied from libhpi simonis@6465: const size_t pnamelen = pname ? strlen(pname) : 0; simonis@6465: simonis@6465: // Return error on buffer overflow. simonis@6465: if (pnamelen + strlen(fname) + 10 > (size_t) buflen) { simonis@6465: *buffer = '\0'; simonis@6465: return retval; simonis@6465: } simonis@6465: simonis@6465: if (pnamelen == 0) { simonis@6465: snprintf(buffer, buflen, "lib%s.so", fname); simonis@6465: retval = true; simonis@6465: } else if (strchr(pname, *os::path_separator()) != NULL) { simonis@6465: int n; simonis@6465: char** pelements = split_path(pname, &n); simonis@6465: for (int i = 0; i < n; i++) { simonis@6465: // Really shouldn't be NULL, but check can't hurt simonis@6465: if (pelements[i] == NULL || strlen(pelements[i]) == 0) { simonis@6465: continue; // skip the empty path values simonis@6465: } simonis@6465: snprintf(buffer, buflen, "%s/lib%s.so", pelements[i], fname); simonis@6465: if (file_exists(buffer)) { simonis@6465: retval = true; simonis@6465: break; simonis@6465: } simonis@6465: } simonis@6465: // release the storage simonis@6465: for (int i = 0; i < n; i++) { simonis@6465: if (pelements[i] != NULL) { simonis@6465: FREE_C_HEAP_ARRAY(char, pelements[i], mtInternal); simonis@6465: } simonis@6465: } simonis@6465: if (pelements != NULL) { simonis@6465: FREE_C_HEAP_ARRAY(char*, pelements, mtInternal); simonis@6465: } simonis@6465: } else { simonis@6465: snprintf(buffer, buflen, "%s/lib%s.so", pname, fname); simonis@6465: retval = true; simonis@6465: } simonis@6465: return retval; simonis@6465: } simonis@6465: simonis@6465: // Check if addr is inside libjvm.so. simonis@6465: bool os::address_is_in_vm(address addr) { simonis@6465: simonis@6465: // Input could be a real pc or a function pointer literal. The latter simonis@6465: // would be a function descriptor residing in the data segment of a module. simonis@6465: simonis@6465: const LoadedLibraryModule* lib = LoadedLibraries::find_for_text_address(addr); simonis@6465: if (lib) { simonis@6465: if (strcmp(lib->get_shortname(), "libjvm.so") == 0) { simonis@6465: return true; simonis@6465: } else { simonis@6465: return false; simonis@6465: } simonis@6465: } else { simonis@6465: lib = LoadedLibraries::find_for_data_address(addr); simonis@6465: if (lib) { simonis@6465: if (strcmp(lib->get_shortname(), "libjvm.so") == 0) { simonis@6465: return true; simonis@6465: } else { simonis@6465: return false; simonis@6465: } simonis@6465: } else { simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // Resolve an AIX function descriptor literal to a code pointer. simonis@6465: // If the input is a valid code pointer to a text segment of a loaded module, simonis@6465: // it is returned unchanged. simonis@6465: // If the input is a valid AIX function descriptor, it is resolved to the simonis@6465: // code entry point. simonis@6465: // If the input is neither a valid function descriptor nor a valid code pointer, simonis@6465: // NULL is returned. simonis@6465: static address resolve_function_descriptor_to_code_pointer(address p) { simonis@6465: simonis@6465: const LoadedLibraryModule* lib = LoadedLibraries::find_for_text_address(p); simonis@6465: if (lib) { simonis@6465: // its a real code pointer simonis@6465: return p; simonis@6465: } else { simonis@6465: lib = LoadedLibraries::find_for_data_address(p); simonis@6465: if (lib) { simonis@6465: // pointer to data segment, potential function descriptor simonis@6465: address code_entry = (address)(((FunctionDescriptor*)p)->entry()); simonis@6465: if (LoadedLibraries::find_for_text_address(code_entry)) { simonis@6465: // Its a function descriptor simonis@6465: return code_entry; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: bool os::dll_address_to_function_name(address addr, char *buf, simonis@6465: int buflen, int *offset) { simonis@6465: if (offset) { simonis@6465: *offset = -1; simonis@6465: } simonis@6465: if (buf) { simonis@6465: buf[0] = '\0'; simonis@6465: } simonis@6465: simonis@6465: // Resolve function ptr literals first. simonis@6465: addr = resolve_function_descriptor_to_code_pointer(addr); simonis@6465: if (!addr) { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // Go through Decoder::decode to call getFuncName which reads the name from the traceback table. simonis@6465: return Decoder::decode(addr, buf, buflen, offset); simonis@6465: } simonis@6465: simonis@6465: static int getModuleName(codeptr_t pc, // [in] program counter simonis@6465: char* p_name, size_t namelen, // [out] optional: function name simonis@6465: char* p_errmsg, size_t errmsglen // [out] optional: user provided buffer for error messages simonis@6465: ) { simonis@6465: simonis@6465: // initialize output parameters simonis@6465: if (p_name && namelen > 0) { simonis@6465: *p_name = '\0'; simonis@6465: } simonis@6465: if (p_errmsg && errmsglen > 0) { simonis@6465: *p_errmsg = '\0'; simonis@6465: } simonis@6465: simonis@6465: const LoadedLibraryModule* const lib = LoadedLibraries::find_for_text_address((address)pc); simonis@6465: if (lib) { simonis@6465: if (p_name && namelen > 0) { simonis@6465: sprintf(p_name, "%.*s", namelen, lib->get_shortname()); simonis@6465: } simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "pc outside any module"); simonis@6465: } simonis@6465: simonis@6465: return -1; simonis@6465: simonis@6465: } simonis@6465: simonis@6465: bool os::dll_address_to_library_name(address addr, char* buf, simonis@6465: int buflen, int* offset) { simonis@6465: if (offset) { simonis@6465: *offset = -1; simonis@6465: } simonis@6465: if (buf) { simonis@6465: buf[0] = '\0'; simonis@6465: } simonis@6465: simonis@6465: // Resolve function ptr literals first. simonis@6465: addr = resolve_function_descriptor_to_code_pointer(addr); simonis@6465: if (!addr) { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: if (::getModuleName((codeptr_t) addr, buf, buflen, 0, 0) == 0) { simonis@6465: return true; simonis@6465: } simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // Loads .dll/.so and in case of error it checks if .dll/.so was built simonis@6465: // for the same architecture as Hotspot is running on simonis@6465: void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { simonis@6465: simonis@6465: if (ebuf && ebuflen > 0) { simonis@6465: ebuf[0] = '\0'; simonis@6465: ebuf[ebuflen - 1] = '\0'; simonis@6465: } simonis@6465: simonis@6465: if (!filename || strlen(filename) == 0) { simonis@6465: ::strncpy(ebuf, "dll_load: empty filename specified", ebuflen - 1); simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: // RTLD_LAZY is currently not implemented. The dl is loaded immediately with all its dependants. simonis@6465: void * result= ::dlopen(filename, RTLD_LAZY); simonis@6465: if (result != NULL) { simonis@6465: // Reload dll cache. Don't do this in signal handling. simonis@6465: LoadedLibraries::reload(); simonis@6465: return result; simonis@6465: } else { simonis@6465: // error analysis when dlopen fails simonis@6465: const char* const error_report = ::dlerror(); simonis@6465: if (error_report && ebuf && ebuflen > 0) { simonis@6465: snprintf(ebuf, ebuflen - 1, "%s, LIBPATH=%s, LD_LIBRARY_PATH=%s : %s", simonis@6465: filename, ::getenv("LIBPATH"), ::getenv("LD_LIBRARY_PATH"), error_report); simonis@6465: } simonis@6465: } simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: // Glibc-2.0 libdl is not MT safe. If you are building with any glibc, simonis@6465: // chances are you might want to run the generated bits against glibc-2.0 simonis@6465: // libdl.so, so always use locking for any version of glibc. simonis@6465: void* os::dll_lookup(void* handle, const char* name) { simonis@6465: pthread_mutex_lock(&dl_mutex); simonis@6465: void* res = dlsym(handle, name); simonis@6465: pthread_mutex_unlock(&dl_mutex); simonis@6465: return res; simonis@6465: } simonis@6465: simonis@6465: void os::print_dll_info(outputStream *st) { simonis@6465: st->print_cr("Dynamic libraries:"); simonis@6465: LoadedLibraries::print(st); simonis@6465: } simonis@6465: simonis@6465: void os::print_os_info(outputStream* st) { simonis@6465: st->print("OS:"); simonis@6465: simonis@6465: st->print("uname:"); simonis@6465: struct utsname name; simonis@6465: uname(&name); simonis@6465: st->print(name.sysname); st->print(" "); simonis@6465: st->print(name.nodename); st->print(" "); simonis@6465: st->print(name.release); st->print(" "); simonis@6465: st->print(name.version); st->print(" "); simonis@6465: st->print(name.machine); simonis@6465: st->cr(); simonis@6465: simonis@6465: // rlimit simonis@6465: st->print("rlimit:"); simonis@6465: struct rlimit rlim; simonis@6465: simonis@6465: st->print(" STACK "); simonis@6465: getrlimit(RLIMIT_STACK, &rlim); simonis@6465: if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); simonis@6465: else st->print("%uk", rlim.rlim_cur >> 10); simonis@6465: simonis@6465: st->print(", CORE "); simonis@6465: getrlimit(RLIMIT_CORE, &rlim); simonis@6465: if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); simonis@6465: else st->print("%uk", rlim.rlim_cur >> 10); simonis@6465: simonis@6465: st->print(", NPROC "); simonis@6465: st->print("%d", sysconf(_SC_CHILD_MAX)); simonis@6465: simonis@6465: st->print(", NOFILE "); simonis@6465: getrlimit(RLIMIT_NOFILE, &rlim); simonis@6465: if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); simonis@6465: else st->print("%d", rlim.rlim_cur); simonis@6465: simonis@6465: st->print(", AS "); simonis@6465: getrlimit(RLIMIT_AS, &rlim); simonis@6465: if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); simonis@6465: else st->print("%uk", rlim.rlim_cur >> 10); simonis@6465: simonis@6465: // Print limits on DATA, because it limits the C-heap. simonis@6465: st->print(", DATA "); simonis@6465: getrlimit(RLIMIT_DATA, &rlim); simonis@6465: if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); simonis@6465: else st->print("%uk", rlim.rlim_cur >> 10); simonis@6465: st->cr(); simonis@6465: simonis@6465: // load average simonis@6465: st->print("load average:"); simonis@6465: double loadavg[3] = {-1.L, -1.L, -1.L}; simonis@6465: os::loadavg(loadavg, 3); simonis@6465: st->print("%0.02f %0.02f %0.02f", loadavg[0], loadavg[1], loadavg[2]); simonis@6465: st->cr(); simonis@6465: } simonis@6465: simonis@6465: void os::print_memory_info(outputStream* st) { simonis@6465: simonis@6465: st->print_cr("Memory:"); simonis@6465: simonis@6465: st->print_cr(" default page size: %s", describe_pagesize(os::vm_page_size())); simonis@6465: st->print_cr(" default stack page size: %s", describe_pagesize(os::vm_page_size())); simonis@6465: st->print_cr(" default shm page size: %s", describe_pagesize(os::Aix::shm_default_page_size())); simonis@6465: st->print_cr(" can use 64K pages dynamically: %s", (os::Aix::can_use_64K_pages() ? "yes" :"no")); simonis@6465: st->print_cr(" can use 16M pages dynamically: %s", (os::Aix::can_use_16M_pages() ? "yes" :"no")); simonis@6465: if (g_multipage_error != 0) { simonis@6465: st->print_cr(" multipage error: %d", g_multipage_error); simonis@6465: } simonis@6465: simonis@6465: // print out LDR_CNTRL because it affects the default page sizes simonis@6465: const char* const ldr_cntrl = ::getenv("LDR_CNTRL"); simonis@6465: st->print_cr(" LDR_CNTRL=%s.", ldr_cntrl ? ldr_cntrl : ""); simonis@6465: simonis@6465: const char* const extshm = ::getenv("EXTSHM"); simonis@6465: st->print_cr(" EXTSHM=%s.", extshm ? extshm : ""); simonis@6465: simonis@6465: // Call os::Aix::get_meminfo() to retrieve memory statistics. simonis@6465: os::Aix::meminfo_t mi; simonis@6465: if (os::Aix::get_meminfo(&mi)) { simonis@6465: char buffer[256]; simonis@6465: if (os::Aix::on_aix()) { simonis@6465: jio_snprintf(buffer, sizeof(buffer), simonis@6465: " physical total : %llu\n" simonis@6465: " physical free : %llu\n" simonis@6465: " swap total : %llu\n" simonis@6465: " swap free : %llu\n", simonis@6465: mi.real_total, simonis@6465: mi.real_free, simonis@6465: mi.pgsp_total, simonis@6465: mi.pgsp_free); simonis@6465: } else { simonis@6465: Unimplemented(); simonis@6465: } simonis@6465: st->print_raw(buffer); simonis@6465: } else { simonis@6465: st->print_cr(" (no more information available)"); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: void os::pd_print_cpu_info(outputStream* st) { simonis@6465: // cpu simonis@6465: st->print("CPU:"); simonis@6465: st->print("total %d", os::processor_count()); simonis@6465: // It's not safe to query number of active processors after crash simonis@6465: // st->print("(active %d)", os::active_processor_count()); simonis@6465: st->print(" %s", VM_Version::cpu_features()); simonis@6465: st->cr(); simonis@6465: } simonis@6465: simonis@6465: void os::print_siginfo(outputStream* st, void* siginfo) { simonis@6465: // Use common posix version. simonis@6465: os::Posix::print_siginfo_brief(st, (const siginfo_t*) siginfo); simonis@6465: st->cr(); simonis@6465: } simonis@6465: simonis@6465: simonis@6465: static void print_signal_handler(outputStream* st, int sig, simonis@6465: char* buf, size_t buflen); simonis@6465: simonis@6465: void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { simonis@6465: st->print_cr("Signal Handlers:"); simonis@6465: print_signal_handler(st, SIGSEGV, buf, buflen); simonis@6465: print_signal_handler(st, SIGBUS , buf, buflen); simonis@6465: print_signal_handler(st, SIGFPE , buf, buflen); simonis@6465: print_signal_handler(st, SIGPIPE, buf, buflen); simonis@6465: print_signal_handler(st, SIGXFSZ, buf, buflen); simonis@6465: print_signal_handler(st, SIGILL , buf, buflen); simonis@6465: print_signal_handler(st, INTERRUPT_SIGNAL, buf, buflen); simonis@6465: print_signal_handler(st, SR_signum, buf, buflen); simonis@6465: print_signal_handler(st, SHUTDOWN1_SIGNAL, buf, buflen); simonis@6465: print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen); simonis@6465: print_signal_handler(st, SHUTDOWN3_SIGNAL , buf, buflen); simonis@6465: print_signal_handler(st, BREAK_SIGNAL, buf, buflen); simonis@6465: print_signal_handler(st, SIGTRAP, buf, buflen); simonis@6465: print_signal_handler(st, SIGDANGER, buf, buflen); simonis@6465: } simonis@6465: simonis@6465: static char saved_jvm_path[MAXPATHLEN] = {0}; simonis@6465: simonis@6465: // Find the full path to the current module, libjvm.so or libjvm_g.so simonis@6465: void os::jvm_path(char *buf, jint buflen) { simonis@6465: // Error checking. simonis@6465: if (buflen < MAXPATHLEN) { simonis@6465: assert(false, "must use a large-enough buffer"); simonis@6465: buf[0] = '\0'; simonis@6465: return; simonis@6465: } simonis@6465: // Lazy resolve the path to current module. simonis@6465: if (saved_jvm_path[0] != 0) { simonis@6465: strcpy(buf, saved_jvm_path); simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: Dl_info dlinfo; simonis@6465: int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo); simonis@6465: assert(ret != 0, "cannot locate libjvm"); simonis@6465: char* rp = realpath((char *)dlinfo.dli_fname, buf); simonis@6465: assert(rp != NULL, "error in realpath(): maybe the 'path' argument is too long?"); simonis@6465: simonis@6465: strcpy(saved_jvm_path, buf); simonis@6465: } simonis@6465: simonis@6465: void os::print_jni_name_prefix_on(outputStream* st, int args_size) { simonis@6465: // no prefix required, not even "_" simonis@6465: } simonis@6465: simonis@6465: void os::print_jni_name_suffix_on(outputStream* st, int args_size) { simonis@6465: // no suffix required simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // sun.misc.Signal support simonis@6465: simonis@6465: static volatile jint sigint_count = 0; simonis@6465: simonis@6465: static void simonis@6465: UserHandler(int sig, void *siginfo, void *context) { simonis@6465: // 4511530 - sem_post is serialized and handled by the manager thread. When simonis@6465: // the program is interrupted by Ctrl-C, SIGINT is sent to every thread. We simonis@6465: // don't want to flood the manager thread with sem_post requests. simonis@6465: if (sig == SIGINT && Atomic::add(1, &sigint_count) > 1) simonis@6465: return; simonis@6465: simonis@6465: // Ctrl-C is pressed during error reporting, likely because the error simonis@6465: // handler fails to abort. Let VM die immediately. simonis@6465: if (sig == SIGINT && is_error_reported()) { simonis@6465: os::die(); simonis@6465: } simonis@6465: simonis@6465: os::signal_notify(sig); simonis@6465: } simonis@6465: simonis@6465: void* os::user_handler() { simonis@6465: return CAST_FROM_FN_PTR(void*, UserHandler); simonis@6465: } simonis@6465: simonis@6465: extern "C" { simonis@6465: typedef void (*sa_handler_t)(int); simonis@6465: typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); simonis@6465: } simonis@6465: simonis@6465: void* os::signal(int signal_number, void* handler) { simonis@6465: struct sigaction sigAct, oldSigAct; simonis@6465: simonis@6465: sigfillset(&(sigAct.sa_mask)); simonis@6465: simonis@6465: // Do not block out synchronous signals in the signal handler. simonis@6465: // Blocking synchronous signals only makes sense if you can really simonis@6465: // be sure that those signals won't happen during signal handling, simonis@6465: // when the blocking applies. Normal signal handlers are lean and simonis@6465: // do not cause signals. But our signal handlers tend to be "risky" simonis@6465: // - secondary SIGSEGV, SIGILL, SIGBUS' may and do happen. simonis@6465: // On AIX, PASE there was a case where a SIGSEGV happened, followed simonis@6465: // by a SIGILL, which was blocked due to the signal mask. The process simonis@6465: // just hung forever. Better to crash from a secondary signal than to hang. simonis@6465: sigdelset(&(sigAct.sa_mask), SIGSEGV); simonis@6465: sigdelset(&(sigAct.sa_mask), SIGBUS); simonis@6465: sigdelset(&(sigAct.sa_mask), SIGILL); simonis@6465: sigdelset(&(sigAct.sa_mask), SIGFPE); simonis@6465: sigdelset(&(sigAct.sa_mask), SIGTRAP); simonis@6465: simonis@6465: sigAct.sa_flags = SA_RESTART|SA_SIGINFO; simonis@6465: simonis@6465: sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler); simonis@6465: simonis@6465: if (sigaction(signal_number, &sigAct, &oldSigAct)) { simonis@6465: // -1 means registration failed simonis@6465: return (void *)-1; simonis@6465: } simonis@6465: simonis@6465: return CAST_FROM_FN_PTR(void*, oldSigAct.sa_handler); simonis@6465: } simonis@6465: simonis@6465: void os::signal_raise(int signal_number) { simonis@6465: ::raise(signal_number); simonis@6465: } simonis@6465: simonis@6465: // simonis@6465: // The following code is moved from os.cpp for making this simonis@6465: // code platform specific, which it is by its very nature. simonis@6465: // simonis@6465: simonis@6465: // Will be modified when max signal is changed to be dynamic simonis@6465: int os::sigexitnum_pd() { simonis@6465: return NSIG; simonis@6465: } simonis@6465: simonis@6465: // a counter for each possible signal value simonis@6465: static volatile jint pending_signals[NSIG+1] = { 0 }; simonis@6465: simonis@6465: // Linux(POSIX) specific hand shaking semaphore. simonis@6465: static sem_t sig_sem; simonis@6465: simonis@6465: void os::signal_init_pd() { simonis@6465: // Initialize signal structures simonis@6465: ::memset((void*)pending_signals, 0, sizeof(pending_signals)); simonis@6465: simonis@6465: // Initialize signal semaphore simonis@6465: int rc = ::sem_init(&sig_sem, 0, 0); simonis@6465: guarantee(rc != -1, "sem_init failed"); simonis@6465: } simonis@6465: simonis@6465: void os::signal_notify(int sig) { simonis@6465: Atomic::inc(&pending_signals[sig]); simonis@6465: ::sem_post(&sig_sem); simonis@6465: } simonis@6465: simonis@6465: static int check_pending_signals(bool wait) { simonis@6465: Atomic::store(0, &sigint_count); simonis@6465: for (;;) { simonis@6465: for (int i = 0; i < NSIG + 1; i++) { simonis@6465: jint n = pending_signals[i]; simonis@6465: if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) { simonis@6465: return i; simonis@6465: } simonis@6465: } simonis@6465: if (!wait) { simonis@6465: return -1; simonis@6465: } simonis@6465: JavaThread *thread = JavaThread::current(); simonis@6465: ThreadBlockInVM tbivm(thread); simonis@6465: simonis@6465: bool threadIsSuspended; simonis@6465: do { simonis@6465: thread->set_suspend_equivalent(); simonis@6465: // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() simonis@6465: simonis@6465: ::sem_wait(&sig_sem); simonis@6465: simonis@6465: // were we externally suspended while we were waiting? simonis@6465: threadIsSuspended = thread->handle_special_suspend_equivalent_condition(); simonis@6465: if (threadIsSuspended) { simonis@6465: // simonis@6465: // The semaphore has been incremented, but while we were waiting simonis@6465: // another thread suspended us. We don't want to continue running simonis@6465: // while suspended because that would surprise the thread that simonis@6465: // suspended us. simonis@6465: // simonis@6465: ::sem_post(&sig_sem); simonis@6465: simonis@6465: thread->java_suspend_self(); simonis@6465: } simonis@6465: } while (threadIsSuspended); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: int os::signal_lookup() { simonis@6465: return check_pending_signals(false); simonis@6465: } simonis@6465: simonis@6465: int os::signal_wait() { simonis@6465: return check_pending_signals(true); simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // Virtual Memory simonis@6465: simonis@6465: // AddrRange describes an immutable address range simonis@6465: // simonis@6465: // This is a helper class for the 'shared memory bookkeeping' below. simonis@6465: class AddrRange { simonis@6465: friend class ShmBkBlock; simonis@6465: simonis@6465: char* _start; simonis@6465: size_t _size; simonis@6465: simonis@6465: public: simonis@6465: simonis@6465: AddrRange(char* start, size_t size) simonis@6465: : _start(start), _size(size) simonis@6465: {} simonis@6465: simonis@6465: AddrRange(const AddrRange& r) simonis@6465: : _start(r.start()), _size(r.size()) simonis@6465: {} simonis@6465: simonis@6465: char* start() const { return _start; } simonis@6465: size_t size() const { return _size; } simonis@6465: char* end() const { return _start + _size; } simonis@6465: bool is_empty() const { return _size == 0 ? true : false; } simonis@6465: simonis@6465: static AddrRange empty_range() { return AddrRange(NULL, 0); } simonis@6465: simonis@6465: bool contains(const char* p) const { simonis@6465: return start() <= p && end() > p; simonis@6465: } simonis@6465: simonis@6465: bool contains(const AddrRange& range) const { simonis@6465: return start() <= range.start() && end() >= range.end(); simonis@6465: } simonis@6465: simonis@6465: bool intersects(const AddrRange& range) const { simonis@6465: return (range.start() <= start() && range.end() > start()) || simonis@6465: (range.start() < end() && range.end() >= end()) || simonis@6465: contains(range); simonis@6465: } simonis@6465: simonis@6465: bool is_same_range(const AddrRange& range) const { simonis@6465: return start() == range.start() && size() == range.size(); simonis@6465: } simonis@6465: simonis@6465: // return the closest inside range consisting of whole pages simonis@6465: AddrRange find_closest_aligned_range(size_t pagesize) const { simonis@6465: if (pagesize == 0 || is_empty()) { simonis@6465: return empty_range(); simonis@6465: } simonis@6465: char* const from = (char*)align_size_up((intptr_t)_start, pagesize); simonis@6465: char* const to = (char*)align_size_down((intptr_t)end(), pagesize); simonis@6465: if (from > to) { simonis@6465: return empty_range(); simonis@6465: } simonis@6465: return AddrRange(from, to - from); simonis@6465: } simonis@6465: }; simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////// simonis@6465: // shared memory bookkeeping simonis@6465: // simonis@6465: // the os::reserve_memory() API and friends hand out different kind of memory, depending simonis@6465: // on need and circumstances. Memory may be allocated with mmap() or with shmget/shmat. simonis@6465: // simonis@6465: // But these memory types have to be treated differently. For example, to uncommit simonis@6465: // mmap-based memory, msync(MS_INVALIDATE) is needed, to uncommit shmat-based memory, simonis@6465: // disclaim64() is needed. simonis@6465: // simonis@6465: // Therefore we need to keep track of the allocated memory segments and their simonis@6465: // properties. simonis@6465: simonis@6465: // ShmBkBlock: base class for all blocks in the shared memory bookkeeping simonis@6465: class ShmBkBlock { simonis@6465: simonis@6465: ShmBkBlock* _next; simonis@6465: simonis@6465: protected: simonis@6465: simonis@6465: AddrRange _range; simonis@6465: const size_t _pagesize; simonis@6465: const bool _pinned; simonis@6465: simonis@6465: public: simonis@6465: simonis@6465: ShmBkBlock(AddrRange range, size_t pagesize, bool pinned) simonis@6465: : _range(range), _pagesize(pagesize), _pinned(pinned) , _next(NULL) { simonis@6465: simonis@6465: assert(_pagesize == SIZE_4K || _pagesize == SIZE_64K || _pagesize == SIZE_16M, "invalid page size"); simonis@6465: assert(!_range.is_empty(), "invalid range"); simonis@6465: } simonis@6465: simonis@6465: virtual void print(outputStream* st) const { simonis@6465: st->print("0x%p ... 0x%p (%llu) - %d %s pages - %s", simonis@6465: _range.start(), _range.end(), _range.size(), simonis@6465: _range.size() / _pagesize, describe_pagesize(_pagesize), simonis@6465: _pinned ? "pinned" : ""); simonis@6465: } simonis@6465: simonis@6465: enum Type { MMAP, SHMAT }; simonis@6465: virtual Type getType() = 0; simonis@6465: simonis@6465: char* base() const { return _range.start(); } simonis@6465: size_t size() const { return _range.size(); } simonis@6465: simonis@6465: void setAddrRange(AddrRange range) { simonis@6465: _range = range; simonis@6465: } simonis@6465: simonis@6465: bool containsAddress(const char* p) const { simonis@6465: return _range.contains(p); simonis@6465: } simonis@6465: simonis@6465: bool containsRange(const char* p, size_t size) const { simonis@6465: return _range.contains(AddrRange((char*)p, size)); simonis@6465: } simonis@6465: simonis@6465: bool isSameRange(const char* p, size_t size) const { simonis@6465: return _range.is_same_range(AddrRange((char*)p, size)); simonis@6465: } simonis@6465: simonis@6465: virtual bool disclaim(char* p, size_t size) = 0; simonis@6465: virtual bool release() = 0; simonis@6465: simonis@6465: // blocks live in a list. simonis@6465: ShmBkBlock* next() const { return _next; } simonis@6465: void set_next(ShmBkBlock* blk) { _next = blk; } simonis@6465: simonis@6465: }; // end: ShmBkBlock simonis@6465: simonis@6465: simonis@6465: // ShmBkMappedBlock: describes an block allocated with mmap() simonis@6465: class ShmBkMappedBlock : public ShmBkBlock { simonis@6465: public: simonis@6465: simonis@6465: ShmBkMappedBlock(AddrRange range) simonis@6465: : ShmBkBlock(range, SIZE_4K, false) {} // mmap: always 4K, never pinned simonis@6465: simonis@6465: void print(outputStream* st) const { simonis@6465: ShmBkBlock::print(st); simonis@6465: st->print_cr(" - mmap'ed"); simonis@6465: } simonis@6465: simonis@6465: Type getType() { simonis@6465: return MMAP; simonis@6465: } simonis@6465: simonis@6465: bool disclaim(char* p, size_t size) { simonis@6465: simonis@6465: AddrRange r(p, size); simonis@6465: simonis@6465: guarantee(_range.contains(r), "invalid disclaim"); simonis@6465: simonis@6465: // only disclaim whole ranges. simonis@6465: const AddrRange r2 = r.find_closest_aligned_range(_pagesize); simonis@6465: if (r2.is_empty()) { simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: const int rc = ::msync(r2.start(), r2.size(), MS_INVALIDATE); simonis@6465: simonis@6465: if (rc != 0) { simonis@6465: warning("msync(0x%p, %llu, MS_INVALIDATE) failed (%d)\n", r2.start(), r2.size(), errno); simonis@6465: } simonis@6465: simonis@6465: return rc == 0 ? true : false; simonis@6465: } simonis@6465: simonis@6465: bool release() { simonis@6465: // mmap'ed blocks are released using munmap simonis@6465: if (::munmap(_range.start(), _range.size()) != 0) { simonis@6465: warning("munmap(0x%p, %llu) failed (%d)\n", _range.start(), _range.size(), errno); simonis@6465: return false; simonis@6465: } simonis@6465: return true; simonis@6465: } simonis@6465: }; // end: ShmBkMappedBlock simonis@6465: simonis@6465: // ShmBkShmatedBlock: describes an block allocated with shmget/shmat() simonis@6465: class ShmBkShmatedBlock : public ShmBkBlock { simonis@6465: public: simonis@6465: simonis@6465: ShmBkShmatedBlock(AddrRange range, size_t pagesize, bool pinned) simonis@6465: : ShmBkBlock(range, pagesize, pinned) {} simonis@6465: simonis@6465: void print(outputStream* st) const { simonis@6465: ShmBkBlock::print(st); simonis@6465: st->print_cr(" - shmat'ed"); simonis@6465: } simonis@6465: simonis@6465: Type getType() { simonis@6465: return SHMAT; simonis@6465: } simonis@6465: simonis@6465: bool disclaim(char* p, size_t size) { simonis@6465: simonis@6465: AddrRange r(p, size); simonis@6465: simonis@6465: if (_pinned) { simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // shmat'ed blocks are disclaimed using disclaim64 simonis@6465: guarantee(_range.contains(r), "invalid disclaim"); simonis@6465: simonis@6465: // only disclaim whole ranges. simonis@6465: const AddrRange r2 = r.find_closest_aligned_range(_pagesize); simonis@6465: if (r2.is_empty()) { simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: const bool rc = my_disclaim64(r2.start(), r2.size()); simonis@6465: simonis@6465: if (Verbose && !rc) { simonis@6465: warning("failed to disclaim shm %p-%p\n", r2.start(), r2.end()); simonis@6465: } simonis@6465: simonis@6465: return rc; simonis@6465: } simonis@6465: simonis@6465: bool release() { simonis@6465: bool rc = false; simonis@6465: if (::shmdt(_range.start()) != 0) { simonis@6465: warning("shmdt(0x%p) failed (%d)\n", _range.start(), errno); simonis@6465: } else { simonis@6465: rc = true; simonis@6465: } simonis@6465: return rc; simonis@6465: } simonis@6465: simonis@6465: }; // end: ShmBkShmatedBlock simonis@6465: simonis@6465: static ShmBkBlock* g_shmbk_list = NULL; simonis@6465: static volatile jint g_shmbk_table_lock = 0; simonis@6465: simonis@6465: // keep some usage statistics simonis@6465: static struct { simonis@6465: int nodes; // number of nodes in list simonis@6465: size_t bytes; // reserved - not committed - bytes. simonis@6465: int reserves; // how often reserve was called simonis@6465: int lookups; // how often a lookup was made simonis@6465: } g_shmbk_stats = { 0, 0, 0, 0 }; simonis@6465: simonis@6465: // add information about a shared memory segment to the bookkeeping simonis@6465: static void shmbk_register(ShmBkBlock* p_block) { simonis@6465: guarantee(p_block, "logic error"); simonis@6465: p_block->set_next(g_shmbk_list); simonis@6465: g_shmbk_list = p_block; simonis@6465: g_shmbk_stats.reserves ++; simonis@6465: g_shmbk_stats.bytes += p_block->size(); simonis@6465: g_shmbk_stats.nodes ++; simonis@6465: } simonis@6465: simonis@6465: // remove information about a shared memory segment by its starting address simonis@6465: static void shmbk_unregister(ShmBkBlock* p_block) { simonis@6465: ShmBkBlock* p = g_shmbk_list; simonis@6465: ShmBkBlock* prev = NULL; simonis@6465: while (p) { simonis@6465: if (p == p_block) { simonis@6465: if (prev) { simonis@6465: prev->set_next(p->next()); simonis@6465: } else { simonis@6465: g_shmbk_list = p->next(); simonis@6465: } simonis@6465: g_shmbk_stats.nodes --; simonis@6465: g_shmbk_stats.bytes -= p->size(); simonis@6465: return; simonis@6465: } simonis@6465: prev = p; simonis@6465: p = p->next(); simonis@6465: } simonis@6465: assert(false, "should not happen"); simonis@6465: } simonis@6465: simonis@6465: // given a pointer, return shared memory bookkeeping record for the segment it points into simonis@6465: // using the returned block info must happen under lock protection simonis@6465: static ShmBkBlock* shmbk_find_by_containing_address(const char* addr) { simonis@6465: g_shmbk_stats.lookups ++; simonis@6465: ShmBkBlock* p = g_shmbk_list; simonis@6465: while (p) { simonis@6465: if (p->containsAddress(addr)) { simonis@6465: return p; simonis@6465: } simonis@6465: p = p->next(); simonis@6465: } simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: // dump all information about all memory segments allocated with os::reserve_memory() simonis@6465: void shmbk_dump_info() { simonis@6465: tty->print_cr("-- shared mem bookkeeping (alive: %d segments, %llu bytes, " simonis@6465: "total reserves: %d total lookups: %d)", simonis@6465: g_shmbk_stats.nodes, g_shmbk_stats.bytes, g_shmbk_stats.reserves, g_shmbk_stats.lookups); simonis@6465: const ShmBkBlock* p = g_shmbk_list; simonis@6465: int i = 0; simonis@6465: while (p) { simonis@6465: p->print(tty); simonis@6465: p = p->next(); simonis@6465: i ++; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: #define LOCK_SHMBK { ThreadCritical _LOCK_SHMBK; simonis@6465: #define UNLOCK_SHMBK } simonis@6465: simonis@6465: // End: shared memory bookkeeping simonis@6465: //////////////////////////////////////////////////////////////////////////////////////////////////// simonis@6465: simonis@6465: int os::vm_page_size() { simonis@6465: // Seems redundant as all get out simonis@6465: assert(os::Aix::page_size() != -1, "must call os::init"); simonis@6465: return os::Aix::page_size(); simonis@6465: } simonis@6465: simonis@6465: // Aix allocates memory by pages. simonis@6465: int os::vm_allocation_granularity() { simonis@6465: assert(os::Aix::page_size() != -1, "must call os::init"); simonis@6465: return os::Aix::page_size(); simonis@6465: } simonis@6465: simonis@6465: int os::Aix::commit_memory_impl(char* addr, size_t size, bool exec) { simonis@6465: simonis@6465: // Commit is a noop. There is no explicit commit simonis@6465: // needed on AIX. Memory is committed when touched. simonis@6465: // simonis@6465: // Debug : check address range for validity simonis@6465: #ifdef ASSERT simonis@6465: LOCK_SHMBK simonis@6465: ShmBkBlock* const block = shmbk_find_by_containing_address(addr); simonis@6465: if (!block) { simonis@6465: fprintf(stderr, "invalid pointer: " INTPTR_FORMAT "\n", addr); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid pointer"); simonis@6465: return false; simonis@6465: } else if (!block->containsRange(addr, size)) { simonis@6465: fprintf(stderr, "invalid range: " INTPTR_FORMAT " .. " INTPTR_FORMAT "\n", addr, addr + size); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid range"); simonis@6465: return false; simonis@6465: } simonis@6465: UNLOCK_SHMBK simonis@6465: #endif // ASSERT simonis@6465: simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: bool os::pd_commit_memory(char* addr, size_t size, bool exec) { simonis@6465: return os::Aix::commit_memory_impl(addr, size, exec) == 0; simonis@6465: } simonis@6465: simonis@6465: void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, simonis@6465: const char* mesg) { simonis@6465: assert(mesg != NULL, "mesg must be specified"); simonis@6465: os::Aix::commit_memory_impl(addr, size, exec); simonis@6465: } simonis@6465: simonis@6465: int os::Aix::commit_memory_impl(char* addr, size_t size, simonis@6465: size_t alignment_hint, bool exec) { simonis@6465: return os::Aix::commit_memory_impl(addr, size, exec); simonis@6465: } simonis@6465: simonis@6465: bool os::pd_commit_memory(char* addr, size_t size, size_t alignment_hint, simonis@6465: bool exec) { simonis@6465: return os::Aix::commit_memory_impl(addr, size, alignment_hint, exec) == 0; simonis@6465: } simonis@6465: simonis@6465: void os::pd_commit_memory_or_exit(char* addr, size_t size, simonis@6465: size_t alignment_hint, bool exec, simonis@6465: const char* mesg) { simonis@6465: os::Aix::commit_memory_impl(addr, size, alignment_hint, exec); simonis@6465: } simonis@6465: simonis@6465: bool os::pd_uncommit_memory(char* addr, size_t size) { simonis@6465: simonis@6465: // Delegate to ShmBkBlock class which knows how to uncommit its memory. simonis@6465: simonis@6465: bool rc = false; simonis@6465: LOCK_SHMBK simonis@6465: ShmBkBlock* const block = shmbk_find_by_containing_address(addr); simonis@6465: if (!block) { simonis@6465: fprintf(stderr, "invalid pointer: 0x%p.\n", addr); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid pointer"); simonis@6465: return false; simonis@6465: } else if (!block->containsRange(addr, size)) { simonis@6465: fprintf(stderr, "invalid range: 0x%p .. 0x%p.\n", addr, addr + size); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid range"); simonis@6465: return false; simonis@6465: } simonis@6465: rc = block->disclaim(addr, size); simonis@6465: UNLOCK_SHMBK simonis@6465: simonis@6465: if (Verbose && !rc) { simonis@6465: warning("failed to disclaim 0x%p .. 0x%p (0x%llX bytes).", addr, addr + size, size); simonis@6465: } simonis@6465: return rc; simonis@6465: } simonis@6465: simonis@6465: bool os::pd_create_stack_guard_pages(char* addr, size_t size) { simonis@6465: return os::guard_memory(addr, size); simonis@6465: } simonis@6465: simonis@6465: bool os::remove_stack_guard_pages(char* addr, size_t size) { simonis@6465: return os::unguard_memory(addr, size); simonis@6465: } simonis@6465: simonis@6465: void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) { simonis@6465: } simonis@6465: simonis@6465: void os::pd_free_memory(char *addr, size_t bytes, size_t alignment_hint) { simonis@6465: } simonis@6465: simonis@6465: void os::numa_make_global(char *addr, size_t bytes) { simonis@6465: } simonis@6465: simonis@6465: void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) { simonis@6465: } simonis@6465: simonis@6465: bool os::numa_topology_changed() { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: size_t os::numa_get_groups_num() { simonis@6465: return 1; simonis@6465: } simonis@6465: simonis@6465: int os::numa_get_group_id() { simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: size_t os::numa_get_leaf_groups(int *ids, size_t size) { simonis@6465: if (size > 0) { simonis@6465: ids[0] = 0; simonis@6465: return 1; simonis@6465: } simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: bool os::get_page_info(char *start, page_info* info) { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { simonis@6465: return end; simonis@6465: } simonis@6465: simonis@6465: // Flags for reserve_shmatted_memory: simonis@6465: #define RESSHM_WISHADDR_OR_FAIL 1 simonis@6465: #define RESSHM_TRY_16M_PAGES 2 simonis@6465: #define RESSHM_16M_PAGES_OR_FAIL 4 simonis@6465: simonis@6465: // Result of reserve_shmatted_memory: simonis@6465: struct shmatted_memory_info_t { simonis@6465: char* addr; simonis@6465: size_t pagesize; simonis@6465: bool pinned; simonis@6465: }; simonis@6465: simonis@6465: // Reserve a section of shmatted memory. simonis@6465: // params: simonis@6465: // bytes [in]: size of memory, in bytes simonis@6465: // requested_addr [in]: wish address. simonis@6465: // NULL = no wish. simonis@6465: // If RESSHM_WISHADDR_OR_FAIL is set in flags and wish address cannot simonis@6465: // be obtained, function will fail. Otherwise wish address is treated as hint and simonis@6465: // another pointer is returned. simonis@6465: // flags [in]: some flags. Valid flags are: simonis@6465: // RESSHM_WISHADDR_OR_FAIL - fail if wish address is given and cannot be obtained. simonis@6465: // RESSHM_TRY_16M_PAGES - try to allocate from 16M page pool simonis@6465: // (requires UseLargePages and Use16MPages) simonis@6465: // RESSHM_16M_PAGES_OR_FAIL - if you cannot allocate from 16M page pool, fail. simonis@6465: // Otherwise any other page size will do. simonis@6465: // p_info [out] : holds information about the created shared memory segment. simonis@6465: static bool reserve_shmatted_memory(size_t bytes, char* requested_addr, int flags, shmatted_memory_info_t* p_info) { simonis@6465: simonis@6465: assert(p_info, "parameter error"); simonis@6465: simonis@6465: // init output struct. simonis@6465: p_info->addr = NULL; simonis@6465: simonis@6465: // neither should we be here for EXTSHM=ON. simonis@6465: if (os::Aix::extshm()) { simonis@6465: ShouldNotReachHere(); simonis@6465: } simonis@6465: simonis@6465: // extract flags. sanity checks. simonis@6465: const bool wishaddr_or_fail = simonis@6465: flags & RESSHM_WISHADDR_OR_FAIL; simonis@6465: const bool try_16M_pages = simonis@6465: flags & RESSHM_TRY_16M_PAGES; simonis@6465: const bool f16M_pages_or_fail = simonis@6465: flags & RESSHM_16M_PAGES_OR_FAIL; simonis@6465: simonis@6465: // first check: if a wish address is given and it is mandatory, but not aligned to segment boundary, simonis@6465: // shmat will fail anyway, so save some cycles by failing right away simonis@6465: if (requested_addr && ((uintptr_t)requested_addr % SIZE_256M == 0)) { simonis@6465: if (wishaddr_or_fail) { simonis@6465: return false; simonis@6465: } else { simonis@6465: requested_addr = NULL; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: char* addr = NULL; simonis@6465: simonis@6465: // Align size of shm up to the largest possible page size, to avoid errors later on when we try to change simonis@6465: // pagesize dynamically. simonis@6465: const size_t size = align_size_up(bytes, SIZE_16M); simonis@6465: simonis@6465: // reserve the shared segment simonis@6465: int shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | S_IRUSR | S_IWUSR); simonis@6465: if (shmid == -1) { simonis@6465: warning("shmget(.., %lld, ..) failed (errno: %d).", size, errno); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // Important note: simonis@6465: // It is very important that we, upon leaving this function, do not leave a shm segment alive. simonis@6465: // We must right after attaching it remove it from the system. System V shm segments are global and simonis@6465: // survive the process. simonis@6465: // So, from here on: Do not assert. Do not return. Always do a "goto cleanup_shm". simonis@6465: simonis@6465: // try forcing the page size simonis@6465: size_t pagesize = -1; // unknown so far simonis@6465: simonis@6465: if (UseLargePages) { simonis@6465: simonis@6465: struct shmid_ds shmbuf; simonis@6465: memset(&shmbuf, 0, sizeof(shmbuf)); simonis@6465: simonis@6465: // First, try to take from 16M page pool if... simonis@6465: if (os::Aix::can_use_16M_pages() // we can ... simonis@6465: && Use16MPages // we are not explicitly forbidden to do so (-XX:-Use16MPages).. simonis@6465: && try_16M_pages) { // caller wants us to. simonis@6465: shmbuf.shm_pagesize = SIZE_16M; simonis@6465: if (shmctl(shmid, SHM_PAGESIZE, &shmbuf) == 0) { simonis@6465: pagesize = SIZE_16M; simonis@6465: } else { simonis@6465: warning("Failed to allocate %d 16M pages. 16M page pool might be exhausted. (shmctl failed with %d)", simonis@6465: size / SIZE_16M, errno); simonis@6465: if (f16M_pages_or_fail) { simonis@6465: goto cleanup_shm; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // Nothing yet? Try setting 64K pages. Note that I never saw this fail, but in theory it might, simonis@6465: // because the 64K page pool may also be exhausted. simonis@6465: if (pagesize == -1) { simonis@6465: shmbuf.shm_pagesize = SIZE_64K; simonis@6465: if (shmctl(shmid, SHM_PAGESIZE, &shmbuf) == 0) { simonis@6465: pagesize = SIZE_64K; simonis@6465: } else { simonis@6465: warning("Failed to allocate %d 64K pages. (shmctl failed with %d)", simonis@6465: size / SIZE_64K, errno); simonis@6465: // here I give up. leave page_size -1 - later, after attaching, we will query the simonis@6465: // real page size of the attached memory. (in theory, it may be something different simonis@6465: // from 4K if LDR_CNTRL SHM_PSIZE is set) simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // sanity point simonis@6465: assert(pagesize == -1 || pagesize == SIZE_16M || pagesize == SIZE_64K, "wrong page size"); simonis@6465: simonis@6465: // Now attach the shared segment. simonis@6465: addr = (char*) shmat(shmid, requested_addr, 0); simonis@6465: if (addr == (char*)-1) { simonis@6465: // How to handle attach failure: simonis@6465: // If it failed for a specific wish address, tolerate this: in that case, if wish address was simonis@6465: // mandatory, fail, if not, retry anywhere. simonis@6465: // If it failed for any other reason, treat that as fatal error. simonis@6465: addr = NULL; simonis@6465: if (requested_addr) { simonis@6465: if (wishaddr_or_fail) { simonis@6465: goto cleanup_shm; simonis@6465: } else { simonis@6465: addr = (char*) shmat(shmid, NULL, 0); simonis@6465: if (addr == (char*)-1) { // fatal simonis@6465: addr = NULL; simonis@6465: warning("shmat failed (errno: %d)", errno); simonis@6465: goto cleanup_shm; simonis@6465: } simonis@6465: } simonis@6465: } else { // fatal simonis@6465: addr = NULL; simonis@6465: warning("shmat failed (errno: %d)", errno); simonis@6465: goto cleanup_shm; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // sanity point simonis@6465: assert(addr && addr != (char*) -1, "wrong address"); simonis@6465: simonis@6465: // after successful Attach remove the segment - right away. simonis@6465: if (::shmctl(shmid, IPC_RMID, NULL) == -1) { simonis@6465: warning("shmctl(%u, IPC_RMID) failed (%d)\n", shmid, errno); simonis@6465: guarantee(false, "failed to remove shared memory segment!"); simonis@6465: } simonis@6465: shmid = -1; simonis@6465: simonis@6465: // query the real page size. In case setting the page size did not work (see above), the system simonis@6465: // may have given us something other then 4K (LDR_CNTRL) simonis@6465: { simonis@6465: const size_t real_pagesize = os::Aix::query_pagesize(addr); simonis@6465: if (pagesize != -1) { simonis@6465: assert(pagesize == real_pagesize, "unexpected pagesize after shmat"); simonis@6465: } else { simonis@6465: pagesize = real_pagesize; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // Now register the reserved block with internal book keeping. simonis@6465: LOCK_SHMBK simonis@6465: const bool pinned = pagesize >= SIZE_16M ? true : false; simonis@6465: ShmBkShmatedBlock* const p_block = new ShmBkShmatedBlock(AddrRange(addr, size), pagesize, pinned); simonis@6465: assert(p_block, ""); simonis@6465: shmbk_register(p_block); simonis@6465: UNLOCK_SHMBK simonis@6465: simonis@6465: cleanup_shm: simonis@6465: simonis@6465: // if we have not done so yet, remove the shared memory segment. This is very important. simonis@6465: if (shmid != -1) { simonis@6465: if (::shmctl(shmid, IPC_RMID, NULL) == -1) { simonis@6465: warning("shmctl(%u, IPC_RMID) failed (%d)\n", shmid, errno); simonis@6465: guarantee(false, "failed to remove shared memory segment!"); simonis@6465: } simonis@6465: shmid = -1; simonis@6465: } simonis@6465: simonis@6465: // trace simonis@6465: if (Verbose && !addr) { simonis@6465: if (requested_addr != NULL) { simonis@6465: warning("failed to shm-allocate 0x%llX bytes at with address 0x%p.", size, requested_addr); simonis@6465: } else { simonis@6465: warning("failed to shm-allocate 0x%llX bytes at any address.", size); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // hand info to caller simonis@6465: if (addr) { simonis@6465: p_info->addr = addr; simonis@6465: p_info->pagesize = pagesize; simonis@6465: p_info->pinned = pagesize == SIZE_16M ? true : false; simonis@6465: } simonis@6465: simonis@6465: // sanity test: simonis@6465: if (requested_addr && addr && wishaddr_or_fail) { simonis@6465: guarantee(addr == requested_addr, "shmat error"); simonis@6465: } simonis@6465: simonis@6465: // just one more test to really make sure we have no dangling shm segments. simonis@6465: guarantee(shmid == -1, "dangling shm segments"); simonis@6465: simonis@6465: return addr ? true : false; simonis@6465: simonis@6465: } // end: reserve_shmatted_memory simonis@6465: simonis@6465: // Reserve memory using mmap. Behaves the same as reserve_shmatted_memory(): simonis@6465: // will return NULL in case of an error. simonis@6465: static char* reserve_mmaped_memory(size_t bytes, char* requested_addr) { simonis@6465: simonis@6465: // if a wish address is given, but not aligned to 4K page boundary, mmap will fail. simonis@6465: if (requested_addr && ((uintptr_t)requested_addr % os::vm_page_size() != 0)) { simonis@6465: warning("Wish address 0x%p not aligned to page boundary.", requested_addr); simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: const size_t size = align_size_up(bytes, SIZE_4K); simonis@6465: simonis@6465: // Note: MAP_SHARED (instead of MAP_PRIVATE) needed to be able to simonis@6465: // msync(MS_INVALIDATE) (see os::uncommit_memory) simonis@6465: int flags = MAP_ANONYMOUS | MAP_SHARED; simonis@6465: simonis@6465: // MAP_FIXED is needed to enforce requested_addr - manpage is vague about what simonis@6465: // it means if wishaddress is given but MAP_FIXED is not set. simonis@6465: // simonis@6465: // Note however that this changes semantics in SPEC1170 mode insofar as MAP_FIXED simonis@6465: // clobbers the address range, which is probably not what the caller wants. That's simonis@6465: // why I assert here (again) that the SPEC1170 compat mode is off. simonis@6465: // If we want to be able to run under SPEC1170, we have to do some porting and simonis@6465: // testing. simonis@6465: if (requested_addr != NULL) { simonis@6465: assert(!os::Aix::xpg_sus_mode(), "SPEC1170 mode not allowed."); simonis@6465: flags |= MAP_FIXED; simonis@6465: } simonis@6465: simonis@6465: char* addr = (char*)::mmap(requested_addr, size, PROT_READ|PROT_WRITE|PROT_EXEC, flags, -1, 0); simonis@6465: simonis@6465: if (addr == MAP_FAILED) { simonis@6465: // attach failed: tolerate for specific wish addresses. Not being able to attach simonis@6465: // anywhere is a fatal error. simonis@6465: if (requested_addr == NULL) { simonis@6465: // It's ok to fail here if the machine has not enough memory. simonis@6465: warning("mmap(NULL, 0x%llX, ..) failed (%d)", size, errno); simonis@6465: } simonis@6465: addr = NULL; simonis@6465: goto cleanup_mmap; simonis@6465: } simonis@6465: simonis@6465: // If we did request a specific address and that address was not available, fail. simonis@6465: if (addr && requested_addr) { simonis@6465: guarantee(addr == requested_addr, "unexpected"); simonis@6465: } simonis@6465: simonis@6465: // register this mmap'ed segment with book keeping simonis@6465: LOCK_SHMBK simonis@6465: ShmBkMappedBlock* const p_block = new ShmBkMappedBlock(AddrRange(addr, size)); simonis@6465: assert(p_block, ""); simonis@6465: shmbk_register(p_block); simonis@6465: UNLOCK_SHMBK simonis@6465: simonis@6465: cleanup_mmap: simonis@6465: simonis@6465: if (addr) { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "mmap-allocated 0x%p .. 0x%p (0x%llX bytes)\n", addr, addr + bytes, bytes); simonis@6465: } simonis@6465: } simonis@6465: else { simonis@6465: if (requested_addr != NULL) { simonis@6465: warning("failed to mmap-allocate 0x%llX bytes at wish address 0x%p.", bytes, requested_addr); simonis@6465: } else { simonis@6465: warning("failed to mmap-allocate 0x%llX bytes at any address.", bytes); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: return addr; simonis@6465: simonis@6465: } // end: reserve_mmaped_memory simonis@6465: simonis@6465: // Reserves and attaches a shared memory segment. simonis@6465: // Will assert if a wish address is given and could not be obtained. simonis@6465: char* os::pd_reserve_memory(size_t bytes, char* requested_addr, size_t alignment_hint) { simonis@6465: return os::attempt_reserve_memory_at(bytes, requested_addr); simonis@6465: } simonis@6465: simonis@6465: bool os::pd_release_memory(char* addr, size_t size) { simonis@6465: simonis@6465: // delegate to ShmBkBlock class which knows how to uncommit its memory. simonis@6465: simonis@6465: bool rc = false; simonis@6465: LOCK_SHMBK simonis@6465: ShmBkBlock* const block = shmbk_find_by_containing_address(addr); simonis@6465: if (!block) { simonis@6465: fprintf(stderr, "invalid pointer: 0x%p.\n", addr); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid pointer"); simonis@6465: return false; simonis@6465: } simonis@6465: else if (!block->isSameRange(addr, size)) { simonis@6465: if (block->getType() == ShmBkBlock::MMAP) { simonis@6465: // Release only the same range or a the beginning or the end of a range. simonis@6465: if (block->base() == addr && size < block->size()) { simonis@6465: ShmBkMappedBlock* const b = new ShmBkMappedBlock(AddrRange(block->base() + size, block->size() - size)); simonis@6465: assert(b, ""); simonis@6465: shmbk_register(b); simonis@6465: block->setAddrRange(AddrRange(addr, size)); simonis@6465: } simonis@6465: else if (addr > block->base() && addr + size == block->base() + block->size()) { simonis@6465: ShmBkMappedBlock* const b = new ShmBkMappedBlock(AddrRange(block->base(), block->size() - size)); simonis@6465: assert(b, ""); simonis@6465: shmbk_register(b); simonis@6465: block->setAddrRange(AddrRange(addr, size)); simonis@6465: } simonis@6465: else { simonis@6465: fprintf(stderr, "invalid mmap range: 0x%p .. 0x%p.\n", addr, addr + size); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid mmap range"); simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: else { simonis@6465: // Release only the same range. No partial release allowed. simonis@6465: // Soften the requirement a bit, because the user may think he owns a smaller size simonis@6465: // than the block is due to alignment etc. simonis@6465: if (block->base() != addr || block->size() < size) { simonis@6465: fprintf(stderr, "invalid shmget range: 0x%p .. 0x%p.\n", addr, addr + size); simonis@6465: shmbk_dump_info(); simonis@6465: assert(false, "invalid shmget range"); simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: rc = block->release(); simonis@6465: assert(rc, "release failed"); simonis@6465: // remove block from bookkeeping simonis@6465: shmbk_unregister(block); simonis@6465: delete block; simonis@6465: UNLOCK_SHMBK simonis@6465: simonis@6465: if (!rc) { simonis@6465: warning("failed to released %lu bytes at 0x%p", size, addr); simonis@6465: } simonis@6465: simonis@6465: return rc; simonis@6465: } simonis@6465: simonis@6465: static bool checked_mprotect(char* addr, size_t size, int prot) { simonis@6465: simonis@6465: // Little problem here: if SPEC1170 behaviour is off, mprotect() on AIX will simonis@6465: // not tell me if protection failed when trying to protect an un-protectable range. simonis@6465: // simonis@6465: // This means if the memory was allocated using shmget/shmat, protection wont work simonis@6465: // but mprotect will still return 0: simonis@6465: // simonis@6465: // See http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/mprotect.htm simonis@6465: simonis@6465: bool rc = ::mprotect(addr, size, prot) == 0 ? true : false; simonis@6465: simonis@6465: if (!rc) { simonis@6465: const char* const s_errno = strerror(errno); simonis@6465: warning("mprotect(" PTR_FORMAT "-" PTR_FORMAT ", 0x%X) failed (%s).", addr, addr + size, prot, s_errno); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // mprotect success check simonis@6465: // simonis@6465: // Mprotect said it changed the protection but can I believe it? simonis@6465: // simonis@6465: // To be sure I need to check the protection afterwards. Try to simonis@6465: // read from protected memory and check whether that causes a segfault. simonis@6465: // simonis@6465: if (!os::Aix::xpg_sus_mode()) { simonis@6465: simonis@6465: if (StubRoutines::SafeFetch32_stub()) { simonis@6465: simonis@6465: const bool read_protected = simonis@6465: (SafeFetch32((int*)addr, 0x12345678) == 0x12345678 && simonis@6465: SafeFetch32((int*)addr, 0x76543210) == 0x76543210) ? true : false; simonis@6465: simonis@6465: if (prot & PROT_READ) { simonis@6465: rc = !read_protected; simonis@6465: } else { simonis@6465: rc = read_protected; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: if (!rc) { simonis@6465: assert(false, "mprotect failed."); simonis@6465: } simonis@6465: return rc; simonis@6465: } simonis@6465: simonis@6465: // Set protections specified simonis@6465: bool os::protect_memory(char* addr, size_t size, ProtType prot, bool is_committed) { simonis@6465: unsigned int p = 0; simonis@6465: switch (prot) { simonis@6465: case MEM_PROT_NONE: p = PROT_NONE; break; simonis@6465: case MEM_PROT_READ: p = PROT_READ; break; simonis@6465: case MEM_PROT_RW: p = PROT_READ|PROT_WRITE; break; simonis@6465: case MEM_PROT_RWX: p = PROT_READ|PROT_WRITE|PROT_EXEC; break; simonis@6465: default: simonis@6465: ShouldNotReachHere(); simonis@6465: } simonis@6465: // is_committed is unused. simonis@6465: return checked_mprotect(addr, size, p); simonis@6465: } simonis@6465: simonis@6465: bool os::guard_memory(char* addr, size_t size) { simonis@6465: return checked_mprotect(addr, size, PROT_NONE); simonis@6465: } simonis@6465: simonis@6465: bool os::unguard_memory(char* addr, size_t size) { simonis@6465: return checked_mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); simonis@6465: } simonis@6465: simonis@6465: // Large page support simonis@6465: simonis@6465: static size_t _large_page_size = 0; simonis@6465: simonis@6465: // Enable large page support if OS allows that. simonis@6465: void os::large_page_init() { simonis@6465: simonis@6465: // Note: os::Aix::query_multipage_support must run first. simonis@6465: simonis@6465: if (!UseLargePages) { simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: if (!Aix::can_use_64K_pages()) { simonis@6465: assert(!Aix::can_use_16M_pages(), "64K is a precondition for 16M."); simonis@6465: UseLargePages = false; simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: if (!Aix::can_use_16M_pages() && Use16MPages) { simonis@6465: fprintf(stderr, "Cannot use 16M pages. Please ensure that there is a 16M page pool " simonis@6465: " and that the VM runs with CAP_BYPASS_RAC_VMM and CAP_PROPAGATE capabilities.\n"); simonis@6465: } simonis@6465: simonis@6465: // Do not report 16M page alignment as part of os::_page_sizes if we are simonis@6465: // explicitly forbidden from using 16M pages. Doing so would increase the simonis@6465: // alignment the garbage collector calculates with, slightly increasing simonis@6465: // heap usage. We should only pay for 16M alignment if we really want to simonis@6465: // use 16M pages. simonis@6465: if (Use16MPages && Aix::can_use_16M_pages()) { simonis@6465: _large_page_size = SIZE_16M; simonis@6465: _page_sizes[0] = SIZE_16M; simonis@6465: _page_sizes[1] = SIZE_64K; simonis@6465: _page_sizes[2] = SIZE_4K; simonis@6465: _page_sizes[3] = 0; simonis@6465: } else if (Aix::can_use_64K_pages()) { simonis@6465: _large_page_size = SIZE_64K; simonis@6465: _page_sizes[0] = SIZE_64K; simonis@6465: _page_sizes[1] = SIZE_4K; simonis@6465: _page_sizes[2] = 0; simonis@6465: } simonis@6465: simonis@6465: if (Verbose) { simonis@6465: ("Default large page size is 0x%llX.", _large_page_size); simonis@6465: } simonis@6465: } // end: os::large_page_init() simonis@6465: simonis@6465: char* os::reserve_memory_special(size_t bytes, size_t alignment, char* req_addr, bool exec) { simonis@6465: // "exec" is passed in but not used. Creating the shared image for simonis@6465: // the code cache doesn't have an SHM_X executable permission to check. simonis@6465: Unimplemented(); simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: bool os::release_memory_special(char* base, size_t bytes) { simonis@6465: // detaching the SHM segment will also delete it, see reserve_memory_special() simonis@6465: Unimplemented(); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: size_t os::large_page_size() { simonis@6465: return _large_page_size; simonis@6465: } simonis@6465: simonis@6465: bool os::can_commit_large_page_memory() { simonis@6465: // Well, sadly we cannot commit anything at all (see comment in simonis@6465: // os::commit_memory) but we claim to so we can make use of large pages simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: bool os::can_execute_large_page_memory() { simonis@6465: // We can do that simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // Reserve memory at an arbitrary address, only if that area is simonis@6465: // available (and not reserved for something else). simonis@6465: char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) { simonis@6465: simonis@6465: bool use_mmap = false; simonis@6465: simonis@6465: // mmap: smaller graining, no large page support simonis@6465: // shm: large graining (256M), large page support, limited number of shm segments simonis@6465: // simonis@6465: // Prefer mmap wherever we either do not need large page support or have OS limits simonis@6465: simonis@6465: if (!UseLargePages || bytes < SIZE_16M) { simonis@6465: use_mmap = true; simonis@6465: } simonis@6465: simonis@6465: char* addr = NULL; simonis@6465: if (use_mmap) { simonis@6465: addr = reserve_mmaped_memory(bytes, requested_addr); simonis@6465: } else { simonis@6465: // shmat: wish address is mandatory, and do not try 16M pages here. simonis@6465: shmatted_memory_info_t info; simonis@6465: const int flags = RESSHM_WISHADDR_OR_FAIL; simonis@6465: if (reserve_shmatted_memory(bytes, requested_addr, flags, &info)) { simonis@6465: addr = info.addr; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: return addr; simonis@6465: } simonis@6465: simonis@6465: size_t os::read(int fd, void *buf, unsigned int nBytes) { simonis@6465: return ::read(fd, buf, nBytes); simonis@6465: } simonis@6465: simonis@6465: #define NANOSECS_PER_MILLISEC 1000000 simonis@6465: simonis@6465: int os::sleep(Thread* thread, jlong millis, bool interruptible) { simonis@6465: assert(thread == Thread::current(), "thread consistency check"); simonis@6465: simonis@6465: // Prevent nasty overflow in deadline calculation simonis@6465: // by handling long sleeps similar to solaris or windows. simonis@6465: const jlong limit = INT_MAX; simonis@6465: int result; simonis@6465: while (millis > limit) { simonis@6465: if ((result = os::sleep(thread, limit, interruptible)) != OS_OK) { simonis@6465: return result; simonis@6465: } simonis@6465: millis -= limit; simonis@6465: } simonis@6465: simonis@6465: ParkEvent * const slp = thread->_SleepEvent; simonis@6465: slp->reset(); simonis@6465: OrderAccess::fence(); simonis@6465: simonis@6465: if (interruptible) { simonis@6465: jlong prevtime = javaTimeNanos(); simonis@6465: simonis@6465: // Prevent precision loss and too long sleeps simonis@6465: jlong deadline = prevtime + millis * NANOSECS_PER_MILLISEC; simonis@6465: simonis@6465: for (;;) { simonis@6465: if (os::is_interrupted(thread, true)) { simonis@6465: return OS_INTRPT; simonis@6465: } simonis@6465: simonis@6465: jlong newtime = javaTimeNanos(); simonis@6465: simonis@6465: assert(newtime >= prevtime, "time moving backwards"); simonis@6465: // Doing prevtime and newtime in microseconds doesn't help precision, simonis@6465: // and trying to round up to avoid lost milliseconds can result in a simonis@6465: // too-short delay. simonis@6465: millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC; simonis@6465: simonis@6465: if (millis <= 0) { simonis@6465: return OS_OK; simonis@6465: } simonis@6465: simonis@6465: // Stop sleeping if we passed the deadline simonis@6465: if (newtime >= deadline) { simonis@6465: return OS_OK; simonis@6465: } simonis@6465: simonis@6465: prevtime = newtime; simonis@6465: simonis@6465: { simonis@6465: assert(thread->is_Java_thread(), "sanity check"); simonis@6465: JavaThread *jt = (JavaThread *) thread; simonis@6465: ThreadBlockInVM tbivm(jt); simonis@6465: OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */); simonis@6465: simonis@6465: jt->set_suspend_equivalent(); simonis@6465: simonis@6465: slp->park(millis); simonis@6465: simonis@6465: // were we externally suspended while we were waiting? simonis@6465: jt->check_and_wait_while_suspended(); simonis@6465: } simonis@6465: } simonis@6465: } else { simonis@6465: OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); simonis@6465: jlong prevtime = javaTimeNanos(); simonis@6465: simonis@6465: // Prevent precision loss and too long sleeps simonis@6465: jlong deadline = prevtime + millis * NANOSECS_PER_MILLISEC; simonis@6465: simonis@6465: for (;;) { simonis@6465: // It'd be nice to avoid the back-to-back javaTimeNanos() calls on simonis@6465: // the 1st iteration ... simonis@6465: jlong newtime = javaTimeNanos(); simonis@6465: simonis@6465: if (newtime - prevtime < 0) { simonis@6465: // time moving backwards, should only happen if no monotonic clock simonis@6465: // not a guarantee() because JVM should not abort on kernel/glibc bugs simonis@6465: // - HS14 Commented out as not implemented. simonis@6465: // - TODO Maybe we should implement it? simonis@6465: //assert(!Aix::supports_monotonic_clock(), "time moving backwards"); simonis@6465: } else { simonis@6465: millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC; simonis@6465: } simonis@6465: simonis@6465: if (millis <= 0) break; simonis@6465: simonis@6465: if (newtime >= deadline) { simonis@6465: break; simonis@6465: } simonis@6465: simonis@6465: prevtime = newtime; simonis@6465: slp->park(millis); simonis@6465: } simonis@6465: return OS_OK; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: int os::naked_sleep() { simonis@6465: // %% make the sleep time an integer flag. for now use 1 millisec. simonis@6465: return os::sleep(Thread::current(), 1, false); simonis@6465: } simonis@6465: simonis@6465: // Sleep forever; naked call to OS-specific sleep; use with CAUTION simonis@6465: void os::infinite_sleep() { simonis@6465: while (true) { // sleep forever ... simonis@6465: ::sleep(100); // ... 100 seconds at a time simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // Used to convert frequent JVM_Yield() to nops simonis@6465: bool os::dont_yield() { simonis@6465: return DontYieldALot; simonis@6465: } simonis@6465: simonis@6465: void os::yield() { simonis@6465: sched_yield(); simonis@6465: } simonis@6465: simonis@6465: os::YieldResult os::NakedYield() { sched_yield(); return os::YIELD_UNKNOWN; } simonis@6465: simonis@6465: void os::yield_all(int attempts) { simonis@6465: // Yields to all threads, including threads with lower priorities simonis@6465: // Threads on Linux are all with same priority. The Solaris style simonis@6465: // os::yield_all() with nanosleep(1ms) is not necessary. simonis@6465: sched_yield(); simonis@6465: } simonis@6465: simonis@6465: // Called from the tight loops to possibly influence time-sharing heuristics simonis@6465: void os::loop_breaker(int attempts) { simonis@6465: os::yield_all(attempts); simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // thread priority support simonis@6465: simonis@6465: // From AIX manpage to pthread_setschedparam simonis@6465: // (see: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp? simonis@6465: // topic=/com.ibm.aix.basetechref/doc/basetrf1/pthread_setschedparam.htm): simonis@6465: // simonis@6465: // "If schedpolicy is SCHED_OTHER, then sched_priority must be in the simonis@6465: // range from 40 to 80, where 40 is the least favored priority and 80 simonis@6465: // is the most favored." simonis@6465: // simonis@6465: // (Actually, I doubt this even has an impact on AIX, as we do kernel simonis@6465: // scheduling there; however, this still leaves iSeries.) simonis@6465: // simonis@6465: // We use the same values for AIX and PASE. simonis@6465: int os::java_to_os_priority[CriticalPriority + 1] = { simonis@6465: 54, // 0 Entry should never be used simonis@6465: simonis@6465: 55, // 1 MinPriority simonis@6465: 55, // 2 simonis@6465: 56, // 3 simonis@6465: simonis@6465: 56, // 4 simonis@6465: 57, // 5 NormPriority simonis@6465: 57, // 6 simonis@6465: simonis@6465: 58, // 7 simonis@6465: 58, // 8 simonis@6465: 59, // 9 NearMaxPriority simonis@6465: simonis@6465: 60, // 10 MaxPriority simonis@6465: simonis@6465: 60 // 11 CriticalPriority simonis@6465: }; simonis@6465: simonis@6465: OSReturn os::set_native_priority(Thread* thread, int newpri) { simonis@6465: if (!UseThreadPriorities) return OS_OK; simonis@6465: pthread_t thr = thread->osthread()->pthread_id(); simonis@6465: int policy = SCHED_OTHER; simonis@6465: struct sched_param param; simonis@6465: param.sched_priority = newpri; simonis@6465: int ret = pthread_setschedparam(thr, policy, ¶m); simonis@6465: simonis@6465: if (Verbose) { simonis@6465: if (ret == 0) { simonis@6465: fprintf(stderr, "changed priority of thread %d to %d\n", (int)thr, newpri); simonis@6465: } else { simonis@6465: fprintf(stderr, "Could not changed priority for thread %d to %d (error %d, %s)\n", simonis@6465: (int)thr, newpri, ret, strerror(ret)); simonis@6465: } simonis@6465: } simonis@6465: return (ret == 0) ? OS_OK : OS_ERR; simonis@6465: } simonis@6465: simonis@6465: OSReturn os::get_native_priority(const Thread* const thread, int *priority_ptr) { simonis@6465: if (!UseThreadPriorities) { simonis@6465: *priority_ptr = java_to_os_priority[NormPriority]; simonis@6465: return OS_OK; simonis@6465: } simonis@6465: pthread_t thr = thread->osthread()->pthread_id(); simonis@6465: int policy = SCHED_OTHER; simonis@6465: struct sched_param param; simonis@6465: int ret = pthread_getschedparam(thr, &policy, ¶m); simonis@6465: *priority_ptr = param.sched_priority; simonis@6465: simonis@6465: return (ret == 0) ? OS_OK : OS_ERR; simonis@6465: } simonis@6465: simonis@6465: // Hint to the underlying OS that a task switch would not be good. simonis@6465: // Void return because it's a hint and can fail. simonis@6465: void os::hint_no_preempt() {} simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // suspend/resume support simonis@6465: simonis@6465: // the low-level signal-based suspend/resume support is a remnant from the simonis@6465: // old VM-suspension that used to be for java-suspension, safepoints etc, simonis@6465: // within hotspot. Now there is a single use-case for this: simonis@6465: // - calling get_thread_pc() on the VMThread by the flat-profiler task simonis@6465: // that runs in the watcher thread. simonis@6465: // The remaining code is greatly simplified from the more general suspension simonis@6465: // code that used to be used. simonis@6465: // simonis@6465: // The protocol is quite simple: simonis@6465: // - suspend: simonis@6465: // - sends a signal to the target thread simonis@6465: // - polls the suspend state of the osthread using a yield loop simonis@6465: // - target thread signal handler (SR_handler) sets suspend state simonis@6465: // and blocks in sigsuspend until continued simonis@6465: // - resume: simonis@6465: // - sets target osthread state to continue simonis@6465: // - sends signal to end the sigsuspend loop in the SR_handler simonis@6465: // simonis@6465: // Note that the SR_lock plays no role in this suspend/resume protocol. simonis@6465: // simonis@6465: simonis@6465: static void resume_clear_context(OSThread *osthread) { simonis@6465: osthread->set_ucontext(NULL); simonis@6465: osthread->set_siginfo(NULL); simonis@6465: } simonis@6465: simonis@6465: static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontext_t* context) { simonis@6465: osthread->set_ucontext(context); simonis@6465: osthread->set_siginfo(siginfo); simonis@6465: } simonis@6465: simonis@6465: // simonis@6465: // Handler function invoked when a thread's execution is suspended or simonis@6465: // resumed. We have to be careful that only async-safe functions are simonis@6465: // called here (Note: most pthread functions are not async safe and simonis@6465: // should be avoided.) simonis@6465: // simonis@6465: // Note: sigwait() is a more natural fit than sigsuspend() from an simonis@6465: // interface point of view, but sigwait() prevents the signal hander simonis@6465: // from being run. libpthread would get very confused by not having simonis@6465: // its signal handlers run and prevents sigwait()'s use with the simonis@6465: // mutex granting granting signal. simonis@6465: // simonis@6465: // Currently only ever called on the VMThread and JavaThreads (PC sampling). simonis@6465: // simonis@6465: static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { simonis@6465: // Save and restore errno to avoid confusing native code with EINTR simonis@6465: // after sigsuspend. simonis@6465: int old_errno = errno; simonis@6465: simonis@6465: Thread* thread = Thread::current(); simonis@6465: OSThread* osthread = thread->osthread(); simonis@6465: assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread"); simonis@6465: simonis@6465: os::SuspendResume::State current = osthread->sr.state(); simonis@6465: if (current == os::SuspendResume::SR_SUSPEND_REQUEST) { simonis@6465: suspend_save_context(osthread, siginfo, context); simonis@6465: simonis@6465: // attempt to switch the state, we assume we had a SUSPEND_REQUEST simonis@6465: os::SuspendResume::State state = osthread->sr.suspended(); simonis@6465: if (state == os::SuspendResume::SR_SUSPENDED) { simonis@6465: sigset_t suspend_set; // signals for sigsuspend() simonis@6465: simonis@6465: // get current set of blocked signals and unblock resume signal simonis@6465: pthread_sigmask(SIG_BLOCK, NULL, &suspend_set); simonis@6465: sigdelset(&suspend_set, SR_signum); simonis@6465: simonis@6465: // wait here until we are resumed simonis@6465: while (1) { simonis@6465: sigsuspend(&suspend_set); simonis@6465: simonis@6465: os::SuspendResume::State result = osthread->sr.running(); simonis@6465: if (result == os::SuspendResume::SR_RUNNING) { simonis@6465: break; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: } else if (state == os::SuspendResume::SR_RUNNING) { simonis@6465: // request was cancelled, continue simonis@6465: } else { simonis@6465: ShouldNotReachHere(); simonis@6465: } simonis@6465: simonis@6465: resume_clear_context(osthread); simonis@6465: } else if (current == os::SuspendResume::SR_RUNNING) { simonis@6465: // request was cancelled, continue simonis@6465: } else if (current == os::SuspendResume::SR_WAKEUP_REQUEST) { simonis@6465: // ignore simonis@6465: } else { simonis@6465: ShouldNotReachHere(); simonis@6465: } simonis@6465: simonis@6465: errno = old_errno; simonis@6465: } simonis@6465: simonis@6465: simonis@6465: static int SR_initialize() { simonis@6465: struct sigaction act; simonis@6465: char *s; simonis@6465: // Get signal number to use for suspend/resume simonis@6465: if ((s = ::getenv("_JAVA_SR_SIGNUM")) != 0) { simonis@6465: int sig = ::strtol(s, 0, 10); simonis@6465: if (sig > 0 || sig < NSIG) { simonis@6465: SR_signum = sig; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: assert(SR_signum > SIGSEGV && SR_signum > SIGBUS, simonis@6465: "SR_signum must be greater than max(SIGSEGV, SIGBUS), see 4355769"); simonis@6465: simonis@6465: sigemptyset(&SR_sigset); simonis@6465: sigaddset(&SR_sigset, SR_signum); simonis@6465: simonis@6465: // Set up signal handler for suspend/resume. simonis@6465: act.sa_flags = SA_RESTART|SA_SIGINFO; simonis@6465: act.sa_handler = (void (*)(int)) SR_handler; simonis@6465: simonis@6465: // SR_signum is blocked by default. simonis@6465: // 4528190 - We also need to block pthread restart signal (32 on all simonis@6465: // supported Linux platforms). Note that LinuxThreads need to block simonis@6465: // this signal for all threads to work properly. So we don't have simonis@6465: // to use hard-coded signal number when setting up the mask. simonis@6465: pthread_sigmask(SIG_BLOCK, NULL, &act.sa_mask); simonis@6465: simonis@6465: if (sigaction(SR_signum, &act, 0) == -1) { simonis@6465: return -1; simonis@6465: } simonis@6465: simonis@6465: // Save signal flag simonis@6465: os::Aix::set_our_sigflags(SR_signum, act.sa_flags); simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: static int SR_finalize() { simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: static int sr_notify(OSThread* osthread) { simonis@6465: int status = pthread_kill(osthread->pthread_id(), SR_signum); simonis@6465: assert_status(status == 0, status, "pthread_kill"); simonis@6465: return status; simonis@6465: } simonis@6465: simonis@6465: // "Randomly" selected value for how long we want to spin simonis@6465: // before bailing out on suspending a thread, also how often simonis@6465: // we send a signal to a thread we want to resume simonis@6465: static const int RANDOMLY_LARGE_INTEGER = 1000000; simonis@6465: static const int RANDOMLY_LARGE_INTEGER2 = 100; simonis@6465: simonis@6465: // returns true on success and false on error - really an error is fatal simonis@6465: // but this seems the normal response to library errors simonis@6465: static bool do_suspend(OSThread* osthread) { simonis@6465: assert(osthread->sr.is_running(), "thread should be running"); simonis@6465: // mark as suspended and send signal simonis@6465: simonis@6465: if (osthread->sr.request_suspend() != os::SuspendResume::SR_SUSPEND_REQUEST) { simonis@6465: // failed to switch, state wasn't running? simonis@6465: ShouldNotReachHere(); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: if (sr_notify(osthread) != 0) { simonis@6465: // try to cancel, switch to running simonis@6465: simonis@6465: os::SuspendResume::State result = osthread->sr.cancel_suspend(); simonis@6465: if (result == os::SuspendResume::SR_RUNNING) { simonis@6465: // cancelled simonis@6465: return false; simonis@6465: } else if (result == os::SuspendResume::SR_SUSPENDED) { simonis@6465: // somehow managed to suspend simonis@6465: return true; simonis@6465: } else { simonis@6465: ShouldNotReachHere(); simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // managed to send the signal and switch to SUSPEND_REQUEST, now wait for SUSPENDED simonis@6465: simonis@6465: for (int n = 0; !osthread->sr.is_suspended(); n++) { simonis@6465: for (int i = 0; i < RANDOMLY_LARGE_INTEGER2 && !osthread->sr.is_suspended(); i++) { simonis@6465: os::yield_all(i); simonis@6465: } simonis@6465: simonis@6465: // timeout, try to cancel the request simonis@6465: if (n >= RANDOMLY_LARGE_INTEGER) { simonis@6465: os::SuspendResume::State cancelled = osthread->sr.cancel_suspend(); simonis@6465: if (cancelled == os::SuspendResume::SR_RUNNING) { simonis@6465: return false; simonis@6465: } else if (cancelled == os::SuspendResume::SR_SUSPENDED) { simonis@6465: return true; simonis@6465: } else { simonis@6465: ShouldNotReachHere(); simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: guarantee(osthread->sr.is_suspended(), "Must be suspended"); simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: static void do_resume(OSThread* osthread) { simonis@6465: //assert(osthread->sr.is_suspended(), "thread should be suspended"); simonis@6465: simonis@6465: if (osthread->sr.request_wakeup() != os::SuspendResume::SR_WAKEUP_REQUEST) { simonis@6465: // failed to switch to WAKEUP_REQUEST simonis@6465: ShouldNotReachHere(); simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: while (!osthread->sr.is_running()) { simonis@6465: if (sr_notify(osthread) == 0) { simonis@6465: for (int n = 0; n < RANDOMLY_LARGE_INTEGER && !osthread->sr.is_running(); n++) { simonis@6465: for (int i = 0; i < 100 && !osthread->sr.is_running(); i++) { simonis@6465: os::yield_all(i); simonis@6465: } simonis@6465: } simonis@6465: } else { simonis@6465: ShouldNotReachHere(); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: guarantee(osthread->sr.is_running(), "Must be running!"); simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // interrupt support simonis@6465: simonis@6465: void os::interrupt(Thread* thread) { simonis@6465: assert(Thread::current() == thread || Threads_lock->owned_by_self(), simonis@6465: "possibility of dangling Thread pointer"); simonis@6465: simonis@6465: OSThread* osthread = thread->osthread(); simonis@6465: simonis@6465: if (!osthread->interrupted()) { simonis@6465: osthread->set_interrupted(true); simonis@6465: // More than one thread can get here with the same value of osthread, simonis@6465: // resulting in multiple notifications. We do, however, want the store simonis@6465: // to interrupted() to be visible to other threads before we execute unpark(). simonis@6465: OrderAccess::fence(); simonis@6465: ParkEvent * const slp = thread->_SleepEvent; simonis@6465: if (slp != NULL) slp->unpark(); simonis@6465: } simonis@6465: simonis@6465: // For JSR166. Unpark even if interrupt status already was set simonis@6465: if (thread->is_Java_thread()) simonis@6465: ((JavaThread*)thread)->parker()->unpark(); simonis@6465: simonis@6465: ParkEvent * ev = thread->_ParkEvent; simonis@6465: if (ev != NULL) ev->unpark(); simonis@6465: simonis@6465: } simonis@6465: simonis@6465: bool os::is_interrupted(Thread* thread, bool clear_interrupted) { simonis@6465: assert(Thread::current() == thread || Threads_lock->owned_by_self(), simonis@6465: "possibility of dangling Thread pointer"); simonis@6465: simonis@6465: OSThread* osthread = thread->osthread(); simonis@6465: simonis@6465: bool interrupted = osthread->interrupted(); simonis@6465: simonis@6465: if (interrupted && clear_interrupted) { simonis@6465: osthread->set_interrupted(false); simonis@6465: // consider thread->_SleepEvent->reset() ... optional optimization simonis@6465: } simonis@6465: simonis@6465: return interrupted; simonis@6465: } simonis@6465: simonis@6465: /////////////////////////////////////////////////////////////////////////////////// simonis@6465: // signal handling (except suspend/resume) simonis@6465: simonis@6465: // This routine may be used by user applications as a "hook" to catch signals. simonis@6465: // The user-defined signal handler must pass unrecognized signals to this simonis@6465: // routine, and if it returns true (non-zero), then the signal handler must simonis@6465: // return immediately. If the flag "abort_if_unrecognized" is true, then this simonis@6465: // routine will never retun false (zero), but instead will execute a VM panic simonis@6465: // routine kill the process. simonis@6465: // simonis@6465: // If this routine returns false, it is OK to call it again. This allows simonis@6465: // the user-defined signal handler to perform checks either before or after simonis@6465: // the VM performs its own checks. Naturally, the user code would be making simonis@6465: // a serious error if it tried to handle an exception (such as a null check simonis@6465: // or breakpoint) that the VM was generating for its own correct operation. simonis@6465: // simonis@6465: // This routine may recognize any of the following kinds of signals: simonis@6465: // SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGQUIT, SIGPIPE, SIGXFSZ, SIGUSR1. simonis@6465: // It should be consulted by handlers for any of those signals. simonis@6465: // simonis@6465: // The caller of this routine must pass in the three arguments supplied simonis@6465: // to the function referred to in the "sa_sigaction" (not the "sa_handler") simonis@6465: // field of the structure passed to sigaction(). This routine assumes that simonis@6465: // the sa_flags field passed to sigaction() includes SA_SIGINFO and SA_RESTART. simonis@6465: // simonis@6465: // Note that the VM will print warnings if it detects conflicting signal simonis@6465: // handlers, unless invoked with the option "-XX:+AllowUserSignalHandlers". simonis@6465: // simonis@6465: extern "C" JNIEXPORT int simonis@6465: JVM_handle_aix_signal(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized); simonis@6465: simonis@6465: // Set thread signal mask (for some reason on AIX sigthreadmask() seems simonis@6465: // to be the thing to call; documentation is not terribly clear about whether simonis@6465: // pthread_sigmask also works, and if it does, whether it does the same. simonis@6465: bool set_thread_signal_mask(int how, const sigset_t* set, sigset_t* oset) { simonis@6465: const int rc = ::pthread_sigmask(how, set, oset); simonis@6465: // return value semantics differ slightly for error case: simonis@6465: // pthread_sigmask returns error number, sigthreadmask -1 and sets global errno simonis@6465: // (so, pthread_sigmask is more theadsafe for error handling) simonis@6465: // But success is always 0. simonis@6465: return rc == 0 ? true : false; simonis@6465: } simonis@6465: simonis@6465: // Function to unblock all signals which are, according simonis@6465: // to POSIX, typical program error signals. If they happen while being blocked, simonis@6465: // they typically will bring down the process immediately. simonis@6465: bool unblock_program_error_signals() { simonis@6465: sigset_t set; simonis@6465: ::sigemptyset(&set); simonis@6465: ::sigaddset(&set, SIGILL); simonis@6465: ::sigaddset(&set, SIGBUS); simonis@6465: ::sigaddset(&set, SIGFPE); simonis@6465: ::sigaddset(&set, SIGSEGV); simonis@6465: return set_thread_signal_mask(SIG_UNBLOCK, &set, NULL); simonis@6465: } simonis@6465: simonis@6465: // Renamed from 'signalHandler' to avoid collision with other shared libs. simonis@6465: void javaSignalHandler(int sig, siginfo_t* info, void* uc) { simonis@6465: assert(info != NULL && uc != NULL, "it must be old kernel"); simonis@6465: simonis@6465: // Never leave program error signals blocked; simonis@6465: // on all our platforms they would bring down the process immediately when simonis@6465: // getting raised while being blocked. simonis@6465: unblock_program_error_signals(); simonis@6465: simonis@6465: JVM_handle_aix_signal(sig, info, uc, true); simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // This boolean allows users to forward their own non-matching signals simonis@6465: // to JVM_handle_aix_signal, harmlessly. simonis@6465: bool os::Aix::signal_handlers_are_installed = false; simonis@6465: simonis@6465: // For signal-chaining simonis@6465: struct sigaction os::Aix::sigact[MAXSIGNUM]; simonis@6465: unsigned int os::Aix::sigs = 0; simonis@6465: bool os::Aix::libjsig_is_loaded = false; simonis@6465: typedef struct sigaction *(*get_signal_t)(int); simonis@6465: get_signal_t os::Aix::get_signal_action = NULL; simonis@6465: simonis@6465: struct sigaction* os::Aix::get_chained_signal_action(int sig) { simonis@6465: struct sigaction *actp = NULL; simonis@6465: simonis@6465: if (libjsig_is_loaded) { simonis@6465: // Retrieve the old signal handler from libjsig simonis@6465: actp = (*get_signal_action)(sig); simonis@6465: } simonis@6465: if (actp == NULL) { simonis@6465: // Retrieve the preinstalled signal handler from jvm simonis@6465: actp = get_preinstalled_handler(sig); simonis@6465: } simonis@6465: simonis@6465: return actp; simonis@6465: } simonis@6465: simonis@6465: static bool call_chained_handler(struct sigaction *actp, int sig, simonis@6465: siginfo_t *siginfo, void *context) { simonis@6465: Unimplemented(); simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: bool os::Aix::chained_handler(int sig, siginfo_t* siginfo, void* context) { simonis@6465: bool chained = false; simonis@6465: // signal-chaining simonis@6465: if (UseSignalChaining) { simonis@6465: struct sigaction *actp = get_chained_signal_action(sig); simonis@6465: if (actp != NULL) { simonis@6465: chained = call_chained_handler(actp, sig, siginfo, context); simonis@6465: } simonis@6465: } simonis@6465: return chained; simonis@6465: } simonis@6465: simonis@6465: struct sigaction* os::Aix::get_preinstalled_handler(int sig) { simonis@6465: if ((((unsigned int)1 << sig) & sigs) != 0) { simonis@6465: return &sigact[sig]; simonis@6465: } simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: void os::Aix::save_preinstalled_handler(int sig, struct sigaction& oldAct) { simonis@6465: assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); simonis@6465: sigact[sig] = oldAct; simonis@6465: sigs |= (unsigned int)1 << sig; simonis@6465: } simonis@6465: simonis@6465: // for diagnostic simonis@6465: int os::Aix::sigflags[MAXSIGNUM]; simonis@6465: simonis@6465: int os::Aix::get_our_sigflags(int sig) { simonis@6465: assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); simonis@6465: return sigflags[sig]; simonis@6465: } simonis@6465: simonis@6465: void os::Aix::set_our_sigflags(int sig, int flags) { simonis@6465: assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); simonis@6465: sigflags[sig] = flags; simonis@6465: } simonis@6465: simonis@6465: void os::Aix::set_signal_handler(int sig, bool set_installed) { simonis@6465: // Check for overwrite. simonis@6465: struct sigaction oldAct; simonis@6465: sigaction(sig, (struct sigaction*)NULL, &oldAct); simonis@6465: simonis@6465: void* oldhand = oldAct.sa_sigaction simonis@6465: ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) simonis@6465: : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); simonis@6465: // Renamed 'signalHandler' to avoid collision with other shared libs. simonis@6465: if (oldhand != CAST_FROM_FN_PTR(void*, SIG_DFL) && simonis@6465: oldhand != CAST_FROM_FN_PTR(void*, SIG_IGN) && simonis@6465: oldhand != CAST_FROM_FN_PTR(void*, (sa_sigaction_t)javaSignalHandler)) { simonis@6465: if (AllowUserSignalHandlers || !set_installed) { simonis@6465: // Do not overwrite; user takes responsibility to forward to us. simonis@6465: return; simonis@6465: } else if (UseSignalChaining) { simonis@6465: // save the old handler in jvm simonis@6465: save_preinstalled_handler(sig, oldAct); simonis@6465: // libjsig also interposes the sigaction() call below and saves the simonis@6465: // old sigaction on it own. simonis@6465: } else { simonis@6465: fatal(err_msg("Encountered unexpected pre-existing sigaction handler " simonis@6465: "%#lx for signal %d.", (long)oldhand, sig)); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: struct sigaction sigAct; simonis@6465: sigfillset(&(sigAct.sa_mask)); simonis@6465: if (!set_installed) { simonis@6465: sigAct.sa_handler = SIG_DFL; simonis@6465: sigAct.sa_flags = SA_RESTART; simonis@6465: } else { simonis@6465: // Renamed 'signalHandler' to avoid collision with other shared libs. simonis@6465: sigAct.sa_sigaction = javaSignalHandler; simonis@6465: sigAct.sa_flags = SA_SIGINFO|SA_RESTART; simonis@6465: } simonis@6465: // Save flags, which are set by ours simonis@6465: assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); simonis@6465: sigflags[sig] = sigAct.sa_flags; simonis@6465: simonis@6465: int ret = sigaction(sig, &sigAct, &oldAct); simonis@6465: assert(ret == 0, "check"); simonis@6465: simonis@6465: void* oldhand2 = oldAct.sa_sigaction simonis@6465: ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) simonis@6465: : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); simonis@6465: assert(oldhand2 == oldhand, "no concurrent signal handler installation"); simonis@6465: } simonis@6465: simonis@6465: // install signal handlers for signals that HotSpot needs to simonis@6465: // handle in order to support Java-level exception handling. simonis@6465: void os::Aix::install_signal_handlers() { simonis@6465: if (!signal_handlers_are_installed) { simonis@6465: signal_handlers_are_installed = true; simonis@6465: simonis@6465: // signal-chaining simonis@6465: typedef void (*signal_setting_t)(); simonis@6465: signal_setting_t begin_signal_setting = NULL; simonis@6465: signal_setting_t end_signal_setting = NULL; simonis@6465: begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, simonis@6465: dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); simonis@6465: if (begin_signal_setting != NULL) { simonis@6465: end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, simonis@6465: dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); simonis@6465: get_signal_action = CAST_TO_FN_PTR(get_signal_t, simonis@6465: dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); simonis@6465: libjsig_is_loaded = true; simonis@6465: assert(UseSignalChaining, "should enable signal-chaining"); simonis@6465: } simonis@6465: if (libjsig_is_loaded) { simonis@6465: // Tell libjsig jvm is setting signal handlers simonis@6465: (*begin_signal_setting)(); simonis@6465: } simonis@6465: simonis@6465: set_signal_handler(SIGSEGV, true); simonis@6465: set_signal_handler(SIGPIPE, true); simonis@6465: set_signal_handler(SIGBUS, true); simonis@6465: set_signal_handler(SIGILL, true); simonis@6465: set_signal_handler(SIGFPE, true); simonis@6465: set_signal_handler(SIGTRAP, true); simonis@6465: set_signal_handler(SIGXFSZ, true); simonis@6465: set_signal_handler(SIGDANGER, true); simonis@6465: simonis@6465: if (libjsig_is_loaded) { simonis@6465: // Tell libjsig jvm finishes setting signal handlers simonis@6465: (*end_signal_setting)(); simonis@6465: } simonis@6465: simonis@6465: // We don't activate signal checker if libjsig is in place, we trust ourselves simonis@6465: // and if UserSignalHandler is installed all bets are off. simonis@6465: // Log that signal checking is off only if -verbose:jni is specified. simonis@6465: if (CheckJNICalls) { simonis@6465: if (libjsig_is_loaded) { simonis@6465: tty->print_cr("Info: libjsig is activated, all active signal checking is disabled"); simonis@6465: check_signals = false; simonis@6465: } simonis@6465: if (AllowUserSignalHandlers) { simonis@6465: tty->print_cr("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); simonis@6465: check_signals = false; simonis@6465: } simonis@6465: // need to initialize check_signal_done simonis@6465: ::sigemptyset(&check_signal_done); simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: static const char* get_signal_handler_name(address handler, simonis@6465: char* buf, int buflen) { simonis@6465: int offset; simonis@6465: bool found = os::dll_address_to_library_name(handler, buf, buflen, &offset); simonis@6465: if (found) { simonis@6465: // skip directory names simonis@6465: const char *p1, *p2; simonis@6465: p1 = buf; simonis@6465: size_t len = strlen(os::file_separator()); simonis@6465: while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len; simonis@6465: // The way os::dll_address_to_library_name is implemented on Aix simonis@6465: // right now, it always returns -1 for the offset which is not simonis@6465: // terribly informative. simonis@6465: // Will fix that. For now, omit the offset. simonis@6465: jio_snprintf(buf, buflen, "%s", p1); simonis@6465: } else { simonis@6465: jio_snprintf(buf, buflen, PTR_FORMAT, handler); simonis@6465: } simonis@6465: return buf; simonis@6465: } simonis@6465: simonis@6465: static void print_signal_handler(outputStream* st, int sig, simonis@6465: char* buf, size_t buflen) { simonis@6465: struct sigaction sa; simonis@6465: sigaction(sig, NULL, &sa); simonis@6465: simonis@6465: st->print("%s: ", os::exception_name(sig, buf, buflen)); simonis@6465: simonis@6465: address handler = (sa.sa_flags & SA_SIGINFO) simonis@6465: ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) simonis@6465: : CAST_FROM_FN_PTR(address, sa.sa_handler); simonis@6465: simonis@6465: if (handler == CAST_FROM_FN_PTR(address, SIG_DFL)) { simonis@6465: st->print("SIG_DFL"); simonis@6465: } else if (handler == CAST_FROM_FN_PTR(address, SIG_IGN)) { simonis@6465: st->print("SIG_IGN"); simonis@6465: } else { simonis@6465: st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); simonis@6465: } simonis@6465: simonis@6465: // Print readable mask. simonis@6465: st->print(", sa_mask[0]="); simonis@6465: os::Posix::print_signal_set_short(st, &sa.sa_mask); simonis@6465: simonis@6465: address rh = VMError::get_resetted_sighandler(sig); simonis@6465: // May be, handler was resetted by VMError? simonis@6465: if (rh != NULL) { simonis@6465: handler = rh; simonis@6465: sa.sa_flags = VMError::get_resetted_sigflags(sig); simonis@6465: } simonis@6465: simonis@6465: // Print textual representation of sa_flags. simonis@6465: st->print(", sa_flags="); simonis@6465: os::Posix::print_sa_flags(st, sa.sa_flags); simonis@6465: simonis@6465: // Check: is it our handler? simonis@6465: if (handler == CAST_FROM_FN_PTR(address, (sa_sigaction_t)javaSignalHandler) || simonis@6465: handler == CAST_FROM_FN_PTR(address, (sa_sigaction_t)SR_handler)) { simonis@6465: // It is our signal handler. simonis@6465: // Check for flags, reset system-used one! simonis@6465: if ((int)sa.sa_flags != os::Aix::get_our_sigflags(sig)) { simonis@6465: st->print(", flags was changed from " PTR32_FORMAT ", consider using jsig library", simonis@6465: os::Aix::get_our_sigflags(sig)); simonis@6465: } simonis@6465: } simonis@6465: st->cr(); simonis@6465: } simonis@6465: simonis@6465: simonis@6465: #define DO_SIGNAL_CHECK(sig) \ simonis@6465: if (!sigismember(&check_signal_done, sig)) \ simonis@6465: os::Aix::check_signal_handler(sig) simonis@6465: simonis@6465: // This method is a periodic task to check for misbehaving JNI applications simonis@6465: // under CheckJNI, we can add any periodic checks here simonis@6465: simonis@6465: void os::run_periodic_checks() { simonis@6465: simonis@6465: if (check_signals == false) return; simonis@6465: simonis@6465: // SEGV and BUS if overridden could potentially prevent simonis@6465: // generation of hs*.log in the event of a crash, debugging simonis@6465: // such a case can be very challenging, so we absolutely simonis@6465: // check the following for a good measure: simonis@6465: DO_SIGNAL_CHECK(SIGSEGV); simonis@6465: DO_SIGNAL_CHECK(SIGILL); simonis@6465: DO_SIGNAL_CHECK(SIGFPE); simonis@6465: DO_SIGNAL_CHECK(SIGBUS); simonis@6465: DO_SIGNAL_CHECK(SIGPIPE); simonis@6465: DO_SIGNAL_CHECK(SIGXFSZ); simonis@6465: if (UseSIGTRAP) { simonis@6465: DO_SIGNAL_CHECK(SIGTRAP); simonis@6465: } simonis@6465: DO_SIGNAL_CHECK(SIGDANGER); simonis@6465: simonis@6465: // ReduceSignalUsage allows the user to override these handlers simonis@6465: // see comments at the very top and jvm_solaris.h simonis@6465: if (!ReduceSignalUsage) { simonis@6465: DO_SIGNAL_CHECK(SHUTDOWN1_SIGNAL); simonis@6465: DO_SIGNAL_CHECK(SHUTDOWN2_SIGNAL); simonis@6465: DO_SIGNAL_CHECK(SHUTDOWN3_SIGNAL); simonis@6465: DO_SIGNAL_CHECK(BREAK_SIGNAL); simonis@6465: } simonis@6465: simonis@6465: DO_SIGNAL_CHECK(SR_signum); simonis@6465: DO_SIGNAL_CHECK(INTERRUPT_SIGNAL); simonis@6465: } simonis@6465: simonis@6465: typedef int (*os_sigaction_t)(int, const struct sigaction *, struct sigaction *); simonis@6465: simonis@6465: static os_sigaction_t os_sigaction = NULL; simonis@6465: simonis@6465: void os::Aix::check_signal_handler(int sig) { simonis@6465: char buf[O_BUFLEN]; simonis@6465: address jvmHandler = NULL; simonis@6465: simonis@6465: struct sigaction act; simonis@6465: if (os_sigaction == NULL) { simonis@6465: // only trust the default sigaction, in case it has been interposed simonis@6465: os_sigaction = (os_sigaction_t)dlsym(RTLD_DEFAULT, "sigaction"); simonis@6465: if (os_sigaction == NULL) return; simonis@6465: } simonis@6465: simonis@6465: os_sigaction(sig, (struct sigaction*)NULL, &act); simonis@6465: simonis@6465: address thisHandler = (act.sa_flags & SA_SIGINFO) simonis@6465: ? CAST_FROM_FN_PTR(address, act.sa_sigaction) simonis@6465: : CAST_FROM_FN_PTR(address, act.sa_handler); simonis@6465: simonis@6465: simonis@6465: switch(sig) { simonis@6465: case SIGSEGV: simonis@6465: case SIGBUS: simonis@6465: case SIGFPE: simonis@6465: case SIGPIPE: simonis@6465: case SIGILL: simonis@6465: case SIGXFSZ: simonis@6465: // Renamed 'signalHandler' to avoid collision with other shared libs. simonis@6465: jvmHandler = CAST_FROM_FN_PTR(address, (sa_sigaction_t)javaSignalHandler); simonis@6465: break; simonis@6465: simonis@6465: case SHUTDOWN1_SIGNAL: simonis@6465: case SHUTDOWN2_SIGNAL: simonis@6465: case SHUTDOWN3_SIGNAL: simonis@6465: case BREAK_SIGNAL: simonis@6465: jvmHandler = (address)user_handler(); simonis@6465: break; simonis@6465: simonis@6465: case INTERRUPT_SIGNAL: simonis@6465: jvmHandler = CAST_FROM_FN_PTR(address, SIG_DFL); simonis@6465: break; simonis@6465: simonis@6465: default: simonis@6465: if (sig == SR_signum) { simonis@6465: jvmHandler = CAST_FROM_FN_PTR(address, (sa_sigaction_t)SR_handler); simonis@6465: } else { simonis@6465: return; simonis@6465: } simonis@6465: break; simonis@6465: } simonis@6465: simonis@6465: if (thisHandler != jvmHandler) { simonis@6465: tty->print("Warning: %s handler ", exception_name(sig, buf, O_BUFLEN)); simonis@6465: tty->print("expected:%s", get_signal_handler_name(jvmHandler, buf, O_BUFLEN)); simonis@6465: tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN)); simonis@6465: // No need to check this sig any longer simonis@6465: sigaddset(&check_signal_done, sig); simonis@6465: } else if (os::Aix::get_our_sigflags(sig) != 0 && (int)act.sa_flags != os::Aix::get_our_sigflags(sig)) { simonis@6465: tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN)); simonis@6465: tty->print("expected:" PTR32_FORMAT, os::Aix::get_our_sigflags(sig)); simonis@6465: tty->print_cr(" found:" PTR32_FORMAT, act.sa_flags); simonis@6465: // No need to check this sig any longer simonis@6465: sigaddset(&check_signal_done, sig); simonis@6465: } simonis@6465: simonis@6465: // Dump all the signal simonis@6465: if (sigismember(&check_signal_done, sig)) { simonis@6465: print_signal_handlers(tty, buf, O_BUFLEN); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: extern bool signal_name(int signo, char* buf, size_t len); simonis@6465: simonis@6465: const char* os::exception_name(int exception_code, char* buf, size_t size) { simonis@6465: if (0 < exception_code && exception_code <= SIGRTMAX) { simonis@6465: // signal simonis@6465: if (!signal_name(exception_code, buf, size)) { simonis@6465: jio_snprintf(buf, size, "SIG%d", exception_code); simonis@6465: } simonis@6465: return buf; simonis@6465: } else { simonis@6465: return NULL; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // To install functions for atexit system call simonis@6465: extern "C" { simonis@6465: static void perfMemory_exit_helper() { simonis@6465: perfMemory_exit(); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // This is called _before_ the most of global arguments have been parsed. simonis@6465: void os::init(void) { simonis@6465: // This is basic, we want to know if that ever changes. simonis@6465: // (shared memory boundary is supposed to be a 256M aligned) simonis@6465: assert(SHMLBA == ((uint64_t)0x10000000ULL)/*256M*/, "unexpected"); simonis@6465: simonis@6465: // First off, we need to know whether we run on AIX or PASE, and simonis@6465: // the OS level we run on. simonis@6465: os::Aix::initialize_os_info(); simonis@6465: simonis@6465: // Scan environment (SPEC1170 behaviour, etc) simonis@6465: os::Aix::scan_environment(); simonis@6465: simonis@6465: // Check which pages are supported by AIX. simonis@6465: os::Aix::query_multipage_support(); simonis@6465: simonis@6465: // Next, we need to initialize libo4 and libperfstat libraries. simonis@6465: if (os::Aix::on_pase()) { simonis@6465: os::Aix::initialize_libo4(); simonis@6465: } else { simonis@6465: os::Aix::initialize_libperfstat(); simonis@6465: } simonis@6465: simonis@6465: // Reset the perfstat information provided by ODM. simonis@6465: if (os::Aix::on_aix()) { simonis@6465: libperfstat::perfstat_reset(); simonis@6465: } simonis@6465: simonis@6465: // Now initialze basic system properties. Note that for some of the values we simonis@6465: // need libperfstat etc. simonis@6465: os::Aix::initialize_system_info(); simonis@6465: simonis@6465: // Initialize large page support. simonis@6465: if (UseLargePages) { simonis@6465: os::large_page_init(); simonis@6465: if (!UseLargePages) { simonis@6465: // initialize os::_page_sizes simonis@6465: _page_sizes[0] = Aix::page_size(); simonis@6465: _page_sizes[1] = 0; simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "Large Page initialization failed: setting UseLargePages=0.\n"); simonis@6465: } simonis@6465: } simonis@6465: } else { simonis@6465: // initialize os::_page_sizes simonis@6465: _page_sizes[0] = Aix::page_size(); simonis@6465: _page_sizes[1] = 0; simonis@6465: } simonis@6465: simonis@6465: // debug trace simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "os::vm_page_size 0x%llX\n", os::vm_page_size()); simonis@6465: fprintf(stderr, "os::large_page_size 0x%llX\n", os::large_page_size()); simonis@6465: fprintf(stderr, "os::_page_sizes = ( "); simonis@6465: for (int i = 0; _page_sizes[i]; i ++) { simonis@6465: fprintf(stderr, " %s ", describe_pagesize(_page_sizes[i])); simonis@6465: } simonis@6465: fprintf(stderr, ")\n"); simonis@6465: } simonis@6465: simonis@6465: _initial_pid = getpid(); simonis@6465: simonis@6465: clock_tics_per_sec = sysconf(_SC_CLK_TCK); simonis@6465: simonis@6465: init_random(1234567); simonis@6465: simonis@6465: ThreadCritical::initialize(); simonis@6465: simonis@6465: // Main_thread points to the aboriginal thread. simonis@6465: Aix::_main_thread = pthread_self(); simonis@6465: simonis@6465: initial_time_count = os::elapsed_counter(); simonis@6465: pthread_mutex_init(&dl_mutex, NULL); simonis@6465: } simonis@6465: simonis@6465: // this is called _after_ the global arguments have been parsed simonis@6465: jint os::init_2(void) { simonis@6465: simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "processor count: %d\n", os::_processor_count); simonis@6465: fprintf(stderr, "physical memory: %lu\n", Aix::_physical_memory); simonis@6465: } simonis@6465: simonis@6465: // initially build up the loaded dll map simonis@6465: LoadedLibraries::reload(); simonis@6465: simonis@6465: const int page_size = Aix::page_size(); simonis@6465: const int map_size = page_size; simonis@6465: simonis@6465: address map_address = (address) MAP_FAILED; simonis@6465: const int prot = PROT_READ; simonis@6465: const int flags = MAP_PRIVATE|MAP_ANONYMOUS; simonis@6465: simonis@6465: // use optimized addresses for the polling page, simonis@6465: // e.g. map it to a special 32-bit address. simonis@6465: if (OptimizePollingPageLocation) { simonis@6465: // architecture-specific list of address wishes: simonis@6465: address address_wishes[] = { simonis@6465: // AIX: addresses lower than 0x30000000 don't seem to work on AIX. simonis@6465: // PPC64: all address wishes are non-negative 32 bit values where simonis@6465: // the lower 16 bits are all zero. we can load these addresses simonis@6465: // with a single ppc_lis instruction. simonis@6465: (address) 0x30000000, (address) 0x31000000, simonis@6465: (address) 0x32000000, (address) 0x33000000, simonis@6465: (address) 0x40000000, (address) 0x41000000, simonis@6465: (address) 0x42000000, (address) 0x43000000, simonis@6465: (address) 0x50000000, (address) 0x51000000, simonis@6465: (address) 0x52000000, (address) 0x53000000, simonis@6465: (address) 0x60000000, (address) 0x61000000, simonis@6465: (address) 0x62000000, (address) 0x63000000 simonis@6465: }; simonis@6465: int address_wishes_length = sizeof(address_wishes)/sizeof(address); simonis@6465: simonis@6465: // iterate over the list of address wishes: simonis@6465: for (int i=0; i %p\n", simonis@6465: address_wishes[i], map_address + (ssize_t)page_size); simonis@6465: } simonis@6465: simonis@6465: if (map_address + (ssize_t)page_size == address_wishes[i]) { simonis@6465: // map succeeded and map_address is at wished address, exit loop. simonis@6465: break; simonis@6465: } simonis@6465: simonis@6465: if (map_address != (address) MAP_FAILED) { simonis@6465: // map succeeded, but polling_page is not at wished address, unmap and continue. simonis@6465: ::munmap(map_address, map_size); simonis@6465: map_address = (address) MAP_FAILED; simonis@6465: } simonis@6465: // map failed, continue loop. simonis@6465: } simonis@6465: } // end OptimizePollingPageLocation simonis@6465: simonis@6465: if (map_address == (address) MAP_FAILED) { simonis@6465: map_address = (address) ::mmap(NULL, map_size, prot, flags, -1, 0); simonis@6465: } simonis@6465: guarantee(map_address != MAP_FAILED, "os::init_2: failed to allocate polling page"); simonis@6465: os::set_polling_page(map_address); simonis@6465: simonis@6465: if (!UseMembar) { simonis@6465: address mem_serialize_page = (address) ::mmap(NULL, Aix::page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); simonis@6465: guarantee(mem_serialize_page != NULL, "mmap Failed for memory serialize page"); simonis@6465: os::set_memory_serialize_page(mem_serialize_page); simonis@6465: simonis@6465: #ifndef PRODUCT simonis@6465: if (Verbose && PrintMiscellaneous) simonis@6465: tty->print("[Memory Serialize Page address: " INTPTR_FORMAT "]\n", (intptr_t)mem_serialize_page); simonis@6465: #endif simonis@6465: } simonis@6465: simonis@6465: // initialize suspend/resume support - must do this before signal_sets_init() simonis@6465: if (SR_initialize() != 0) { simonis@6465: perror("SR_initialize failed"); simonis@6465: return JNI_ERR; simonis@6465: } simonis@6465: simonis@6465: Aix::signal_sets_init(); simonis@6465: Aix::install_signal_handlers(); simonis@6465: simonis@6465: // Check minimum allowable stack size for thread creation and to initialize simonis@6465: // the java system classes, including StackOverflowError - depends on page simonis@6465: // size. Add a page for compiler2 recursion in main thread. simonis@6465: // Add in 2*BytesPerWord times page size to account for VM stack during simonis@6465: // class initialization depending on 32 or 64 bit VM. simonis@6465: os::Aix::min_stack_allowed = MAX2(os::Aix::min_stack_allowed, simonis@6465: (size_t)(StackYellowPages+StackRedPages+StackShadowPages + simonis@6465: 2*BytesPerWord COMPILER2_PRESENT(+1)) * Aix::page_size()); simonis@6465: simonis@6465: size_t threadStackSizeInBytes = ThreadStackSize * K; simonis@6465: if (threadStackSizeInBytes != 0 && simonis@6465: threadStackSizeInBytes < os::Aix::min_stack_allowed) { simonis@6465: tty->print_cr("\nThe stack size specified is too small, " simonis@6465: "Specify at least %dk", simonis@6465: os::Aix::min_stack_allowed / K); simonis@6465: return JNI_ERR; simonis@6465: } simonis@6465: simonis@6465: // Make the stack size a multiple of the page size so that simonis@6465: // the yellow/red zones can be guarded. simonis@6465: // note that this can be 0, if no default stacksize was set simonis@6465: JavaThread::set_stack_size_at_create(round_to(threadStackSizeInBytes, vm_page_size())); simonis@6465: simonis@6465: Aix::libpthread_init(); simonis@6465: simonis@6465: if (MaxFDLimit) { simonis@6465: // set the number of file descriptors to max. print out error simonis@6465: // if getrlimit/setrlimit fails but continue regardless. simonis@6465: struct rlimit nbr_files; simonis@6465: int status = getrlimit(RLIMIT_NOFILE, &nbr_files); simonis@6465: if (status != 0) { simonis@6465: if (PrintMiscellaneous && (Verbose || WizardMode)) simonis@6465: perror("os::init_2 getrlimit failed"); simonis@6465: } else { simonis@6465: nbr_files.rlim_cur = nbr_files.rlim_max; simonis@6465: status = setrlimit(RLIMIT_NOFILE, &nbr_files); simonis@6465: if (status != 0) { simonis@6465: if (PrintMiscellaneous && (Verbose || WizardMode)) simonis@6465: perror("os::init_2 setrlimit failed"); simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: if (PerfAllowAtExitRegistration) { simonis@6465: // only register atexit functions if PerfAllowAtExitRegistration is set. simonis@6465: // atexit functions can be delayed until process exit time, which simonis@6465: // can be problematic for embedded VM situations. Embedded VMs should simonis@6465: // call DestroyJavaVM() to assure that VM resources are released. simonis@6465: simonis@6465: // note: perfMemory_exit_helper atexit function may be removed in simonis@6465: // the future if the appropriate cleanup code can be added to the simonis@6465: // VM_Exit VMOperation's doit method. simonis@6465: if (atexit(perfMemory_exit_helper) != 0) { simonis@6465: warning("os::init_2 atexit(perfMemory_exit_helper) failed"); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: return JNI_OK; simonis@6465: } simonis@6465: simonis@6465: // this is called at the end of vm_initialization simonis@6465: void os::init_3(void) { simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: // Mark the polling page as unreadable simonis@6465: void os::make_polling_page_unreadable(void) { simonis@6465: if (!guard_memory((char*)_polling_page, Aix::page_size())) { simonis@6465: fatal("Could not disable polling page"); simonis@6465: } simonis@6465: }; simonis@6465: simonis@6465: // Mark the polling page as readable simonis@6465: void os::make_polling_page_readable(void) { simonis@6465: // Changed according to os_linux.cpp. simonis@6465: if (!checked_mprotect((char *)_polling_page, Aix::page_size(), PROT_READ)) { simonis@6465: fatal(err_msg("Could not enable polling page at " PTR_FORMAT, _polling_page)); simonis@6465: } simonis@6465: }; simonis@6465: simonis@6465: int os::active_processor_count() { simonis@6465: int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); simonis@6465: assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); simonis@6465: return online_cpus; simonis@6465: } simonis@6465: simonis@6465: void os::set_native_thread_name(const char *name) { simonis@6465: // Not yet implemented. simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: bool os::distribute_processes(uint length, uint* distribution) { simonis@6465: // Not yet implemented. simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: bool os::bind_to_processor(uint processor_id) { simonis@6465: // Not yet implemented. simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: void os::SuspendedThreadTask::internal_do_task() { simonis@6465: if (do_suspend(_thread->osthread())) { simonis@6465: SuspendedThreadTaskContext context(_thread, _thread->osthread()->ucontext()); simonis@6465: do_task(context); simonis@6465: do_resume(_thread->osthread()); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: class PcFetcher : public os::SuspendedThreadTask { simonis@6465: public: simonis@6465: PcFetcher(Thread* thread) : os::SuspendedThreadTask(thread) {} simonis@6465: ExtendedPC result(); simonis@6465: protected: simonis@6465: void do_task(const os::SuspendedThreadTaskContext& context); simonis@6465: private: simonis@6465: ExtendedPC _epc; simonis@6465: }; simonis@6465: simonis@6465: ExtendedPC PcFetcher::result() { simonis@6465: guarantee(is_done(), "task is not done yet."); simonis@6465: return _epc; simonis@6465: } simonis@6465: simonis@6465: void PcFetcher::do_task(const os::SuspendedThreadTaskContext& context) { simonis@6465: Thread* thread = context.thread(); simonis@6465: OSThread* osthread = thread->osthread(); simonis@6465: if (osthread->ucontext() != NULL) { simonis@6465: _epc = os::Aix::ucontext_get_pc((ucontext_t *) context.ucontext()); simonis@6465: } else { simonis@6465: // NULL context is unexpected, double-check this is the VMThread. simonis@6465: guarantee(thread->is_VM_thread(), "can only be called for VMThread"); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // Suspends the target using the signal mechanism and then grabs the PC before simonis@6465: // resuming the target. Used by the flat-profiler only simonis@6465: ExtendedPC os::get_thread_pc(Thread* thread) { simonis@6465: // Make sure that it is called by the watcher for the VMThread. simonis@6465: assert(Thread::current()->is_Watcher_thread(), "Must be watcher"); simonis@6465: assert(thread->is_VM_thread(), "Can only be called for VMThread"); simonis@6465: simonis@6465: PcFetcher fetcher(thread); simonis@6465: fetcher.run(); simonis@6465: return fetcher.result(); simonis@6465: } simonis@6465: simonis@6465: // Not neede on Aix. simonis@6465: // int os::Aix::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime) { simonis@6465: // } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // debug support simonis@6465: simonis@6465: static address same_page(address x, address y) { simonis@6465: intptr_t page_bits = -os::vm_page_size(); simonis@6465: if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) simonis@6465: return x; simonis@6465: else if (x > y) simonis@6465: return (address)(intptr_t(y) | ~page_bits) + 1; simonis@6465: else simonis@6465: return (address)(intptr_t(y) & page_bits); simonis@6465: } simonis@6465: simonis@6465: bool os::find(address addr, outputStream* st) { simonis@6465: Unimplemented(); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: //////////////////////////////////////////////////////////////////////////////// simonis@6465: // misc simonis@6465: simonis@6465: // This does not do anything on Aix. This is basically a hook for being simonis@6465: // able to use structured exception handling (thread-local exception filters) simonis@6465: // on, e.g., Win32. simonis@6465: void simonis@6465: os::os_exception_wrapper(java_call_t f, JavaValue* value, methodHandle* method, simonis@6465: JavaCallArguments* args, Thread* thread) { simonis@6465: f(value, method, args, thread); simonis@6465: } simonis@6465: simonis@6465: void os::print_statistics() { simonis@6465: } simonis@6465: simonis@6465: int os::message_box(const char* title, const char* message) { simonis@6465: int i; simonis@6465: fdStream err(defaultStream::error_fd()); simonis@6465: for (i = 0; i < 78; i++) err.print_raw("="); simonis@6465: err.cr(); simonis@6465: err.print_raw_cr(title); simonis@6465: for (i = 0; i < 78; i++) err.print_raw("-"); simonis@6465: err.cr(); simonis@6465: err.print_raw_cr(message); simonis@6465: for (i = 0; i < 78; i++) err.print_raw("="); simonis@6465: err.cr(); simonis@6465: simonis@6465: char buf[16]; simonis@6465: // Prevent process from exiting upon "read error" without consuming all CPU simonis@6465: while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); } simonis@6465: simonis@6465: return buf[0] == 'y' || buf[0] == 'Y'; simonis@6465: } simonis@6465: simonis@6465: int os::stat(const char *path, struct stat *sbuf) { simonis@6465: char pathbuf[MAX_PATH]; simonis@6465: if (strlen(path) > MAX_PATH - 1) { simonis@6465: errno = ENAMETOOLONG; simonis@6465: return -1; simonis@6465: } simonis@6465: os::native_path(strcpy(pathbuf, path)); simonis@6465: return ::stat(pathbuf, sbuf); simonis@6465: } simonis@6465: simonis@6465: bool os::check_heap(bool force) { simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // int local_vsnprintf(char* buf, size_t count, const char* format, va_list args) { simonis@6465: // return ::vsnprintf(buf, count, format, args); simonis@6465: // } simonis@6465: simonis@6465: // Is a (classpath) directory empty? simonis@6465: bool os::dir_is_empty(const char* path) { simonis@6465: Unimplemented(); simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: // This code originates from JDK's sysOpen and open64_w simonis@6465: // from src/solaris/hpi/src/system_md.c simonis@6465: simonis@6465: #ifndef O_DELETE simonis@6465: #define O_DELETE 0x10000 simonis@6465: #endif simonis@6465: simonis@6465: // Open a file. Unlink the file immediately after open returns simonis@6465: // if the specified oflag has the O_DELETE flag set. simonis@6465: // O_DELETE is used only in j2se/src/share/native/java/util/zip/ZipFile.c simonis@6465: simonis@6465: int os::open(const char *path, int oflag, int mode) { simonis@6465: simonis@6465: if (strlen(path) > MAX_PATH - 1) { simonis@6465: errno = ENAMETOOLONG; simonis@6465: return -1; simonis@6465: } simonis@6465: int fd; simonis@6465: int o_delete = (oflag & O_DELETE); simonis@6465: oflag = oflag & ~O_DELETE; simonis@6465: simonis@6465: fd = ::open64(path, oflag, mode); simonis@6465: if (fd == -1) return -1; simonis@6465: simonis@6465: //If the open succeeded, the file might still be a directory simonis@6465: { simonis@6465: struct stat64 buf64; simonis@6465: int ret = ::fstat64(fd, &buf64); simonis@6465: int st_mode = buf64.st_mode; simonis@6465: simonis@6465: if (ret != -1) { simonis@6465: if ((st_mode & S_IFMT) == S_IFDIR) { simonis@6465: errno = EISDIR; simonis@6465: ::close(fd); simonis@6465: return -1; simonis@6465: } simonis@6465: } else { simonis@6465: ::close(fd); simonis@6465: return -1; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // All file descriptors that are opened in the JVM and not simonis@6465: // specifically destined for a subprocess should have the simonis@6465: // close-on-exec flag set. If we don't set it, then careless 3rd simonis@6465: // party native code might fork and exec without closing all simonis@6465: // appropriate file descriptors (e.g. as we do in closeDescriptors in simonis@6465: // UNIXProcess.c), and this in turn might: simonis@6465: // simonis@6465: // - cause end-of-file to fail to be detected on some file simonis@6465: // descriptors, resulting in mysterious hangs, or simonis@6465: // simonis@6465: // - might cause an fopen in the subprocess to fail on a system simonis@6465: // suffering from bug 1085341. simonis@6465: // simonis@6465: // (Yes, the default setting of the close-on-exec flag is a Unix simonis@6465: // design flaw.) simonis@6465: // simonis@6465: // See: simonis@6465: // 1085341: 32-bit stdio routines should support file descriptors >255 simonis@6465: // 4843136: (process) pipe file descriptor from Runtime.exec not being closed simonis@6465: // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9 simonis@6465: #ifdef FD_CLOEXEC simonis@6465: { simonis@6465: int flags = ::fcntl(fd, F_GETFD); simonis@6465: if (flags != -1) simonis@6465: ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC); simonis@6465: } simonis@6465: #endif simonis@6465: simonis@6465: if (o_delete != 0) { simonis@6465: ::unlink(path); simonis@6465: } simonis@6465: return fd; simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // create binary file, rewriting existing file if required simonis@6465: int os::create_binary_file(const char* path, bool rewrite_existing) { simonis@6465: Unimplemented(); simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: // return current position of file pointer simonis@6465: jlong os::current_file_offset(int fd) { simonis@6465: return (jlong)::lseek64(fd, (off64_t)0, SEEK_CUR); simonis@6465: } simonis@6465: simonis@6465: // move file pointer to the specified offset simonis@6465: jlong os::seek_to_file_offset(int fd, jlong offset) { simonis@6465: return (jlong)::lseek64(fd, (off64_t)offset, SEEK_SET); simonis@6465: } simonis@6465: simonis@6465: // This code originates from JDK's sysAvailable simonis@6465: // from src/solaris/hpi/src/native_threads/src/sys_api_td.c simonis@6465: simonis@6465: int os::available(int fd, jlong *bytes) { simonis@6465: jlong cur, end; simonis@6465: int mode; simonis@6465: struct stat64 buf64; simonis@6465: simonis@6465: if (::fstat64(fd, &buf64) >= 0) { simonis@6465: mode = buf64.st_mode; simonis@6465: if (S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { simonis@6465: // XXX: is the following call interruptible? If so, this might simonis@6465: // need to go through the INTERRUPT_IO() wrapper as for other simonis@6465: // blocking, interruptible calls in this file. simonis@6465: int n; simonis@6465: if (::ioctl(fd, FIONREAD, &n) >= 0) { simonis@6465: *bytes = n; simonis@6465: return 1; simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: if ((cur = ::lseek64(fd, 0L, SEEK_CUR)) == -1) { simonis@6465: return 0; simonis@6465: } else if ((end = ::lseek64(fd, 0L, SEEK_END)) == -1) { simonis@6465: return 0; simonis@6465: } else if (::lseek64(fd, cur, SEEK_SET) == -1) { simonis@6465: return 0; simonis@6465: } simonis@6465: *bytes = end - cur; simonis@6465: return 1; simonis@6465: } simonis@6465: simonis@6465: int os::socket_available(int fd, jint *pbytes) { simonis@6465: // Linux doc says EINTR not returned, unlike Solaris simonis@6465: int ret = ::ioctl(fd, FIONREAD, pbytes); simonis@6465: simonis@6465: //%% note ioctl can return 0 when successful, JVM_SocketAvailable simonis@6465: // is expected to return 0 on failure and 1 on success to the jdk. simonis@6465: return (ret < 0) ? 0 : 1; simonis@6465: } simonis@6465: simonis@6465: // Map a block of memory. simonis@6465: char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, simonis@6465: char *addr, size_t bytes, bool read_only, simonis@6465: bool allow_exec) { simonis@6465: Unimplemented(); simonis@6465: return NULL; simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // Remap a block of memory. simonis@6465: char* os::pd_remap_memory(int fd, const char* file_name, size_t file_offset, simonis@6465: char *addr, size_t bytes, bool read_only, simonis@6465: bool allow_exec) { simonis@6465: // same as map_memory() on this OS simonis@6465: return os::map_memory(fd, file_name, file_offset, addr, bytes, read_only, simonis@6465: allow_exec); simonis@6465: } simonis@6465: simonis@6465: // Unmap a block of memory. simonis@6465: bool os::pd_unmap_memory(char* addr, size_t bytes) { simonis@6465: return munmap(addr, bytes) == 0; simonis@6465: } simonis@6465: simonis@6465: // current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) simonis@6465: // are used by JVM M&M and JVMTI to get user+sys or user CPU time simonis@6465: // of a thread. simonis@6465: // simonis@6465: // current_thread_cpu_time() and thread_cpu_time(Thread*) returns simonis@6465: // the fast estimate available on the platform. simonis@6465: simonis@6465: jlong os::current_thread_cpu_time() { simonis@6465: // return user + sys since the cost is the same simonis@6465: const jlong n = os::thread_cpu_time(Thread::current(), true /* user + sys */); simonis@6465: assert(n >= 0, "negative CPU time"); simonis@6465: return n; simonis@6465: } simonis@6465: simonis@6465: jlong os::thread_cpu_time(Thread* thread) { simonis@6465: // consistent with what current_thread_cpu_time() returns simonis@6465: const jlong n = os::thread_cpu_time(thread, true /* user + sys */); simonis@6465: assert(n >= 0, "negative CPU time"); simonis@6465: return n; simonis@6465: } simonis@6465: simonis@6465: jlong os::current_thread_cpu_time(bool user_sys_cpu_time) { simonis@6465: const jlong n = os::thread_cpu_time(Thread::current(), user_sys_cpu_time); simonis@6465: assert(n >= 0, "negative CPU time"); simonis@6465: return n; simonis@6465: } simonis@6465: simonis@6465: static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong* p_user_time) { simonis@6465: bool error = false; simonis@6465: simonis@6465: jlong sys_time = 0; simonis@6465: jlong user_time = 0; simonis@6465: simonis@6465: // reimplemented using getthrds64(). simonis@6465: // simonis@6465: // goes like this: simonis@6465: // For the thread in question, get the kernel thread id. Then get the simonis@6465: // kernel thread statistics using that id. simonis@6465: // simonis@6465: // This only works of course when no pthread scheduling is used, simonis@6465: // ie there is a 1:1 relationship to kernel threads. simonis@6465: // On AIX, see AIXTHREAD_SCOPE variable. simonis@6465: simonis@6465: pthread_t pthtid = thread->osthread()->pthread_id(); simonis@6465: simonis@6465: // retrieve kernel thread id for the pthread: simonis@6465: tid64_t tid = 0; simonis@6465: struct __pthrdsinfo pinfo; simonis@6465: // I just love those otherworldly IBM APIs which force me to hand down simonis@6465: // dummy buffers for stuff I dont care for... simonis@6465: char dummy[1]; simonis@6465: int dummy_size = sizeof(dummy); simonis@6465: if (pthread_getthrds_np(&pthtid, PTHRDSINFO_QUERY_TID, &pinfo, sizeof(pinfo), simonis@6465: dummy, &dummy_size) == 0) { simonis@6465: tid = pinfo.__pi_tid; simonis@6465: } else { simonis@6465: tty->print_cr("pthread_getthrds_np failed."); simonis@6465: error = true; simonis@6465: } simonis@6465: simonis@6465: // retrieve kernel timing info for that kernel thread simonis@6465: if (!error) { simonis@6465: struct thrdentry64 thrdentry; simonis@6465: if (getthrds64(getpid(), &thrdentry, sizeof(thrdentry), &tid, 1) == 1) { simonis@6465: sys_time = thrdentry.ti_ru.ru_stime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_stime.tv_usec * 1000LL; simonis@6465: user_time = thrdentry.ti_ru.ru_utime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_utime.tv_usec * 1000LL; simonis@6465: } else { simonis@6465: tty->print_cr("pthread_getthrds_np failed."); simonis@6465: error = true; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: if (p_sys_time) { simonis@6465: *p_sys_time = sys_time; simonis@6465: } simonis@6465: simonis@6465: if (p_user_time) { simonis@6465: *p_user_time = user_time; simonis@6465: } simonis@6465: simonis@6465: if (error) { simonis@6465: return false; simonis@6465: } simonis@6465: simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { simonis@6465: jlong sys_time; simonis@6465: jlong user_time; simonis@6465: simonis@6465: if (!thread_cpu_time_unchecked(thread, &sys_time, &user_time)) { simonis@6465: return -1; simonis@6465: } simonis@6465: simonis@6465: return user_sys_cpu_time ? sys_time + user_time : user_time; simonis@6465: } simonis@6465: simonis@6465: void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { simonis@6465: info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits simonis@6465: info_ptr->may_skip_backward = false; // elapsed time not wall time simonis@6465: info_ptr->may_skip_forward = false; // elapsed time not wall time simonis@6465: info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned simonis@6465: } simonis@6465: simonis@6465: void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { simonis@6465: info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits simonis@6465: info_ptr->may_skip_backward = false; // elapsed time not wall time simonis@6465: info_ptr->may_skip_forward = false; // elapsed time not wall time simonis@6465: info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned simonis@6465: } simonis@6465: simonis@6465: bool os::is_thread_cpu_time_supported() { simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // System loadavg support. Returns -1 if load average cannot be obtained. simonis@6465: // For now just return the system wide load average (no processor sets). simonis@6465: int os::loadavg(double values[], int nelem) { simonis@6465: simonis@6465: // Implemented using libperfstat on AIX. simonis@6465: simonis@6465: guarantee(nelem >= 0 && nelem <= 3, "argument error"); simonis@6465: guarantee(values, "argument error"); simonis@6465: simonis@6465: if (os::Aix::on_pase()) { simonis@6465: Unimplemented(); simonis@6465: return -1; simonis@6465: } else { simonis@6465: // AIX: use libperfstat simonis@6465: // simonis@6465: // See also: simonis@6465: // http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/perfstat_cputot.htm simonis@6465: // /usr/include/libperfstat.h: simonis@6465: simonis@6465: // Use the already AIX version independent get_cpuinfo. simonis@6465: os::Aix::cpuinfo_t ci; simonis@6465: if (os::Aix::get_cpuinfo(&ci)) { simonis@6465: for (int i = 0; i < nelem; i++) { simonis@6465: values[i] = ci.loadavg[i]; simonis@6465: } simonis@6465: } else { simonis@6465: return -1; simonis@6465: } simonis@6465: return nelem; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: void os::pause() { simonis@6465: char filename[MAX_PATH]; simonis@6465: if (PauseAtStartupFile && PauseAtStartupFile[0]) { simonis@6465: jio_snprintf(filename, MAX_PATH, PauseAtStartupFile); simonis@6465: } else { simonis@6465: jio_snprintf(filename, MAX_PATH, "./vm.paused.%d", current_process_id()); simonis@6465: } simonis@6465: simonis@6465: int fd = ::open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); simonis@6465: if (fd != -1) { simonis@6465: struct stat buf; simonis@6465: ::close(fd); simonis@6465: while (::stat(filename, &buf) == 0) { simonis@6465: (void)::poll(NULL, 0, 100); simonis@6465: } simonis@6465: } else { simonis@6465: jio_fprintf(stderr, simonis@6465: "Could not open pause file '%s', continuing immediately.\n", filename); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: bool os::Aix::is_primordial_thread() { simonis@6465: if (pthread_self() == (pthread_t)1) { simonis@6465: return true; simonis@6465: } else { simonis@6465: return false; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: // OS recognitions (PASE/AIX, OS level) call this before calling any simonis@6465: // one of Aix::on_pase(), Aix::os_version() static simonis@6465: void os::Aix::initialize_os_info() { simonis@6465: simonis@6465: assert(_on_pase == -1 && _os_version == -1, "already called."); simonis@6465: simonis@6465: struct utsname uts; simonis@6465: memset(&uts, 0, sizeof(uts)); simonis@6465: strcpy(uts.sysname, "?"); simonis@6465: if (::uname(&uts) == -1) { simonis@6465: fprintf(stderr, "uname failed (%d)\n", errno); simonis@6465: guarantee(0, "Could not determine whether we run on AIX or PASE"); simonis@6465: } else { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr,"uname says: sysname \"%s\" version \"%s\" release \"%s\" " simonis@6465: "node \"%s\" machine \"%s\"\n", simonis@6465: uts.sysname, uts.version, uts.release, uts.nodename, uts.machine); simonis@6465: } simonis@6465: const int major = atoi(uts.version); simonis@6465: assert(major > 0, "invalid OS version"); simonis@6465: const int minor = atoi(uts.release); simonis@6465: assert(minor > 0, "invalid OS release"); simonis@6465: _os_version = (major << 8) | minor; simonis@6465: if (strcmp(uts.sysname, "OS400") == 0) { simonis@6465: Unimplemented(); simonis@6465: } else if (strcmp(uts.sysname, "AIX") == 0) { simonis@6465: // We run on AIX. We do not support versions older than AIX 5.3. simonis@6465: _on_pase = 0; simonis@6465: if (_os_version < 0x0503) { simonis@6465: fprintf(stderr, "AIX release older than AIX 5.3 not supported.\n"); simonis@6465: assert(false, "AIX release too old."); simonis@6465: } else { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "We run on AIX %d.%d\n", major, minor); simonis@6465: } simonis@6465: } simonis@6465: } else { simonis@6465: assert(false, "unknown OS"); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: guarantee(_on_pase != -1 && _os_version, "Could not determine AIX/OS400 release"); simonis@6465: simonis@6465: } // end: os::Aix::initialize_os_info() simonis@6465: simonis@6465: // Scan environment for important settings which might effect the VM. simonis@6465: // Trace out settings. Warn about invalid settings and/or correct them. simonis@6465: // simonis@6465: // Must run after os::Aix::initialue_os_info(). simonis@6465: void os::Aix::scan_environment() { simonis@6465: simonis@6465: char* p; simonis@6465: int rc; simonis@6465: simonis@6465: // Warn explicity if EXTSHM=ON is used. That switch changes how simonis@6465: // System V shared memory behaves. One effect is that page size of simonis@6465: // shared memory cannot be change dynamically, effectivly preventing simonis@6465: // large pages from working. simonis@6465: // This switch was needed on AIX 32bit, but on AIX 64bit the general simonis@6465: // recommendation is (in OSS notes) to switch it off. simonis@6465: p = ::getenv("EXTSHM"); simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "EXTSHM=%s.\n", p ? p : ""); simonis@6465: } simonis@6465: if (p && strcmp(p, "ON") == 0) { simonis@6465: fprintf(stderr, "Unsupported setting: EXTSHM=ON. Large Page support will be disabled.\n"); simonis@6465: _extshm = 1; simonis@6465: } else { simonis@6465: _extshm = 0; simonis@6465: } simonis@6465: simonis@6465: // SPEC1170 behaviour: will change the behaviour of a number of POSIX APIs. simonis@6465: // Not tested, not supported. simonis@6465: // simonis@6465: // Note that it might be worth the trouble to test and to require it, if only to simonis@6465: // get useful return codes for mprotect. simonis@6465: // simonis@6465: // Note: Setting XPG_SUS_ENV in the process is too late. Must be set earlier (before simonis@6465: // exec() ? before loading the libjvm ? ....) simonis@6465: p = ::getenv("XPG_SUS_ENV"); simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "XPG_SUS_ENV=%s.\n", p ? p : ""); simonis@6465: } simonis@6465: if (p && strcmp(p, "ON") == 0) { simonis@6465: _xpg_sus_mode = 1; simonis@6465: fprintf(stderr, "Unsupported setting: XPG_SUS_ENV=ON\n"); simonis@6465: // This is not supported. Worst of all, it changes behaviour of mmap MAP_FIXED to simonis@6465: // clobber address ranges. If we ever want to support that, we have to do some simonis@6465: // testing first. simonis@6465: guarantee(false, "XPG_SUS_ENV=ON not supported"); simonis@6465: } else { simonis@6465: _xpg_sus_mode = 0; simonis@6465: } simonis@6465: simonis@6465: // Switch off AIX internal (pthread) guard pages. This has simonis@6465: // immediate effect for any pthread_create calls which follow. simonis@6465: p = ::getenv("AIXTHREAD_GUARDPAGES"); simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "AIXTHREAD_GUARDPAGES=%s.\n", p ? p : ""); simonis@6465: fprintf(stderr, "setting AIXTHREAD_GUARDPAGES=0.\n"); simonis@6465: } simonis@6465: rc = ::putenv("AIXTHREAD_GUARDPAGES=0"); simonis@6465: guarantee(rc == 0, ""); simonis@6465: simonis@6465: } // end: os::Aix::scan_environment() simonis@6465: simonis@6465: // PASE: initialize the libo4 library (AS400 PASE porting library). simonis@6465: void os::Aix::initialize_libo4() { simonis@6465: Unimplemented(); simonis@6465: } simonis@6465: simonis@6465: // AIX: initialize the libperfstat library (we load this dynamically simonis@6465: // because it is only available on AIX. simonis@6465: void os::Aix::initialize_libperfstat() { simonis@6465: simonis@6465: assert(os::Aix::on_aix(), "AIX only"); simonis@6465: simonis@6465: if (!libperfstat::init()) { simonis@6465: fprintf(stderr, "libperfstat initialization failed.\n"); simonis@6465: assert(false, "libperfstat initialization failed"); simonis@6465: } else { simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, "libperfstat initialized.\n"); simonis@6465: } simonis@6465: } simonis@6465: } // end: os::Aix::initialize_libperfstat simonis@6465: simonis@6465: ///////////////////////////////////////////////////////////////////////////// simonis@6465: // thread stack simonis@6465: simonis@6465: // function to query the current stack size using pthread_getthrds_np simonis@6465: // simonis@6465: // ! do not change anything here unless you know what you are doing ! simonis@6465: static void query_stack_dimensions(address* p_stack_base, size_t* p_stack_size) { simonis@6465: simonis@6465: // This only works when invoked on a pthread. As we agreed not to use simonis@6465: // primordial threads anyway, I assert here simonis@6465: guarantee(!os::Aix::is_primordial_thread(), "not allowed on the primordial thread"); simonis@6465: simonis@6465: // information about this api can be found (a) in the pthread.h header and simonis@6465: // (b) in http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/pthread_getthrds_np.htm simonis@6465: // simonis@6465: // The use of this API to find out the current stack is kind of undefined. simonis@6465: // But after a lot of tries and asking IBM about it, I concluded that it is safe simonis@6465: // enough for cases where I let the pthread library create its stacks. For cases simonis@6465: // where I create an own stack and pass this to pthread_create, it seems not to simonis@6465: // work (the returned stack size in that case is 0). simonis@6465: simonis@6465: pthread_t tid = pthread_self(); simonis@6465: struct __pthrdsinfo pinfo; simonis@6465: char dummy[1]; // we only need this to satisfy the api and to not get E simonis@6465: int dummy_size = sizeof(dummy); simonis@6465: simonis@6465: memset(&pinfo, 0, sizeof(pinfo)); simonis@6465: simonis@6465: const int rc = pthread_getthrds_np (&tid, PTHRDSINFO_QUERY_ALL, &pinfo, simonis@6465: sizeof(pinfo), dummy, &dummy_size); simonis@6465: simonis@6465: if (rc != 0) { simonis@6465: fprintf(stderr, "pthread_getthrds_np failed (%d)\n", rc); simonis@6465: guarantee(0, "pthread_getthrds_np failed"); simonis@6465: } simonis@6465: simonis@6465: guarantee(pinfo.__pi_stackend, "returned stack base invalid"); simonis@6465: simonis@6465: // the following can happen when invoking pthread_getthrds_np on a pthread running on a user provided stack simonis@6465: // (when handing down a stack to pthread create, see pthread_attr_setstackaddr). simonis@6465: // Not sure what to do here - I feel inclined to forbid this use case completely. simonis@6465: guarantee(pinfo.__pi_stacksize, "returned stack size invalid"); simonis@6465: simonis@6465: // On AIX, stacks are not necessarily page aligned so round the base and size accordingly simonis@6465: if (p_stack_base) { simonis@6465: (*p_stack_base) = (address) align_size_up((intptr_t)pinfo.__pi_stackend, os::Aix::stack_page_size()); simonis@6465: } simonis@6465: simonis@6465: if (p_stack_size) { simonis@6465: (*p_stack_size) = pinfo.__pi_stacksize - os::Aix::stack_page_size(); simonis@6465: } simonis@6465: simonis@6465: #ifndef PRODUCT simonis@6465: if (Verbose) { simonis@6465: fprintf(stderr, simonis@6465: "query_stack_dimensions() -> real stack_base=" INTPTR_FORMAT ", real stack_addr=" INTPTR_FORMAT simonis@6465: ", real stack_size=" INTPTR_FORMAT simonis@6465: ", stack_base=" INTPTR_FORMAT ", stack_size=" INTPTR_FORMAT "\n", simonis@6465: (intptr_t)pinfo.__pi_stackend, (intptr_t)pinfo.__pi_stackaddr, pinfo.__pi_stacksize, simonis@6465: (intptr_t)align_size_up((intptr_t)pinfo.__pi_stackend, os::Aix::stack_page_size()), simonis@6465: pinfo.__pi_stacksize - os::Aix::stack_page_size()); simonis@6465: } simonis@6465: #endif simonis@6465: simonis@6465: } // end query_stack_dimensions simonis@6465: simonis@6465: // get the current stack base from the OS (actually, the pthread library) simonis@6465: address os::current_stack_base() { simonis@6465: address p; simonis@6465: query_stack_dimensions(&p, 0); simonis@6465: return p; simonis@6465: } simonis@6465: simonis@6465: // get the current stack size from the OS (actually, the pthread library) simonis@6465: size_t os::current_stack_size() { simonis@6465: size_t s; simonis@6465: query_stack_dimensions(0, &s); simonis@6465: return s; simonis@6465: } simonis@6465: simonis@6465: // Refer to the comments in os_solaris.cpp park-unpark. simonis@6465: // simonis@6465: // Beware -- Some versions of NPTL embody a flaw where pthread_cond_timedwait() can simonis@6465: // hang indefinitely. For instance NPTL 0.60 on 2.4.21-4ELsmp is vulnerable. simonis@6465: // For specifics regarding the bug see GLIBC BUGID 261237 : simonis@6465: // http://www.mail-archive.com/debian-glibc@lists.debian.org/msg10837.html. simonis@6465: // Briefly, pthread_cond_timedwait() calls with an expiry time that's not in the future simonis@6465: // will either hang or corrupt the condvar, resulting in subsequent hangs if the condvar simonis@6465: // is used. (The simple C test-case provided in the GLIBC bug report manifests the simonis@6465: // hang). The JVM is vulernable via sleep(), Object.wait(timo), LockSupport.parkNanos() simonis@6465: // and monitorenter when we're using 1-0 locking. All those operations may result in simonis@6465: // calls to pthread_cond_timedwait(). Using LD_ASSUME_KERNEL to use an older version simonis@6465: // of libpthread avoids the problem, but isn't practical. simonis@6465: // simonis@6465: // Possible remedies: simonis@6465: // simonis@6465: // 1. Establish a minimum relative wait time. 50 to 100 msecs seems to work. simonis@6465: // This is palliative and probabilistic, however. If the thread is preempted simonis@6465: // between the call to compute_abstime() and pthread_cond_timedwait(), more simonis@6465: // than the minimum period may have passed, and the abstime may be stale (in the simonis@6465: // past) resultin in a hang. Using this technique reduces the odds of a hang simonis@6465: // but the JVM is still vulnerable, particularly on heavily loaded systems. simonis@6465: // simonis@6465: // 2. Modify park-unpark to use per-thread (per ParkEvent) pipe-pairs instead simonis@6465: // of the usual flag-condvar-mutex idiom. The write side of the pipe is set simonis@6465: // NDELAY. unpark() reduces to write(), park() reduces to read() and park(timo) simonis@6465: // reduces to poll()+read(). This works well, but consumes 2 FDs per extant simonis@6465: // thread. simonis@6465: // simonis@6465: // 3. Embargo pthread_cond_timedwait() and implement a native "chron" thread simonis@6465: // that manages timeouts. We'd emulate pthread_cond_timedwait() by enqueuing simonis@6465: // a timeout request to the chron thread and then blocking via pthread_cond_wait(). simonis@6465: // This also works well. In fact it avoids kernel-level scalability impediments simonis@6465: // on certain platforms that don't handle lots of active pthread_cond_timedwait() simonis@6465: // timers in a graceful fashion. simonis@6465: // simonis@6465: // 4. When the abstime value is in the past it appears that control returns simonis@6465: // correctly from pthread_cond_timedwait(), but the condvar is left corrupt. simonis@6465: // Subsequent timedwait/wait calls may hang indefinitely. Given that, we simonis@6465: // can avoid the problem by reinitializing the condvar -- by cond_destroy() simonis@6465: // followed by cond_init() -- after all calls to pthread_cond_timedwait(). simonis@6465: // It may be possible to avoid reinitialization by checking the return simonis@6465: // value from pthread_cond_timedwait(). In addition to reinitializing the simonis@6465: // condvar we must establish the invariant that cond_signal() is only called simonis@6465: // within critical sections protected by the adjunct mutex. This prevents simonis@6465: // cond_signal() from "seeing" a condvar that's in the midst of being simonis@6465: // reinitialized or that is corrupt. Sadly, this invariant obviates the simonis@6465: // desirable signal-after-unlock optimization that avoids futile context switching. simonis@6465: // simonis@6465: // I'm also concerned that some versions of NTPL might allocate an auxilliary simonis@6465: // structure when a condvar is used or initialized. cond_destroy() would simonis@6465: // release the helper structure. Our reinitialize-after-timedwait fix simonis@6465: // put excessive stress on malloc/free and locks protecting the c-heap. simonis@6465: // simonis@6465: // We currently use (4). See the WorkAroundNTPLTimedWaitHang flag. simonis@6465: // It may be possible to refine (4) by checking the kernel and NTPL verisons simonis@6465: // and only enabling the work-around for vulnerable environments. simonis@6465: simonis@6465: // utility to compute the abstime argument to timedwait: simonis@6465: // millis is the relative timeout time simonis@6465: // abstime will be the absolute timeout time simonis@6465: // TODO: replace compute_abstime() with unpackTime() simonis@6465: simonis@6465: static struct timespec* compute_abstime(timespec* abstime, jlong millis) { simonis@6465: if (millis < 0) millis = 0; simonis@6465: struct timeval now; simonis@6465: int status = gettimeofday(&now, NULL); simonis@6465: assert(status == 0, "gettimeofday"); simonis@6465: jlong seconds = millis / 1000; simonis@6465: millis %= 1000; simonis@6465: if (seconds > 50000000) { // see man cond_timedwait(3T) simonis@6465: seconds = 50000000; simonis@6465: } simonis@6465: abstime->tv_sec = now.tv_sec + seconds; simonis@6465: long usec = now.tv_usec + millis * 1000; simonis@6465: if (usec >= 1000000) { simonis@6465: abstime->tv_sec += 1; simonis@6465: usec -= 1000000; simonis@6465: } simonis@6465: abstime->tv_nsec = usec * 1000; simonis@6465: return abstime; simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // Test-and-clear _Event, always leaves _Event set to 0, returns immediately. simonis@6465: // Conceptually TryPark() should be equivalent to park(0). simonis@6465: simonis@6465: int os::PlatformEvent::TryPark() { simonis@6465: for (;;) { simonis@6465: const int v = _Event; simonis@6465: guarantee ((v == 0) || (v == 1), "invariant"); simonis@6465: if (Atomic::cmpxchg (0, &_Event, v) == v) return v; simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: void os::PlatformEvent::park() { // AKA "down()" simonis@6465: // Invariant: Only the thread associated with the Event/PlatformEvent simonis@6465: // may call park(). simonis@6465: // TODO: assert that _Assoc != NULL or _Assoc == Self simonis@6465: int v; simonis@6465: for (;;) { simonis@6465: v = _Event; simonis@6465: if (Atomic::cmpxchg (v-1, &_Event, v) == v) break; simonis@6465: } simonis@6465: guarantee (v >= 0, "invariant"); simonis@6465: if (v == 0) { simonis@6465: // Do this the hard way by blocking ... simonis@6465: int status = pthread_mutex_lock(_mutex); simonis@6465: assert_status(status == 0, status, "mutex_lock"); simonis@6465: guarantee (_nParked == 0, "invariant"); simonis@6465: ++ _nParked; simonis@6465: while (_Event < 0) { simonis@6465: status = pthread_cond_wait(_cond, _mutex); simonis@6465: assert_status(status == 0 || status == ETIMEDOUT, status, "cond_timedwait"); simonis@6465: } simonis@6465: -- _nParked; simonis@6465: simonis@6465: // In theory we could move the ST of 0 into _Event past the unlock(), simonis@6465: // but then we'd need a MEMBAR after the ST. simonis@6465: _Event = 0; simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert_status(status == 0, status, "mutex_unlock"); simonis@6465: } simonis@6465: guarantee (_Event >= 0, "invariant"); simonis@6465: } simonis@6465: simonis@6465: int os::PlatformEvent::park(jlong millis) { simonis@6465: guarantee (_nParked == 0, "invariant"); simonis@6465: simonis@6465: int v; simonis@6465: for (;;) { simonis@6465: v = _Event; simonis@6465: if (Atomic::cmpxchg (v-1, &_Event, v) == v) break; simonis@6465: } simonis@6465: guarantee (v >= 0, "invariant"); simonis@6465: if (v != 0) return OS_OK; simonis@6465: simonis@6465: // We do this the hard way, by blocking the thread. simonis@6465: // Consider enforcing a minimum timeout value. simonis@6465: struct timespec abst; simonis@6465: compute_abstime(&abst, millis); simonis@6465: simonis@6465: int ret = OS_TIMEOUT; simonis@6465: int status = pthread_mutex_lock(_mutex); simonis@6465: assert_status(status == 0, status, "mutex_lock"); simonis@6465: guarantee (_nParked == 0, "invariant"); simonis@6465: ++_nParked; simonis@6465: simonis@6465: // Object.wait(timo) will return because of simonis@6465: // (a) notification simonis@6465: // (b) timeout simonis@6465: // (c) thread.interrupt simonis@6465: // simonis@6465: // Thread.interrupt and object.notify{All} both call Event::set. simonis@6465: // That is, we treat thread.interrupt as a special case of notification. simonis@6465: // The underlying Solaris implementation, cond_timedwait, admits simonis@6465: // spurious/premature wakeups, but the JLS/JVM spec prevents the simonis@6465: // JVM from making those visible to Java code. As such, we must simonis@6465: // filter out spurious wakeups. We assume all ETIME returns are valid. simonis@6465: // simonis@6465: // TODO: properly differentiate simultaneous notify+interrupt. simonis@6465: // In that case, we should propagate the notify to another waiter. simonis@6465: simonis@6465: while (_Event < 0) { simonis@6465: status = pthread_cond_timedwait(_cond, _mutex, &abst); simonis@6465: assert_status(status == 0 || status == ETIMEDOUT, simonis@6465: status, "cond_timedwait"); simonis@6465: if (!FilterSpuriousWakeups) break; // previous semantics simonis@6465: if (status == ETIMEDOUT) break; simonis@6465: // We consume and ignore EINTR and spurious wakeups. simonis@6465: } simonis@6465: --_nParked; simonis@6465: if (_Event >= 0) { simonis@6465: ret = OS_OK; simonis@6465: } simonis@6465: _Event = 0; simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert_status(status == 0, status, "mutex_unlock"); simonis@6465: assert (_nParked == 0, "invariant"); simonis@6465: return ret; simonis@6465: } simonis@6465: simonis@6465: void os::PlatformEvent::unpark() { simonis@6465: int v, AnyWaiters; simonis@6465: for (;;) { simonis@6465: v = _Event; simonis@6465: if (v > 0) { simonis@6465: // The LD of _Event could have reordered or be satisfied simonis@6465: // by a read-aside from this processor's write buffer. simonis@6465: // To avoid problems execute a barrier and then simonis@6465: // ratify the value. simonis@6465: OrderAccess::fence(); simonis@6465: if (_Event == v) return; simonis@6465: continue; simonis@6465: } simonis@6465: if (Atomic::cmpxchg (v+1, &_Event, v) == v) break; simonis@6465: } simonis@6465: if (v < 0) { simonis@6465: // Wait for the thread associated with the event to vacate simonis@6465: int status = pthread_mutex_lock(_mutex); simonis@6465: assert_status(status == 0, status, "mutex_lock"); simonis@6465: AnyWaiters = _nParked; simonis@6465: simonis@6465: if (AnyWaiters != 0) { simonis@6465: // We intentional signal *after* dropping the lock simonis@6465: // to avoid a common class of futile wakeups. simonis@6465: status = pthread_cond_signal(_cond); simonis@6465: assert_status(status == 0, status, "cond_signal"); simonis@6465: } simonis@6465: // Mutex should be locked for pthread_cond_signal(_cond). simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert_status(status == 0, status, "mutex_unlock"); simonis@6465: } simonis@6465: simonis@6465: // Note that we signal() _after dropping the lock for "immortal" Events. simonis@6465: // This is safe and avoids a common class of futile wakeups. In rare simonis@6465: // circumstances this can cause a thread to return prematurely from simonis@6465: // cond_{timed}wait() but the spurious wakeup is benign and the victim will simonis@6465: // simply re-test the condition and re-park itself. simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // JSR166 simonis@6465: // ------------------------------------------------------- simonis@6465: simonis@6465: // simonis@6465: // The solaris and linux implementations of park/unpark are fairly simonis@6465: // conservative for now, but can be improved. They currently use a simonis@6465: // mutex/condvar pair, plus a a count. simonis@6465: // Park decrements count if > 0, else does a condvar wait. Unpark simonis@6465: // sets count to 1 and signals condvar. Only one thread ever waits simonis@6465: // on the condvar. Contention seen when trying to park implies that someone simonis@6465: // is unparking you, so don't wait. And spurious returns are fine, so there simonis@6465: // is no need to track notifications. simonis@6465: // simonis@6465: simonis@6465: #define MAX_SECS 100000000 simonis@6465: // simonis@6465: // This code is common to linux and solaris and will be moved to a simonis@6465: // common place in dolphin. simonis@6465: // simonis@6465: // The passed in time value is either a relative time in nanoseconds simonis@6465: // or an absolute time in milliseconds. Either way it has to be unpacked simonis@6465: // into suitable seconds and nanoseconds components and stored in the simonis@6465: // given timespec structure. simonis@6465: // Given time is a 64-bit value and the time_t used in the timespec is only simonis@6465: // a signed-32-bit value (except on 64-bit Linux) we have to watch for simonis@6465: // overflow if times way in the future are given. Further on Solaris versions simonis@6465: // prior to 10 there is a restriction (see cond_timedwait) that the specified simonis@6465: // number of seconds, in abstime, is less than current_time + 100,000,000. simonis@6465: // As it will be 28 years before "now + 100000000" will overflow we can simonis@6465: // ignore overflow and just impose a hard-limit on seconds using the value simonis@6465: // of "now + 100,000,000". This places a limit on the timeout of about 3.17 simonis@6465: // years from "now". simonis@6465: // simonis@6465: simonis@6465: static void unpackTime(timespec* absTime, bool isAbsolute, jlong time) { simonis@6465: assert (time > 0, "convertTime"); simonis@6465: simonis@6465: struct timeval now; simonis@6465: int status = gettimeofday(&now, NULL); simonis@6465: assert(status == 0, "gettimeofday"); simonis@6465: simonis@6465: time_t max_secs = now.tv_sec + MAX_SECS; simonis@6465: simonis@6465: if (isAbsolute) { simonis@6465: jlong secs = time / 1000; simonis@6465: if (secs > max_secs) { simonis@6465: absTime->tv_sec = max_secs; simonis@6465: } simonis@6465: else { simonis@6465: absTime->tv_sec = secs; simonis@6465: } simonis@6465: absTime->tv_nsec = (time % 1000) * NANOSECS_PER_MILLISEC; simonis@6465: } simonis@6465: else { simonis@6465: jlong secs = time / NANOSECS_PER_SEC; simonis@6465: if (secs >= MAX_SECS) { simonis@6465: absTime->tv_sec = max_secs; simonis@6465: absTime->tv_nsec = 0; simonis@6465: } simonis@6465: else { simonis@6465: absTime->tv_sec = now.tv_sec + secs; simonis@6465: absTime->tv_nsec = (time % NANOSECS_PER_SEC) + now.tv_usec*1000; simonis@6465: if (absTime->tv_nsec >= NANOSECS_PER_SEC) { simonis@6465: absTime->tv_nsec -= NANOSECS_PER_SEC; simonis@6465: ++absTime->tv_sec; // note: this must be <= max_secs simonis@6465: } simonis@6465: } simonis@6465: } simonis@6465: assert(absTime->tv_sec >= 0, "tv_sec < 0"); simonis@6465: assert(absTime->tv_sec <= max_secs, "tv_sec > max_secs"); simonis@6465: assert(absTime->tv_nsec >= 0, "tv_nsec < 0"); simonis@6465: assert(absTime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec"); simonis@6465: } simonis@6465: simonis@6465: void Parker::park(bool isAbsolute, jlong time) { simonis@6465: // Optional fast-path check: simonis@6465: // Return immediately if a permit is available. simonis@6465: if (_counter > 0) { simonis@6465: _counter = 0; simonis@6465: OrderAccess::fence(); simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: Thread* thread = Thread::current(); simonis@6465: assert(thread->is_Java_thread(), "Must be JavaThread"); simonis@6465: JavaThread *jt = (JavaThread *)thread; simonis@6465: simonis@6465: // Optional optimization -- avoid state transitions if there's an interrupt pending. simonis@6465: // Check interrupt before trying to wait simonis@6465: if (Thread::is_interrupted(thread, false)) { simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: // Next, demultiplex/decode time arguments simonis@6465: timespec absTime; simonis@6465: if (time < 0 || (isAbsolute && time == 0)) { // don't wait at all simonis@6465: return; simonis@6465: } simonis@6465: if (time > 0) { simonis@6465: unpackTime(&absTime, isAbsolute, time); simonis@6465: } simonis@6465: simonis@6465: simonis@6465: // Enter safepoint region simonis@6465: // Beware of deadlocks such as 6317397. simonis@6465: // The per-thread Parker:: mutex is a classic leaf-lock. simonis@6465: // In particular a thread must never block on the Threads_lock while simonis@6465: // holding the Parker:: mutex. If safepoints are pending both the simonis@6465: // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. simonis@6465: ThreadBlockInVM tbivm(jt); simonis@6465: simonis@6465: // Don't wait if cannot get lock since interference arises from simonis@6465: // unblocking. Also. check interrupt before trying wait simonis@6465: if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: int status; simonis@6465: if (_counter > 0) { // no wait needed simonis@6465: _counter = 0; simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert (status == 0, "invariant"); simonis@6465: OrderAccess::fence(); simonis@6465: return; simonis@6465: } simonis@6465: simonis@6465: #ifdef ASSERT simonis@6465: // Don't catch signals while blocked; let the running threads have the signals. simonis@6465: // (This allows a debugger to break into the running thread.) simonis@6465: sigset_t oldsigs; simonis@6465: sigset_t* allowdebug_blocked = os::Aix::allowdebug_blocked_signals(); simonis@6465: pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); simonis@6465: #endif simonis@6465: simonis@6465: OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); simonis@6465: jt->set_suspend_equivalent(); simonis@6465: // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() simonis@6465: simonis@6465: if (time == 0) { simonis@6465: status = pthread_cond_wait (_cond, _mutex); simonis@6465: } else { simonis@6465: status = pthread_cond_timedwait (_cond, _mutex, &absTime); simonis@6465: if (status != 0 && WorkAroundNPTLTimedWaitHang) { simonis@6465: pthread_cond_destroy (_cond); simonis@6465: pthread_cond_init (_cond, NULL); simonis@6465: } simonis@6465: } simonis@6465: assert_status(status == 0 || status == EINTR || simonis@6465: status == ETIME || status == ETIMEDOUT, simonis@6465: status, "cond_timedwait"); simonis@6465: simonis@6465: #ifdef ASSERT simonis@6465: pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); simonis@6465: #endif simonis@6465: simonis@6465: _counter = 0; simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert_status(status == 0, status, "invariant"); simonis@6465: // If externally suspended while waiting, re-suspend simonis@6465: if (jt->handle_special_suspend_equivalent_condition()) { simonis@6465: jt->java_suspend_self(); simonis@6465: } simonis@6465: simonis@6465: OrderAccess::fence(); simonis@6465: } simonis@6465: simonis@6465: void Parker::unpark() { simonis@6465: int s, status; simonis@6465: status = pthread_mutex_lock(_mutex); simonis@6465: assert (status == 0, "invariant"); simonis@6465: s = _counter; simonis@6465: _counter = 1; simonis@6465: if (s < 1) { simonis@6465: if (WorkAroundNPTLTimedWaitHang) { simonis@6465: status = pthread_cond_signal (_cond); simonis@6465: assert (status == 0, "invariant"); simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert (status == 0, "invariant"); simonis@6465: } else { simonis@6465: status = pthread_mutex_unlock(_mutex); simonis@6465: assert (status == 0, "invariant"); simonis@6465: status = pthread_cond_signal (_cond); simonis@6465: assert (status == 0, "invariant"); simonis@6465: } simonis@6465: } else { simonis@6465: pthread_mutex_unlock(_mutex); simonis@6465: assert (status == 0, "invariant"); simonis@6465: } simonis@6465: } simonis@6465: simonis@6465: simonis@6465: extern char** environ; simonis@6465: simonis@6465: // Run the specified command in a separate process. Return its exit value, simonis@6465: // or -1 on failure (e.g. can't fork a new process). simonis@6465: // Unlike system(), this function can be called from signal handler. It simonis@6465: // doesn't block SIGINT et al. simonis@6465: int os::fork_and_exec(char* cmd) { simonis@6471: char * argv[4] = {"sh", "-c", cmd, NULL}; simonis@6471: simonis@6471: pid_t pid = fork(); simonis@6471: simonis@6471: if (pid < 0) { simonis@6471: // fork failed simonis@6471: return -1; simonis@6471: simonis@6471: } else if (pid == 0) { simonis@6471: // child process simonis@6471: simonis@6471: // try to be consistent with system(), which uses "/usr/bin/sh" on AIX simonis@6471: execve("/usr/bin/sh", argv, environ); simonis@6471: simonis@6471: // execve failed simonis@6471: _exit(-1); simonis@6471: simonis@6471: } else { simonis@6471: // copied from J2SE ..._waitForProcessExit() in UNIXProcess_md.c; we don't simonis@6471: // care about the actual exit code, for now. simonis@6471: simonis@6471: int status; simonis@6471: simonis@6471: // Wait for the child process to exit. This returns immediately if simonis@6471: // the child has already exited. */ simonis@6471: while (waitpid(pid, &status, 0) < 0) { simonis@6471: switch (errno) { simonis@6471: case ECHILD: return 0; simonis@6471: case EINTR: break; simonis@6471: default: return -1; simonis@6471: } simonis@6471: } simonis@6471: simonis@6471: if (WIFEXITED(status)) { simonis@6471: // The child exited normally; get its exit code. simonis@6471: return WEXITSTATUS(status); simonis@6471: } else if (WIFSIGNALED(status)) { simonis@6471: // The child exited because of a signal simonis@6471: // The best value to return is 0x80 + signal number, simonis@6471: // because that is what all Unix shells do, and because simonis@6471: // it allows callers to distinguish between process exit and simonis@6471: // process death by signal. simonis@6471: return 0x80 + WTERMSIG(status); simonis@6471: } else { simonis@6471: // Unknown exit code; pass it through simonis@6471: return status; simonis@6471: } simonis@6471: } simonis@6471: // Remove warning. simonis@6471: return -1; simonis@6465: } simonis@6465: simonis@6465: // is_headless_jre() simonis@6465: // simonis@6465: // Test for the existence of xawt/libmawt.so or libawt_xawt.so simonis@6465: // in order to report if we are running in a headless jre. simonis@6465: // simonis@6465: // Since JDK8 xawt/libmawt.so is moved into the same directory simonis@6465: // as libawt.so, and renamed libawt_xawt.so simonis@6465: bool os::is_headless_jre() { simonis@6465: struct stat statbuf; simonis@6465: char buf[MAXPATHLEN]; simonis@6465: char libmawtpath[MAXPATHLEN]; simonis@6465: const char *xawtstr = "/xawt/libmawt.so"; simonis@6465: const char *new_xawtstr = "/libawt_xawt.so"; simonis@6465: simonis@6465: char *p; simonis@6465: simonis@6465: // Get path to libjvm.so simonis@6465: os::jvm_path(buf, sizeof(buf)); simonis@6465: simonis@6465: // Get rid of libjvm.so simonis@6465: p = strrchr(buf, '/'); simonis@6465: if (p == NULL) return false; simonis@6465: else *p = '\0'; simonis@6465: simonis@6465: // Get rid of client or server simonis@6465: p = strrchr(buf, '/'); simonis@6465: if (p == NULL) return false; simonis@6465: else *p = '\0'; simonis@6465: simonis@6465: // check xawt/libmawt.so simonis@6465: strcpy(libmawtpath, buf); simonis@6465: strcat(libmawtpath, xawtstr); simonis@6465: if (::stat(libmawtpath, &statbuf) == 0) return false; simonis@6465: simonis@6465: // check libawt_xawt.so simonis@6465: strcpy(libmawtpath, buf); simonis@6465: strcat(libmawtpath, new_xawtstr); simonis@6465: if (::stat(libmawtpath, &statbuf) == 0) return false; simonis@6465: simonis@6465: return true; simonis@6465: } simonis@6465: simonis@6465: // Get the default path to the core file simonis@6465: // Returns the length of the string simonis@6465: int os::get_core_path(char* buffer, size_t bufferSize) { simonis@6465: const char* p = get_current_directory(buffer, bufferSize); simonis@6465: simonis@6465: if (p == NULL) { simonis@6465: assert(p != NULL, "failed to get current directory"); simonis@6465: return 0; simonis@6465: } simonis@6465: simonis@6465: return strlen(buffer); simonis@6465: } simonis@6465: simonis@6465: #ifndef PRODUCT simonis@6465: void TestReserveMemorySpecial_test() { simonis@6465: // No tests available for this platform simonis@6465: } simonis@6465: #endif