Home | History | Annotate | Download | only in 924-threads
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdio.h>
     18 
     19 #include <mutex>
     20 #include <string>
     21 #include <vector>
     22 
     23 #include "android-base/logging.h"
     24 #include "android-base/stringprintf.h"
     25 #include "jni.h"
     26 #include "jvmti.h"
     27 #include "scoped_local_ref.h"
     28 
     29 // Test infrastructure
     30 #include "jni_helper.h"
     31 #include "jvmti_helper.h"
     32 #include "test_env.h"
     33 #include "ti_macros.h"
     34 
     35 namespace art {
     36 namespace Test924Threads {
     37 
     38 struct WaiterStruct {
     39   std::atomic<bool> started;
     40   std::atomic<bool> finish;
     41 };
     42 
     43 extern "C" JNIEXPORT jlong JNICALL Java_art_Test924_nativeWaiterStructAlloc(
     44     JNIEnv* env, jclass TestClass ATTRIBUTE_UNUSED) {
     45   WaiterStruct* s = nullptr;
     46   if (JvmtiErrorToException(env,
     47                             jvmti_env,
     48                             jvmti_env->Allocate(sizeof(WaiterStruct),
     49                                                 reinterpret_cast<unsigned char**>(&s)))) {
     50     return 0;
     51   }
     52   s->started = false;
     53   s->finish = false;
     54   return static_cast<jlong>(reinterpret_cast<intptr_t>(s));
     55 }
     56 
     57 extern "C" JNIEXPORT void JNICALL Java_art_Test924_nativeWaiterStructWaitForNative(
     58     JNIEnv* env ATTRIBUTE_UNUSED, jclass TestClass ATTRIBUTE_UNUSED, jlong waiter_struct) {
     59   WaiterStruct* s = reinterpret_cast<WaiterStruct*>(static_cast<intptr_t>(waiter_struct));
     60   while (!s->started) { }
     61 }
     62 
     63 extern "C" JNIEXPORT void JNICALL Java_art_Test924_nativeWaiterStructFinish(
     64     JNIEnv* env ATTRIBUTE_UNUSED, jclass TestClass ATTRIBUTE_UNUSED, jlong waiter_struct) {
     65   WaiterStruct* s = reinterpret_cast<WaiterStruct*>(static_cast<intptr_t>(waiter_struct));
     66   s->finish = true;
     67 }
     68 
     69 extern "C" JNIEXPORT void JNICALL Java_art_Test924_nativeLoop(JNIEnv* env,
     70                                                               jclass TestClass ATTRIBUTE_UNUSED,
     71                                                               jlong waiter_struct) {
     72   WaiterStruct* s = reinterpret_cast<WaiterStruct*>(static_cast<intptr_t>(waiter_struct));
     73   s->started = true;
     74   while (!s->finish) { }
     75   JvmtiErrorToException(env, jvmti_env, jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(s)));
     76 }
     77 
     78 // private static native Thread getCurrentThread();
     79 // private static native Object[] getThreadInfo(Thread t);
     80 
     81 extern "C" JNIEXPORT jthread JNICALL Java_art_Test924_getCurrentThread(
     82     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
     83   jthread thread = nullptr;
     84   jvmtiError result = jvmti_env->GetCurrentThread(&thread);
     85   if (JvmtiErrorToException(env, jvmti_env, result)) {
     86     return nullptr;
     87   }
     88   return thread;
     89 }
     90 
     91 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadInfo(
     92     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
     93   jvmtiThreadInfo info;
     94   memset(&info, 0, sizeof(jvmtiThreadInfo));
     95 
     96   jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
     97   if (JvmtiErrorToException(env, jvmti_env, result)) {
     98     return nullptr;
     99   }
    100 
    101   auto callback = [&](jint component_index) -> jobject {
    102     switch (component_index) {
    103       // The name.
    104       case 0:
    105         return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name);
    106 
    107       // The priority. Use a string for simplicity of construction.
    108       case 1:
    109         return env->NewStringUTF(android::base::StringPrintf("%d", info.priority).c_str());
    110 
    111       // Whether it's a daemon. Use a string for simplicity of construction.
    112       case 2:
    113         return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false");
    114 
    115       // The thread group;
    116       case 3:
    117         return env->NewLocalRef(info.thread_group);
    118 
    119       // The context classloader.
    120       case 4:
    121         return env->NewLocalRef(info.context_class_loader);
    122     }
    123     LOG(FATAL) << "Should not reach here";
    124     UNREACHABLE();
    125   };
    126   jobjectArray ret = CreateObjectArray(env, 5, "java/lang/Object", callback);
    127 
    128   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
    129   if (info.thread_group != nullptr) {
    130     env->DeleteLocalRef(info.thread_group);
    131   }
    132   if (info.context_class_loader != nullptr) {
    133     env->DeleteLocalRef(info.context_class_loader);
    134   }
    135 
    136   return ret;
    137 }
    138 
    139 extern "C" JNIEXPORT jint JNICALL Java_art_Test924_getThreadState(
    140     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
    141   jint state;
    142   jvmtiError result = jvmti_env->GetThreadState(thread, &state);
    143   if (JvmtiErrorToException(env, jvmti_env, result)) {
    144     return 0;
    145   }
    146   return state;
    147 }
    148 
    149 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getAllThreads(
    150     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
    151   jint thread_count;
    152   jthread* threads;
    153 
    154   jvmtiError result = jvmti_env->GetAllThreads(&thread_count, &threads);
    155   if (JvmtiErrorToException(env, jvmti_env, result)) {
    156     return nullptr;
    157   }
    158 
    159   auto callback = [&](jint index) {
    160     return threads[index];
    161   };
    162   jobjectArray ret = CreateObjectArray(env, thread_count, "java/lang/Thread", callback);
    163 
    164   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads));
    165 
    166   return ret;
    167 }
    168 
    169 extern "C" JNIEXPORT jlong JNICALL Java_art_Test924_getTLS(
    170     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
    171   void* tls;
    172   jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
    173   if (JvmtiErrorToException(env, jvmti_env, result)) {
    174     return 0;
    175   }
    176   return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
    177 }
    178 
    179 extern "C" JNIEXPORT void JNICALL Java_art_Test924_setTLS(
    180     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
    181   const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
    182   jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
    183   JvmtiErrorToException(env, jvmti_env, result);
    184 }
    185 
    186 static std::mutex gEventsMutex;
    187 static std::vector<std::string> gEvents;
    188 
    189 static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
    190                                 JNIEnv* jni_env,
    191                                 jthread thread,
    192                                 bool is_start) {
    193   jvmtiThreadInfo info;
    194   {
    195     std::lock_guard<std::mutex> guard(gEventsMutex);
    196 
    197     jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
    198     if (result != JVMTI_ERROR_NONE) {
    199       gEvents.push_back("Error getting thread info");
    200       return;
    201     }
    202 
    203     gEvents.push_back(android::base::StringPrintf("Thread(%s): %s",
    204                                                   info.name,
    205                                                   is_start ? "start" : "end"));
    206   }
    207 
    208   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
    209   jni_env->DeleteLocalRef(info.thread_group);
    210   jni_env->DeleteLocalRef(info.context_class_loader);
    211 }
    212 
    213 static void JNICALL ThreadStart(jvmtiEnv* jvmti_env,
    214                                 JNIEnv* jni_env,
    215                                 jthread thread) {
    216   ThreadEvent(jvmti_env, jni_env, thread, true);
    217 }
    218 
    219 static void JNICALL ThreadEnd(jvmtiEnv* jvmti_env,
    220                               JNIEnv* jni_env,
    221                               jthread thread) {
    222   ThreadEvent(jvmti_env, jni_env, thread, false);
    223 }
    224 
    225 extern "C" JNIEXPORT void JNICALL Java_art_Test924_enableThreadEvents(
    226     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
    227   if (b == JNI_FALSE) {
    228     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    229                                                          JVMTI_EVENT_THREAD_START,
    230                                                          nullptr);
    231     if (JvmtiErrorToException(env, jvmti_env, ret)) {
    232       return;
    233     }
    234     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    235                                               JVMTI_EVENT_THREAD_END,
    236                                               nullptr);
    237     JvmtiErrorToException(env, jvmti_env, ret);
    238     return;
    239   }
    240 
    241   jvmtiEventCallbacks callbacks;
    242   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
    243   callbacks.ThreadStart = ThreadStart;
    244   callbacks.ThreadEnd = ThreadEnd;
    245   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
    246   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    247     return;
    248   }
    249 
    250   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    251                                             JVMTI_EVENT_THREAD_START,
    252                                             nullptr);
    253   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    254     return;
    255   }
    256   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    257                                             JVMTI_EVENT_THREAD_END,
    258                                             nullptr);
    259   JvmtiErrorToException(env, jvmti_env, ret);
    260 }
    261 
    262 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadEventMessages(
    263     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
    264   std::lock_guard<std::mutex> guard(gEventsMutex);
    265   jobjectArray ret = CreateObjectArray(env,
    266                                        static_cast<jint>(gEvents.size()),
    267                                        "java/lang/String",
    268                                        [&](jint i) {
    269     return env->NewStringUTF(gEvents[i].c_str());
    270   });
    271   gEvents.clear();
    272   return ret;
    273 }
    274 
    275 }  // namespace Test924Threads
    276 }  // namespace art
    277