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 // private static native Thread getCurrentThread();
     39 // private static native Object[] getThreadInfo(Thread t);
     40 
     41 extern "C" JNIEXPORT jthread JNICALL Java_art_Test924_getCurrentThread(
     42     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
     43   jthread thread = nullptr;
     44   jvmtiError result = jvmti_env->GetCurrentThread(&thread);
     45   if (JvmtiErrorToException(env, jvmti_env, result)) {
     46     return nullptr;
     47   }
     48   return thread;
     49 }
     50 
     51 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadInfo(
     52     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
     53   jvmtiThreadInfo info;
     54   memset(&info, 0, sizeof(jvmtiThreadInfo));
     55 
     56   jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
     57   if (JvmtiErrorToException(env, jvmti_env, result)) {
     58     return nullptr;
     59   }
     60 
     61   auto callback = [&](jint component_index) -> jobject {
     62     switch (component_index) {
     63       // The name.
     64       case 0:
     65         return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name);
     66 
     67       // The priority. Use a string for simplicity of construction.
     68       case 1:
     69         return env->NewStringUTF(android::base::StringPrintf("%d", info.priority).c_str());
     70 
     71       // Whether it's a daemon. Use a string for simplicity of construction.
     72       case 2:
     73         return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false");
     74 
     75       // The thread group;
     76       case 3:
     77         return env->NewLocalRef(info.thread_group);
     78 
     79       // The context classloader.
     80       case 4:
     81         return env->NewLocalRef(info.context_class_loader);
     82     }
     83     LOG(FATAL) << "Should not reach here";
     84     UNREACHABLE();
     85   };
     86   jobjectArray ret = CreateObjectArray(env, 5, "java/lang/Object", callback);
     87 
     88   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
     89   if (info.thread_group != nullptr) {
     90     env->DeleteLocalRef(info.thread_group);
     91   }
     92   if (info.context_class_loader != nullptr) {
     93     env->DeleteLocalRef(info.context_class_loader);
     94   }
     95 
     96   return ret;
     97 }
     98 
     99 extern "C" JNIEXPORT jint JNICALL Java_art_Test924_getThreadState(
    100     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
    101   jint state;
    102   jvmtiError result = jvmti_env->GetThreadState(thread, &state);
    103   if (JvmtiErrorToException(env, jvmti_env, result)) {
    104     return 0;
    105   }
    106   return state;
    107 }
    108 
    109 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getAllThreads(
    110     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
    111   jint thread_count;
    112   jthread* threads;
    113 
    114   jvmtiError result = jvmti_env->GetAllThreads(&thread_count, &threads);
    115   if (JvmtiErrorToException(env, jvmti_env, result)) {
    116     return nullptr;
    117   }
    118 
    119   auto callback = [&](jint index) {
    120     return threads[index];
    121   };
    122   jobjectArray ret = CreateObjectArray(env, thread_count, "java/lang/Thread", callback);
    123 
    124   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads));
    125 
    126   return ret;
    127 }
    128 
    129 extern "C" JNIEXPORT jlong JNICALL Java_art_Test924_getTLS(
    130     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
    131   void* tls;
    132   jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
    133   if (JvmtiErrorToException(env, jvmti_env, result)) {
    134     return 0;
    135   }
    136   return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
    137 }
    138 
    139 extern "C" JNIEXPORT void JNICALL Java_art_Test924_setTLS(
    140     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
    141   const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
    142   jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
    143   JvmtiErrorToException(env, jvmti_env, result);
    144 }
    145 
    146 static std::mutex gEventsMutex;
    147 static std::vector<std::string> gEvents;
    148 
    149 static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
    150                                 JNIEnv* jni_env,
    151                                 jthread thread,
    152                                 bool is_start) {
    153   jvmtiThreadInfo info;
    154   {
    155     std::lock_guard<std::mutex> guard(gEventsMutex);
    156 
    157     jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
    158     if (result != JVMTI_ERROR_NONE) {
    159       gEvents.push_back("Error getting thread info");
    160       return;
    161     }
    162 
    163     gEvents.push_back(android::base::StringPrintf("Thread(%s): %s",
    164                                                   info.name,
    165                                                   is_start ? "start" : "end"));
    166   }
    167 
    168   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
    169   jni_env->DeleteLocalRef(info.thread_group);
    170   jni_env->DeleteLocalRef(info.context_class_loader);
    171 }
    172 
    173 static void JNICALL ThreadStart(jvmtiEnv* jvmti_env,
    174                                 JNIEnv* jni_env,
    175                                 jthread thread) {
    176   ThreadEvent(jvmti_env, jni_env, thread, true);
    177 }
    178 
    179 static void JNICALL ThreadEnd(jvmtiEnv* jvmti_env,
    180                               JNIEnv* jni_env,
    181                               jthread thread) {
    182   ThreadEvent(jvmti_env, jni_env, thread, false);
    183 }
    184 
    185 extern "C" JNIEXPORT void JNICALL Java_art_Test924_enableThreadEvents(
    186     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
    187   if (b == JNI_FALSE) {
    188     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    189                                                          JVMTI_EVENT_THREAD_START,
    190                                                          nullptr);
    191     if (JvmtiErrorToException(env, jvmti_env, ret)) {
    192       return;
    193     }
    194     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
    195                                               JVMTI_EVENT_THREAD_END,
    196                                               nullptr);
    197     JvmtiErrorToException(env, jvmti_env, ret);
    198     return;
    199   }
    200 
    201   jvmtiEventCallbacks callbacks;
    202   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
    203   callbacks.ThreadStart = ThreadStart;
    204   callbacks.ThreadEnd = ThreadEnd;
    205   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
    206   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    207     return;
    208   }
    209 
    210   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    211                                             JVMTI_EVENT_THREAD_START,
    212                                             nullptr);
    213   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    214     return;
    215   }
    216   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
    217                                             JVMTI_EVENT_THREAD_END,
    218                                             nullptr);
    219   JvmtiErrorToException(env, jvmti_env, ret);
    220 }
    221 
    222 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadEventMessages(
    223     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
    224   std::lock_guard<std::mutex> guard(gEventsMutex);
    225   jobjectArray ret = CreateObjectArray(env,
    226                                        static_cast<jint>(gEvents.size()),
    227                                        "java/lang/String",
    228                                        [&](jint i) {
    229     return env->NewStringUTF(gEvents[i].c_str());
    230   });
    231   gEvents.clear();
    232   return ret;
    233 }
    234 
    235 }  // namespace Test924Threads
    236 }  // namespace art
    237