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 
Java_art_Test924_nativeWaiterStructAlloc(JNIEnv * env,jclass TestClass ATTRIBUTE_UNUSED)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 
Java_art_Test924_nativeWaiterStructWaitForNative(JNIEnv * env ATTRIBUTE_UNUSED,jclass TestClass ATTRIBUTE_UNUSED,jlong waiter_struct)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 
Java_art_Test924_nativeWaiterStructFinish(JNIEnv * env ATTRIBUTE_UNUSED,jclass TestClass ATTRIBUTE_UNUSED,jlong waiter_struct)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 
Java_art_Test924_nativeLoop(JNIEnv * env,jclass TestClass ATTRIBUTE_UNUSED,jlong waiter_struct)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 
Java_art_Test924_getCurrentThread(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED)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 
Java_art_Test924_getThreadInfo(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jthread thread)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 
Java_art_Test924_getThreadState(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jthread thread)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 
Java_art_Test924_getAllThreads(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED)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 
Java_art_Test924_getTLS(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jthread thread)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 
Java_art_Test924_setTLS(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jthread thread,jlong val)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 
ThreadEvent(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread,bool is_start)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 
ThreadStart(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread)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 
ThreadEnd(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jthread thread)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 
Java_art_Test924_enableThreadEvents(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jboolean b)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 
Java_art_Test924_getThreadEventMessages(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED)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