| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| |
| #include <mutex> |
| #include <string> |
| #include <vector> |
| |
| #include "android-base/logging.h" |
| #include "android-base/stringprintf.h" |
| #include "jni.h" |
| #include "jvmti.h" |
| #include "scoped_local_ref.h" |
| |
| // Test infrastructure |
| #include "jni_helper.h" |
| #include "jvmti_helper.h" |
| #include "test_env.h" |
| #include "ti_macros.h" |
| |
| namespace art { |
| namespace Test924Threads { |
| |
| struct WaiterStruct { |
| std::atomic<bool> started; |
| std::atomic<bool> finish; |
| }; |
| |
| extern "C" JNIEXPORT jlong JNICALL Java_art_Test924_nativeWaiterStructAlloc( |
| JNIEnv* env, [[maybe_unused]] jclass TestClass) { |
| WaiterStruct* s = nullptr; |
| if (JvmtiErrorToException(env, |
| jvmti_env, |
| jvmti_env->Allocate(sizeof(WaiterStruct), |
| reinterpret_cast<unsigned char**>(&s)))) { |
| return 0; |
| } |
| s->started = false; |
| s->finish = false; |
| return static_cast<jlong>(reinterpret_cast<intptr_t>(s)); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test924_nativeWaiterStructWaitForNative( |
| [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass TestClass, jlong waiter_struct) { |
| WaiterStruct* s = reinterpret_cast<WaiterStruct*>(static_cast<intptr_t>(waiter_struct)); |
| while (!s->started) { } |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test924_nativeWaiterStructFinish( |
| [[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass TestClass, jlong waiter_struct) { |
| WaiterStruct* s = reinterpret_cast<WaiterStruct*>(static_cast<intptr_t>(waiter_struct)); |
| s->finish = true; |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test924_nativeLoop(JNIEnv* env, |
| [[maybe_unused]] jclass TestClass, |
| jlong waiter_struct) { |
| WaiterStruct* s = reinterpret_cast<WaiterStruct*>(static_cast<intptr_t>(waiter_struct)); |
| s->started = true; |
| while (!s->finish) { } |
| JvmtiErrorToException(env, jvmti_env, jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(s))); |
| } |
| |
| // private static native Thread getCurrentThread(); |
| // private static native Object[] getThreadInfo(Thread t); |
| |
| extern "C" JNIEXPORT jthread JNICALL Java_art_Test924_getCurrentThread( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass) { |
| jthread thread = nullptr; |
| jvmtiError result = jvmti_env->GetCurrentThread(&thread); |
| if (JvmtiErrorToException(env, jvmti_env, result)) { |
| return nullptr; |
| } |
| return thread; |
| } |
| |
| extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadInfo( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass, jthread thread) { |
| jvmtiThreadInfo info; |
| memset(&info, 0, sizeof(jvmtiThreadInfo)); |
| |
| jvmtiError result = jvmti_env->GetThreadInfo(thread, &info); |
| if (JvmtiErrorToException(env, jvmti_env, result)) { |
| return nullptr; |
| } |
| |
| auto callback = [&](jint component_index) -> jobject { |
| switch (component_index) { |
| // The name. |
| case 0: |
| return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name); |
| |
| // The priority. Use a string for simplicity of construction. |
| case 1: |
| return env->NewStringUTF(android::base::StringPrintf("%d", info.priority).c_str()); |
| |
| // Whether it's a daemon. Use a string for simplicity of construction. |
| case 2: |
| return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false"); |
| |
| // The thread group; |
| case 3: |
| return env->NewLocalRef(info.thread_group); |
| |
| // The context classloader. |
| case 4: |
| return env->NewLocalRef(info.context_class_loader); |
| } |
| LOG(FATAL) << "Should not reach here"; |
| UNREACHABLE(); |
| }; |
| jobjectArray ret = CreateObjectArray(env, 5, "java/lang/Object", callback); |
| |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name)); |
| if (info.thread_group != nullptr) { |
| env->DeleteLocalRef(info.thread_group); |
| } |
| if (info.context_class_loader != nullptr) { |
| env->DeleteLocalRef(info.context_class_loader); |
| } |
| |
| return ret; |
| } |
| |
| extern "C" JNIEXPORT jint JNICALL Java_art_Test924_getThreadState( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass, jthread thread) { |
| jint state; |
| jvmtiError result = jvmti_env->GetThreadState(thread, &state); |
| if (JvmtiErrorToException(env, jvmti_env, result)) { |
| return 0; |
| } |
| return state; |
| } |
| |
| extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getAllThreads( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass) { |
| jint thread_count; |
| jthread* threads; |
| |
| jvmtiError result = jvmti_env->GetAllThreads(&thread_count, &threads); |
| if (JvmtiErrorToException(env, jvmti_env, result)) { |
| return nullptr; |
| } |
| |
| auto callback = [&](jint index) { |
| return threads[index]; |
| }; |
| jobjectArray ret = CreateObjectArray(env, thread_count, "java/lang/Thread", callback); |
| |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads)); |
| |
| return ret; |
| } |
| |
| extern "C" JNIEXPORT jlong JNICALL Java_art_Test924_getTLS( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass, jthread thread) { |
| void* tls; |
| jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls); |
| if (JvmtiErrorToException(env, jvmti_env, result)) { |
| return 0; |
| } |
| return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls)); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test924_setTLS( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass, jthread thread, jlong val) { |
| const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val)); |
| jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls); |
| JvmtiErrorToException(env, jvmti_env, result); |
| } |
| |
| static std::mutex gEventsMutex; |
| static std::vector<std::string> gEvents; |
| |
| static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env, |
| JNIEnv* jni_env, |
| jthread thread, |
| bool is_start) { |
| jvmtiThreadInfo info; |
| { |
| std::lock_guard<std::mutex> guard(gEventsMutex); |
| |
| jvmtiError result = jvmti_env->GetThreadInfo(thread, &info); |
| if (result != JVMTI_ERROR_NONE) { |
| gEvents.push_back("Error getting thread info"); |
| return; |
| } |
| |
| gEvents.push_back(android::base::StringPrintf("Thread(%s): %s", |
| info.name, |
| is_start ? "start" : "end")); |
| } |
| |
| jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name)); |
| jni_env->DeleteLocalRef(info.thread_group); |
| jni_env->DeleteLocalRef(info.context_class_loader); |
| } |
| |
| static void JNICALL ThreadStart(jvmtiEnv* jvmti_env, |
| JNIEnv* jni_env, |
| jthread thread) { |
| ThreadEvent(jvmti_env, jni_env, thread, true); |
| } |
| |
| static void JNICALL ThreadEnd(jvmtiEnv* jvmti_env, |
| JNIEnv* jni_env, |
| jthread thread) { |
| ThreadEvent(jvmti_env, jni_env, thread, false); |
| } |
| |
| extern "C" JNIEXPORT void JNICALL Java_art_Test924_enableThreadEvents( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass, jboolean b) { |
| if (b == JNI_FALSE) { |
| jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_THREAD_START, |
| nullptr); |
| if (JvmtiErrorToException(env, jvmti_env, ret)) { |
| return; |
| } |
| ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, |
| JVMTI_EVENT_THREAD_END, |
| nullptr); |
| JvmtiErrorToException(env, jvmti_env, ret); |
| return; |
| } |
| |
| jvmtiEventCallbacks callbacks; |
| memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); |
| callbacks.ThreadStart = ThreadStart; |
| callbacks.ThreadEnd = ThreadEnd; |
| jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); |
| if (JvmtiErrorToException(env, jvmti_env, ret)) { |
| return; |
| } |
| |
| ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_THREAD_START, |
| nullptr); |
| if (JvmtiErrorToException(env, jvmti_env, ret)) { |
| return; |
| } |
| ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, |
| JVMTI_EVENT_THREAD_END, |
| nullptr); |
| JvmtiErrorToException(env, jvmti_env, ret); |
| } |
| |
| extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test924_getThreadEventMessages( |
| JNIEnv* env, [[maybe_unused]] jclass Main_klass) { |
| std::lock_guard<std::mutex> guard(gEventsMutex); |
| jobjectArray ret = CreateObjectArray(env, |
| static_cast<jint>(gEvents.size()), |
| "java/lang/String", |
| [&](jint i) { |
| return env->NewStringUTF(gEvents[i].c_str()); |
| }); |
| gEvents.clear(); |
| return ret; |
| } |
| |
| } // namespace Test924Threads |
| } // namespace art |