1 /*
   2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include <assert.h>
  25 #include <jni.h>
  26 #include <jvmti.h>
  27 #include <stdio.h>
  28 #include "jni_tools.h"
  29 
  30 extern "C" {
  31 
  32 #define FIND_CLASS(_class, _className)\
  33     if (!NSK_JNI_VERIFY(env, (_class = \
  34             env->FindClass(_className)) != NULL))\
  35         return
  36 
  37 #define GET_OBJECT_CLASS(_class, _obj)\
  38     if (!NSK_JNI_VERIFY(env, (_class = \
  39             env->GetObjectClass(_obj)) != NULL))\
  40         return
  41 
  42 #define GET_STATIC_FIELD_ID(_fieldID, _class, _fieldName, _fieldSig)\
  43     if (!NSK_JNI_VERIFY(env, (_fieldID = \
  44             env->GetStaticFieldID(_class, _fieldName, _fieldSig)) != NULL))\
  45         return
  46 
  47 #define GET_STATIC_OBJ_FIELD(_value, _class, _fieldName, _fieldSig)\
  48     GET_STATIC_FIELD_ID(field, _class, _fieldName, _fieldSig);\
  49     _value = env->GetStaticObjectField(_class, field)
  50 
  51 #define GET_STATIC_BOOL_FIELD(_value, _class, _fieldName)\
  52     GET_STATIC_FIELD_ID(field, _class, _fieldName, "Z");\
  53     _value = env->GetStaticBooleanField(_class, field)
  54 
  55 #define GET_FIELD_ID(_fieldID, _class, _fieldName, _fieldSig)\
  56     if (!NSK_JNI_VERIFY(env, (_fieldID = \
  57             env->GetFieldID(_class, _fieldName, _fieldSig)) != NULL))\
  58         return
  59 
  60 #define GET_INT_FIELD(_value, _obj, _class, _fieldName)\
  61     GET_FIELD_ID(field, _class, _fieldName, "I");\
  62     _value = env->GetIntField(_obj, field)
  63 
  64 #define GET_BOOL_FIELD(_value, _obj, _class, _fieldName)\
  65     GET_FIELD_ID(field, _class, _fieldName, "Z");\
  66     _value = env->GetBooleanField(_obj, field)
  67 
  68 #define GET_LONG_FIELD(_value, _obj, _class, _fieldName)\
  69     GET_FIELD_ID(field, _class, _fieldName, "J");\
  70     _value = env->GetLongField(_obj, field)
  71 
  72 #define GET_STATIC_INT_FIELD(_value, _class, _fieldName)\
  73     GET_STATIC_FIELD_ID(field, _class, _fieldName, "I");\
  74     _value = env->GetStaticIntField(_class, field)
  75 
  76 #define SET_INT_FIELD(_obj, _class, _fieldName, _newValue)\
  77     GET_FIELD_ID(field, _class, _fieldName, "I");\
  78     env->SetIntField(_obj, field, _newValue)
  79 
  80 #define GET_OBJ_FIELD(_value, _obj, _class, _fieldName, _fieldSig)\
  81     GET_FIELD_ID(field, _class, _fieldName, _fieldSig);\
  82     _value = env->GetObjectField(_obj, field)
  83 
  84 
  85 #define GET_ARR_ELEMENT(_arr, _index)\
  86     env->GetObjectArrayElement(_arr, _index)
  87 
  88 #define SET_ARR_ELEMENT(_arr, _index, _newValue)\
  89     env->SetObjectArrayElement(_arr, _index, _newValue)
  90 
  91 #define GET_STATIC_METHOD_ID(_methodID, _class, _methodName, _sig)\
  92     if (!NSK_JNI_VERIFY(env, (_methodID = \
  93             env->GetStaticMethodID(_class, _methodName, _sig)) != NULL))\
  94         return
  95 
  96 #define GET_METHOD_ID(_methodID, _class, _methodName, _sig)\
  97     if (!NSK_JNI_VERIFY(env, (_methodID = \
  98             env->GetMethodID(_class, _methodName, _sig)) != NULL))\
  99         return
 100 
 101 #define CALL_STATIC_VOID_NOPARAM(_class, _methodName)\
 102     GET_STATIC_METHOD_ID(method, _class, _methodName, "()V");\
 103     if (!NSK_JNI_VERIFY_VOID(env, env->CallStaticVoidMethod(_class, method)))\
 104         return
 105 
 106 #define CALL_STATIC_VOID(_class, _methodName, _sig, _param)\
 107     GET_STATIC_METHOD_ID(method, _class, _methodName, _sig);\
 108     if (!NSK_JNI_VERIFY_VOID(env, env->CallStaticVoidMethod(_class, method, _param)))\
 109         return
 110 
 111 #define CALL_STATIC_OBJ(_value, _class, _methodName, _sig, _param)\
 112     GET_STATIC_METHOD_ID(method, _class, _methodName, _sig);\
 113     _value = env->CallStaticObjectMethod(_class, method, _param)
 114 
 115 #define CALL_VOID_NOPARAM(_obj, _class, _methodName)\
 116     GET_METHOD_ID(method, _class, _methodName, "()V");\
 117     if (!NSK_JNI_VERIFY_VOID(env, env->CallVoidMethod(_obj, method)))\
 118         return
 119 
 120 #define CALL_VOID(_obj, _class, _methodName, _sig, _param)\
 121     GET_METHOD_ID(method, _class, _methodName, "()V");\
 122     if (!NSK_JNI_VERIFY_VOID(env, env->CallVoidMethod(_obj, method, _param)))\
 123         return
 124 
 125 #define CALL_VOID2(_obj, _class, _methodName, _sig, _param1, _param2)\
 126     GET_METHOD_ID(method, _class, _methodName, _sig);\
 127     if (!NSK_JNI_VERIFY_VOID(env, env->CallVoidMethod(_obj, method, _param1, _param2)))\
 128         return
 129 
 130 #define CALL_INT_NOPARAM(_value, _obj, _class, _methodName)\
 131     GET_METHOD_ID(method, _class, _methodName, "()I");\
 132     _value = env->CallIntMethod(_obj, method)
 133 
 134 #define NEW_OBJ(_obj, _class, _constructorName, _sig, _params)\
 135     GET_METHOD_ID(method, _class, _constructorName, _sig);\
 136     if (!NSK_JNI_VERIFY(env, (_obj = \
 137             env->NewObject(_class, method, _params)) != NULL))\
 138         return
 139 
 140 #define MONITOR_ENTER(x) \
 141     NSK_JNI_VERIFY(env, env->MonitorEnter(x) == 0)
 142 
 143 #define MONITOR_EXIT(x) \
 144     NSK_JNI_VERIFY(env, env->MonitorExit(x) == 0)
 145 
 146 #define TRACE(msg)\
 147    GET_OBJ_FIELD(logger, obj, threadClass, "logger", "Lnsk/share/Log$Logger;");\
 148    jmsg = env->NewStringUTF(msg);\
 149    CALL_VOID2(logger, loggerClass, "trace",\
 150                            "(ILjava/lang/String;)V", 50, jmsg)
 151 
 152     static const char *SctrlClassName="nsk/monitoring/share/ThreadController";
 153     static const char *SthreadControllerSig
 154             = "Lnsk/monitoring/share/ThreadController;";
 155 
 156     static const char *SThreadsGroupLocksSig
 157     ="Lnsk/monitoring/share/ThreadsGroupLocks;";
 158     static const char *SThreadsGroupLocksClassName
 159     ="nsk/monitoring/share/ThreadsGroupLocks";
 160 
 161 
 162     static const char *SbringState_mn="bringState";
 163     static const char *SnativeBringState_mn="nativeBringState";
 164     static const char *SrecursiveMethod_mn="recursiveMethod";
 165     static const char *SnativeRecursiveMethod_mn="nativeRecursiveMethod";
 166     static const char *SloggerClassName = "nsk/share/Log$Logger";
 167 
 168     static const char *Snoparams="()V";
 169     static const char *Slongparam="(J)V";
 170 
 171     /*
 172      * Class:     nsk_monitoring_share_BaseThread
 173      * Method:    nativeRecursiveMethod
 174      * Signature: ()V
 175      */
 176     JNIEXPORT void JNICALL
 177     Java_nsk_monitoring_share_BaseThread_nativeRecursiveMethod(JNIEnv *env,
 178             jobject obj) {
 179         jint currDepth, maxDepth;
 180         jobject logger;
 181         jstring jmsg;
 182         jfieldID field;
 183         jmethodID method;
 184 
 185         jobject controller;
 186         jclass threadClass, ctrlClass, loggerClass;
 187 
 188         int invocationType;
 189 
 190         GET_OBJECT_CLASS(threadClass, obj);
 191         FIND_CLASS(ctrlClass, SctrlClassName);
 192         FIND_CLASS(loggerClass, SloggerClassName);
 193 
 194 
 195         /* currDepth++ */
 196         GET_INT_FIELD(currDepth, obj, threadClass, "currentDepth");
 197         currDepth++;
 198         SET_INT_FIELD(obj, threadClass, "currentDepth", currDepth);
 199 
 200         GET_OBJ_FIELD(controller, obj, threadClass, "controller",
 201                       SthreadControllerSig);
 202         GET_INT_FIELD(maxDepth, controller, ctrlClass, "maxDepth");
 203 
 204         GET_STATIC_INT_FIELD(invocationType, ctrlClass, "invocationType");
 205 
 206         if (maxDepth - currDepth > 0)
 207         {
 208             CALL_STATIC_VOID_NOPARAM(threadClass, "yield");
 209 
 210             if (invocationType == 2/*MIXED_TYPE*/)
 211             {
 212                 CALL_VOID_NOPARAM(obj, threadClass, SrecursiveMethod_mn);
 213             }
 214             else
 215             {
 216                 CALL_VOID_NOPARAM(obj, threadClass, SnativeRecursiveMethod_mn);
 217             }
 218         }
 219         else
 220         {
 221             TRACE("state has been reached");
 222             if (invocationType == 2/*MIXED_TYPE*/)
 223             {
 224                 CALL_VOID_NOPARAM(obj, threadClass, SbringState_mn);
 225             }
 226             else
 227             {
 228                 CALL_VOID_NOPARAM(obj, threadClass, SnativeBringState_mn);
 229             }
 230         }
 231 
 232         currDepth--;
 233         GET_OBJECT_CLASS(threadClass, obj);
 234         SET_INT_FIELD(obj, threadClass, "currentDepth", currDepth);
 235     }
 236 
 237     /*
 238      * Class:     nsk_monitoring_share_BlockedThread
 239      * Method:    nativeBringState
 240      * Signature: ()V
 241      */
 242     JNIEXPORT void JNICALL
 243     Java_nsk_monitoring_share_BlockedThread_nativeBringState(JNIEnv *env,
 244             jobject obj) {
 245         jobject logger;
 246         jstring jmsg;
 247         jfieldID field;
 248         jmethodID method;
 249 
 250         jclass threadClass, loggerClass;
 251 
 252         jobject STATE;
 253 
 254         //ThreadsGroupLocks:
 255         jclass ThreadsGroupLocks;
 256         jobject  threadsGroupLocks;
 257         jmethodID getBarrier;
 258 
 259 
 260         //CountDownLatch
 261         jobject barrier;
 262         jclass CountDownLatch;
 263 
 264         //Blocker
 265         jobject blocker;
 266         jclass Blocker;
 267 
 268         GET_OBJECT_CLASS(threadClass, obj);
 269 
 270         FIND_CLASS(loggerClass, SloggerClassName);
 271         FIND_CLASS(ThreadsGroupLocks, SThreadsGroupLocksClassName);
 272         FIND_CLASS(Blocker, "Lnsk/monitoring/share/ThreadsGroupLocks$Blocker;");
 273         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
 274 
 275         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", SThreadsGroupLocksSig);
 276         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
 277         GET_OBJ_FIELD(blocker, threadsGroupLocks, ThreadsGroupLocks, "blocker", "Lnsk/monitoring/share/ThreadsGroupLocks$Blocker;");
 278 
 279         getBarrier = env->GetMethodID(ThreadsGroupLocks, "getBarrier",
 280             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
 281         barrier = env->CallObjectMethod(threadsGroupLocks, getBarrier, STATE);
 282 
 283 
 284         TRACE("entering to monitor");
 285 
 286         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
 287         CALL_VOID_NOPARAM(blocker, Blocker, "block");
 288         TRACE("exiting from monitor");
 289 
 290     }
 291 
 292     /*
 293      * Class:     nsk_monitoring_share_WaitingThread
 294      * Method:    nativeBringState
 295      * Signature: ()V
 296      */
 297     JNIEXPORT void JNICALL
 298     Java_nsk_monitoring_share_WaitingThread_nativeBringState(JNIEnv *env,
 299             jobject obj) {
 300         jobject logger;
 301         jstring jmsg;
 302         jfieldID field;
 303         jmethodID method;
 304 
 305         jclass threadClass, loggerClass;
 306 
 307         //STATE
 308         jobject STATE;
 309 
 310         //ThreadsGroupLocks:
 311         jclass ThreadsGroupLocks;
 312         jobject  threadsGroupLocks;
 313         jmethodID getBarrier;
 314 
 315         //CountDownLatch
 316         jobject barrier;
 317         jclass CountDownLatch;
 318 
 319         GET_OBJECT_CLASS(threadClass, obj);
 320 
 321         FIND_CLASS(loggerClass, SloggerClassName);
 322         FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
 323         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
 324 
 325         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
 326         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
 327 
 328         getBarrier = env->GetMethodID(ThreadsGroupLocks, "getBarrier",
 329             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
 330         barrier = env->CallObjectMethod(threadsGroupLocks, getBarrier, STATE);
 331         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
 332 
 333         TRACE("waiting on a monitor");
 334         CALL_VOID_NOPARAM(barrier, CountDownLatch, "await");
 335     }
 336 
 337     /*
 338      * Class:     nsk_monitoring_share_SleepingThread
 339      * Method:    nativeBringState
 340      * Signature: ()V
 341      */
 342     JNIEXPORT void JNICALL
 343     Java_nsk_monitoring_share_SleepingThread_nativeBringState(JNIEnv *env,
 344             jobject obj) {
 345         jfieldID field;
 346         jmethodID method;
 347 
 348         jclass threadClass, loggerClass;
 349 
 350         //STATE
 351         jobject STATE;
 352 
 353         //ThreadsGroupLocks:
 354         jclass ThreadsGroupLocks;
 355         jobject  threadsGroupLocks;
 356         jmethodID getBarrier;
 357 
 358         //CountDownLatch
 359         jobject barrier;
 360         jclass CountDownLatch;
 361 
 362         //Thread
 363         jclass Thread;
 364 
 365         jlong sleepTime = 20 * 60 * 1000;
 366 
 367 
 368         GET_OBJECT_CLASS(threadClass, obj);
 369 
 370         FIND_CLASS(loggerClass, SloggerClassName);
 371         FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
 372         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
 373 
 374         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
 375         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
 376 
 377         // Thread.sleep(3600 * 1000);
 378         FIND_CLASS(Thread, "java/lang/Thread");
 379 
 380         getBarrier = env->GetMethodID(ThreadsGroupLocks, "getBarrier",
 381             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
 382         barrier = env->CallObjectMethod(threadsGroupLocks, getBarrier, STATE);
 383         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
 384 
 385         CALL_STATIC_VOID(Thread, "sleep", "(J)V", sleepTime);
 386     }
 387 
 388     /*
 389      * Class:     nsk_monitoring_share_RunningThread
 390      * Method:    nativeBringState
 391      * Signature: ()V
 392      */
 393     JNIEXPORT void JNICALL
 394     Java_nsk_monitoring_share_RunningThread_nativeBringState(JNIEnv *env,
 395             jobject obj) {
 396         jobject logger;
 397         jstring jmsg;
 398         jfieldID field;
 399         jmethodID method;
 400 
 401         jclass threadClass, loggerClass;
 402 
 403         //STATE
 404         jobject STATE;
 405 
 406         //ThreadsGroupLocks:
 407         jclass ThreadsGroupLocks;
 408         jobject  threadsGroupLocks;
 409         jmethodID getBarrier;
 410 
 411         //CountDownLatch
 412         jobject barrier;
 413         jclass CountDownLatch;
 414 
 415         //Thread
 416         jclass Thread;
 417 
 418         //runnableCanExit
 419         jboolean flag = JNI_FALSE;
 420 
 421         GET_OBJECT_CLASS(threadClass, obj);
 422 
 423         FIND_CLASS(loggerClass, SloggerClassName);
 424         FIND_CLASS(ThreadsGroupLocks, "nsk/monitoring/share/ThreadsGroupLocks");
 425         FIND_CLASS(CountDownLatch, "nsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch");
 426 
 427         GET_STATIC_OBJ_FIELD(STATE, threadClass, "STATE", "Ljava/lang/Thread$State;");
 428         GET_OBJ_FIELD(threadsGroupLocks, obj, threadClass, "threadsGroupLocks", "Lnsk/monitoring/share/ThreadsGroupLocks;");
 429 
 430         // Thread.sleep(3600 * 1000);
 431         FIND_CLASS(Thread, "java/lang/Thread");
 432 
 433         getBarrier = env->GetMethodID(ThreadsGroupLocks, "getBarrier",
 434             "(Ljava/lang/Thread$State;)Lnsk/monitoring/share/ThreadsGroupLocks$PlainCountDownLatch;");
 435 
 436         TRACE("running loop");
 437 
 438         barrier = env->CallObjectMethod(threadsGroupLocks, getBarrier, STATE);
 439         CALL_VOID_NOPARAM(barrier, CountDownLatch, "countDown");
 440 
 441         // while (!threadsGroupLocks.runnableCanExit.get()) {
 442         //        Thread.yield();
 443         //    }
 444         while(flag==JNI_FALSE)
 445         {
 446             GET_BOOL_FIELD(flag, threadsGroupLocks, ThreadsGroupLocks, "runnableCanExit");
 447             CALL_STATIC_VOID_NOPARAM(Thread, "yield");
 448         }
 449 
 450     }
 451 
 452     jstring getStateName(JNIEnv *env, jint state) {
 453         switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) {
 454             case JVMTI_JAVA_LANG_THREAD_STATE_NEW:
 455                 return env->NewStringUTF("NEW");
 456             case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED:
 457                 return env->NewStringUTF("TERMINATED");
 458             case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE:
 459                 return env->NewStringUTF("RUNNABLE");
 460             case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED:
 461                 return env->NewStringUTF("BLOCKED");
 462             case JVMTI_JAVA_LANG_THREAD_STATE_WAITING:
 463                 return env->NewStringUTF("WAITING");
 464             case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING:
 465                 return env->NewStringUTF("TIMED_WAITING");
 466             }
 467         // should never reach
 468         assert(0);
 469         return 0;
 470     }
 471 
 472     /*
 473      * Class:     nsk_monitoring_share_ThreadController
 474      * Method:    getThreadState
 475      * Signature: (Ljava/lang/Thread;)Ljava/lang/Thread/State;
 476      */
 477     JNIEXPORT jobject JNICALL
 478     Java_nsk_monitoring_share_ThreadController_getThreadState(JNIEnv *env,
 479             jobject obj, jobject thread){
 480 
 481         JavaVM *vm;
 482         jvmtiEnv *jvmti;
 483         jclass ThreadState;
 484         jmethodID method;
 485         jobject threadState;
 486         jstring stateName;
 487         jint state;
 488 
 489         if(!NSK_VERIFY(
 490              env->GetJavaVM(&vm) == 0)) {
 491             return NULL;
 492         }
 493 
 494         if(!NSK_VERIFY(
 495              vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1)
 496                     == JNI_OK)) {
 497             return NULL;
 498         }
 499 
 500         if(!NSK_VERIFY(
 501              jvmti->GetThreadState((jthread)thread, &state)
 502              == JVMTI_ERROR_NONE)) {
 503             return NULL;
 504         }
 505 
 506         stateName = getStateName(env, state);
 507         if (!NSK_JNI_VERIFY(env, (ThreadState = env->FindClass("java/lang/Thread$State")) != NULL))
 508             return NULL;
 509 
 510         if (!NSK_JNI_VERIFY(env, (method = env->GetStaticMethodID(ThreadState, "valueOf", "(Ljava/lang/String;)Ljava/lang/Thread$State;")) != NULL))
 511             return NULL;
 512         threadState = env->CallStaticObjectMethod(ThreadState, method, stateName);
 513 
 514         return threadState;
 515     }
 516 
 517 }