# HG changeset patch # User dsamersoff # Date 1394490365 0 # Node ID 99ed2a7d2f8754b69110c495b8ce502d8a66ce87 # Parent 3b188862d691ff2d1acd807584d724dafb2bf7c4 8009062: poor performance of JNI AttachCurrentThread after fix for 7017193 Summary: don't re-evaluate stack bounds for main thread before install guard page Reviewed-by: coleenp, dholmes, dlong diff -r 3b188862d691 -r 99ed2a7d2f87 src/os/linux/vm/os_linux.cpp --- a/src/os/linux/vm/os_linux.cpp Wed Feb 19 21:08:04 2014 +0000 +++ b/src/os/linux/vm/os_linux.cpp Mon Mar 10 22:26:05 2014 +0000 @@ -2861,6 +2861,53 @@ return res != (uintptr_t) MAP_FAILED; } +static +address get_stack_commited_bottom(address bottom, size_t size) { + address nbot = bottom; + address ntop = bottom + size; + + size_t page_sz = os::vm_page_size(); + unsigned pages = size / page_sz; + + unsigned char vec[1]; + unsigned imin = 1, imax = pages + 1, imid; + int mincore_return_value; + + while (imin < imax) { + imid = (imax + imin) / 2; + nbot = ntop - (imid * page_sz); + + // Use a trick with mincore to check whether the page is mapped or not. + // mincore sets vec to 1 if page resides in memory and to 0 if page + // is swapped output but if page we are asking for is unmapped + // it returns -1,ENOMEM + mincore_return_value = mincore(nbot, page_sz, vec); + + if (mincore_return_value == -1) { + // Page is not mapped go up + // to find first mapped page + if (errno != EAGAIN) { + assert(errno == ENOMEM, "Unexpected mincore errno"); + imax = imid; + } + } else { + // Page is mapped go down + // to find first not mapped page + imin = imid + 1; + } + } + + nbot = nbot + page_sz; + + // Adjust stack bottom one page up if last checked page is not mapped + if (mincore_return_value == -1) { + nbot = nbot + page_sz; + } + + return nbot; +} + + // Linux uses a growable mapping for the stack, and if the mapping for // the stack guard pages is not removed when we detach a thread the // stack cannot grow beyond the pages where the stack guard was @@ -2875,59 +2922,37 @@ // So, we need to know the extent of the stack mapping when // create_stack_guard_pages() is called. -// Find the bounds of the stack mapping. Return true for success. -// // We only need this for stacks that are growable: at the time of // writing thread stacks don't use growable mappings (i.e. those // creeated with MAP_GROWSDOWN), and aren't marked "[stack]", so this // only applies to the main thread. -static -bool get_stack_bounds(uintptr_t *bottom, uintptr_t *top) { - - char buf[128]; - int fd, sz; - - if ((fd = ::open("/proc/self/maps", O_RDONLY)) < 0) { - return false; - } - - const char kw[] = "[stack]"; - const int kwlen = sizeof(kw)-1; - - // Address part of /proc/self/maps couldn't be more than 128 bytes - while ((sz = os::get_line_chars(fd, buf, sizeof(buf))) > 0) { - if (sz > kwlen && ::memcmp(buf+sz-kwlen, kw, kwlen) == 0) { - // Extract addresses - if (sscanf(buf, "%" SCNxPTR "-%" SCNxPTR, bottom, top) == 2) { - uintptr_t sp = (uintptr_t) __builtin_frame_address(0); - if (sp >= *bottom && sp <= *top) { - ::close(fd); - return true; - } - } - } - } - - ::close(fd); - return false; -} - - // If the (growable) stack mapping already extends beyond the point // where we're going to put our guard pages, truncate the mapping at // that point by munmap()ping it. This ensures that when we later // munmap() the guard pages we don't leave a hole in the stack -// mapping. This only affects the main/initial thread, but guard -// against future OS changes +// mapping. This only affects the main/initial thread + bool os::create_stack_guard_pages(char* addr, size_t size) { - uintptr_t stack_extent, stack_base; - bool chk_bounds = NOT_DEBUG(os::Linux::is_initial_thread()) DEBUG_ONLY(true); - if (chk_bounds && get_stack_bounds(&stack_extent, &stack_base)) { - assert(os::Linux::is_initial_thread(), - "growable stack in non-initial thread"); - if (stack_extent < (uintptr_t)addr) - ::munmap((void*)stack_extent, (uintptr_t)addr - stack_extent); + + if (os::Linux::is_initial_thread()) { + // As we manually grow stack up to bottom inside create_attached_thread(), + // it's likely that os::Linux::initial_thread_stack_bottom is mapped and + // we don't need to do anything special. + // Check it first, before calling heavy function. + uintptr_t stack_extent = (uintptr_t) os::Linux::initial_thread_stack_bottom(); + unsigned char vec[1]; + + if (mincore((address)stack_extent, os::vm_page_size(), vec) == -1) { + // Fallback to slow path on all errors, including EAGAIN + stack_extent = (uintptr_t) get_stack_commited_bottom( + os::Linux::initial_thread_stack_bottom(), + (size_t)addr - stack_extent); + } + + if (stack_extent < (uintptr_t)addr) { + ::munmap((void*)stack_extent, (uintptr_t)(addr - stack_extent)); + } } return os::commit_memory(addr, size, !ExecMem); @@ -2936,13 +2961,13 @@ // If this is a growable mapping, remove the guard pages entirely by // munmap()ping them. If not, just call uncommit_memory(). This only // affects the main/initial thread, but guard against future OS changes +// It's safe to always unmap guard pages for initial thread because we +// always place it right after end of the mapped region + bool os::remove_stack_guard_pages(char* addr, size_t size) { uintptr_t stack_extent, stack_base; - bool chk_bounds = NOT_DEBUG(os::Linux::is_initial_thread()) DEBUG_ONLY(true); - if (chk_bounds && get_stack_bounds(&stack_extent, &stack_base)) { - assert(os::Linux::is_initial_thread(), - "growable stack in non-initial thread"); - + + if (os::Linux::is_initial_thread()) { return ::munmap(addr, size) == 0; } diff -r 3b188862d691 -r 99ed2a7d2f87 src/share/vm/runtime/os.cpp --- a/src/share/vm/runtime/os.cpp Wed Feb 19 21:08:04 2014 +0000 +++ b/src/share/vm/runtime/os.cpp Mon Mar 10 22:26:05 2014 +0000 @@ -1329,40 +1329,3 @@ return result; } -// Read file line by line, if line is longer than bsize, -// skip rest of line. -int os::get_line_chars(int fd, char* buf, const size_t bsize){ - size_t sz, i = 0; - - // read until EOF, EOL or buf is full - while ((sz = (int) read(fd, &buf[i], 1)) == 1 && i < (bsize-2) && buf[i] != '\n') { - ++i; - } - - if (buf[i] == '\n') { - // EOL reached so ignore EOL character and return - - buf[i] = 0; - return (int) i; - } - - buf[i+1] = 0; - - if (sz != 1) { - // EOF reached. if we read chars before EOF return them and - // return EOF on next call otherwise return EOF - - return (i == 0) ? -1 : (int) i; - } - - // line is longer than size of buf, skip to EOL - char ch; - while (read(fd, &ch, 1) == 1 && ch != '\n') { - // Do nothing - } - - // return initial part of line that fits in buf. - // If we reached EOF, it will be returned on next call. - - return (int) i; -} diff -r 3b188862d691 -r 99ed2a7d2f87 src/share/vm/runtime/os.hpp --- a/src/share/vm/runtime/os.hpp Wed Feb 19 21:08:04 2014 +0000 +++ b/src/share/vm/runtime/os.hpp Mon Mar 10 22:26:05 2014 +0000 @@ -683,10 +683,6 @@ // Hook for os specific jvm options that we don't want to abort on seeing static bool obsolete_option(const JavaVMOption *option); - // Read file line by line. If line is longer than bsize, - // rest of line is skipped. Returns number of bytes read or -1 on EOF - static int get_line_chars(int fd, char *buf, const size_t bsize); - // Extensions #include "runtime/os_ext.hpp"