xref: /aosp_15_r20/cts/hostsidetests/jvmti/base/jni/tracking.cpp (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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