Thu, 29 Aug 2013 21:48:23 +0400
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
1.1 --- a/src/os/linux/vm/os_linux.cpp Thu Aug 29 10:33:13 2013 -0400 1.2 +++ b/src/os/linux/vm/os_linux.cpp Thu Aug 29 21:48:23 2013 +0400 1.3 @@ -2943,6 +2943,53 @@ 1.4 return res != (uintptr_t) MAP_FAILED; 1.5 } 1.6 1.7 +static 1.8 +address get_stack_commited_bottom(address bottom, size_t size) { 1.9 + address nbot = bottom; 1.10 + address ntop = bottom + size; 1.11 + 1.12 + size_t page_sz = os::vm_page_size(); 1.13 + unsigned pages = size / page_sz; 1.14 + 1.15 + unsigned char vec[1]; 1.16 + unsigned imin = 1, imax = pages + 1, imid; 1.17 + int mincore_return_value; 1.18 + 1.19 + while (imin < imax) { 1.20 + imid = (imax + imin) / 2; 1.21 + nbot = ntop - (imid * page_sz); 1.22 + 1.23 + // Use a trick with mincore to check whether the page is mapped or not. 1.24 + // mincore sets vec to 1 if page resides in memory and to 0 if page 1.25 + // is swapped output but if page we are asking for is unmapped 1.26 + // it returns -1,ENOMEM 1.27 + mincore_return_value = mincore(nbot, page_sz, vec); 1.28 + 1.29 + if (mincore_return_value == -1) { 1.30 + // Page is not mapped go up 1.31 + // to find first mapped page 1.32 + if (errno != EAGAIN) { 1.33 + assert(errno == ENOMEM, "Unexpected mincore errno"); 1.34 + imax = imid; 1.35 + } 1.36 + } else { 1.37 + // Page is mapped go down 1.38 + // to find first not mapped page 1.39 + imin = imid + 1; 1.40 + } 1.41 + } 1.42 + 1.43 + nbot = nbot + page_sz; 1.44 + 1.45 + // Adjust stack bottom one page up if last checked page is not mapped 1.46 + if (mincore_return_value == -1) { 1.47 + nbot = nbot + page_sz; 1.48 + } 1.49 + 1.50 + return nbot; 1.51 +} 1.52 + 1.53 + 1.54 // Linux uses a growable mapping for the stack, and if the mapping for 1.55 // the stack guard pages is not removed when we detach a thread the 1.56 // stack cannot grow beyond the pages where the stack guard was 1.57 @@ -2957,59 +3004,37 @@ 1.58 // So, we need to know the extent of the stack mapping when 1.59 // create_stack_guard_pages() is called. 1.60 1.61 -// Find the bounds of the stack mapping. Return true for success. 1.62 -// 1.63 // We only need this for stacks that are growable: at the time of 1.64 // writing thread stacks don't use growable mappings (i.e. those 1.65 // creeated with MAP_GROWSDOWN), and aren't marked "[stack]", so this 1.66 // only applies to the main thread. 1.67 1.68 -static 1.69 -bool get_stack_bounds(uintptr_t *bottom, uintptr_t *top) { 1.70 - 1.71 - char buf[128]; 1.72 - int fd, sz; 1.73 - 1.74 - if ((fd = ::open("/proc/self/maps", O_RDONLY)) < 0) { 1.75 - return false; 1.76 - } 1.77 - 1.78 - const char kw[] = "[stack]"; 1.79 - const int kwlen = sizeof(kw)-1; 1.80 - 1.81 - // Address part of /proc/self/maps couldn't be more than 128 bytes 1.82 - while ((sz = os::get_line_chars(fd, buf, sizeof(buf))) > 0) { 1.83 - if (sz > kwlen && ::memcmp(buf+sz-kwlen, kw, kwlen) == 0) { 1.84 - // Extract addresses 1.85 - if (sscanf(buf, "%" SCNxPTR "-%" SCNxPTR, bottom, top) == 2) { 1.86 - uintptr_t sp = (uintptr_t) __builtin_frame_address(0); 1.87 - if (sp >= *bottom && sp <= *top) { 1.88 - ::close(fd); 1.89 - return true; 1.90 - } 1.91 - } 1.92 - } 1.93 - } 1.94 - 1.95 - ::close(fd); 1.96 - return false; 1.97 -} 1.98 - 1.99 - 1.100 // If the (growable) stack mapping already extends beyond the point 1.101 // where we're going to put our guard pages, truncate the mapping at 1.102 // that point by munmap()ping it. This ensures that when we later 1.103 // munmap() the guard pages we don't leave a hole in the stack 1.104 -// mapping. This only affects the main/initial thread, but guard 1.105 -// against future OS changes 1.106 +// mapping. This only affects the main/initial thread 1.107 + 1.108 bool os::pd_create_stack_guard_pages(char* addr, size_t size) { 1.109 - uintptr_t stack_extent, stack_base; 1.110 - bool chk_bounds = NOT_DEBUG(os::Linux::is_initial_thread()) DEBUG_ONLY(true); 1.111 - if (chk_bounds && get_stack_bounds(&stack_extent, &stack_base)) { 1.112 - assert(os::Linux::is_initial_thread(), 1.113 - "growable stack in non-initial thread"); 1.114 - if (stack_extent < (uintptr_t)addr) 1.115 - ::munmap((void*)stack_extent, (uintptr_t)addr - stack_extent); 1.116 + 1.117 + if (os::Linux::is_initial_thread()) { 1.118 + // As we manually grow stack up to bottom inside create_attached_thread(), 1.119 + // it's likely that os::Linux::initial_thread_stack_bottom is mapped and 1.120 + // we don't need to do anything special. 1.121 + // Check it first, before calling heavy function. 1.122 + uintptr_t stack_extent = (uintptr_t) os::Linux::initial_thread_stack_bottom(); 1.123 + unsigned char vec[1]; 1.124 + 1.125 + if (mincore((address)stack_extent, os::vm_page_size(), vec) == -1) { 1.126 + // Fallback to slow path on all errors, including EAGAIN 1.127 + stack_extent = (uintptr_t) get_stack_commited_bottom( 1.128 + os::Linux::initial_thread_stack_bottom(), 1.129 + (size_t)addr - stack_extent); 1.130 + } 1.131 + 1.132 + if (stack_extent < (uintptr_t)addr) { 1.133 + ::munmap((void*)stack_extent, (uintptr_t)(addr - stack_extent)); 1.134 + } 1.135 } 1.136 1.137 return os::commit_memory(addr, size, !ExecMem); 1.138 @@ -3018,13 +3043,13 @@ 1.139 // If this is a growable mapping, remove the guard pages entirely by 1.140 // munmap()ping them. If not, just call uncommit_memory(). This only 1.141 // affects the main/initial thread, but guard against future OS changes 1.142 +// It's safe to always unmap guard pages for initial thread because we 1.143 +// always place it right after end of the mapped region 1.144 + 1.145 bool os::remove_stack_guard_pages(char* addr, size_t size) { 1.146 uintptr_t stack_extent, stack_base; 1.147 - bool chk_bounds = NOT_DEBUG(os::Linux::is_initial_thread()) DEBUG_ONLY(true); 1.148 - if (chk_bounds && get_stack_bounds(&stack_extent, &stack_base)) { 1.149 - assert(os::Linux::is_initial_thread(), 1.150 - "growable stack in non-initial thread"); 1.151 - 1.152 + 1.153 + if (os::Linux::is_initial_thread()) { 1.154 return ::munmap(addr, size) == 0; 1.155 } 1.156
2.1 --- a/src/share/vm/runtime/os.cpp Thu Aug 29 10:33:13 2013 -0400 2.2 +++ b/src/share/vm/runtime/os.cpp Thu Aug 29 21:48:23 2013 +0400 2.3 @@ -1424,44 +1424,6 @@ 2.4 return result; 2.5 } 2.6 2.7 -// Read file line by line, if line is longer than bsize, 2.8 -// skip rest of line. 2.9 -int os::get_line_chars(int fd, char* buf, const size_t bsize){ 2.10 - size_t sz, i = 0; 2.11 - 2.12 - // read until EOF, EOL or buf is full 2.13 - while ((sz = (int) read(fd, &buf[i], 1)) == 1 && i < (bsize-2) && buf[i] != '\n') { 2.14 - ++i; 2.15 - } 2.16 - 2.17 - if (buf[i] == '\n') { 2.18 - // EOL reached so ignore EOL character and return 2.19 - 2.20 - buf[i] = 0; 2.21 - return (int) i; 2.22 - } 2.23 - 2.24 - buf[i+1] = 0; 2.25 - 2.26 - if (sz != 1) { 2.27 - // EOF reached. if we read chars before EOF return them and 2.28 - // return EOF on next call otherwise return EOF 2.29 - 2.30 - return (i == 0) ? -1 : (int) i; 2.31 - } 2.32 - 2.33 - // line is longer than size of buf, skip to EOL 2.34 - char ch; 2.35 - while (read(fd, &ch, 1) == 1 && ch != '\n') { 2.36 - // Do nothing 2.37 - } 2.38 - 2.39 - // return initial part of line that fits in buf. 2.40 - // If we reached EOF, it will be returned on next call. 2.41 - 2.42 - return (int) i; 2.43 -} 2.44 - 2.45 void os::SuspendedThreadTask::run() { 2.46 assert(Threads_lock->owned_by_self() || (_thread == VMThread::vm_thread()), "must have threads lock to call this"); 2.47 internal_do_task();
3.1 --- a/src/share/vm/runtime/os.hpp Thu Aug 29 10:33:13 2013 -0400 3.2 +++ b/src/share/vm/runtime/os.hpp Thu Aug 29 21:48:23 2013 +0400 3.3 @@ -725,10 +725,6 @@ 3.4 // Hook for os specific jvm options that we don't want to abort on seeing 3.5 static bool obsolete_option(const JavaVMOption *option); 3.6 3.7 - // Read file line by line. If line is longer than bsize, 3.8 - // rest of line is skipped. Returns number of bytes read or -1 on EOF 3.9 - static int get_line_chars(int fd, char *buf, const size_t bsize); 3.10 - 3.11 // Extensions 3.12 #include "runtime/os_ext.hpp" 3.13
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/runtime/InitialThreadOverflow/DoOverflow.java Thu Aug 29 21:48:23 2013 +0400 4.3 @@ -0,0 +1,41 @@ 4.4 +/* 4.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +public class DoOverflow { 4.28 + 4.29 + static int count; 4.30 + 4.31 + public void overflow() { 4.32 + count+=1; 4.33 + overflow(); 4.34 + } 4.35 + 4.36 + public static void printIt() { 4.37 + System.out.println("Going to overflow stack"); 4.38 + try { 4.39 + new DoOverflow().overflow(); 4.40 + } catch(java.lang.StackOverflowError e) { 4.41 + System.out.println("Overflow OK " + count); 4.42 + } 4.43 + } 4.44 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/runtime/InitialThreadOverflow/invoke.cxx Thu Aug 29 21:48:23 2013 +0400 5.3 @@ -0,0 +1,70 @@ 5.4 +/* 5.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + */ 5.26 + 5.27 +#include <assert.h> 5.28 +#include <jni.h> 5.29 + 5.30 +#include <pthread.h> 5.31 + 5.32 +JavaVM* jvm; 5.33 + 5.34 +void * 5.35 +floobydust (void *p) { 5.36 + JNIEnv *env; 5.37 + 5.38 + jvm->AttachCurrentThread((void**)&env, NULL); 5.39 + 5.40 + jclass class_id = env->FindClass ("DoOverflow"); 5.41 + assert (class_id); 5.42 + 5.43 + jmethodID method_id = env->GetStaticMethodID(class_id, "printIt", "()V"); 5.44 + assert (method_id); 5.45 + 5.46 + env->CallStaticVoidMethod(class_id, method_id, NULL); 5.47 + 5.48 + jvm->DetachCurrentThread(); 5.49 +} 5.50 + 5.51 +int 5.52 +main (int argc, const char** argv) { 5.53 + JavaVMOption options[1]; 5.54 + options[0].optionString = (char*) "-Xss320k"; 5.55 + 5.56 + JavaVMInitArgs vm_args; 5.57 + vm_args.version = JNI_VERSION_1_2; 5.58 + vm_args.ignoreUnrecognized = JNI_TRUE; 5.59 + vm_args.options = options; 5.60 + vm_args.nOptions = 1; 5.61 + 5.62 + JNIEnv* env; 5.63 + jint result = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); 5.64 + assert(result >= 0); 5.65 + 5.66 + pthread_t thr; 5.67 + pthread_create(&thr, NULL, floobydust, NULL); 5.68 + pthread_join(thr, NULL); 5.69 + 5.70 + floobydust(NULL); 5.71 + 5.72 + return 0; 5.73 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/runtime/InitialThreadOverflow/testme.sh Thu Aug 29 21:48:23 2013 +0400 6.3 @@ -0,0 +1,73 @@ 6.4 +#!/bin/sh 6.5 + 6.6 +# Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. 6.7 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.8 +# 6.9 +# This code is free software; you can redistribute it and/or modify it 6.10 +# under the terms of the GNU General Public License version 2 only, as 6.11 +# published by the Free Software Foundation. 6.12 +# 6.13 +# This code is distributed in the hope that it will be useful, but WITHOUT 6.14 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.15 +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.16 +# version 2 for more details (a copy is included in the LICENSE file that 6.17 +# accompanied this code). 6.18 +# 6.19 +# You should have received a copy of the GNU General Public License version 6.20 +# 2 along with this work; if not, write to the Free Software Foundation, 6.21 +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.22 +# 6.23 +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.24 +# or visit www.oracle.com if you need additional information or have any 6.25 +# questions. 6.26 + 6.27 +# @test testme.sh 6.28 +# @bug 8009062 6.29 +# @summary Poor performance of JNI AttachCurrentThread after fix for 7017193 6.30 +# @compile DoOverflow.java 6.31 +# @run shell testme.sh 6.32 + 6.33 +set -x 6.34 +if [ "${TESTSRC}" = "" ] 6.35 +then 6.36 + TESTSRC=${PWD} 6.37 + echo "TESTSRC not set. Using "${TESTSRC}" as default" 6.38 +fi 6.39 +echo "TESTSRC=${TESTSRC}" 6.40 +## Adding common setup Variables for running shell tests. 6.41 +. ${TESTSRC}/../../test_env.sh 6.42 + 6.43 +if [ "${VM_OS}" != "linux" ] 6.44 +then 6.45 + echo "Test only valid for Linux" 6.46 + exit 0 6.47 +fi 6.48 + 6.49 +gcc_cmd=`which gcc` 6.50 +if [ "x$gcc_cmd" == "x" ]; then 6.51 + echo "WARNING: gcc not found. Cannot execute test." 2>&1 6.52 + exit 0; 6.53 +fi 6.54 + 6.55 +CFLAGS="-m${VM_BITS}" 6.56 + 6.57 +LD_LIBRARY_PATH=.:${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE}:/usr/lib:$LD_LIBRARY_PATH 6.58 +export LD_LIBRARY_PATH 6.59 + 6.60 +cp ${TESTSRC}${FS}invoke.cxx . 6.61 + 6.62 +# Copy the result of our @compile action: 6.63 +cp ${TESTCLASSES}${FS}DoOverflow.class . 6.64 + 6.65 +echo "Compilation flag: ${COMP_FLAG}" 6.66 +# Note pthread may not be found thus invoke creation will fail to be created. 6.67 +# Check to ensure you have a /usr/lib/libpthread.so if you don't please look 6.68 +# for /usr/lib/`uname -m`-linux-gnu version ensure to add that path to below compilation. 6.69 + 6.70 +$gcc_cmd -DLINUX ${CFLAGS} -o invoke \ 6.71 + -I${COMPILEJAVA}/include -I${COMPILEJAVA}/include/linux \ 6.72 + -L${COMPILEJAVA}/jre/lib/${VM_CPU}/${VM_TYPE} \ 6.73 + -ljvm -lpthread invoke.cxx 6.74 + 6.75 +./invoke 6.76 +exit $?