8231595: [TEST] develop a test case for SuspendThreadList including current thread
Extend test coverage for SuspendThreadList Reviewed-by: amenkov, cjplummer, dholmes
This commit is contained in:
parent
341c223f92
commit
4091899994
test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread
222
test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread/SuspendWithCurrentThread.java
Normal file
222
test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread/SuspendWithCurrentThread.java
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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
|
||||
* @bug 8231595
|
||||
* @summary [TEST] develop a test case for SuspendThreadList including current thread
|
||||
* @library /test/lib
|
||||
* @compile SuspendWithCurrentThread.java
|
||||
* @run main/othervm/native -agentlib:SuspendWithCurrentThread SuspendWithCurrentThread SuspenderIndex=first
|
||||
* @run main/othervm/native -agentlib:SuspendWithCurrentThread SuspendWithCurrentThread SuspenderIndex=last
|
||||
*/
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class SuspendWithCurrentThread {
|
||||
private static final String AGENT_LIB = "SuspendWithCurrentThread";
|
||||
private static final String SUSPENDER_OPT = "SuspenderIndex=";
|
||||
private static final int THREADS_COUNT = 10;
|
||||
|
||||
private static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
private static native void registerTestedThreads(Thread[] threads);
|
||||
private static native boolean checkTestedThreadsSuspended();
|
||||
private static native void resumeTestedThreads();
|
||||
private static native void releaseTestedThreadsInfo();
|
||||
|
||||
// The suspender thread index defines the thread which has to suspend
|
||||
// the tested threads including itself with the JVMTI SuspendThreadList
|
||||
private static int suspenderIndex;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
try {
|
||||
System.loadLibrary(AGENT_LIB);
|
||||
log("Loaded library: " + AGENT_LIB);
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
log("Failed to load library: " + AGENT_LIB);
|
||||
log("java.library.path: " + System.getProperty("java.library.path"));
|
||||
throw ule;
|
||||
}
|
||||
if (args.length != 1) {
|
||||
throw new RuntimeException("Main: wrong arguments count: " + args.length + ", expected: 1");
|
||||
}
|
||||
String arg = args[0];
|
||||
if (arg.equals(SUSPENDER_OPT + "first")) {
|
||||
suspenderIndex = 0;
|
||||
} else if (arg.equals(SUSPENDER_OPT + "last")) {
|
||||
suspenderIndex = THREADS_COUNT - 1;
|
||||
} else {
|
||||
throw new RuntimeException("Main: wrong argument: " + arg + ", expected: SuspenderIndex={first|last}");
|
||||
}
|
||||
log("Main: suspenderIndex: " + suspenderIndex);
|
||||
|
||||
SuspendWithCurrentThread test = new SuspendWithCurrentThread();
|
||||
test.run();
|
||||
}
|
||||
|
||||
private ThreadToSuspend[] startTestedThreads(int threadsCount) throws RuntimeException {
|
||||
ThreadToSuspend[]threads = new ThreadToSuspend[threadsCount];
|
||||
|
||||
// create tested threads
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
threads[i] = new ThreadToSuspend("ThreadToSuspend#" + i,
|
||||
i == suspenderIndex // isSuspender
|
||||
);
|
||||
}
|
||||
log("Main: starting tested threads");
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
threads[i].start();
|
||||
if (!threads[i].checkReady()) {
|
||||
throw new RuntimeException("Main: unable to prepare tested thread: " + threads[i]);
|
||||
}
|
||||
}
|
||||
log("Main: tested threads started");
|
||||
|
||||
registerTestedThreads(threads);
|
||||
return threads;
|
||||
}
|
||||
|
||||
private boolean checkSuspendedStatus() throws RuntimeException {
|
||||
log("Main: checking all tested threads have been suspended");
|
||||
return checkTestedThreadsSuspended();
|
||||
}
|
||||
|
||||
/* The test does the following steps:
|
||||
* - main thread starts several (THREADS_COUNT) ThreadToSuspend tested threads
|
||||
* - main thread waits for threads to be ready with the thread.checkReady()
|
||||
* - main thread registers tested threads within the native agent library
|
||||
* with the native method registerTestedThreads()
|
||||
* - main thread triggers the suspender tested thread with the
|
||||
* ThreadToSuspend.setAllThreadsReady() to suspend tested threads
|
||||
* - suspender thread suspends tested threads including itself with the native
|
||||
* method suspendTestedThreads() (uses the JVMTI SuspendThreadList function)
|
||||
* - main thread checks tested threads suspended status with the native method
|
||||
* checkSuspendedStatus(); the tested threads are expected to have suspended status
|
||||
* - main thread resumes tested threads with the native method resumeTestedThreads()
|
||||
* - main thread releases tested threads with the native method releaseTestedThreads()
|
||||
* - main thread triggers the tested threads to finish with the thread.letFinish()
|
||||
*/
|
||||
private void run() throws Exception {
|
||||
ThreadToSuspend[] threads = null; // tested threads
|
||||
|
||||
log("Main: started");
|
||||
try {
|
||||
threads = startTestedThreads(THREADS_COUNT);
|
||||
|
||||
log("Main: trigger " + threads[suspenderIndex].getName() +
|
||||
" to suspend all tested threads including itself");
|
||||
ThreadToSuspend.setAllThreadsReady();
|
||||
|
||||
if (!checkSuspendedStatus()) {
|
||||
throw new RuntimeException("Main: FAILED status returned from checkTestedThreadsSuspended");
|
||||
}
|
||||
|
||||
log("Main: resuming all tested threads");
|
||||
resumeTestedThreads();
|
||||
} finally {
|
||||
// let threads to finish
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
threads[i].letFinish();
|
||||
}
|
||||
log("Main: tested threads finished");
|
||||
}
|
||||
|
||||
// wait for threads to finish
|
||||
log("Main: joining tested threads");
|
||||
try {
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
log("Main: tested thread joined");
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
log("Main: releasing tested threads native info");
|
||||
releaseTestedThreadsInfo();
|
||||
|
||||
log("Main: finished");
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
|
||||
// tested threads
|
||||
class ThreadToSuspend extends Thread {
|
||||
private static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
private static native void init();
|
||||
private static native void suspendTestedThreads();
|
||||
private static volatile boolean allThreadsReady = false;
|
||||
|
||||
public static void setAllThreadsReady() {
|
||||
allThreadsReady = true;
|
||||
}
|
||||
|
||||
private volatile boolean threadReady = false;
|
||||
private volatile boolean shouldFinish = false;
|
||||
private boolean isSuspender = false;
|
||||
|
||||
// make thread with specific name
|
||||
public ThreadToSuspend(String name, boolean isSuspender) {
|
||||
super(name);
|
||||
this.isSuspender = isSuspender;
|
||||
}
|
||||
|
||||
// run thread continuously
|
||||
public void run() {
|
||||
boolean needSuspend = true;
|
||||
|
||||
if (isSuspender) {
|
||||
init();
|
||||
}
|
||||
threadReady = true;
|
||||
|
||||
// run in a loop
|
||||
while (!shouldFinish) {
|
||||
if (isSuspender && needSuspend && allThreadsReady) {
|
||||
log(getName() + ": before suspending all tested threads including myself");
|
||||
needSuspend = false;
|
||||
suspendTestedThreads();
|
||||
log(getName() + ": after suspending all tested threads including myself");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if thread is ready
|
||||
public boolean checkReady() {
|
||||
try {
|
||||
while (!threadReady) {
|
||||
sleep(1);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("checkReady: sleep was interrupted\n\t" + e);
|
||||
}
|
||||
return threadReady;
|
||||
}
|
||||
|
||||
// let thread to finish
|
||||
public void letFinish() {
|
||||
shouldFinish = true;
|
||||
}
|
||||
}
|
209
test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread/libSuspendWithCurrentThread.cpp
Normal file
209
test/hotspot/jtreg/serviceability/jvmti/SuspendWithCurrentThread/libSuspendWithCurrentThread.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "jvmti.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
static jvmtiEnv* jvmti = NULL;
|
||||
static jthread* threads = NULL;
|
||||
static jsize threads_count = 0;
|
||||
static jrawMonitorID agent_monitor = NULL;
|
||||
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
printf(__VA_ARGS__); \
|
||||
printf("\n"); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
static void
|
||||
check_jvmti_status(JNIEnv* jni, jvmtiError err, const char* msg) {
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG("check_jvmti_status: JVMTI function returned error: %d", err);
|
||||
jni->FatalError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
agent_lock(JNIEnv* jni) {
|
||||
jvmtiError err = jvmti->RawMonitorEnter(agent_monitor);
|
||||
check_jvmti_status(jni, err, "monitor_enter: error in JVMTI RawMonitorEnter");
|
||||
}
|
||||
|
||||
static void
|
||||
agent_unlock(JNIEnv* jni) {
|
||||
jvmtiError err = jvmti->RawMonitorExit(agent_monitor);
|
||||
check_jvmti_status(jni, err, "monitor_exit: error in JVMTI RawMonitorExit");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_SuspendWithCurrentThread_registerTestedThreads(JNIEnv *jni, jclass cls, jobjectArray threadsArr) {
|
||||
LOG("\nregisterTestedThreads: started");
|
||||
threads_count = jni->GetArrayLength(threadsArr);
|
||||
|
||||
jvmtiError err = jvmti->Allocate((threads_count * sizeof(jthread)),
|
||||
(unsigned char**)&threads);
|
||||
check_jvmti_status(jni, err, "registerTestedThreads: error in JVMTI Allocate threads array");
|
||||
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
jobject elem = jni->GetObjectArrayElement(threadsArr, i);
|
||||
threads[i] = (jthread)jni->NewGlobalRef(elem);
|
||||
}
|
||||
LOG("registerTestedThreads: finished\n");
|
||||
}
|
||||
|
||||
/* This function is executed on the suspender thread, not the Main thread */
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ThreadToSuspend_init(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError err = jvmti->CreateRawMonitor("Agent monitor", &agent_monitor);
|
||||
check_jvmti_status(jni, err, "Java_ThreadToSuspend_init: error in JVMTI CreateRawMonitor");
|
||||
|
||||
// Main thread has to wait for the suspender thread to complete tested threads suspension
|
||||
agent_lock(jni);
|
||||
}
|
||||
|
||||
/* This function is executed on the suspender thread which is not Main thread */
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ThreadToSuspend_suspendTestedThreads(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError* results = NULL;
|
||||
jvmtiError err;
|
||||
|
||||
LOG("\nsuspendTestedThreads: started");
|
||||
err = jvmti->Allocate((threads_count * sizeof(jvmtiError)),
|
||||
(unsigned char**)&results);
|
||||
check_jvmti_status(jni, err, "suspendTestedThreads: error in JVMTI Allocate results array");
|
||||
|
||||
LOG("suspendTestedThreads: before JVMTI SuspendThreadList");
|
||||
err = jvmti->SuspendThreadList(threads_count, threads, results);
|
||||
check_jvmti_status(jni, err, "suspendTestedThreads: error in JVMTI SuspendThreadList");
|
||||
|
||||
LOG("suspendTestedThreads: check and print SuspendThreadList results:");
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
LOG(" thread #%d: (%d)", i, (int)results[i]);
|
||||
check_jvmti_status(jni, results[i], "suspendTestedThreads: error in SuspendThreadList results[i]");
|
||||
}
|
||||
LOG("suspendTestedThreads: finished\n");
|
||||
|
||||
// Allow the Main thread to inspect the result of tested threads suspension
|
||||
agent_unlock(jni);
|
||||
|
||||
err = jvmti->Deallocate((unsigned char*)results);
|
||||
check_jvmti_status(jni, err, "suspendTestedThreads: error in JVMTI Deallocate results");
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_SuspendWithCurrentThread_checkTestedThreadsSuspended(JNIEnv *jni, jclass cls) {
|
||||
LOG("checkTestedThreadsSuspended: started");
|
||||
|
||||
// Block until the suspender thread competes the tested threads suspension
|
||||
agent_lock(jni);
|
||||
agent_unlock(jni);
|
||||
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
jint state = 0;
|
||||
jvmtiError err = jvmti->GetThreadState(threads[i], &state);
|
||||
check_jvmti_status(jni, err, "checkTestedThreadsSuspended: error in GetThreadState");
|
||||
|
||||
if ((state & JVMTI_THREAD_STATE_SUSPENDED) == 0) {
|
||||
LOG("thread #%d has not been suspended yet: "
|
||||
"# state: (%#x)", i, (int)state);
|
||||
jni->FatalError("checkTestedThreadsSuspended: error: expected all tested threads suspended");
|
||||
}
|
||||
}
|
||||
LOG("checkTestedThreadsSuspended: finished\n");
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_SuspendWithCurrentThread_resumeTestedThreads(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError* results = NULL;
|
||||
jvmtiError err;
|
||||
|
||||
LOG("\nresumeTestedThreads: started");
|
||||
err = jvmti->Allocate((threads_count * sizeof(jvmtiError)),
|
||||
(unsigned char**)&results);
|
||||
check_jvmti_status(jni, err, "resumeTestedThreads: error in JVMTI Allocate results array");
|
||||
|
||||
LOG("resumeTestedThreads: before JVMTI ResumeThreadList");
|
||||
err = jvmti->ResumeThreadList(threads_count, threads, results);
|
||||
check_jvmti_status(jni, err, "resumeTestedThreads: error in ResumeThreadList");
|
||||
|
||||
LOG("resumeTestedThreads: check and print ResumeThreadList results:");
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
LOG(" thread #%d: (%d)", i, (int)results[i]);
|
||||
check_jvmti_status(jni, results[i], "resumeTestedThreads: error in ResumeThreadList results[i]");
|
||||
}
|
||||
|
||||
err = jvmti->Deallocate((unsigned char*)results);
|
||||
check_jvmti_status(jni, err, "resumeTestedThreads: error in JVMTI Deallocate results");
|
||||
|
||||
LOG("resumeTestedThreads: finished\n");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_SuspendWithCurrentThread_releaseTestedThreadsInfo(JNIEnv *jni, jclass cls) {
|
||||
jvmtiError err;
|
||||
|
||||
LOG("\nreleaseTestedThreadsInfo: started");
|
||||
err = jvmti->DestroyRawMonitor(agent_monitor);
|
||||
check_jvmti_status(jni, err, "releaseTestedThreadsInfo: error in JVMTI DestroyRawMonitor");
|
||||
|
||||
for (int i = 0; i < threads_count; i++) {
|
||||
if (threads[i] != NULL) {
|
||||
jni->DeleteGlobalRef(threads[i]);
|
||||
}
|
||||
}
|
||||
err = jvmti->Deallocate((unsigned char*)threads);
|
||||
check_jvmti_status(jni, err, "releaseTestedThreadsInfo: error in JVMTI Deallocate threads");
|
||||
|
||||
LOG("releaseTestedThreadsInfo: finished\n");
|
||||
}
|
||||
|
||||
|
||||
/** Agent library initialization. */
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
LOG("\nAgent_OnLoad started");
|
||||
|
||||
// create JVMTI environment
|
||||
if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// add specific capabilities for suspending thread
|
||||
jvmtiCapabilities suspendCaps;
|
||||
memset(&suspendCaps, 0, sizeof(suspendCaps));
|
||||
suspendCaps.can_suspend = 1;
|
||||
|
||||
jvmtiError err = jvmti->AddCapabilities(&suspendCaps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
LOG("Agent_OnLoad finished\n");
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user