blob: 592f9223c1b973cef659c3e58c1c69c565bdd915 [file] [log] [blame]
/*
* Copyright (c) 2003, 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.
*/
#include <assert.h>
#include <jni.h>
#include <jvmti.h>
#include <stdio.h>
#include "jni_tools.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FIND_CLASS(_class, _className)\
if (!NSK_JNI_VERIFY(env, (_class = \
NSK_CPP_STUB2(FindClass, env, _className)) != NULL))\
return
#define GET_OBJECT_CLASS(_class, _obj)\
if (!NSK_JNI_VERIFY(env, (_class = \
NSK_CPP_STUB2(GetObjectClass, env, _obj)) != NULL))\
return
#define GET_STATIC_FIELD_ID(_fieldID, _class, _fieldName, _fieldSig)\
if (!NSK_JNI_VERIFY(env, (_fieldID = \
NSK_CPP_STUB4(GetStaticFieldID, env, _class,\
_fieldName, _fieldSig)) != NULL))\
return
#define GET_STATIC_OBJ_FIELD(_value, _class, _fieldName, _fieldSig)\
GET_STATIC_FIELD_ID(field, _class, _fieldName, _fieldSig);\
_value = NSK_CPP_STUB3(GetStaticObjectField, env, _class, \
field)
#define GET_STATIC_BOOL_FIELD(_value, _class, _fieldName)\
GET_STATIC_FIELD_ID(field, _class, _fieldName, "Z");\
_value = NSK_CPP_STUB3(GetStaticBooleanField, env, _class, field)
#define GET_FIELD_ID(_fieldID, _class, _fieldName, _fieldSig)\
if (!NSK_JNI_VERIFY(env, (_fieldID = \
NSK_CPP_STUB4(GetFieldID, env, _class,\
_fieldName, _fieldSig)) != NULL))\
return
#define GET_INT_FIELD(_value, _obj, _class, _fieldName)\
GET_FIELD_ID(field, _class, _fieldName, "I");\
_value = NSK_CPP_STUB3(GetIntField, env, _obj, field)
#define GET_BOOL_FIELD(_value, _obj, _class, _fieldName)\
GET_FIELD_ID(field, _class, _fieldName, "Z");\
_value = NSK_CPP_STUB3(GetBooleanField, env, _obj, field)
#define GET_LONG_FIELD(_value, _obj, _class, _fieldName)\
GET_FIELD_ID(field, _class, _fieldName, "J");\
_value = NSK_CPP_STUB3(GetLongField, env, _obj, field)
#define GET_STATIC_INT_FIELD(_value, _class, _fieldName)\
GET_STATIC_FIELD_ID(field, _class, _fieldName, "I");\
_value = NSK_CPP_STUB3(GetStaticIntField, env, _class, field)
#define SET_INT_FIELD(_obj, _class, _fieldName, _newValue)\
GET_FIELD_ID(field, _class, _fieldName, "I");\
NSK_CPP_STUB4(SetIntField, env, _obj, field, _newValue)
#define GET_OBJ_FIELD(_value, _obj, _class, _fieldName, _fieldSig)\
GET_FIELD_ID(field, _class, _fieldName, _fieldSig);\
_value = NSK_CPP_STUB3(GetObjectField, env, _obj, field)
#define GET_ARR_ELEMENT(_arr, _index)\
NSK_CPP_STUB3(GetObjectArrayElement, env, _arr, _index)
#define SET_ARR_ELEMENT(_arr, _index, _newValue)\
NSK_CPP_STUB4(SetObjectArrayElement, env, _arr, _index, _newValue)
#define GET_STATIC_METHOD_ID(_methodID, _class, _methodName, _sig)\
if (!NSK_JNI_VERIFY(env, (_methodID = \
NSK_CPP_STUB4(GetStaticMethodID, env, _class,\
_methodName, _sig)) != NULL))\
return
#define GET_METHOD_ID(_methodID, _class, _methodName, _sig)\
if (!NSK_JNI_VERIFY(env, (_methodID = \
NSK_CPP_STUB4(GetMethodID, env, _class,\
_methodName, _sig)) != NULL))\
return
#define CALL_STATIC_VOID_NOPARAM(_class, _methodName)\
GET_STATIC_METHOD_ID(method, _class, _methodName, "()V");\
if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB3(CallStaticVoidMethod, env,\
_class, method)))\
return
#define CALL_STATIC_VOID(_class, _methodName, _sig, _param)\
GET_STATIC_METHOD_ID(method, _class, _methodName, _sig);\
if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB4(CallStaticVoidMethod, env,\
_class, method, _param)))\
return
#define CALL_STATIC_OBJ(_value, _class, _methodName, _sig, _param)\
GET_STATIC_METHOD_ID(method, _class, _methodName, _sig);\
_value = NSK_CPP_STUB4(CallStaticObjectMethod, env, _class, method, _param)
#define CALL_VOID_NOPARAM(_obj, _class, _methodName)\
GET_METHOD_ID(method, _class, _methodName, "()V");\
if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB3(CallVoidMethod, env, _obj,\
method)))\
return
#define CALL_VOID(_obj, _class, _methodName, _sig, _param)\
GET_METHOD_ID(method, _class, _methodName, "()V");\
if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB4(CallVoidMethod, env, _obj,\
method, _param)))\
return
#define CALL_VOID2(_obj, _class, _methodName, _sig, _param1, _param2)\
GET_METHOD_ID(method, _class, _methodName, _sig);\
if (!NSK_JNI_VERIFY_VOID(env, NSK_CPP_STUB5(CallVoidMethod, env, _obj, \
method, _param1, _param2)))\
return
#define CALL_INT_NOPARAM(_value, _obj, _class, _methodName)\
GET_METHOD_ID(method, _class, _methodName, "()I");\
_value = NSK_CPP_STUB3(CallIntMethod, env, _obj, method)
#define NEW_OBJ(_obj, _class, _constructorName, _sig, _params)\
GET_METHOD_ID(method, _class, _constructorName, _sig);\
if (!NSK_JNI_VERIFY(env, (_obj = \
NSK_CPP_STUB4(NewObject, env, _class, method, _params)) != NULL))\
return
#define MONITOR_ENTER(x) \
NSK_JNI_VERIFY(env, NSK_CPP_STUB2(MonitorEnter, env, x) == 0)
#define MONITOR_EXIT(x) \
NSK_JNI_VERIFY(env, NSK_CPP_STUB2(MonitorExit, env, x) == 0)
#define TRACE(msg)\
GET_OBJ_FIELD(logger, obj, threadClass, "logger", "Lnsk/share/Log$Logger;");\
jmsg = NSK_CPP_STUB2(NewStringUTF, env, msg);\
CALL_VOID2(logger, loggerClass, "trace",\
"(ILjava/lang/String;)V", 50, jmsg)
static const char *SctrlClassName="nsk/monitoring/share/ThreadController";
static const char *SthreadControllerSig
= "Lnsk/monitoring/share/ThreadController;";
static const char *SThreadsGroupLocksSig
="Lnsk/monitoring/share/ThreadsGroupLocks;";
static const char *SThreadsGroupLocksClassName
="nsk/monitoring/share/ThreadsGroupLocks";
static const char *SbringState_mn="bringState";
static const char *SnativeBringState_mn="nativeBringState";
static const char *SrecursiveMethod_mn="recursiveMethod";
static const char *SnativeRecursiveMethod_mn="nativeRecursiveMethod";
static const char *SloggerClassName = "nsk/share/Log$Logger";
static const char *Snoparams="()V";
static const char *Slongparam="(J)V";
/*
* Class: nsk_monitoring_share_BaseThread
* Method: nativeRecursiveMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_nsk_monitoring_share_BaseThread_nativeRecursiveMethod(JNIEnv *env,
jobject obj) {
jint currDepth, maxDepth;
jobject logger;
jstring jmsg;
jfieldID field;
jmethodID method;
jobject controller;
jclass threadClass, ctrlClass, loggerClass;
int invocationType;
GET_OBJECT_CLASS(threadClass, obj);
FIND_CLASS(ctrlClass, SctrlClassName);
FIND_CLASS(loggerClass, SloggerClassName);
/* currDepth++ */
GET_INT_FIELD(currDepth, obj, threadClass, "currentDepth");
currDepth++;
SET_INT_FIELD(obj, threadClass, "currentDepth", currDepth);
GET_OBJ_FIELD(controller, obj, threadClass, "controller",
SthreadControllerSig);
GET_INT_FIELD(maxDepth, controller, ctrlClass, "maxDepth");
GET_STATIC_INT_FIELD(invocationType, ctrlClass, "invocationType");
if (maxDepth - currDepth > 0)
{
CALL_STATIC_VOID_NOPARAM(threadClass, "yield");
if (invocationType == 2/*MIXED_TYPE*/)
{
CALL_VOID_NOPARAM(obj, threadClass, SrecursiveMethod_mn);
}
else
{
CALL_VOID_NOPARAM(obj, threadClass, SnativeRecursiveMethod_mn);
}
}
else
{
TRACE("state has been reached");
if (invocationType == 2/*MIXED_TYPE*/)
{
CALL_VOID_NOPARAM(obj, threadClass, SbringState_mn);
}
else
{
CALL_VOID_NOPARAM(obj, threadClass, SnativeBringState_mn);
}
}
currDepth--;
GET_OBJECT_CLASS(threadClass, obj);
SET_INT_FIELD(obj, threadClass, "currentDepth", currDepth);
}
/*
* Class: nsk_monitoring_share_BlockedThread
* Method: nativeBringState
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_nsk_monitoring_share_BlockedThread_nativeBringState(JNIEnv *env,
jobject obj) {
jobject logger;
jstring jmsg;
jfieldID field;
jmethodID method;
jclass threadClass, loggerClass;
jobject STATE;
//ThreadsGroupLocks:
jclass ThreadsGroupLocks;
jobject threadsGroupLocks;
jmethodID getBarrier;
//CountDownLatch
jobject barrier;
jclass CountDownLatch;
//Blocker
jobject blocker;
jclass Blocker;
GET_OBJECT_CLASS(threadClass, obj);
FIND_CLASS(loggerClass, SloggerClassName);
FIND_CLASS(ThreadsGroupLocks, SThreadsGroupLocksClassName);
FIND_CLASS(Blocker, "Lnsk/monitoring/share/ThreadsGroupLocks$Blocker;");
FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", SThreadsGroupLocksSig);
GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
GET_OBJ_FIELD(blocker, threadsGroupLocks, ThreadsGroupLocks, "blocker", "Lnsk/monitoring/share/ThreadsGroupLocks$Blocker;");
getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
"(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
TRACE("entering to monitor");
CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
CALL_VOID_NOPARAM(blocker, Blocker, "block");
TRACE("exiting from monitor");
}
/*
* Class: nsk_monitoring_share_WaitingThread
* Method: nativeBringState
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_nsk_monitoring_share_WaitingThread_nativeBringState(JNIEnv *env,
jobject obj) {
jobject logger;
jstring jmsg;
jfieldID field;
jmethodID method;
jclass threadClass, loggerClass;
//STATE
jobject STATE;
//ThreadsGroupLocks:
jclass ThreadsGroupLocks;
jobject threadsGroupLocks;
jmethodID getBarrier;
//CountDownLatch
jobject barrier;
jclass CountDownLatch;
GET_OBJECT_CLASS(threadClass, obj);
FIND_CLASS(loggerClass, SloggerClassName);
FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
"(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
TRACE("waiting on a monitor");
CALL_VOID_NOPARAM(barrier, CountDownLatch, "await");
}
/*
* Class: nsk_monitoring_share_SleepingThread
* Method: nativeBringState
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_nsk_monitoring_share_SleepingThread_nativeBringState(JNIEnv *env,
jobject obj) {
jfieldID field;
jmethodID method;
jclass threadClass, loggerClass;
//STATE
jobject STATE;
//ThreadsGroupLocks:
jclass ThreadsGroupLocks;
jobject threadsGroupLocks;
jmethodID getBarrier;
//CountDownLatch
jobject barrier;
jclass CountDownLatch;
//Thread
jclass Thread;
jlong sleepTime = 20 * 60 * 1000;
GET_OBJECT_CLASS(threadClass, obj);
FIND_CLASS(loggerClass, SloggerClassName);
FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
// Thread.sleep(3600 * 1000);
FIND_CLASS(Thread, "java/lang/Thread");
getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
"(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
CALL_STATIC_VOID(Thread, "sleep", "(J)V", sleepTime);
}
/*
* Class: nsk_monitoring_share_RunningThread
* Method: nativeBringState
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_nsk_monitoring_share_RunningThread_nativeBringState(JNIEnv *env,
jobject obj) {
jobject logger;
jstring jmsg;
jfieldID field;
jmethodID method;
jclass threadClass, loggerClass;
//STATE
jobject STATE;
//ThreadsGroupLocks:
jclass ThreadsGroupLocks;
jobject threadsGroupLocks;
jmethodID getBarrier;
//CountDownLatch
jobject barrier;
jclass CountDownLatch;
//Thread
jclass Thread;
//runnableCanExit
jboolean flag = JNI_FALSE;
GET_OBJECT_CLASS(threadClass, obj);
FIND_CLASS(loggerClass, SloggerClassName);
FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
// Thread.sleep(3600 * 1000);
FIND_CLASS(Thread, "java/lang/Thread");
getBarrier = (*env)->GetMethodID(env, ThreadsGroupLocks, "getBarrier",
"(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
TRACE("running loop");
barrier = (*env)->CallObjectMethod(env, threadsGroupLocks, getBarrier, STATE);
CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
// while (!threadsGroupLocks.runnableCanExit.get()) {
// Thread.yield();
// }
while(flag==JNI_FALSE)
{
GET_BOOL_FIELD(flag, threadsGroupLocks, ThreadsGroupLocks, "runnableCanExit");
CALL_STATIC_VOID_NOPARAM(Thread, "yield");
}
}
jstring getStateName(JNIEnv *env, jint state) {
switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) {
case JVMTI_JAVA_LANG_THREAD_STATE_NEW:
return (*env)->NewStringUTF(env,"NEW");
case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED:
return (*env)->NewStringUTF(env,"TERMINATED");
case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE:
return (*env)->NewStringUTF(env,"RUNNABLE");
case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED:
return (*env)->NewStringUTF(env,"BLOCKED");
case JVMTI_JAVA_LANG_THREAD_STATE_WAITING:
return (*env)->NewStringUTF(env, "WAITING");
case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING:
return (*env)->NewStringUTF(env,"TIMED_WAITING");
}
// should never reach
assert(0);
return 0;
}
/*
* Class: nsk_monitoring_share_ThreadController
* Method: getThreadState
* Signature: (Ljava/lang/Thread;)Ljava/lang/Thread/State;
*/
JNIEXPORT jobject JNICALL
Java_nsk_monitoring_share_ThreadController_getThreadState(JNIEnv *env,
jobject obj, jobject thread){
JavaVM *vm;
jvmtiEnv *jvmti;
jclass ThreadState;
jmethodID method;
jobject threadState;
jstring stateName;
jint state;
if(!NSK_VERIFY(
NSK_CPP_STUB2(GetJavaVM, env, &vm) == 0)) {
return NULL;
}
if(!NSK_VERIFY(
NSK_CPP_STUB3(GetEnv, vm, (void **)&jvmti, JVMTI_VERSION_1)
== JNI_OK)) {
return NULL;
}
if(!NSK_VERIFY(
NSK_CPP_STUB3(GetThreadState, jvmti, (jthread)thread, &state)
== JVMTI_ERROR_NONE)) {
return NULL;
}
stateName = getStateName(env, state);
if (!NSK_JNI_VERIFY(env, (ThreadState = NSK_CPP_STUB2(FindClass, env, "java/lang/Thread$State")) != NULL))
return NULL;
if (!NSK_JNI_VERIFY(env, (method = NSK_CPP_STUB4(GetStaticMethodID, env, ThreadState, "valueOf", "(Ljava/lang/String;)Ljava/lang/Thread$State;")) != NULL))
return NULL;
threadState = NSK_CPP_STUB4(CallStaticObjectMethod, env, ThreadState, method, stateName);
return threadState;
}
#ifdef __cplusplus
}
#endif