179 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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 <inttypes.h>
 | |
| #include <pthread.h>
 | |
| #include <sched.h>
 | |
| 
 | |
| #include "android-base/logging.h"
 | |
| #include "android-base/macros.h"
 | |
| #include "jni.h"
 | |
| #include "jvmti.h"
 | |
| #include "scoped_local_ref.h"
 | |
| 
 | |
| // Test infrastructure
 | |
| #include "jvmti_helper.h"
 | |
| #include "test_env.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace Test930AgentThread {
 | |
| 
 | |
| struct AgentData {
 | |
|   AgentData() : main_thread(nullptr),
 | |
|                 jvmti_env(nullptr),
 | |
|                 priority(0) {
 | |
|   }
 | |
| 
 | |
|   jthread main_thread;
 | |
|   jvmtiEnv* jvmti_env;
 | |
|   pthread_barrier_t b;
 | |
|   jint priority;
 | |
| };
 | |
| 
 | |
| static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) {
 | |
|   AgentData* data = reinterpret_cast<AgentData*>(arg);
 | |
| 
 | |
|   // Check some basics.
 | |
|   // This thread is not the main thread.
 | |
|   jthread this_thread;
 | |
|   jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread);
 | |
|   CheckJvmtiError(jenv, this_thread_result);
 | |
|   CHECK(!env->IsSameObject(this_thread, data->main_thread));
 | |
| 
 | |
|   // The thread is a daemon.
 | |
|   jvmtiThreadInfo info;
 | |
|   jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info);
 | |
|   CheckJvmtiError(jenv, info_result);
 | |
|   CHECK(info.is_daemon);
 | |
|   CheckJvmtiError(jenv, jenv->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);
 | |
|   }
 | |
| 
 | |
|   // The thread has the requested priority.
 | |
|   // TODO: Our thread priorities do not work on the host.
 | |
|   // CHECK_EQ(info.priority, data->priority);
 | |
| 
 | |
|   // Check further parts of the thread:
 | |
|   jint thread_count;
 | |
|   jthread* threads;
 | |
|   jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads);
 | |
|   CheckJvmtiError(jenv, threads_result);
 | |
|   bool found = false;
 | |
|   for (jint i = 0; i != thread_count; ++i) {
 | |
|     if (env->IsSameObject(threads[i], this_thread)) {
 | |
|       found = true;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   CHECK(found);
 | |
| 
 | |
|   // Done, let the main thread progress.
 | |
|   int wait_result = pthread_barrier_wait(&data->b);
 | |
|   CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0);
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT void JNICALL Java_art_Test931_testAgentThread(
 | |
|     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
 | |
|   // Create a Thread object.
 | |
|   ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread"));
 | |
|   if (thread_name.get() == nullptr) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
 | |
|   if (thread_klass.get() == nullptr) {
 | |
|     return;
 | |
|   }
 | |
|   ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get()));
 | |
|   if (thread.get() == nullptr) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Get a ThreadGroup from the current thread. We need a non-null one as we're gonna call a
 | |
|   // runtime-only constructor (so we can set priority and daemon state).
 | |
|   jvmtiThreadInfo cur_thread_info;
 | |
|   jvmtiError info_result = jvmti_env->GetThreadInfo(nullptr, &cur_thread_info);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, info_result)) {
 | |
|     return;
 | |
|   }
 | |
|   CheckJvmtiError(jvmti_env,
 | |
|                   jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(cur_thread_info.name)));
 | |
|   ScopedLocalRef<jobject> thread_group(env, cur_thread_info.thread_group);
 | |
|   if (cur_thread_info.context_class_loader != nullptr) {
 | |
|     env->DeleteLocalRef(cur_thread_info.context_class_loader);
 | |
|   }
 | |
| 
 | |
|   jmethodID initID = env->GetMethodID(
 | |
|       thread_klass.get(), "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V");
 | |
|   if (initID == nullptr) {
 | |
|     return;
 | |
|   }
 | |
|   env->CallNonvirtualVoidMethod(
 | |
|       thread.get(), thread_klass.get(), initID, thread_group.get(), thread_name.get());
 | |
|   if (env->ExceptionCheck()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   jthread main_thread;
 | |
|   jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, main_thread_result)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   AgentData data;
 | |
|   data.main_thread = env->NewGlobalRef(main_thread);
 | |
|   data.jvmti_env = jvmti_env;
 | |
|   data.priority = JVMTI_THREAD_MIN_PRIORITY;
 | |
|   CHECK_EQ(0, pthread_barrier_init(&data.b, nullptr, 2));
 | |
| 
 | |
|   jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, result)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   int wait_result = pthread_barrier_wait(&data.b);
 | |
|   CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0);
 | |
| 
 | |
|   // Scheduling may mean that the agent thread is put to sleep. Wait until it's dead in an effort
 | |
|   // to not unload the plugin and crash.
 | |
|   for (;;) {
 | |
|     sleep(1);
 | |
|     jint thread_state;
 | |
|     jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state);
 | |
|     if (JvmtiErrorToException(env, jvmti_env, state_result)) {
 | |
|       return;
 | |
|     }
 | |
|     if (thread_state == 0 ||                                    // Was never alive.
 | |
|         (thread_state & JVMTI_THREAD_STATE_TERMINATED) != 0) {  // Was alive and died.
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   // Yield and sleep a bit more, to give the plugin time to tear down the native thread structure.
 | |
|   sched_yield();
 | |
|   sleep(1);
 | |
| 
 | |
|   env->DeleteGlobalRef(data.main_thread);
 | |
| 
 | |
|   pthread_barrier_destroy(&data.b);
 | |
| }
 | |
| 
 | |
| }  // namespace Test930AgentThread
 | |
| }  // namespace art
 |