blob: 1f6954ef99bdea1647847d9e9f967b6f95452310 [file] [log] [blame]
Andreas Gampee492ae32016-10-28 19:34:57 -07001/*
2 * Copyright (C) 2013 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
Andreas Gampee492ae32016-10-28 19:34:57 -070017#include <stdio.h>
18
Igor Murashkin5573c372017-11-16 13:34:30 -080019#include <condition_variable>
20#include <mutex>
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070021#include <vector>
Andreas Gampe027444b2017-03-31 12:49:07 -070022
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070023#include "android-base/macros.h"
24#include "android-base/stringprintf.h"
25
Andreas Gampee492ae32016-10-28 19:34:57 -070026#include "jni.h"
Andreas Gampe5e03a302017-03-13 13:10:00 -070027#include "jvmti.h"
Andreas Gampee492ae32016-10-28 19:34:57 -070028
Andreas Gampe3f46c962017-03-30 10:26:59 -070029// Test infrastructure
30#include "jni_helper.h"
31#include "jvmti_helper.h"
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070032#include "scoped_local_ref.h"
33#include "scoped_utf_chars.h"
Andreas Gampe3f46c962017-03-30 10:26:59 -070034#include "test_env.h"
Andreas Gampee492ae32016-10-28 19:34:57 -070035
36namespace art {
37namespace Test912Classes {
38
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070039extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isModifiableClass(
40 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
Alex Lighte4a88632017-01-10 07:41:24 -080041 jboolean res = JNI_FALSE;
42 jvmtiError result = jvmti_env->IsModifiableClass(klass, &res);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070043 JvmtiErrorToException(env, jvmti_env, result);
Alex Lighte4a88632017-01-10 07:41:24 -080044 return res;
45}
46
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070047extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassSignature(
Andreas Gampee492ae32016-10-28 19:34:57 -070048 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
49 char* sig;
50 char* gen;
51 jvmtiError result = jvmti_env->GetClassSignature(klass, &sig, &gen);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070052 if (JvmtiErrorToException(env, jvmti_env, result)) {
Andreas Gampee492ae32016-10-28 19:34:57 -070053 return nullptr;
54 }
55
Andreas Gampe336c3c32016-11-08 17:02:19 -080056 auto callback = [&](jint i) {
57 if (i == 0) {
58 return sig == nullptr ? nullptr : env->NewStringUTF(sig);
59 } else {
60 return gen == nullptr ? nullptr : env->NewStringUTF(gen);
61 }
62 };
63 jobjectArray ret = CreateObjectArray(env, 2, "java/lang/String", callback);
Andreas Gampee492ae32016-10-28 19:34:57 -070064
65 // Need to deallocate the strings.
66 if (sig != nullptr) {
67 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
68 }
69 if (gen != nullptr) {
70 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
71 }
72
73 return ret;
74}
75
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070076extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isInterface(
77 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
Andreas Gampe4fd66ec2017-01-05 14:42:13 -080078 jboolean is_interface = JNI_FALSE;
79 jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070080 JvmtiErrorToException(env, jvmti_env, result);
Andreas Gampe4fd66ec2017-01-05 14:42:13 -080081 return is_interface;
82}
83
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070084extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isArrayClass(
85 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
Andreas Gampe4fd66ec2017-01-05 14:42:13 -080086 jboolean is_array_class = JNI_FALSE;
87 jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070088 JvmtiErrorToException(env, jvmti_env, result);
Andreas Gampe4fd66ec2017-01-05 14:42:13 -080089 return is_array_class;
90}
91
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070092extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassModifiers(
93 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
Andreas Gampe64013e52017-01-06 13:07:19 -080094 jint mod;
95 jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -070096 JvmtiErrorToException(env, jvmti_env, result);
Andreas Gampe64013e52017-01-06 13:07:19 -080097 return mod;
98}
99
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700100extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassFields(
Andreas Gampeac587272017-01-05 15:21:34 -0800101 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
102 jint count = 0;
103 jfieldID* fields = nullptr;
104 jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700105 if (JvmtiErrorToException(env, jvmti_env, result)) {
Andreas Gampeac587272017-01-05 15:21:34 -0800106 return nullptr;
107 }
108
109 auto callback = [&](jint i) {
110 jint modifiers;
111 // Ignore any errors for simplicity.
112 jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers);
113 constexpr jint kStatic = 0x8;
114 return env->ToReflectedField(klass,
115 fields[i],
116 (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
117 };
Andreas Gampe8b07e472017-01-06 14:20:39 -0800118 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
119 if (fields != nullptr) {
120 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
121 }
122 return ret;
Andreas Gampeac587272017-01-05 15:21:34 -0800123}
124
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700125extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassMethods(
Andreas Gampe18fee4d2017-01-06 11:36:35 -0800126 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
127 jint count = 0;
128 jmethodID* methods = nullptr;
129 jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700130 if (JvmtiErrorToException(env, jvmti_env, result)) {
Andreas Gampe18fee4d2017-01-06 11:36:35 -0800131 return nullptr;
132 }
133
134 auto callback = [&](jint i) {
135 jint modifiers;
136 // Ignore any errors for simplicity.
137 jvmti_env->GetMethodModifiers(methods[i], &modifiers);
138 constexpr jint kStatic = 0x8;
139 return env->ToReflectedMethod(klass,
140 methods[i],
141 (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
142 };
Andreas Gampe8b07e472017-01-06 14:20:39 -0800143 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
144 if (methods != nullptr) {
145 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods));
146 }
147 return ret;
148}
149
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700150extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getImplementedInterfaces(
Andreas Gampe8b07e472017-01-06 14:20:39 -0800151 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
152 jint count = 0;
153 jclass* classes = nullptr;
154 jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700155 if (JvmtiErrorToException(env, jvmti_env, result)) {
Andreas Gampe8b07e472017-01-06 14:20:39 -0800156 return nullptr;
157 }
158
159 auto callback = [&](jint i) {
160 return classes[i];
161 };
162 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
163 if (classes != nullptr) {
164 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
165 }
166 return ret;
Andreas Gampe18fee4d2017-01-06 11:36:35 -0800167}
168
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700169extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassStatus(
170 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
Andreas Gampeff9d2092017-01-06 09:12:49 -0800171 jint status;
172 jvmtiError result = jvmti_env->GetClassStatus(klass, &status);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700173 JvmtiErrorToException(env, jvmti_env, result);
Andreas Gampeff9d2092017-01-06 09:12:49 -0800174 return status;
175}
176
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700177extern "C" JNIEXPORT jobject JNICALL Java_art_Test912_getClassLoader(
178 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
Andreas Gampe8f5b6032017-01-06 15:50:55 -0800179 jobject classloader;
180 jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader);
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700181 JvmtiErrorToException(env, jvmti_env, result);
Andreas Gampe8f5b6032017-01-06 15:50:55 -0800182 return classloader;
183}
184
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700185extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoaderClasses(
Andreas Gampe70f16392017-01-16 14:20:10 -0800186 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) {
187 jint count = 0;
188 jclass* classes = nullptr;
189 jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700190 if (JvmtiErrorToException(env, jvmti_env, result)) {
Andreas Gampe70f16392017-01-16 14:20:10 -0800191 return nullptr;
192 }
193
194 auto callback = [&](jint i) {
195 return classes[i];
196 };
197 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
198 if (classes != nullptr) {
199 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
200 }
201 return ret;
202}
203
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700204extern "C" JNIEXPORT jintArray JNICALL Java_art_Test912_getClassVersion(
Andreas Gampe812a2442017-01-19 22:04:46 -0800205 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
206 jint major, minor;
207 jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700208 if (JvmtiErrorToException(env, jvmti_env, result)) {
Andreas Gampe812a2442017-01-19 22:04:46 -0800209 return nullptr;
210 }
211
212 jintArray int_array = env->NewIntArray(2);
213 if (int_array == nullptr) {
214 return nullptr;
215 }
216 jint buf[2] = { major, minor };
217 env->SetIntArrayRegion(int_array, 0, 2, buf);
218
219 return int_array;
220}
221
Andreas Gampee6377462017-01-20 17:37:50 -0800222static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) {
223 char* name;
224 jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr);
225 if (result != JVMTI_ERROR_NONE) {
226 if (jni_env != nullptr) {
Andreas Gampe3f46c962017-03-30 10:26:59 -0700227 JvmtiErrorToException(jni_env, jenv, result);
Andreas Gampee6377462017-01-20 17:37:50 -0800228 } else {
229 printf("Failed to get class signature.\n");
230 }
231 return "";
232 }
233
234 std::string tmp(name);
235 jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
236
237 return tmp;
238}
239
Andreas Gampee2744c62017-02-08 16:28:59 +0000240static void EnableEvents(JNIEnv* env,
241 jboolean enable,
242 decltype(jvmtiEventCallbacks().ClassLoad) class_load,
243 decltype(jvmtiEventCallbacks().ClassPrepare) class_prepare) {
244 if (enable == JNI_FALSE) {
Andreas Gampee6377462017-01-20 17:37:50 -0800245 jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
246 JVMTI_EVENT_CLASS_LOAD,
247 nullptr);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700248 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampee6377462017-01-20 17:37:50 -0800249 return;
250 }
251 ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
252 JVMTI_EVENT_CLASS_PREPARE,
253 nullptr);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700254 JvmtiErrorToException(env, jvmti_env, ret);
Andreas Gampee6377462017-01-20 17:37:50 -0800255 return;
256 }
257
258 jvmtiEventCallbacks callbacks;
259 memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
Andreas Gampee2744c62017-02-08 16:28:59 +0000260 callbacks.ClassLoad = class_load;
261 callbacks.ClassPrepare = class_prepare;
Andreas Gampee6377462017-01-20 17:37:50 -0800262 jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
Andreas Gampe3f46c962017-03-30 10:26:59 -0700263 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampee6377462017-01-20 17:37:50 -0800264 return;
265 }
266
267 ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
268 JVMTI_EVENT_CLASS_LOAD,
269 nullptr);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700270 if (JvmtiErrorToException(env, jvmti_env, ret)) {
Andreas Gampee6377462017-01-20 17:37:50 -0800271 return;
272 }
273 ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
274 JVMTI_EVENT_CLASS_PREPARE,
275 nullptr);
Andreas Gampe3f46c962017-03-30 10:26:59 -0700276 JvmtiErrorToException(env, jvmti_env, ret);
Andreas Gampee6377462017-01-20 17:37:50 -0800277}
278
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700279static std::mutex gEventsMutex;
280static std::vector<std::string> gEvents;
281
282extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoadMessages(
283 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
284 std::lock_guard<std::mutex> guard(gEventsMutex);
285 jobjectArray ret = CreateObjectArray(env,
286 static_cast<jint>(gEvents.size()),
287 "java/lang/String",
288 [&](jint i) {
289 return env->NewStringUTF(gEvents[i].c_str());
290 });
291 gEvents.clear();
292 return ret;
293}
294
Andreas Gampee2744c62017-02-08 16:28:59 +0000295class ClassLoadPreparePrinter {
296 public:
297 static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
298 JNIEnv* jni_env,
299 jthread thread,
300 jclass klass) {
301 std::string name = GetClassName(jenv, jni_env, klass);
302 if (name == "") {
303 return;
304 }
305 std::string thread_name = GetThreadName(jenv, jni_env, thread);
306 if (thread_name == "") {
307 return;
308 }
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700309 if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) {
310 return;
311 }
312
313 std::lock_guard<std::mutex> guard(gEventsMutex);
314 gEvents.push_back(android::base::StringPrintf("Load: %s on %s",
315 name.c_str(),
316 thread_name.c_str()));
Andreas Gampee2744c62017-02-08 16:28:59 +0000317 }
318
319 static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
320 JNIEnv* jni_env,
321 jthread thread,
322 jclass klass) {
323 std::string name = GetClassName(jenv, jni_env, klass);
324 if (name == "") {
325 return;
326 }
327 std::string thread_name = GetThreadName(jenv, jni_env, thread);
328 if (thread_name == "") {
329 return;
330 }
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700331 if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) {
332 return;
333 }
334 std::string cur_thread_name = GetThreadName(jenv, jni_env, nullptr);
335
336 std::lock_guard<std::mutex> guard(gEventsMutex);
337 gEvents.push_back(android::base::StringPrintf("Prepare: %s on %s (cur=%s)",
338 name.c_str(),
339 thread_name.c_str(),
340 cur_thread_name.c_str()));
Andreas Gampee2744c62017-02-08 16:28:59 +0000341 }
342
Andreas Gampee2744c62017-02-08 16:28:59 +0000343 static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) {
344 jvmtiThreadInfo info;
345 jvmtiError result = jenv->GetThreadInfo(thread, &info);
346 if (result != JVMTI_ERROR_NONE) {
347 if (jni_env != nullptr) {
Andreas Gampe3f46c962017-03-30 10:26:59 -0700348 JvmtiErrorToException(jni_env, jenv, result);
Andreas Gampee2744c62017-02-08 16:28:59 +0000349 } else {
350 printf("Failed to get thread name.\n");
351 }
352 return "";
353 }
354
355 std::string tmp(info.name);
356 jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name));
357 jni_env->DeleteLocalRef(info.context_class_loader);
358 jni_env->DeleteLocalRef(info.thread_group);
359
360 return tmp;
361 }
362
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700363 static std::string thread_name_filter_;
Andreas Gampee2744c62017-02-08 16:28:59 +0000364};
Igor Murashkin2ffb7032017-11-08 13:35:21 -0800365std::string ClassLoadPreparePrinter::thread_name_filter_; // NOLINT [runtime/string] [4]
Andreas Gampee2744c62017-02-08 16:28:59 +0000366
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700367extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPreparePrintEvents(
368 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable, jthread thread) {
369 if (thread != nullptr) {
370 ClassLoadPreparePrinter::thread_name_filter_ =
371 ClassLoadPreparePrinter::GetThreadName(jvmti_env, env, thread);
372 } else {
373 ClassLoadPreparePrinter::thread_name_filter_ = "";
374 }
375
Andreas Gampee2744c62017-02-08 16:28:59 +0000376 EnableEvents(env,
377 enable,
378 ClassLoadPreparePrinter::ClassLoadCallback,
379 ClassLoadPreparePrinter::ClassPrepareCallback);
380}
381
Alex Light9df79b72017-09-12 08:57:31 -0700382template<typename T>
383static jthread RunEventThread(const std::string& name,
384 jvmtiEnv* jvmti,
385 JNIEnv* env,
386 void (*func)(jvmtiEnv*, JNIEnv*, T*),
387 T* data) {
388 // Create a Thread object.
389 std::string name_str = name;
390 name_str += ": JVMTI_THREAD-Test912";
391 ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name_str.c_str()));
392 CHECK(thread_name.get() != nullptr);
393
394 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
395 CHECK(thread_klass.get() != nullptr);
396
397 ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get()));
398 CHECK(thread.get() != nullptr);
399
400 jmethodID initID = env->GetMethodID(thread_klass.get(), "<init>", "(Ljava/lang/String;)V");
401 CHECK(initID != nullptr);
402
403 env->CallNonvirtualVoidMethod(thread.get(), thread_klass.get(), initID, thread_name.get());
404 CHECK(!env->ExceptionCheck());
405
406 // Run agent thread.
407 CheckJvmtiError(jvmti, jvmti->RunAgentThread(thread.get(),
408 reinterpret_cast<jvmtiStartFunction>(func),
409 reinterpret_cast<void*>(data),
410 JVMTI_THREAD_NORM_PRIORITY));
411 return thread.release();
412}
413
414static void JoinTread(JNIEnv* env, jthread thr) {
415 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
416 CHECK(thread_klass.get() != nullptr);
417
418 jmethodID joinID = env->GetMethodID(thread_klass.get(), "join", "()V");
419 CHECK(joinID != nullptr);
420
421 env->CallVoidMethod(thr, joinID);
422}
423
Andreas Gampe691051b2017-02-09 09:15:24 -0800424class ClassLoadPrepareEquality {
425 public:
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700426 static constexpr const char* kClassName = "Lart/Test912$ClassE;";
Andreas Gampea67354b2017-02-10 16:18:30 -0800427 static constexpr const char* kStorageFieldName = "STATIC";
428 static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;";
Andreas Gampe52784ac2017-02-13 18:10:09 -0800429 static constexpr const char* kStorageWeakFieldName = "WEAK";
430 static constexpr const char* kStorageWeakFieldSig = "Ljava/lang/ref/Reference;";
431 static constexpr const char* kWeakClassName = "java/lang/ref/WeakReference";
432 static constexpr const char* kWeakInitSig = "(Ljava/lang/Object;)V";
433 static constexpr const char* kWeakGetSig = "()Ljava/lang/Object;";
Andreas Gampe691051b2017-02-09 09:15:24 -0800434
Alex Light9df79b72017-09-12 08:57:31 -0700435 static void AgentThreadTest(jvmtiEnv* jvmti ATTRIBUTE_UNUSED,
436 JNIEnv* env,
437 jobject* obj_global) {
438 jobject target = *obj_global;
439 jobject target_local = env->NewLocalRef(target);
440 {
441 std::unique_lock<std::mutex> lk(mutex_);
442 started_ = true;
443 cond_started_.notify_all();
444 cond_finished_.wait(lk, [] { return finished_; });
445 CHECK(finished_);
446 }
447 CHECK(env->IsSameObject(target, target_local));
448 }
449
Andreas Gampe691051b2017-02-09 09:15:24 -0800450 static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
451 JNIEnv* jni_env,
452 jthread thread ATTRIBUTE_UNUSED,
453 jclass klass) {
454 std::string name = GetClassName(jenv, jni_env, klass);
455 if (name == kClassName) {
456 found_ = true;
457 stored_class_ = jni_env->NewGlobalRef(klass);
458 weakly_stored_class_ = jni_env->NewWeakGlobalRef(klass);
Alex Light9df79b72017-09-12 08:57:31 -0700459 // Check that we update the local refs.
460 agent_thread_ = static_cast<jthread>(jni_env->NewGlobalRef(RunEventThread<jobject>(
461 "local-ref", jenv, jni_env, &AgentThreadTest, static_cast<jobject*>(&stored_class_))));
462 {
463 std::unique_lock<std::mutex> lk(mutex_);
464 cond_started_.wait(lk, [] { return started_; });
465 }
Andreas Gampea67354b2017-02-10 16:18:30 -0800466 // Store the value into a field in the heap.
467 SetOrCompare(jni_env, klass, true);
Andreas Gampe691051b2017-02-09 09:15:24 -0800468 }
469 }
470
471 static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
472 JNIEnv* jni_env,
473 jthread thread ATTRIBUTE_UNUSED,
474 jclass klass) {
475 std::string name = GetClassName(jenv, jni_env, klass);
476 if (name == kClassName) {
477 CHECK(stored_class_ != nullptr);
478 CHECK(jni_env->IsSameObject(stored_class_, klass));
479 CHECK(jni_env->IsSameObject(weakly_stored_class_, klass));
Alex Light9df79b72017-09-12 08:57:31 -0700480 {
481 std::unique_lock<std::mutex> lk(mutex_);
482 finished_ = true;
483 cond_finished_.notify_all();
484 }
Andreas Gampea67354b2017-02-10 16:18:30 -0800485 // Look up the value in a field in the heap.
486 SetOrCompare(jni_env, klass, false);
Alex Light9df79b72017-09-12 08:57:31 -0700487 JoinTread(jni_env, agent_thread_);
Andreas Gampe691051b2017-02-09 09:15:24 -0800488 compared_ = true;
489 }
490 }
491
Andreas Gampea67354b2017-02-10 16:18:30 -0800492 static void SetOrCompare(JNIEnv* jni_env, jobject value, bool set) {
493 CHECK(storage_class_ != nullptr);
Andreas Gampe52784ac2017-02-13 18:10:09 -0800494
495 // Simple direct storage.
Andreas Gampea67354b2017-02-10 16:18:30 -0800496 jfieldID field = jni_env->GetStaticFieldID(storage_class_, kStorageFieldName, kStorageFieldSig);
497 CHECK(field != nullptr);
498
499 if (set) {
500 jni_env->SetStaticObjectField(storage_class_, field, value);
501 CHECK(!jni_env->ExceptionCheck());
502 } else {
503 ScopedLocalRef<jobject> stored(jni_env, jni_env->GetStaticObjectField(storage_class_, field));
504 CHECK(jni_env->IsSameObject(value, stored.get()));
505 }
Andreas Gampe52784ac2017-02-13 18:10:09 -0800506
507 // Storage as a reference.
508 ScopedLocalRef<jclass> weak_ref_class(jni_env, jni_env->FindClass(kWeakClassName));
509 CHECK(weak_ref_class.get() != nullptr);
510 jfieldID weak_field = jni_env->GetStaticFieldID(storage_class_,
511 kStorageWeakFieldName,
512 kStorageWeakFieldSig);
513 CHECK(weak_field != nullptr);
514 if (set) {
515 // Create a WeakReference.
516 jmethodID weak_init = jni_env->GetMethodID(weak_ref_class.get(), "<init>", kWeakInitSig);
517 CHECK(weak_init != nullptr);
518 ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->NewObject(weak_ref_class.get(),
519 weak_init,
520 value));
521 CHECK(weak_obj.get() != nullptr);
522 jni_env->SetStaticObjectField(storage_class_, weak_field, weak_obj.get());
523 CHECK(!jni_env->ExceptionCheck());
524 } else {
525 // Check the reference value.
526 jmethodID get_referent = jni_env->GetMethodID(weak_ref_class.get(), "get", kWeakGetSig);
527 CHECK(get_referent != nullptr);
528 ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->GetStaticObjectField(storage_class_,
529 weak_field));
530 CHECK(weak_obj.get() != nullptr);
531 ScopedLocalRef<jobject> weak_referent(jni_env, jni_env->CallObjectMethod(weak_obj.get(),
532 get_referent));
533 CHECK(weak_referent.get() != nullptr);
534 CHECK(jni_env->IsSameObject(value, weak_referent.get()));
535 }
Andreas Gampea67354b2017-02-10 16:18:30 -0800536 }
537
Andreas Gampe691051b2017-02-09 09:15:24 -0800538 static void CheckFound() {
539 CHECK(found_);
540 CHECK(compared_);
541 }
542
543 static void Free(JNIEnv* env) {
544 if (stored_class_ != nullptr) {
545 env->DeleteGlobalRef(stored_class_);
546 DCHECK(weakly_stored_class_ != nullptr);
547 env->DeleteWeakGlobalRef(weakly_stored_class_);
Andreas Gampe94dda932017-02-09 18:19:21 -0800548 // Do not attempt to delete the local ref. It will be out of date by now.
Andreas Gampe691051b2017-02-09 09:15:24 -0800549 }
550 }
551
Andreas Gampea67354b2017-02-10 16:18:30 -0800552 static jclass storage_class_;
553
Andreas Gampe691051b2017-02-09 09:15:24 -0800554 private:
555 static jobject stored_class_;
556 static jweak weakly_stored_class_;
Alex Light9df79b72017-09-12 08:57:31 -0700557 static jthread agent_thread_;
558 static std::mutex mutex_;
559 static bool started_;
560 static std::condition_variable cond_finished_;
561 static bool finished_;
562 static std::condition_variable cond_started_;
Andreas Gampe691051b2017-02-09 09:15:24 -0800563 static bool found_;
564 static bool compared_;
565};
Alex Light9df79b72017-09-12 08:57:31 -0700566
Andreas Gampea67354b2017-02-10 16:18:30 -0800567jclass ClassLoadPrepareEquality::storage_class_ = nullptr;
Andreas Gampe691051b2017-02-09 09:15:24 -0800568jobject ClassLoadPrepareEquality::stored_class_ = nullptr;
569jweak ClassLoadPrepareEquality::weakly_stored_class_ = nullptr;
Alex Light9df79b72017-09-12 08:57:31 -0700570jthread ClassLoadPrepareEquality::agent_thread_ = nullptr;
571std::mutex ClassLoadPrepareEquality::mutex_;
572bool ClassLoadPrepareEquality::started_ = false;
573std::condition_variable ClassLoadPrepareEquality::cond_started_;
574bool ClassLoadPrepareEquality::finished_ = false;
575std::condition_variable ClassLoadPrepareEquality::cond_finished_;
Andreas Gampe691051b2017-02-09 09:15:24 -0800576bool ClassLoadPrepareEquality::found_ = false;
577bool ClassLoadPrepareEquality::compared_ = false;
578
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700579extern "C" JNIEXPORT void JNICALL Java_art_Test912_setEqualityEventStorageClass(
Andreas Gampea67354b2017-02-10 16:18:30 -0800580 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
581 ClassLoadPrepareEquality::storage_class_ =
582 reinterpret_cast<jclass>(env->NewGlobalRef(klass));
583}
584
Andreas Gamped5f2ccc2017-04-19 13:37:48 -0700585extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPrepareEqualityEvents(
Andreas Gampe691051b2017-02-09 09:15:24 -0800586 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
587 EnableEvents(env,
588 b,
589 ClassLoadPrepareEquality::ClassLoadCallback,
590 ClassLoadPrepareEquality::ClassPrepareCallback);
591 if (b == JNI_FALSE) {
592 ClassLoadPrepareEquality::Free(env);
593 ClassLoadPrepareEquality::CheckFound();
Andreas Gampea67354b2017-02-10 16:18:30 -0800594 env->DeleteGlobalRef(ClassLoadPrepareEquality::storage_class_);
595 ClassLoadPrepareEquality::storage_class_ = nullptr;
Andreas Gampe691051b2017-02-09 09:15:24 -0800596 }
597}
598
Andreas Gampee492ae32016-10-28 19:34:57 -0700599} // namespace Test912Classes
600} // namespace art