/* * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// Check core dump limit and report possible place where core can be found void os::check_dump_limit(char* buffer, size_t bufferSize) { if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) {
jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line");
VMError::record_coredump_status(buffer, false); return;
}
int n; struct rlimit rlim; bool success;
char core_path[PATH_MAX];
n = get_core_path(core_path, PATH_MAX);
if (n <= 0) {
jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id());
success = true; #ifdef LINUX
} elseif (core_path[0] == '"') { // redirect to user process
jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path);
success = true; #endif
} elseif (getrlimit(RLIMIT_CORE, &rlim) != 0) {
jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path);
success = true;
} else { switch(rlim.rlim_cur) { case RLIM_INFINITY:
jio_snprintf(buffer, bufferSize, "%s", core_path);
success = true; break; case 0:
jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again");
success = false; break; default:
jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K);
success = true; break;
}
}
constchar *s = os::strerror(errno);
size_t n = ::strlen(s); if (n >= len) {
n = len - 1;
}
::strncpy(buf, s, n);
buf[n] = '\0'; return n;
}
// Return true if user is running as root. bool os::have_special_privileges() { staticbool privileges = (getuid() != geteuid()) || (getgid() != getegid()); return privileges;
}
void os::wait_for_keypress_at_exit(void) { // don't do anything on posix platforms return;
}
int os::create_file_for_heap(constchar* dir) { int fd;
#ifdefined(LINUX) && defined(O_TMPFILE) char* native_dir = os::strdup(dir); if (native_dir == NULL) {
vm_exit_during_initialization(err_msg("strdup failed during creation of backing file for heap (%s)", os::strerror(errno))); return -1;
}
os::native_path(native_dir);
fd = os::open(dir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
os::free(native_dir);
if (fd == -1) #endif
{ constchar name_template[] = "/jvmheap.XXXXXX";
size_t fullname_len = strlen(dir) + strlen(name_template); char *fullname = (char*)os::malloc(fullname_len + 1, mtInternal); if (fullname == NULL) {
vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno))); return -1;
} int n = snprintf(fullname, fullname_len + 1, "%s%s", dir, name_template);
assert((size_t)n == fullname_len, "Unexpected number of characters in string");
os::native_path(fullname);
// create a new file.
fd = mkstemp(fullname);
if (fd < 0) {
warning("Could not create file for heap with template %s", fullname);
os::free(fullname); return -1;
} else { // delete the name from the filesystem. When 'fd' is closed, the file (and space) will be deleted. int ret = unlink(fullname);
assert_with_errno(ret == 0, "unlink returned error");
}
os::free(fullname);
}
return fd;
}
// Is a (classpath) directory empty? bool os::dir_is_empty(constchar* path) {
DIR *dir = NULL; struct dirent *ptr;
dir = ::opendir(path); if (dir == NULL) returntrue;
// Scan the directory bool result = true; while (result && (ptr = ::readdir(dir)) != NULL) { if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0) {
result = false;
}
}
::closedir(dir); return result;
}
staticchar* reserve_mmapped_memory(size_t bytes, char* requested_addr) { char * addr; int flags = MAP_PRIVATE NOT_AIX( | MAP_NORESERVE ) | MAP_ANONYMOUS; if (requested_addr != NULL) {
assert((uintptr_t)requested_addr % os::vm_page_size() == 0, "Requested address should be aligned to OS page size");
flags |= MAP_FIXED;
}
// Map reserved/uncommitted pages PROT_NONE so we fail early if we // touch an uncommitted page. Otherwise, the read/write might // succeed if we have enough swap space to back the physical page.
addr = (char*)::mmap(requested_addr, bytes, PROT_NONE,
flags, -1, 0);
staticint util_posix_fallocate(int fd, off_t offset, off_t len) { #ifdef __APPLE__
fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, len }; // First we try to get a continuous chunk of disk space int ret = fcntl(fd, F_PREALLOCATE, &store); if (ret == -1) { // Maybe we are too fragmented, try to allocate non-continuous range
store.fst_flags = F_ALLOCATEALL;
ret = fcntl(fd, F_PREALLOCATE, &store);
} if(ret != -1) { return ftruncate(fd, len);
} return -1; #else return posix_fallocate(fd, offset, len); #endif
}
// Map the given address range to the provided file descriptor. char* os::map_memory_to_file(char* base, size_t size, int fd) {
assert(fd != -1, "File descriptor is not valid");
// allocate space for the file int ret = util_posix_fallocate(fd, 0, (off_t)size); if (ret != 0) {
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory. error(%d)", ret)); return NULL;
}
int prot = PROT_READ | PROT_WRITE; int flags = MAP_SHARED; if (base != NULL) {
flags |= MAP_FIXED;
} char* addr = (char*)mmap(base, size, prot, flags, fd, 0);
if (addr == MAP_FAILED) {
warning("Failed mmap to file. (%s)", os::strerror(errno)); return NULL;
} if (base != NULL && addr != base) { if (!os::release_memory(addr, size)) {
warning("Could not release memory on unsuccessful file mapping");
} return NULL;
} return addr;
}
char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) {
assert(fd != -1, "File descriptor is not valid");
assert(base != NULL, "Base cannot be NULL");
return map_memory_to_file(base, size, fd);
}
static size_t calculate_aligned_extra_size(size_t size, size_t alignment) {
assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, "Alignment must be a multiple of allocation granularity (page size)");
assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");
size_t extra_size = size + alignment;
assert(extra_size >= size, "overflow, size is too large to allow alignment"); return extra_size;
}
// After a bigger chunk was mapped, unmaps start and end parts to get the requested alignment. staticchar* chop_extra_memory(size_t size, size_t alignment, char* extra_base, size_t extra_size) { // Do manual alignment char* aligned_base = align_up(extra_base, alignment);
// Multiple threads can race in this code, and can remap over each other with MAP_FIXED, // so on posix, unmap the section at the start and at the end of the chunk that we mapped // rather than unmapping and remapping the whole chunk to get requested alignment. char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) {
size_t extra_size = calculate_aligned_extra_size(size, alignment); char* extra_base = os::reserve_memory(extra_size, exec); if (extra_base == NULL) { return NULL;
} return chop_extra_memory(size, alignment, extra_base, extra_size);
}
char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int file_desc) {
size_t extra_size = calculate_aligned_extra_size(size, alignment); // For file mapping, we do not call os:map_memory_to_file(size,fd) since: // - we later chop away parts of the mapping using os::release_memory and that could fail if the // original mmap call had been tied to an fd. // - The memory API os::reserve_memory uses is an implementation detail. It may (and usually is) // mmap but it also may System V shared memory which cannot be uncommitted as a whole, so // chopping off and unmapping excess bits back and front (see below) would not work. char* extra_base = reserve_mmapped_memory(extra_size, NULL); if (extra_base == NULL) { return NULL;
} char* aligned_base = chop_extra_memory(size, alignment, extra_base, extra_size); // After we have an aligned address, we can replace anonymous mapping with file mapping if (replace_existing_mapping_with_file_mapping(aligned_base, size, file_desc) == NULL) {
vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
}
MemTracker::record_virtual_memory_commit((address)aligned_base, size, CALLER_PC); return aligned_base;
}
int os::vsnprintf(char* buf, size_t len, constchar* fmt, va_list args) { // All supported POSIX platforms provide C99 semantics.
ALLOW_C_FUNCTION(::vsnprintf, int result = ::vsnprintf(buf, len, fmt, args);) // If an encoding error occurred (result < 0) then it's not clear // whether the buffer is NUL terminated, so ensure it is. if ((result < 0) && (len > 0)) {
buf[len - 1] = '\0';
} return result;
}
int os::get_fileno(FILE* fp) { return NOT_AIX(::)fileno(fp);
}
// boot/uptime information; // unfortunately it does not work on macOS and Linux because the utx chain has no entry // for reboot at least on my test machines void os::Posix::print_uptime_info(outputStream* st) { int bootsec = -1; int currsec = time(NULL); struct utmpx* ent;
setutxent(); while ((ent = getutxent())) { if (!strcmp("system boot", ent->ut_line)) {
bootsec = ent->ut_tv.tv_sec; break;
}
}
// maximum size of files that the process may create
print_rlimit(st, ", FSIZE", RLIMIT_FSIZE, true);
#ifdefined(LINUX) || defined(__APPLE__) // maximum number of bytes of memory that may be locked into RAM // (rounded down to the nearest multiple of system pagesize)
print_rlimit(st, ", MEMLOCK", RLIMIT_MEMLOCK, true); #endif
// MacOS; The maximum size (in bytes) to which a process's resident set size may grow. #ifdefined(__APPLE__)
print_rlimit(st, ", RSS", RLIMIT_RSS, true); #endif
// Print all active locale categories, one line each void os::print_active_locale(outputStream* st) {
st->print_cr("Active Locale:"); // Posix is quiet about how exactly LC_ALL is implemented. // Just print it out too, in case LC_ALL is held separately // from the individual categories. #define LOCALE_CAT_DO(f) \
f(LC_ALL) \
f(LC_COLLATE) \
f(LC_CTYPE) \
f(LC_MESSAGES) \
f(LC_MONETARY) \
f(LC_NUMERIC) \
f(LC_TIME) #define XX(cat) { cat, #cat }, conststruct { int c; constchar* name; } categories[] = {
LOCALE_CAT_DO(XX)
{ -1, NULL }
}; #undef XX #undef LOCALE_CAT_DO for (int i = 0; categories[i].c != -1; i ++) { constchar* locale = setlocale(categories[i].c, NULL);
st->print_cr("%s=%s", categories[i].name,
((locale != NULL) ? locale : ""));
}
}
void os::print_jni_name_prefix_on(outputStream* st, int args_size) { // no prefix required
}
void os::print_jni_name_suffix_on(outputStream* st, int args_size) { // no suffix required
}
#ifndef _LP64 // Helper, on 32bit, for os::has_allocatable_memory_limit staticbool is_allocatable(size_t s) { if (s < 2 * G) { returntrue;
} // Use raw anonymous mmap here; no need to go through any // of our reservation layers. We will unmap right away. void* p = ::mmap(NULL, s, PROT_NONE,
MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS, -1, 0); if (p == MAP_FAILED) { returnfalse;
} else {
::munmap(p, s); returntrue;
}
} #endif// !_LP64
bool os::has_allocatable_memory_limit(size_t* limit) { struct rlimit rlim; int getrlimit_res = getrlimit(RLIMIT_AS, &rlim); // if there was an error when calling getrlimit, assume that there is no limitation // on virtual memory. bool result; if ((getrlimit_res != 0) || (rlim.rlim_cur == RLIM_INFINITY)) {
result = false;
} else {
*limit = (size_t)rlim.rlim_cur;
result = true;
} #ifdef _LP64 return result; #else // arbitrary virtual space limit for 32 bit Unices found by testing. If // getrlimit above returned a limit, bound it with this limit. Otherwise // directly use it. const size_t max_virtual_limit = 3800*M; if (result) {
*limit = MIN2(*limit, max_virtual_limit);
} else {
*limit = max_virtual_limit;
}
// bound by actually allocatable memory. The algorithm uses two bounds, an // upper and a lower limit. The upper limit is the current highest amount of // memory that could not be allocated, the lower limit is the current highest // amount of memory that could be allocated. // The algorithm iteratively refines the result by halving the difference // between these limits, updating either the upper limit (if that value could // not be allocated) or the lower limit (if the that value could be allocated) // until the difference between these limits is "small".
// the minimum amount of memory we care about allocating. const size_t min_allocation_size = M;
size_t upper_limit = *limit;
// first check a few trivial cases if (is_allocatable(upper_limit) || (upper_limit <= min_allocation_size)) {
*limit = upper_limit;
} elseif (!is_allocatable(min_allocation_size)) { // we found that not even min_allocation_size is allocatable. Return it // anyway. There is no point to search for a better value any more.
*limit = min_allocation_size;
} else { // perform the binary search.
size_t lower_limit = min_allocation_size; while ((upper_limit - lower_limit) > min_allocation_size) {
size_t temp_limit = ((upper_limit - lower_limit) / 2) + lower_limit;
temp_limit = align_down(temp_limit, min_allocation_size); if (is_allocatable(temp_limit)) {
lower_limit = temp_limit;
} else {
upper_limit = temp_limit;
}
}
*limit = lower_limit;
} returntrue; #endif
}
void* os::get_default_process_handle() { #ifdef __APPLE__ // MacOS X needs to use RTLD_FIRST instead of RTLD_LAZY // to avoid finding unexpected symbols on second (or later) // loads of a library. return (void*)::dlopen(NULL, RTLD_FIRST); #else return (void*)::dlopen(NULL, RTLD_LAZY); #endif
}
void os::dll_unload(void *lib) { // os::Linux::dll_path returns a pointer to a string that is owned by the dynamic loader. Upon // calling dlclose the dynamic loader may free the memory containing the string, thus we need to // copy the string to be able to reference it after dlclose. constchar* l_path = NULL; #ifdef LINUX char* l_pathdup = NULL;
l_path = os::Linux::dll_path(lib); if (l_path != NULL) {
l_path = l_pathdup = os::strdup(l_path);
} #endif// LINUX if (l_path == NULL) {
l_path = "";
} int res = ::dlclose(lib);
// Builds a platform dependent Agent_OnLoad_<lib_name> function name // which is used to find statically linked in agents. // Parameters: // sym_name: Symbol in library we are looking for // lib_name: Name of library to look in, NULL for shared libs. // is_absolute_path == true if lib_name is absolute path to agent // such as "/a/b/libL.so" // == false if only the base name of the library is passed in // such as "L" char* os::build_agent_function_name(constchar *sym_name, constchar *lib_name, bool is_absolute_path) { char *agent_entry_name;
size_t len;
size_t name_len;
size_t prefix_len = strlen(JNI_LIB_PREFIX);
size_t suffix_len = strlen(JNI_LIB_SUFFIX); constchar *start;
if (lib_name != NULL) {
name_len = strlen(lib_name); if (is_absolute_path) { // Need to strip path, prefix and suffix if ((start = strrchr(lib_name, *os::file_separator())) != NULL) {
lib_name = ++start;
} if (strlen(lib_name) <= (prefix_len + suffix_len)) { return NULL;
}
lib_name += prefix_len;
name_len = strlen(lib_name) - suffix_len;
}
}
len = (lib_name != NULL ? name_len : 0) + strlen(sym_name) + 2;
agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread); if (agent_entry_name == NULL) { return NULL;
}
strcpy(agent_entry_name, sym_name); if (lib_name != NULL) {
strcat(agent_entry_name, "_");
strncat(agent_entry_name, lib_name, name_len);
} return agent_entry_name;
}
// Sleep forever; naked call to OS-specific sleep; use with CAUTION void os::infinite_sleep() { while (true) { // sleep forever ...
::sleep(100); // ... 100 seconds at a time
}
}
void os::naked_short_nanosleep(jlong ns) { struct timespec req;
assert(ns > -1 && ns < NANOUNITS, "Un-interruptable sleep, short time use only");
req.tv_sec = 0;
req.tv_nsec = ns;
::nanosleep(&req, NULL); return;
}
void os::naked_short_sleep(jlong ms) {
assert(ms < MILLIUNITS, "Un-interruptable sleep, short time use only");
os::naked_short_nanosleep(millis_to_nanos(ms)); return;
}
char* os::Posix::describe_pthread_attr(char* buf, size_t buflen, const pthread_attr_t* attr) {
size_t stack_size = 0;
size_t guard_size = 0; int detachstate = 0;
pthread_attr_getstacksize(attr, &stack_size);
pthread_attr_getguardsize(attr, &guard_size); // Work around linux NPTL implementation error, see also os::create_thread() in os_linux.cpp.
LINUX_ONLY(stack_size -= guard_size);
pthread_attr_getdetachstate(attr, &detachstate);
jio_snprintf(buf, buflen, "stacksize: " SIZE_FORMAT "k, guardsize: " SIZE_FORMAT "k, %s",
stack_size / K, guard_size / K,
(detachstate == PTHREAD_CREATE_DETACHED ? "detached" : "joinable")); return buf;
}
// This assumes platform realpath() is implemented according to POSIX.1-2008. // POSIX.1-2008 allows to specify NULL for the output buffer, in which case // output buffer is dynamically allocated and must be ::free()'d by the caller.
ALLOW_C_FUNCTION(::realpath, char* p = ::realpath(filename, NULL);) if (p != NULL) { if (strlen(p) < outbuflen) {
strcpy(outbuf, p);
result = outbuf;
} else {
errno = ENAMETOOLONG;
}
ALLOW_C_FUNCTION(::free, ::free(p);) // *not* os::free
} else { // Fallback for platforms struggling with modern Posix standards (AIX 5.3, 6.1). If realpath // returns EINVAL, this may indicate that realpath is not POSIX.1-2008 compatible and // that it complains about the NULL we handed down as user buffer. // In this case, use the user provided buffer but at least check whether realpath caused // a memory overwrite. if (errno == EINVAL) {
outbuf[outbuflen - 1] = '\0';
ALLOW_C_FUNCTION(::realpath, p = ::realpath(filename, outbuf);) if (p != NULL) {
guarantee(outbuf[outbuflen - 1] == '\0', "realpath buffer overwrite detected.");
result = p;
}
}
} return result;
}
int os::stat(constchar *path, struct stat *sbuf) { return ::stat(path, sbuf);
}
bool is_same = false; struct stat st1; struct stat st2;
if (os::stat(file1, &st1) < 0) { returnfalse;
}
if (os::stat(file2, &st2) < 0) { returnfalse;
}
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) { // same files
is_same = true;
} return is_same;
}
// Called when creating the thread. The minimum stack sizes have already been calculated
size_t os::Posix::get_initial_stack_size(ThreadType thr_type, size_t req_stack_size) {
size_t stack_size; if (req_stack_size == 0) {
stack_size = default_stack_size(thr_type);
} else {
stack_size = req_stack_size;
}
switch (thr_type) { case os::java_thread: // Java threads use ThreadStackSize which default value can be // changed with the flag -Xss if (req_stack_size == 0 && JavaThread::stack_size_at_create() > 0) { // no requested size and we have a more specific default value
stack_size = JavaThread::stack_size_at_create();
}
stack_size = MAX2(stack_size,
_java_thread_min_stack_allowed); break; case os::compiler_thread: if (req_stack_size == 0 && CompilerThreadStackSize > 0) { // no requested size and we have a more specific default value
stack_size = (size_t)(CompilerThreadStackSize * K);
}
stack_size = MAX2(stack_size,
_compiler_thread_min_stack_allowed); break; case os::vm_thread: case os::gc_thread: case os::watcher_thread: default: // presume the unknown thr_type is a VM internal if (req_stack_size == 0 && VMThreadStackSize > 0) { // no requested size and we have a more specific default value
stack_size = (size_t)(VMThreadStackSize * K);
}
// pthread_attr_setstacksize() may require that the size be rounded up to the OS page size. // Be careful not to round up to 0. Align down in that case. if (stack_size <= SIZE_MAX - vm_page_size()) {
stack_size = align_up(stack_size, vm_page_size());
} else {
stack_size = align_down(stack_size, vm_page_size());
}
return stack_size;
}
#ifndef ZERO #ifndef ARM staticbool get_frame_at_stack_banging_point(JavaThread* thread, address pc, constvoid* ucVoid, frame* fr) { if (Interpreter::contains(pc)) { // interpreter performs stack banging after the fixed frame header has // been generated while the compilers perform it before. To maintain // semantic consistency between interpreted and compiled frames, the // method returns the Java sender of the current frame.
*fr = os::fetch_frame_from_context(ucVoid); if (!fr->is_first_java_frame()) { // get_frame_at_stack_banging_point() is only called when we // have well defined stacks so java_sender() calls do not need // to assert safe_for_sender() first.
*fr = fr->java_sender();
}
} else { // more complex code with compiled code
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
CodeBlob* cb = CodeCache::find_blob(pc); if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) { // Not sure where the pc points to, fallback to default // stack overflow handling returnfalse;
} else { // in compiled code, the stack banging is performed just after the return pc // has been pushed on the stack
*fr = os::fetch_compiled_frame_from_context(ucVoid); if (!fr->is_java_frame()) {
assert(!fr->is_first_frame(), "Safety check"); // See java_sender() comment above.
*fr = fr->java_sender();
}
}
}
assert(fr->is_java_frame(), "Safety check"); returntrue;
} #endif// ARM
// This return true if the signal handler should just continue, ie. return after calling this bool os::Posix::handle_stack_overflow(JavaThread* thread, address addr, address pc, constvoid* ucVoid, address* stub) { // stack overflow
StackOverflow* overflow_state = thread->stack_overflow_state(); if (overflow_state->in_stack_yellow_reserved_zone(addr)) { if (thread->thread_state() == _thread_in_Java) { #ifndef ARM // arm32 doesn't have this // vthreads don't support this if (!thread->is_vthread_mounted() && overflow_state->in_stack_reserved_zone(addr)) {
frame fr; if (get_frame_at_stack_banging_point(thread, pc, ucVoid, &fr)) {
assert(fr.is_java_frame(), "Must be a Java frame");
frame activation =
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr); if (activation.sp() != NULL) {
overflow_state->disable_stack_reserved_zone(); if (activation.is_interpreted_frame()) {
overflow_state->set_reserved_stack_activation((address)(activation.fp() // Some platforms use frame pointers for interpreter frames, others use initial sp. #if !defined(PPC64) && !defined(S390)
+ frame::interpreter_frame_initial_sp_offset #endif
));
} else {
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
} returntrue; // just continue
}
}
} #endif// ARM // Throw a stack overflow exception. Guard pages will be re-enabled // while unwinding the stack.
overflow_state->disable_stack_yellow_reserved_zone();
*stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
} else { // Thread was in the vm or native code. Return and try to finish.
overflow_state->disable_stack_yellow_reserved_zone(); returntrue; // just continue
}
} elseif (overflow_state->in_stack_red_zone(addr)) { // Fatal red zone violation. Disable the guard pages and keep // on handling the signal.
overflow_state->disable_stack_red_zone();
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
// This is a likely cause, but hard to verify. Let's just print // it as a hint.
tty->print_raw_cr("Please check if any of your loaded .so files has " "enabled executable stack (see man page execstack(8))");
} else { #ifdef LINUX // This only works with os::Linux::manually_expand_stack()
// Accessing stack address below sp may cause SEGV if current // thread has MAP_GROWSDOWN stack. This should only happen when // current thread was created by user code with MAP_GROWSDOWN flag // and then attached to VM. See notes in os_linux.cpp. if (thread->osthread()->expanding_stack() == 0) {
thread->osthread()->set_expanding_stack(); if (os::Linux::manually_expand_stack(thread, addr)) {
thread->osthread()->clear_expanding_stack(); returntrue; // just continue
}
thread->osthread()->clear_expanding_stack();
} else {
fatal("recursive segv. expanding stack.");
} #else
tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone."); #endif// LINUX
} returnfalse;
} #endif// ZERO
// Shared clock/time and other supporting routines for pthread_mutex/cond // initialization. This is enabled on Solaris but only some of the clock/time // functionality is actually used there.
// Shared condattr object for use with relative timed-waits. Will be associated // with CLOCK_MONOTONIC if available to avoid issues with time-of-day changes, // but otherwise whatever default is used by the platform - generally the // time-of-day clock. static pthread_condattr_t _condAttr[1];
// Shared mutexattr to explicitly set the type to PTHREAD_MUTEX_NORMAL as not // all systems (e.g. FreeBSD) map the default to "normal". static pthread_mutexattr_t _mutexAttr[1];
// common basic initialization that is always supported staticvoid pthread_init_common(void) { int status; if ((status = pthread_condattr_init(_condAttr)) != 0) {
fatal("pthread_condattr_init: %s", os::strerror(status));
} if ((status = pthread_mutexattr_init(_mutexAttr)) != 0) {
fatal("pthread_mutexattr_init: %s", os::strerror(status));
} if ((status = pthread_mutexattr_settype(_mutexAttr, PTHREAD_MUTEX_NORMAL)) != 0) {
fatal("pthread_mutexattr_settype: %s", os::strerror(status));
}
PlatformMutex::init();
}
// Determine what POSIX API's are present and do appropriate // configuration. void os::Posix::init(void) { #ifdefined(_ALLBSD_SOURCE)
clock_tics_per_sec = CLK_TCK; #else
clock_tics_per_sec = sysconf(_SC_CLK_TCK); #endif // NOTE: no logging available when this is called. Put logging // statements in init_2().
// Check for pthread_condattr_setclock support.
// libpthread is already loaded. int (*condattr_setclock_func)(pthread_condattr_t*, clockid_t) =
(int (*)(pthread_condattr_t*, clockid_t))dlsym(RTLD_DEFAULT, "pthread_condattr_setclock"); if (condattr_setclock_func != NULL) {
_pthread_condattr_setclock = condattr_setclock_func;
}
// Now do general initialization.
pthread_init_common();
int status; if (_pthread_condattr_setclock != NULL) { if ((status = _pthread_condattr_setclock(_condAttr, CLOCK_MONOTONIC)) != 0) { if (status == EINVAL) {
_use_clock_monotonic_condattr = false;
warning("Unable to use monotonic clock with relative timed-waits" \ " - changes to the time-of-day clock may have adverse affects");
} else {
fatal("pthread_condattr_setclock: %s", os::strerror(status));
}
} else {
_use_clock_monotonic_condattr = true;
}
}
initial_time_count = javaTimeNanos();
}
void os::Posix::init_2(void) {
log_info(os)("Use of CLOCK_MONOTONIC is supported");
log_info(os)("Use of pthread_condattr_setclock is%s supported",
(_pthread_condattr_setclock != NULL ? "" : " not"));
log_info(os)("Relative timed-wait using pthread_cond_timedwait is associated with %s",
_use_clock_monotonic_condattr ? "CLOCK_MONOTONIC" : "the default clock");
}
// Utility to convert the given timeout to an absolute timespec // (based on the appropriate clock) to use with pthread_cond_timewait, // and sem_timedwait(). // The clock queried here must be the clock used to manage the // timeout of the condition variable or semaphore. // // The passed in timeout value is either a relative time in nanoseconds // or an absolute time in milliseconds. A relative timeout will be // associated with CLOCK_MONOTONIC if available, unless the real-time clock // is explicitly requested; otherwise, or if absolute, // the default time-of-day clock will be used.
// Given time is a 64-bit value and the time_t used in the timespec is // sometimes a signed-32-bit value we have to watch for overflow if times // way in the future are given. Further on Solaris versions // prior to 10 there is a restriction (see cond_timedwait) that the specified // number of seconds, in abstime, is less than current_time + 100000000. // As it will be over 20 years before "now + 100000000" will overflow we can // ignore overflow and just impose a hard-limit on seconds using the value // of "now + 100000000". This places a limit on the timeout of about 3.17 // years from "now". // #define MAX_SECS 100000000
// Calculate a new absolute time that is "timeout" nanoseconds from "now". // "unit" indicates the unit of "now_part_sec" (may be nanos or micros depending // on which clock API is being used). staticvoid calc_rel_time(timespec* abstime, jlong timeout, jlong now_sec,
jlong now_part_sec, jlong unit) {
time_t max_secs = now_sec + MAX_SECS;
if (seconds >= MAX_SECS) { // More seconds than we can add, so pin to max_secs.
abstime->tv_sec = max_secs;
abstime->tv_nsec = 0;
} else {
abstime->tv_sec = now_sec + seconds; long nanos = (now_part_sec * (NANOUNITS / unit)) + timeout; if (nanos >= NANOUNITS) { // overflow
abstime->tv_sec += 1;
nanos -= NANOUNITS;
}
abstime->tv_nsec = nanos;
}
}
// Unpack the given deadline in milliseconds since the epoch, into the given timespec. // The current time in seconds is also passed in to enforce an upper bound as discussed above. staticvoid unpack_abs_time(timespec* abstime, jlong deadline, jlong now_sec) {
time_t max_secs = now_sec + MAX_SECS;
static jlong millis_to_nanos_bounded(jlong millis) { // We have to watch for overflow when converting millis to nanos, // but if millis is that large then we will end up limiting to // MAX_SECS anyway, so just do that here. if (millis / MILLIUNITS > MAX_SECS) {
millis = jlong(MAX_SECS) * MILLIUNITS;
} return millis_to_nanos(millis);
}
// Create an absolute time 'millis' milliseconds in the future, using the // real-time (time-of-day) clock. Used by PosixSemaphore. void os::Posix::to_RTC_abstime(timespec* abstime, int64_t millis) {
to_abstime(abstime, millis_to_nanos_bounded(millis), false/* not absolute */, true/* use real-time clock */);
}
// macOS and AIX have platform specific implementations for javaTimeNanos() // using native clock/timer access APIs. These have historically worked well // for those platforms, but it may be possible for them to switch to the // generic clock_gettime mechanism in the future. #if !defined(__APPLE__) && !defined(AIX)
jlong os::javaTimeNanos() { struct timespec tp; int status = clock_gettime(CLOCK_MONOTONIC, &tp);
assert(status == 0, "clock_gettime error: %s", os::strerror(errno));
jlong result = jlong(tp.tv_sec) * NANOSECS_PER_SEC + jlong(tp.tv_nsec); return result;
}
// for timer info max values which include all bits #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF)
void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { // CLOCK_MONOTONIC - amount of time since some arbitrary point in the past
info_ptr->max_value = ALL_64_BITS;
info_ptr->may_skip_backward = false; // not subject to resetting or drifting
info_ptr->may_skip_forward = false; // not subject to resetting or drifting
info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time
} #endif// ! APPLE && !AIX
// Time since start-up in seconds to a fine granularity. double os::elapsedTime() { return ((double)os::elapsed_counter()) / os::elapsed_frequency(); // nanosecond resolution
}
// Return the real, user, and system times in seconds from an // arbitrary fixed point in the past. bool os::getTimesSecs(double* process_real_time, double* process_user_time, double* process_system_time) { struct tms ticks;
clock_t real_ticks = times(&ticks);
// PlatformEvent // // Assumption: // Only one parker can exist on an event, which is why we allocate // them per-thread. Multiple unparkers can coexist. // // _event serves as a restricted-range semaphore. // -1 : thread is blocked, i.e. there is a waiter // 0 : neutral: thread is running or ready, // could have been signaled after a wait started // 1 : signaled - thread is running or ready // // Having three states allows for some detection of bad usage - see // comments on unpark().
PlatformEvent::PlatformEvent() { int status = pthread_cond_init(_cond, _condAttr);
assert_status(status == 0, status, "cond_init");
status = pthread_mutex_init(_mutex, _mutexAttr);
assert_status(status == 0, status, "mutex_init");
_event = 0;
_nParked = 0;
}
void PlatformEvent::park() { // AKA "down()" // Transitions for _event: // -1 => -1 : illegal // 1 => 0 : pass - return immediately // 0 => -1 : block; then set _event to 0 before returning
// Invariant: Only the thread associated with the PlatformEvent // may call park().
assert(_nParked == 0, "invariant");
int v;
// atomically decrement _event for (;;) {
v = _event; if (Atomic::cmpxchg(&_event, v, v - 1) == v) break;
}
guarantee(v >= 0, "invariant");
if (v == 0) { // Do this the hard way by blocking ... int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
guarantee(_nParked == 0, "invariant");
++_nParked; while (_event < 0) { // OS-level "spurious wakeups" are ignored
status = pthread_cond_wait(_cond, _mutex);
assert_status(status == 0 MACOS_ONLY(|| status == ETIMEDOUT),
status, "cond_wait");
}
--_nParked;
_event = 0;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock"); // Paranoia to ensure our locked and lock-free paths interact // correctly with each other.
OrderAccess::fence();
}
guarantee(_event >= 0, "invariant");
}
int PlatformEvent::park(jlong millis) { // Transitions for _event: // -1 => -1 : illegal // 1 => 0 : pass - return immediately // 0 => -1 : block; then set _event to 0 before returning
// Invariant: Only the thread associated with the Event/PlatformEvent // may call park().
assert(_nParked == 0, "invariant");
int v; // atomically decrement _event for (;;) {
v = _event; if (Atomic::cmpxchg(&_event, v, v - 1) == v) break;
}
guarantee(v >= 0, "invariant");
if (v == 0) { // Do this the hard way by blocking ... struct timespec abst;
to_abstime(&abst, millis_to_nanos_bounded(millis), false, false);
int ret = OS_TIMEOUT; int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
guarantee(_nParked == 0, "invariant");
++_nParked;
while (_event < 0) {
status = pthread_cond_timedwait(_cond, _mutex, &abst);
assert_status(status == 0 || status == ETIMEDOUT,
status, "cond_timedwait"); // OS-level "spurious wakeups" are ignored if (status == ETIMEDOUT) break;
}
--_nParked;
if (_event >= 0) {
ret = OS_OK;
}
_event = 0;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock"); // Paranoia to ensure our locked and lock-free paths interact // correctly with each other.
OrderAccess::fence(); return ret;
} return OS_OK;
}
void PlatformEvent::unpark() { // Transitions for _event: // 0 => 1 : just return // 1 => 1 : just return // -1 => either 0 or 1; must signal target thread // That is, we can safely transition _event from -1 to either // 0 or 1. // See also: "Semaphores in Plan 9" by Mullender & Cox // // Note: Forcing a transition from "-1" to "1" on an unpark() means // that it will take two back-to-back park() calls for the owning // thread to block. This has the benefit of forcing a spurious return // from the first park() call after an unpark() call which will help // shake out uses of park() and unpark() without checking state conditions // properly. This spurious return doesn't manifest itself in any user code // but only in the correctly written condition checking loops of ObjectMonitor, // Mutex/Monitor, and JavaThread::sleep
if (Atomic::xchg(&_event, 1) >= 0) return;
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock"); int anyWaiters = _nParked;
assert(anyWaiters == 0 || anyWaiters == 1, "invariant");
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
// Note that we signal() *after* dropping the lock for "immortal" Events. // This is safe and avoids a common class of futile wakeups. In rare // circumstances this can cause a thread to return prematurely from // cond_{timed}wait() but the spurious wakeup is benign and the victim // will simply re-test the condition and re-park itself. // This provides particular benefit if the underlying platform does not // provide wait morphing.
if (anyWaiters != 0) {
status = pthread_cond_signal(_cond);
assert_status(status == 0, status, "cond_signal");
}
}
// JSR166 support
PlatformParker::PlatformParker() : _counter(0), _cur_index(-1) { int status = pthread_cond_init(&_cond[REL_INDEX], _condAttr);
assert_status(status == 0, status, "cond_init rel");
status = pthread_cond_init(&_cond[ABS_INDEX], NULL);
assert_status(status == 0, status, "cond_init abs");
status = pthread_mutex_init(_mutex, _mutexAttr);
assert_status(status == 0, status, "mutex_init");
}
PlatformParker::~PlatformParker() { int status = pthread_cond_destroy(&_cond[REL_INDEX]);
assert_status(status == 0, status, "cond_destroy rel");
status = pthread_cond_destroy(&_cond[ABS_INDEX]);
assert_status(status == 0, status, "cond_destroy abs");
status = pthread_mutex_destroy(_mutex);
assert_status(status == 0, status, "mutex_destroy");
}
// Parker::park decrements count if > 0, else does a condvar wait. Unpark // sets count to 1 and signals condvar. Only one thread ever waits // on the condvar. Contention seen when trying to park implies that someone // is unparking you, so don't wait. And spurious returns are fine, so there // is no need to track notifications.
void Parker::park(bool isAbsolute, jlong time) {
// Optional fast-path check: // Return immediately if a permit is available. // We depend on Atomic::xchg() having full barrier semantics // since we are doing a lock-free update to _counter. if (Atomic::xchg(&_counter, 0) > 0) return;
JavaThread *jt = JavaThread::current();
// Optional optimization -- avoid state transitions if there's // an interrupt pending. if (jt->is_interrupted(false)) { return;
}
// Next, demultiplex/decode time arguments struct timespec absTime; if (time < 0 || (isAbsolute && time == 0)) { // don't wait at all return;
} if (time > 0) {
to_abstime(&absTime, time, isAbsolute, false);
}
// Enter safepoint region // Beware of deadlocks such as 6317397. // The per-thread Parker:: mutex is a classic leaf-lock. // In particular a thread must never block on the Threads_lock while // holding the Parker:: mutex. If safepoints are pending both the // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.
ThreadBlockInVM tbivm(jt);
// Can't access interrupt state now that we are _thread_blocked. If we've // been interrupted since we checked above then _counter will be > 0.
// Don't wait if cannot get lock since interference arises from // unparking. if (pthread_mutex_trylock(_mutex) != 0) { return;
}
int status; if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "invariant"); // Paranoia to ensure our locked and lock-free paths interact // correctly with each other and Java-level accesses.
OrderAccess::fence(); return;
}
OSThreadWaitState osts(jt->osthread(), false/* not Object.wait() */);
assert(_cur_index == -1, "invariant"); if (time == 0) {
_cur_index = REL_INDEX; // arbitrary choice when not timed
status = pthread_cond_wait(&_cond[_cur_index], _mutex);
assert_status(status == 0 MACOS_ONLY(|| status == ETIMEDOUT),
status, "cond_wait");
} else {
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
status = pthread_cond_timedwait(&_cond[_cur_index], _mutex, &absTime);
assert_status(status == 0 || status == ETIMEDOUT,
status, "cond_timedwait");
}
_cur_index = -1;
_counter = 0;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "invariant"); // Paranoia to ensure our locked and lock-free paths interact // correctly with each other and Java-level accesses.
OrderAccess::fence();
}
void Parker::unpark() { int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "invariant"); constint s = _counter;
_counter = 1; // must capture correct index before unlocking int index = _cur_index;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "invariant");
// Note that we signal() *after* dropping the lock for "immortal" Events. // This is safe and avoids a common class of futile wakeups. In rare // circumstances this can cause a thread to return prematurely from // cond_{timed}wait() but the spurious wakeup is benign and the victim // will simply re-test the condition and re-park itself. // This provides particular benefit if the underlying platform does not // provide wait morphing.
if (s < 1 && index != -1) { // thread is definitely parked
status = pthread_cond_signal(&_cond[index]);
assert_status(status == 0, status, "invariant");
}
}
// Platform Mutex/Monitor implementation
#if PLATFORM_MONITOR_IMPL_INDIRECT
PlatformMutex::Mutex::Mutex() : _next(NULL) { int status = pthread_mutex_init(&_mutex, _mutexAttr);
assert_status(status == 0, status, "mutex_init");
}
PlatformMutex::Mutex::~Mutex() { int status = pthread_mutex_destroy(&_mutex);
assert_status(status == 0, status, "mutex_destroy");
}
PlatformMutex::PlatformMutex() { int status = pthread_mutex_init(&_mutex, _mutexAttr);
assert_status(status == 0, status, "mutex_init");
}
PlatformMutex::~PlatformMutex() { int status = pthread_mutex_destroy(&_mutex);
assert_status(status == 0, status, "mutex_destroy");
}
PlatformMonitor::PlatformMonitor() { int status = pthread_cond_init(&_cond, _condAttr);
assert_status(status == 0, status, "cond_init");
}
PlatformMonitor::~PlatformMonitor() { int status = pthread_cond_destroy(&_cond);
assert_status(status == 0, status, "cond_destroy");
}
#endif// PLATFORM_MONITOR_IMPL_INDIRECT
// Must already be locked int PlatformMonitor::wait(jlong millis) {
assert(millis >= 0, "negative timeout"); if (millis > 0) { struct timespec abst; // We have to watch for overflow when converting millis to nanos, // but if millis is that large then we will end up limiting to // MAX_SECS anyway, so just do that here. if (millis / MILLIUNITS > MAX_SECS) {
millis = jlong(MAX_SECS) * MILLIUNITS;
}
to_abstime(&abst, millis_to_nanos(millis), false, false);
int ret = OS_TIMEOUT; int status = pthread_cond_timedwait(cond(), mutex(), &abst);
assert_status(status == 0 || status == ETIMEDOUT,
status, "cond_timedwait"); if (status == 0) {
ret = OS_OK;
} return ret;
} else { int status = pthread_cond_wait(cond(), mutex());
assert_status(status == 0 MACOS_ONLY(|| status == ETIMEDOUT),
status, "cond_wait"); return OS_OK;
}
}
// Darwin has no "environ" in a dynamic library. #ifdef __APPLE__ #define environ (*_NSGetEnviron()) #else externchar** environ; #endif
char** os::get_environ() { return environ; }
// Run the specified command in a separate process. Return its exit value, // or -1 on failure (e.g. can't fork a new process). // Notes: -Unlike system(), this function can be called from signal handler. It // doesn't block SIGINT et al. // -this function is unsafe to use in non-error situations, mainly // because the child process will inherit all parent descriptors. int os::fork_and_exec(constchar* cmd) { constchar* argv[4] = {"sh", "-c", cmd, NULL};
pid_t pid = -1; char** env = os::get_environ(); // Note: cast is needed because posix_spawn() requires - for compatibility with ancient // C-code - a non-const argv/envp pointer array. But it is fine to hand in literal // strings and just cast the constness away. See also ProcessImpl_md.c. int rc = ::posix_spawn(&pid, "/bin/sh", NULL, NULL, (char**) argv, env); if (rc == 0) { int status; // Wait for the child process to exit. This returns immediately if // the child has already exited. */ while (::waitpid(pid, &status, 0) < 0) { switch (errno) { case ECHILD: return 0; case EINTR: break; default: return -1;
}
} if (WIFEXITED(status)) { // The child exited normally; get its exit code. return WEXITSTATUS(status);
} elseif (WIFSIGNALED(status)) { // The child exited because of a signal // The best value to return is 0x80 + signal number, // because that is what all Unix shells do, and because // it allows callers to distinguish between process exit and // process death by signal. return 0x80 + WTERMSIG(status);
} else { // Unknown exit code; pass it through return status;
}
} else { // Don't log, we are inside error handling return -1;
}
}
bool os::message_box(constchar* title, constchar* message) { int i;
fdStream err(defaultStream::error_fd()); for (i = 0; i < 78; i++) err.print_raw("=");
err.cr();
err.print_raw_cr(title); for (i = 0; i < 78; i++) err.print_raw("-");
err.cr();
err.print_raw_cr(message); for (i = 0; i < 78; i++) err.print_raw("=");
err.cr();
char buf[16]; // Prevent process from exiting upon "read error" without consuming all CPU while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); }
return buf[0] == 'y' || buf[0] == 'Y';
}
//////////////////////////////////////////////////////////////////////////////// // runtime exit support
// Note: os::shutdown() might be called very early during initialization, or // called from signal handler. Before adding something to os::shutdown(), make // sure it is async-safe and can handle partially initialized VM. void os::shutdown() {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.58 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.