1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker *
4*b7c941bbSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker *
8*b7c941bbSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker *
10*b7c941bbSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker */
16*b7c941bbSAndroid Build Coastguard Worker
17*b7c941bbSAndroid Build Coastguard Worker #include <mutex>
18*b7c941bbSAndroid Build Coastguard Worker
19*b7c941bbSAndroid Build Coastguard Worker #include "jni.h"
20*b7c941bbSAndroid Build Coastguard Worker #include "jvmti.h"
21*b7c941bbSAndroid Build Coastguard Worker
22*b7c941bbSAndroid Build Coastguard Worker #include "android-base/logging.h"
23*b7c941bbSAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
24*b7c941bbSAndroid Build Coastguard Worker #include "jni_binder.h"
25*b7c941bbSAndroid Build Coastguard Worker #include "jvmti_helper.h"
26*b7c941bbSAndroid Build Coastguard Worker #include "scoped_local_ref.h"
27*b7c941bbSAndroid Build Coastguard Worker #include "scoped_utf_chars.h"
28*b7c941bbSAndroid Build Coastguard Worker #include "test_env.h"
29*b7c941bbSAndroid Build Coastguard Worker
30*b7c941bbSAndroid Build Coastguard Worker namespace art {
31*b7c941bbSAndroid Build Coastguard Worker
GetClassName(JNIEnv * jni_env,jclass cls)32*b7c941bbSAndroid Build Coastguard Worker static std::string GetClassName(JNIEnv* jni_env, jclass cls) {
33*b7c941bbSAndroid Build Coastguard Worker ScopedLocalRef<jclass> class_class(jni_env, jni_env->GetObjectClass(cls));
34*b7c941bbSAndroid Build Coastguard Worker jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;");
35*b7c941bbSAndroid Build Coastguard Worker ScopedLocalRef<jstring> str(
36*b7c941bbSAndroid Build Coastguard Worker jni_env, reinterpret_cast<jstring>(jni_env->CallObjectMethod(cls, mid)));
37*b7c941bbSAndroid Build Coastguard Worker ScopedUtfChars utf_chars(jni_env, str.get());
38*b7c941bbSAndroid Build Coastguard Worker return utf_chars.c_str();
39*b7c941bbSAndroid Build Coastguard Worker }
40*b7c941bbSAndroid Build Coastguard Worker
41*b7c941bbSAndroid Build Coastguard Worker static std::mutex gLock;
42*b7c941bbSAndroid Build Coastguard Worker static std::string gCollection;
43*b7c941bbSAndroid Build Coastguard Worker static jthread gExpectedThread = nullptr;
44*b7c941bbSAndroid Build Coastguard Worker
RecordAllocationEvent(JNIEnv * jni_env,jobject object,jclass object_klass,jlong size)45*b7c941bbSAndroid Build Coastguard Worker static void RecordAllocationEvent(JNIEnv* jni_env, jobject object, jclass object_klass,
46*b7c941bbSAndroid Build Coastguard Worker jlong size) {
47*b7c941bbSAndroid Build Coastguard Worker std::string object_klass_descriptor = GetClassName(jni_env, object_klass);
48*b7c941bbSAndroid Build Coastguard Worker ScopedLocalRef<jclass> object_klass2(jni_env, jni_env->GetObjectClass(object));
49*b7c941bbSAndroid Build Coastguard Worker std::string object_klass_descriptor2 = GetClassName(jni_env, object_klass2.get());
50*b7c941bbSAndroid Build Coastguard Worker std::string result = android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
51*b7c941bbSAndroid Build Coastguard Worker object_klass_descriptor.c_str(),
52*b7c941bbSAndroid Build Coastguard Worker object_klass_descriptor2.c_str(),
53*b7c941bbSAndroid Build Coastguard Worker static_cast<size_t>(size));
54*b7c941bbSAndroid Build Coastguard Worker std::unique_lock<std::mutex> mu(gLock);
55*b7c941bbSAndroid Build Coastguard Worker gCollection += result + "#";
56*b7c941bbSAndroid Build Coastguard Worker }
57*b7c941bbSAndroid Build Coastguard Worker
ObjectAllocatedGlobal(jvmtiEnv * ti_env ATTRIBUTE_UNUSED,JNIEnv * jni_env,jthread thread,jobject object,jclass object_klass,jlong size)58*b7c941bbSAndroid Build Coastguard Worker static void JNICALL ObjectAllocatedGlobal(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, JNIEnv* jni_env,
59*b7c941bbSAndroid Build Coastguard Worker jthread thread, jobject object, jclass object_klass,
60*b7c941bbSAndroid Build Coastguard Worker jlong size) {
61*b7c941bbSAndroid Build Coastguard Worker // Ignore events from threads other than the test thread. It is not possible
62*b7c941bbSAndroid Build Coastguard Worker // to make sure that we don't receive any outstanding callbacks after
63*b7c941bbSAndroid Build Coastguard Worker // disabling the allocation events. So just ignore events from other threads
64*b7c941bbSAndroid Build Coastguard Worker // to make the test stable.
65*b7c941bbSAndroid Build Coastguard Worker if (!jni_env->IsSameObject(thread, gExpectedThread)) {
66*b7c941bbSAndroid Build Coastguard Worker return;
67*b7c941bbSAndroid Build Coastguard Worker }
68*b7c941bbSAndroid Build Coastguard Worker RecordAllocationEvent(jni_env, object, object_klass, size);
69*b7c941bbSAndroid Build Coastguard Worker }
70*b7c941bbSAndroid Build Coastguard Worker
ObjectAllocatedThread(jvmtiEnv * ti_env ATTRIBUTE_UNUSED,JNIEnv * jni_env,jthread thread,jobject object,jclass object_klass,jlong size)71*b7c941bbSAndroid Build Coastguard Worker static void JNICALL ObjectAllocatedThread(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, JNIEnv* jni_env,
72*b7c941bbSAndroid Build Coastguard Worker jthread thread, jobject object, jclass object_klass,
73*b7c941bbSAndroid Build Coastguard Worker jlong size) {
74*b7c941bbSAndroid Build Coastguard Worker CHECK(jni_env->IsSameObject(thread, gExpectedThread));
75*b7c941bbSAndroid Build Coastguard Worker RecordAllocationEvent(jni_env, object, object_klass, size);
76*b7c941bbSAndroid Build Coastguard Worker }
77*b7c941bbSAndroid Build Coastguard Worker
Java_android_jvmti_cts_JvmtiTrackingTest_setupObjectAllocCallback(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jboolean enable,jboolean global)78*b7c941bbSAndroid Build Coastguard Worker extern "C" JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiTrackingTest_setupObjectAllocCallback(
79*b7c941bbSAndroid Build Coastguard Worker JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable, jboolean global) {
80*b7c941bbSAndroid Build Coastguard Worker jvmtiEventCallbacks callbacks;
81*b7c941bbSAndroid Build Coastguard Worker memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
82*b7c941bbSAndroid Build Coastguard Worker if (enable) {
83*b7c941bbSAndroid Build Coastguard Worker callbacks.VMObjectAlloc = global ? ObjectAllocatedGlobal : ObjectAllocatedThread;
84*b7c941bbSAndroid Build Coastguard Worker } else {
85*b7c941bbSAndroid Build Coastguard Worker callbacks.VMObjectAlloc = nullptr;
86*b7c941bbSAndroid Build Coastguard Worker }
87*b7c941bbSAndroid Build Coastguard Worker
88*b7c941bbSAndroid Build Coastguard Worker jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
89*b7c941bbSAndroid Build Coastguard Worker JvmtiErrorToException(env, jvmti_env, ret);
90*b7c941bbSAndroid Build Coastguard Worker }
91*b7c941bbSAndroid Build Coastguard Worker
Java_android_jvmti_cts_JvmtiTrackingTest_enableAllocationTracking(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jthread thread,jboolean enable)92*b7c941bbSAndroid Build Coastguard Worker extern "C" JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiTrackingTest_enableAllocationTracking(
93*b7c941bbSAndroid Build Coastguard Worker JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jboolean enable) {
94*b7c941bbSAndroid Build Coastguard Worker jvmtiError ret = jvmti_env->SetEventNotificationMode(
95*b7c941bbSAndroid Build Coastguard Worker enable ? JVMTI_ENABLE : JVMTI_DISABLE,
96*b7c941bbSAndroid Build Coastguard Worker JVMTI_EVENT_VM_OBJECT_ALLOC,
97*b7c941bbSAndroid Build Coastguard Worker thread);
98*b7c941bbSAndroid Build Coastguard Worker if (enable) {
99*b7c941bbSAndroid Build Coastguard Worker if (thread == nullptr) {
100*b7c941bbSAndroid Build Coastguard Worker // We are enabling the allocation events globally but can only deterministically check the
101*b7c941bbSAndroid Build Coastguard Worker // ones on the current thread.
102*b7c941bbSAndroid Build Coastguard Worker jthread curr_thread;
103*b7c941bbSAndroid Build Coastguard Worker jvmti_env->GetCurrentThread(&curr_thread);
104*b7c941bbSAndroid Build Coastguard Worker gExpectedThread = env->NewGlobalRef(curr_thread);
105*b7c941bbSAndroid Build Coastguard Worker } else {
106*b7c941bbSAndroid Build Coastguard Worker gExpectedThread = env->NewGlobalRef(thread);
107*b7c941bbSAndroid Build Coastguard Worker }
108*b7c941bbSAndroid Build Coastguard Worker } else if (gExpectedThread != nullptr) {
109*b7c941bbSAndroid Build Coastguard Worker env->DeleteGlobalRef(gExpectedThread);
110*b7c941bbSAndroid Build Coastguard Worker gExpectedThread = nullptr;
111*b7c941bbSAndroid Build Coastguard Worker }
112*b7c941bbSAndroid Build Coastguard Worker JvmtiErrorToException(env, jvmti_env, ret);
113*b7c941bbSAndroid Build Coastguard Worker }
114*b7c941bbSAndroid Build Coastguard Worker
115*b7c941bbSAndroid Build Coastguard Worker extern "C" JNIEXPORT
Java_android_jvmti_cts_JvmtiTrackingTest_getAndResetAllocationTrackingString(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED)116*b7c941bbSAndroid Build Coastguard Worker jstring JNICALL Java_android_jvmti_cts_JvmtiTrackingTest_getAndResetAllocationTrackingString(
117*b7c941bbSAndroid Build Coastguard Worker JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
118*b7c941bbSAndroid Build Coastguard Worker // We will have a string allocation. So only do the C++ string retrieval under lock.
119*b7c941bbSAndroid Build Coastguard Worker std::string result;
120*b7c941bbSAndroid Build Coastguard Worker {
121*b7c941bbSAndroid Build Coastguard Worker std::unique_lock<std::mutex> mu(gLock);
122*b7c941bbSAndroid Build Coastguard Worker result.swap(gCollection);
123*b7c941bbSAndroid Build Coastguard Worker }
124*b7c941bbSAndroid Build Coastguard Worker // Make sure we give any other threads that might have been waiting to get a last crack time to
125*b7c941bbSAndroid Build Coastguard Worker // run. We will ignore their additions however.
126*b7c941bbSAndroid Build Coastguard Worker bool is_empty = false;
127*b7c941bbSAndroid Build Coastguard Worker do {
128*b7c941bbSAndroid Build Coastguard Worker {
129*b7c941bbSAndroid Build Coastguard Worker std::unique_lock<std::mutex> mu(gLock);
130*b7c941bbSAndroid Build Coastguard Worker is_empty = gCollection.empty();
131*b7c941bbSAndroid Build Coastguard Worker gCollection.clear();
132*b7c941bbSAndroid Build Coastguard Worker }
133*b7c941bbSAndroid Build Coastguard Worker sched_yield();
134*b7c941bbSAndroid Build Coastguard Worker } while (!is_empty);
135*b7c941bbSAndroid Build Coastguard Worker
136*b7c941bbSAndroid Build Coastguard Worker if (result.empty()) {
137*b7c941bbSAndroid Build Coastguard Worker return nullptr;
138*b7c941bbSAndroid Build Coastguard Worker }
139*b7c941bbSAndroid Build Coastguard Worker
140*b7c941bbSAndroid Build Coastguard Worker return env->NewStringUTF(result.c_str());
141*b7c941bbSAndroid Build Coastguard Worker }
142*b7c941bbSAndroid Build Coastguard Worker
143*b7c941bbSAndroid Build Coastguard Worker static JNINativeMethod gMethods[] = {
144*b7c941bbSAndroid Build Coastguard Worker { "setupObjectAllocCallback", "(Z;Z)V",
145*b7c941bbSAndroid Build Coastguard Worker (void*)Java_android_jvmti_cts_JvmtiTrackingTest_setupObjectAllocCallback},
146*b7c941bbSAndroid Build Coastguard Worker
147*b7c941bbSAndroid Build Coastguard Worker { "enableAllocationTracking", "(Ljava/lang/Thread;Z)V",
148*b7c941bbSAndroid Build Coastguard Worker (void*)Java_android_jvmti_cts_JvmtiTrackingTest_enableAllocationTracking },
149*b7c941bbSAndroid Build Coastguard Worker
150*b7c941bbSAndroid Build Coastguard Worker { "getAndResetAllocationTrackingString", "()Ljava/lang/String;",
151*b7c941bbSAndroid Build Coastguard Worker (void*)Java_android_jvmti_cts_JvmtiTrackingTest_getAndResetAllocationTrackingString },
152*b7c941bbSAndroid Build Coastguard Worker };
153*b7c941bbSAndroid Build Coastguard Worker
register_android_jvmti_cts_JvmtiTrackingTest(jvmtiEnv * jenv,JNIEnv * env)154*b7c941bbSAndroid Build Coastguard Worker void register_android_jvmti_cts_JvmtiTrackingTest(jvmtiEnv* jenv, JNIEnv* env) {
155*b7c941bbSAndroid Build Coastguard Worker ScopedLocalRef<jclass> klass(env, GetClass(jenv, env,
156*b7c941bbSAndroid Build Coastguard Worker "android/jvmti/cts/JvmtiTrackingTest", nullptr));
157*b7c941bbSAndroid Build Coastguard Worker if (klass.get() == nullptr) {
158*b7c941bbSAndroid Build Coastguard Worker env->ExceptionClear();
159*b7c941bbSAndroid Build Coastguard Worker return;
160*b7c941bbSAndroid Build Coastguard Worker }
161*b7c941bbSAndroid Build Coastguard Worker
162*b7c941bbSAndroid Build Coastguard Worker env->RegisterNatives(klass.get(), gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
163*b7c941bbSAndroid Build Coastguard Worker if (env->ExceptionCheck()) {
164*b7c941bbSAndroid Build Coastguard Worker env->ExceptionClear();
165*b7c941bbSAndroid Build Coastguard Worker LOG(ERROR) << "Could not register natives for JvmtiTrackingTest class";
166*b7c941bbSAndroid Build Coastguard Worker }
167*b7c941bbSAndroid Build Coastguard Worker }
168*b7c941bbSAndroid Build Coastguard Worker
169*b7c941bbSAndroid Build Coastguard Worker } // namespace art
170