1 /*
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 
17 #include <pthread.h>
18 
19 #include <cstdio>
20 #include <iostream>
21 #include <mutex>
22 #include <vector>
23 
24 #include "android-base/logging.h"
25 #include "android-base/stringprintf.h"
26 #include "jni.h"
27 #include "jvmti.h"
28 #include "scoped_local_ref.h"
29 #include "scoped_utf_chars.h"
30 
31 // Test infrastructure
32 #include "jvmti_helper.h"
33 #include "test_env.h"
34 
35 namespace art {
36 namespace Test904ObjectAllocation {
37 
38 static JavaVM* vm;
39 
GetClassName(JNIEnv * jni_env,jclass cls)40 static std::string GetClassName(JNIEnv* jni_env, jclass cls) {
41   ScopedLocalRef<jclass> class_class(jni_env, jni_env->GetObjectClass(cls));
42   jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;");
43   ScopedLocalRef<jstring> str(
44       jni_env, reinterpret_cast<jstring>(jni_env->CallObjectMethod(cls, mid)));
45   ScopedUtfChars utf_chars(jni_env, str.get());
46   return utf_chars.c_str();
47 }
48 
49 template <typename T>
50 class ScopedGlobalRef {
51  public:
ScopedGlobalRef(JNIEnv * env,T obj)52   ScopedGlobalRef(JNIEnv* env, T obj) : obj_(env->NewGlobalRef(obj)) {}
ScopedGlobalRef(const ScopedGlobalRef<T> & src)53   ScopedGlobalRef(const ScopedGlobalRef<T>& src) noexcept
54       : obj_(GetEnv()->NewGlobalRef(src.obj_)) {}
ScopedGlobalRef(ScopedGlobalRef<T> && src)55   ScopedGlobalRef(ScopedGlobalRef<T>&& src) noexcept : obj_(src.obj_) {
56     src.obj_ = nullptr;
57   }
58 
~ScopedGlobalRef()59   ~ScopedGlobalRef() {
60     GetEnv()->DeleteGlobalRef(obj_);
61   }
62 
Get(JNIEnv * env) const63   T Get(JNIEnv* env) const {
64     return reinterpret_cast<T>(env->NewLocalRef(obj_));
65   }
66 
67  private:
GetEnv() const68   JNIEnv* GetEnv() const {
69     JNIEnv* env = nullptr;
70     CHECK_EQ(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), 0);
71     return env;
72   }
73 
74   jobject obj_;
75 };
76 
77 struct EventLog {
78   ScopedGlobalRef<jclass> object_klass;
79   ScopedGlobalRef<jclass> object_klass2;
80   jlong size;
81   ScopedGlobalRef<jthread> thr_;
82 };
83 
84 static std::mutex gEventsMutex;
85 static std::vector<EventLog> gEvents;
86 
ObjectAllocated(jvmtiEnv * ti_env ATTRIBUTE_UNUSED,JNIEnv * jni_env,jthread thread,jobject object,jclass object_klass,jlong size)87 static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED,
88                                     JNIEnv* jni_env,
89                                     jthread thread,
90                                     jobject object,
91                                     jclass object_klass,
92                                     jlong size) {
93   ScopedLocalRef<jclass> object_klass2(jni_env, jni_env->GetObjectClass(object));
94   std::lock_guard<std::mutex> guard(gEventsMutex);
95   gEvents.push_back({ScopedGlobalRef<jclass>(jni_env, object_klass),
96                      ScopedGlobalRef<jclass>(jni_env, object_klass2.get()),
97                      size,
98                      ScopedGlobalRef<jthread>(jni_env, thread)});
99 }
100 
Java_art_Test904_setupObjectAllocCallback(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jboolean enable)101 extern "C" JNIEXPORT void JNICALL Java_art_Test904_setupObjectAllocCallback(
102     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
103   env->GetJavaVM(&vm);
104   jvmtiEventCallbacks callbacks;
105   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
106   callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
107 
108   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
109   JvmtiErrorToException(env, jvmti_env, ret);
110 }
111 
Java_art_Test904_enableAllocationTracking(JNIEnv * env,jclass,jthread thread,jboolean enable)112 extern "C" JNIEXPORT void JNICALL Java_art_Test904_enableAllocationTracking(
113     JNIEnv* env, jclass, jthread thread, jboolean enable) {
114   jvmtiError ret = jvmti_env->SetEventNotificationMode(
115       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
116       JVMTI_EVENT_VM_OBJECT_ALLOC,
117       thread);
118   JvmtiErrorToException(env, jvmti_env, ret);
119 }
120 
Java_art_Test904_getTrackingEventMessages(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jobjectArray threads)121 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test904_getTrackingEventMessages(
122     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobjectArray threads) {
123   std::lock_guard<std::mutex> guard(gEventsMutex);
124   std::vector<std::string> real_events;
125   std::vector<jthread> thread_lst;
126   jint nthreads = env->GetArrayLength(threads);
127   {
128     env->PushLocalFrame(nthreads + 1);
129     for (jint i = 0; i < nthreads; i++) {
130       thread_lst.push_back(reinterpret_cast<jthread>(env->GetObjectArrayElement(threads, i)));
131     }
132     for (const EventLog& ev : gEvents) {
133       ScopedLocalRef<jthread> thr(env, ev.thr_.Get(env));
134       for (jthread req_thread : thread_lst) {
135         if (env->IsSameObject(req_thread, thr.get())) {
136           ScopedLocalRef<jclass> klass(env, ev.object_klass.Get(env));
137           ScopedLocalRef<jclass> klass2(env, ev.object_klass2.Get(env));
138           std::string object_klass_descriptor = GetClassName(env, klass.get());
139           std::string object_klass_descriptor2 = GetClassName(env, klass2.get());
140           std::string res(android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
141                                                       object_klass_descriptor.c_str(),
142                                                       object_klass_descriptor2.c_str(),
143                                                       static_cast<size_t>(ev.size)));
144           real_events.push_back(res);
145           break;
146         }
147       }
148     }
149     env->PopLocalFrame(nullptr);
150   }
151   jobjectArray ret = CreateObjectArray(env,
152                                        static_cast<jint>(real_events.size()),
153                                        "java/lang/String",
154                                        [&](jint i) {
155     return env->NewStringUTF(real_events[i].c_str());
156   });
157   gEvents.clear();
158   return ret;
159 }
160 
161 }  // namespace Test904ObjectAllocation
162 }  // namespace art
163