8220774: Add HandshakeALot diag option

Reviewed-by: dcubed, dholmes
This commit is contained in:
Robbin Ehn 2019-03-26 12:08:51 +01:00
parent afa3178149
commit ed3542d53b
10 changed files with 171 additions and 125 deletions

@ -371,6 +371,10 @@ define_pd_global(uint64_t,MaxRAM, 1ULL*G);
"Generate a lot of safepoints. This works with " \
"GuaranteedSafepointInterval") \
\
diagnostic(bool, HandshakeALot, false, \
"Generate a lot of handshakes. This works with " \
"GuaranteedSafepointInterval") \
\
product_pd(bool, BackgroundCompilation, \
"A thread requesting compilation is not blocked during " \
"compilation") \

@ -2928,9 +2928,21 @@ void JavaThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
}
}
#ifdef ASSERT
void JavaThread::verify_states_for_handshake() {
// This checks that the thread has a correct frame state during a handshake.
assert((!has_last_Java_frame() && java_call_counter() == 0) ||
(has_last_Java_frame() && java_call_counter() > 0),
"unexpected frame info: has_last_frame=%d, java_call_counter=%d",
has_last_Java_frame(), java_call_counter());
}
#endif
void JavaThread::nmethods_do(CodeBlobClosure* cf) {
assert((!has_last_Java_frame() && java_call_counter() == 0) ||
(has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!");
(has_last_Java_frame() && java_call_counter() > 0),
"unexpected frame info: has_last_frame=%d, java_call_counter=%d",
has_last_Java_frame(), java_call_counter());
if (has_last_Java_frame()) {
// Traverse the execution stack

@ -1874,6 +1874,9 @@ class JavaThread: public Thread {
// RedefineClasses Support
void metadata_do(MetadataClosure* f);
// Debug method asserting thread states are correct during a handshake operation.
DEBUG_ONLY(void verify_states_for_handshake();)
// Misc. operations
char* name() const { return (char*)get_thread_name(); }
void print_on(outputStream* st, bool print_extended_info) const;

@ -434,22 +434,35 @@ void VMThread::evaluate_operation(VM_Operation* op) {
static VM_None safepointALot_op("SafepointALot");
static VM_Cleanup cleanup_op;
VM_Operation* VMThread::no_op_safepoint(bool check_time) {
class HandshakeALotTC : public ThreadClosure {
public:
virtual void do_thread(Thread* thread) {
#ifdef ASSERT
assert(thread->is_Java_thread(), "must be");
JavaThread* jt = (JavaThread*)thread;
jt->verify_states_for_handshake();
#endif
}
};
VM_Operation* VMThread::no_op_safepoint() {
// Check for handshakes first since we may need to return a VMop.
if (HandshakeALot) {
HandshakeALotTC haltc;
Handshake::execute(&haltc);
}
// Check for a cleanup before SafepointALot to keep stats correct.
long interval_ms = SafepointTracing::time_since_last_safepoint_ms();
bool max_time_exceeded = GuaranteedSafepointInterval != 0 &&
(interval_ms >= GuaranteedSafepointInterval);
if (max_time_exceeded && SafepointSynchronize::is_cleanup_needed()) {
return &cleanup_op;
}
if (SafepointALot) {
return &safepointALot_op;
}
if (!SafepointSynchronize::is_cleanup_needed()) {
return NULL;
}
if (check_time) {
long interval_ms = SafepointTracing::time_since_last_safepoint_ms();
bool max_time_exceeded = GuaranteedSafepointInterval != 0 &&
(interval_ms > GuaranteedSafepointInterval);
if (!max_time_exceeded) {
return NULL;
}
}
return &cleanup_op;
// Nothing to be done.
return NULL;
}
void VMThread::loop() {
@ -491,19 +504,22 @@ void VMThread::loop() {
exit(-1);
}
if (timedout && (_cur_vm_operation = VMThread::no_op_safepoint(false)) != NULL) {
MutexUnlockerEx mul(VMOperationQueue_lock,
Mutex::_no_safepoint_check_flag);
// Force a safepoint since we have not had one for at least
// 'GuaranteedSafepointInterval' milliseconds. This will run all
// the clean-up processing that needs to be done regularly at a
// safepoint
SafepointSynchronize::begin();
#ifdef ASSERT
if (timedout) {
// Have to unlock VMOperationQueue_lock just in case no_op_safepoint()
// has to do a handshake.
MutexUnlockerEx mul(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag);
if ((_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) {
// Force a safepoint since we have not had one for at least
// 'GuaranteedSafepointInterval' milliseconds and we need to clean
// something. This will run all the clean-up processing that needs
// to be done at a safepoint.
SafepointSynchronize::begin();
#ifdef ASSERT
if (GCALotAtAllSafepoints) InterfaceSupport::check_gc_alot();
#endif
SafepointSynchronize::end();
_cur_vm_operation = NULL;
#endif
SafepointSynchronize::end();
_cur_vm_operation = NULL;
}
}
_cur_vm_operation = _vm_queue->remove_next();
@ -615,10 +631,9 @@ void VMThread::loop() {
VMOperationRequest_lock->notify_all();
}
//
// We want to make sure that we get to a safepoint regularly.
//
if ((_cur_vm_operation = VMThread::no_op_safepoint(false)) != NULL) {
// We want to make sure that we get to a safepoint regularly
// even when executing VMops that don't require safepoints.
if ((_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) {
HandleMark hm(VMThread::vm_thread());
SafepointSynchronize::begin();
SafepointSynchronize::end();

@ -123,7 +123,7 @@ class VMThread: public NamedThread {
static VMOperationTimeoutTask* _timeout_task;
static VM_Operation* no_op_safepoint(bool check_time);
static VM_Operation* no_op_safepoint();
void evaluate_operation(VM_Operation* op);

@ -80,7 +80,6 @@ gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java 8193639 solaris-all
# :hotspot_runtime
runtime/handshake/HandshakeWalkSuspendExitTest.java 8214174 generic-all
runtime/NMT/CheckForProperDetailStackTrace.java 8218458 generic-all
runtime/SharedArchiveFile/SASymbolTableTest.java 8193639 solaris-all
runtime/containers/docker/TestCPUSets.java 8220672 generic-all

@ -26,6 +26,7 @@
* @bug 8167108
* @summary Stress test java.lang.Thread.suspend() at thread exit.
* @run main/othervm -Xlog:thread+smr=debug SuspendAtExit
* @run main/othervm -Xlog:thread+smr=debug -XX:+UnlockDiagnosticVMOptions -XX:GuaranteedSafepointInterval=1 -XX:+HandshakeALot SuspendAtExit
*/
import java.util.concurrent.CountDownLatch;

@ -0,0 +1,104 @@
/*
* Copyright (c) 2019, 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.
*
*/
/*
* @test HandshakeSuspendExitTest
* @summary This test tries to stress the handshakes with new and exiting threads while suspending them.
* @library /testlibrary /test/lib
* @build HandshakeSuspendExitTest
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:GuaranteedSafepointInterval=1 -XX:+HandshakeALot HandshakeSuspendExitTest
*/
public class HandshakeSuspendExitTest implements Runnable {
static Thread[] _suspend_threads = new Thread[16];
static volatile boolean _exit_now = false;
static java.util.concurrent.Semaphore _sem = new java.util.concurrent.Semaphore(0);
@Override
public void run() {
_sem.release();
while (!_exit_now) {
// Leave last 2 threads running.
for (int i = 0; i < _suspend_threads.length - 2; i++) {
if (Thread.currentThread() != thr) {
thr.suspend();
thr.resume();
}
}
}
_sem.release();
}
public static void main(String... args) throws Exception {
HandshakeSuspendExitTest test = new HandshakeSuspendExitTest();
// Fire-up suspend threads.
for (int i = 0; i < _suspend_threads.length; i++) {
_suspend_threads[i] = new Thread(test);
}
for (int i = 0; i < _suspend_threads.length; i++) {
_suspend_threads[i].start();
}
// Wait for all suspend-threads to start looping.
for (Thread thr : _suspend_threads) {
_sem.acquire();
}
// Fire-up exiting threads.
Thread[] exit_threads = new Thread[128];
for (int i = 0; i < exit_threads.length; i++) {
exit_threads[i] = new Thread();
exit_threads[i].start();
}
// Try to suspend them.
for (Thread thr : exit_threads) {
thr.suspend();
}
for (Thread thr : exit_threads) {
thr.resume();
}
// Start exit and join.
_exit_now = true;
int waiting = _suspend_threads.length;
do {
// Resume any worker threads that might have suspended
// each other at exactly the same time so they can see
// _exit_now and check in via the semaphore.
for (Thread thr : _suspend_threads) {
thr.resume();
}
while (_sem.tryAcquire()) {
--waiting;
}
} while (waiting > 0);
for (Thread thr : _suspend_threads) {
thr.join();
}
for (Thread thr : exit_threads) {
thr.join();
}
}
}

@ -62,6 +62,7 @@ public class HandshakeTransitionTest {
true,
"-Djava.library.path=" + lib,
"-XX:+SafepointALot",
"-XX:+HandshakeALot",
"-XX:GuaranteedSafepointInterval=20",
"-Xlog:ergo*",
"-XX:ParallelGCThreads=1",

@ -1,93 +0,0 @@
/*
* Copyright (c) 2018, 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.
*
*/
/*
* @test HandshakeWalkSuspendExitTest
* @summary This test tries to stress the handshakes with new and exiting threads while suspending them.
* @library /testlibrary /test/lib
* @build HandshakeWalkSuspendExitTest
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkSuspendExitTest
*/
import jdk.test.lib.Asserts;
import sun.hotspot.WhiteBox;
public class HandshakeWalkSuspendExitTest implements Runnable {
static final int _test_threads = 8;
static final int _test_exit_threads = 128;
static Thread[] _threads = new Thread[_test_threads];
static volatile boolean exit_now = false;
static java.util.concurrent.Semaphore _sem = new java.util.concurrent.Semaphore(0);
@Override
public void run() {
WhiteBox wb = WhiteBox.getWhiteBox();
while (!exit_now) {
_sem.release();
// We only suspend threads on even index and not ourself.
// Otherwise we can accidentially suspend all threads.
for (int i = 0; i < _threads.length; i += 2) {
wb.handshakeWalkStack(null /* ignored */, true /* stackwalk all threads */);
if (Thread.currentThread() != _threads[i]) {
_threads[i].suspend();
_threads[i].resume();
}
}
for (int i = 0; i < _threads.length; i += 2) {
wb.handshakeWalkStack(_threads[i] /* thread to stackwalk */, false /* stackwalk one thread */);
if (Thread.currentThread() != _threads[i]) {
_threads[i].suspend();
_threads[i].resume();
}
}
}
}
public static void main(String... args) throws Exception {
HandshakeWalkSuspendExitTest test = new HandshakeWalkSuspendExitTest();
for (int i = 0; i < _threads.length; i++) {
_threads[i] = new Thread(test);
_threads[i].start();
}
for (int i = 0; i < _test_threads; i++) {
_sem.acquire();
}
Thread[] exit_threads = new Thread[_test_exit_threads];
for (int i = 0; i < _test_exit_threads; i++) {
exit_threads[i] = new Thread(new Runnable() { public void run() {} });
exit_threads[i].start();
}
exit_now = true;
for (int i = 0; i < _threads.length; i++) {
_threads[i].join();
}
for (int i = 0; i < exit_threads.length; i++) {
exit_threads[i].join();
}
}
}