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