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 {
AgentDataart::Test930AgentThread::AgentData35   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 
AgentMain(jvmtiEnv * jenv,JNIEnv * env,void * arg)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 
Java_art_Test931_testAgentThread(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED)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