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 <inttypes.h> 18 #include <pthread.h> 19 #include <sched.h> 20 21 #include "android-base/logging.h" 22 #include "android-base/macros.h" 23 #include "jni.h" 24 #include "jvmti.h" 25 #include "scoped_local_ref.h" 26 27 // Test infrastructure 28 #include "jvmti_helper.h" 29 #include "test_env.h" 30 31 namespace art { 32 namespace Test930AgentThread { 33 34 struct AgentData { 35 AgentData() : main_thread(nullptr), 36 jvmti_env(nullptr), 37 priority(0) { 38 } 39 40 jthread main_thread; 41 jvmtiEnv* jvmti_env; 42 pthread_barrier_t b; 43 jint priority; 44 }; 45 46 static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) { 47 AgentData* data = reinterpret_cast<AgentData*>(arg); 48 49 // Check some basics. 50 // This thread is not the main thread. 51 jthread this_thread; 52 jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread); 53 CheckJvmtiError(jenv, this_thread_result); 54 CHECK(!env->IsSameObject(this_thread, data->main_thread)); 55 56 // The thread is a daemon. 57 jvmtiThreadInfo info; 58 jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info); 59 CheckJvmtiError(jenv, info_result); 60 CHECK(info.is_daemon); 61 CheckJvmtiError(jenv, jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name))); 62 if (info.thread_group != nullptr) { 63 env->DeleteLocalRef(info.thread_group); 64 } 65 if (info.context_class_loader != nullptr) { 66 env->DeleteLocalRef(info.context_class_loader); 67 } 68 69 // The thread has the requested priority. 70 // TODO: Our thread priorities do not work on the host. 71 // CHECK_EQ(info.priority, data->priority); 72 73 // Check further parts of the thread: 74 jint thread_count; 75 jthread* threads; 76 jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads); 77 CheckJvmtiError(jenv, threads_result); 78 bool found = false; 79 for (jint i = 0; i != thread_count; ++i) { 80 if (env->IsSameObject(threads[i], this_thread)) { 81 found = true; 82 break; 83 } 84 } 85 CHECK(found); 86 87 // Done, let the main thread progress. 88 int wait_result = pthread_barrier_wait(&data->b); 89 CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0); 90 } 91 92 extern "C" JNIEXPORT void JNICALL Java_art_Test931_testAgentThread( 93 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { 94 // Create a Thread object. 95 ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread")); 96 if (thread_name.get() == nullptr) { 97 return; 98 } 99 100 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread")); 101 if (thread_klass.get() == nullptr) { 102 return; 103 } 104 ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get())); 105 if (thread.get() == nullptr) { 106 return; 107 } 108 109 // Get a ThreadGroup from the current thread. We need a non-null one as we're gonna call a 110 // runtime-only constructor (so we can set priority and daemon state). 111 jvmtiThreadInfo cur_thread_info; 112 jvmtiError info_result = jvmti_env->GetThreadInfo(nullptr, &cur_thread_info); 113 if (JvmtiErrorToException(env, jvmti_env, info_result)) { 114 return; 115 } 116 CheckJvmtiError(jvmti_env, 117 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(cur_thread_info.name))); 118 ScopedLocalRef<jobject> thread_group(env, cur_thread_info.thread_group); 119 if (cur_thread_info.context_class_loader != nullptr) { 120 env->DeleteLocalRef(cur_thread_info.context_class_loader); 121 } 122 123 jmethodID initID = env->GetMethodID(thread_klass.get(), 124 "<init>", 125 "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); 126 if (initID == nullptr) { 127 return; 128 } 129 env->CallNonvirtualVoidMethod(thread.get(), 130 thread_klass.get(), 131 initID, 132 thread_group.get(), 133 thread_name.get(), 134 0, 135 JNI_FALSE); 136 if (env->ExceptionCheck()) { 137 return; 138 } 139 140 jthread main_thread; 141 jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread); 142 if (JvmtiErrorToException(env, jvmti_env, main_thread_result)) { 143 return; 144 } 145 146 AgentData data; 147 data.main_thread = env->NewGlobalRef(main_thread); 148 data.jvmti_env = jvmti_env; 149 data.priority = JVMTI_THREAD_MIN_PRIORITY; 150 CHECK_EQ(0, pthread_barrier_init(&data.b, nullptr, 2)); 151 152 jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority); 153 if (JvmtiErrorToException(env, jvmti_env, result)) { 154 return; 155 } 156 157 int wait_result = pthread_barrier_wait(&data.b); 158 CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0); 159 160 // Scheduling may mean that the agent thread is put to sleep. Wait until it's dead in an effort 161 // to not unload the plugin and crash. 162 for (;;) { 163 sleep(1); 164 jint thread_state; 165 jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state); 166 if (JvmtiErrorToException(env, jvmti_env, state_result)) { 167 return; 168 } 169 if (thread_state == 0 || // Was never alive. 170 (thread_state & JVMTI_THREAD_STATE_TERMINATED) != 0) { // Was alive and died. 171 break; 172 } 173 } 174 // Yield and sleep a bit more, to give the plugin time to tear down the native thread structure. 175 sched_yield(); 176 sleep(1); 177 178 env->DeleteGlobalRef(data.main_thread); 179 180 pthread_barrier_destroy(&data.b); 181 } 182 183 } // namespace Test930AgentThread 184 } // namespace art 185