163 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2013 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #include <pthread.h>
 | |
| 
 | |
| #include <cstdio>
 | |
| #include <iostream>
 | |
| #include <mutex>
 | |
| #include <vector>
 | |
| 
 | |
| #include "android-base/logging.h"
 | |
| #include "android-base/stringprintf.h"
 | |
| #include "jni.h"
 | |
| #include "jvmti.h"
 | |
| #include "scoped_local_ref.h"
 | |
| #include "scoped_utf_chars.h"
 | |
| 
 | |
| // Test infrastructure
 | |
| #include "jvmti_helper.h"
 | |
| #include "test_env.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace Test904ObjectAllocation {
 | |
| 
 | |
| static JavaVM* vm;
 | |
| 
 | |
| static std::string GetClassName(JNIEnv* jni_env, jclass cls) {
 | |
|   ScopedLocalRef<jclass> class_class(jni_env, jni_env->GetObjectClass(cls));
 | |
|   jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;");
 | |
|   ScopedLocalRef<jstring> str(
 | |
|       jni_env, reinterpret_cast<jstring>(jni_env->CallObjectMethod(cls, mid)));
 | |
|   ScopedUtfChars utf_chars(jni_env, str.get());
 | |
|   return utf_chars.c_str();
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| class ScopedGlobalRef {
 | |
|  public:
 | |
|   ScopedGlobalRef(JNIEnv* env, T obj) : obj_(env->NewGlobalRef(obj)) {}
 | |
|   ScopedGlobalRef(const ScopedGlobalRef<T>& src) noexcept
 | |
|       : obj_(GetEnv()->NewGlobalRef(src.obj_)) {}
 | |
|   ScopedGlobalRef(ScopedGlobalRef<T>&& src) noexcept : obj_(src.obj_) {
 | |
|     src.obj_ = nullptr;
 | |
|   }
 | |
| 
 | |
|   ~ScopedGlobalRef() {
 | |
|     GetEnv()->DeleteGlobalRef(obj_);
 | |
|   }
 | |
| 
 | |
|   T Get(JNIEnv* env) const {
 | |
|     return reinterpret_cast<T>(env->NewLocalRef(obj_));
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   JNIEnv* GetEnv() const {
 | |
|     JNIEnv* env = nullptr;
 | |
|     CHECK_EQ(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), 0);
 | |
|     return env;
 | |
|   }
 | |
| 
 | |
|   jobject obj_;
 | |
| };
 | |
| 
 | |
| struct EventLog {
 | |
|   ScopedGlobalRef<jclass> object_klass;
 | |
|   ScopedGlobalRef<jclass> object_klass2;
 | |
|   jlong size;
 | |
|   ScopedGlobalRef<jthread> thr_;
 | |
| };
 | |
| 
 | |
| static std::mutex gEventsMutex;
 | |
| static std::vector<EventLog> gEvents;
 | |
| 
 | |
| static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED,
 | |
|                                     JNIEnv* jni_env,
 | |
|                                     jthread thread,
 | |
|                                     jobject object,
 | |
|                                     jclass object_klass,
 | |
|                                     jlong size) {
 | |
|   ScopedLocalRef<jclass> object_klass2(jni_env, jni_env->GetObjectClass(object));
 | |
|   std::lock_guard<std::mutex> guard(gEventsMutex);
 | |
|   gEvents.push_back({ScopedGlobalRef<jclass>(jni_env, object_klass),
 | |
|                      ScopedGlobalRef<jclass>(jni_env, object_klass2.get()),
 | |
|                      size,
 | |
|                      ScopedGlobalRef<jthread>(jni_env, thread)});
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT void JNICALL Java_art_Test904_setupObjectAllocCallback(
 | |
|     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
 | |
|   env->GetJavaVM(&vm);
 | |
|   jvmtiEventCallbacks callbacks;
 | |
|   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
 | |
|   callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
 | |
| 
 | |
|   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
 | |
|   JvmtiErrorToException(env, jvmti_env, ret);
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT void JNICALL Java_art_Test904_enableAllocationTracking(
 | |
|     JNIEnv* env, jclass, jthread thread, jboolean enable) {
 | |
|   jvmtiError ret = jvmti_env->SetEventNotificationMode(
 | |
|       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
 | |
|       JVMTI_EVENT_VM_OBJECT_ALLOC,
 | |
|       thread);
 | |
|   JvmtiErrorToException(env, jvmti_env, ret);
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test904_getTrackingEventMessages(
 | |
|     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobjectArray threads) {
 | |
|   std::lock_guard<std::mutex> guard(gEventsMutex);
 | |
|   std::vector<std::string> real_events;
 | |
|   std::vector<jthread> thread_lst;
 | |
|   jint nthreads = env->GetArrayLength(threads);
 | |
|   {
 | |
|     env->PushLocalFrame(nthreads + 1);
 | |
|     for (jint i = 0; i < nthreads; i++) {
 | |
|       thread_lst.push_back(reinterpret_cast<jthread>(env->GetObjectArrayElement(threads, i)));
 | |
|     }
 | |
|     for (const EventLog& ev : gEvents) {
 | |
|       ScopedLocalRef<jthread> thr(env, ev.thr_.Get(env));
 | |
|       for (jthread req_thread : thread_lst) {
 | |
|         if (env->IsSameObject(req_thread, thr.get())) {
 | |
|           ScopedLocalRef<jclass> klass(env, ev.object_klass.Get(env));
 | |
|           ScopedLocalRef<jclass> klass2(env, ev.object_klass2.Get(env));
 | |
|           std::string object_klass_descriptor = GetClassName(env, klass.get());
 | |
|           std::string object_klass_descriptor2 = GetClassName(env, klass2.get());
 | |
|           std::string res(android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
 | |
|                                                       object_klass_descriptor.c_str(),
 | |
|                                                       object_klass_descriptor2.c_str(),
 | |
|                                                       static_cast<size_t>(ev.size)));
 | |
|           real_events.push_back(res);
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     env->PopLocalFrame(nullptr);
 | |
|   }
 | |
|   jobjectArray ret = CreateObjectArray(env,
 | |
|                                        static_cast<jint>(real_events.size()),
 | |
|                                        "java/lang/String",
 | |
|                                        [&](jint i) {
 | |
|     return env->NewStringUTF(real_events[i].c_str());
 | |
|   });
 | |
|   gEvents.clear();
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| }  // namespace Test904ObjectAllocation
 | |
| }  // namespace art
 |