8009062: poor performance of JNI AttachCurrentThread after fix for 7017193

Thu, 29 Aug 2013 21:48:23 +0400

author
dsamersoff
date
Thu, 29 Aug 2013 21:48:23 +0400
changeset 5612
d8e99408faad
parent 5610
76482cbba706
child 5613
cef1e56a4d88

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

src/os/linux/vm/os_linux.cpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/os.cpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/os.hpp file | annotate | diff | comparison | revisions
test/runtime/InitialThreadOverflow/DoOverflow.java file | annotate | diff | comparison | revisions
test/runtime/InitialThreadOverflow/invoke.cxx file | annotate | diff | comparison | revisions
test/runtime/InitialThreadOverflow/testme.sh file | annotate | diff | comparison | revisions
     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 $?

mercurial