202 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2016 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 "1919-vminit-thread-start-timing/vminit.h"
 | |
| 
 | |
| #include <mutex>
 | |
| #include <thread>
 | |
| #include <vector>
 | |
| 
 | |
| #include <jni.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include "android-base/macros.h"
 | |
| #include "jvmti.h"
 | |
| 
 | |
| // Test infrastructure
 | |
| #include "scoped_local_ref.h"
 | |
| #include "jvmti_helper.h"
 | |
| #include "jni_helper.h"
 | |
| #include "test_env.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace Test1919VMInitThreadStart {
 | |
| 
 | |
| struct EventData {
 | |
|   std::string event;
 | |
|   jobject data;
 | |
| };
 | |
| 
 | |
| struct EventList {
 | |
|   jrawMonitorID events_mutex;
 | |
|   std::vector<EventData> events;
 | |
| };
 | |
| 
 | |
| // The thread we started for testing.
 | |
| static jthread the_thread;
 | |
| 
 | |
| static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) {
 | |
|   jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr);
 | |
|   if (error != JVMTI_ERROR_NONE) {
 | |
|     printf("Failed to enable event");
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void JNICALL ThreadStartCallback(jvmtiEnv *jvmti, JNIEnv* env, jthread thread) {
 | |
|   EventList* list = nullptr;
 | |
|   CheckJvmtiError(jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list)));
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorEnter(list->events_mutex));
 | |
|   list->events.push_back({ "ThreadStart", env->NewGlobalRef(thread) });
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorExit(list->events_mutex));
 | |
| }
 | |
| 
 | |
| static void JNICALL Test1919AgentThread(jvmtiEnv* jvmti,
 | |
|                                         JNIEnv* env,
 | |
|                                         void* arg ATTRIBUTE_UNUSED) {
 | |
|   EventList* list = nullptr;
 | |
|   CheckJvmtiError(jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list)));
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorEnter(list->events_mutex));
 | |
|   jthread cur;
 | |
|   CheckJvmtiError(jvmti, jvmti->GetCurrentThread(&cur));
 | |
|   list->events.push_back({ "Test1919AgentThread", env->NewGlobalRef(cur) });
 | |
|   env->DeleteLocalRef(cur);
 | |
|   // Wake up VMInit
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorNotify(list->events_mutex));
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorExit(list->events_mutex));
 | |
| }
 | |
| 
 | |
| static void CreateAgentThread(jvmtiEnv* jvmti, JNIEnv* env) {
 | |
|   // Create a Thread object.
 | |
|   ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("JVMTI_THREAD-Test1919"));
 | |
|   CHECK(thread_name.get() != nullptr);
 | |
| 
 | |
|   ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
 | |
|   CHECK(thread_klass.get() != nullptr);
 | |
| 
 | |
|   ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get()));
 | |
|   CHECK(thread.get() != nullptr);
 | |
| 
 | |
|   jmethodID initID = env->GetMethodID(thread_klass.get(), "<init>", "(Ljava/lang/String;)V");
 | |
|   CHECK(initID != nullptr);
 | |
| 
 | |
|   env->CallNonvirtualVoidMethod(thread.get(), thread_klass.get(), initID, thread_name.get());
 | |
|   CHECK(!env->ExceptionCheck());
 | |
| 
 | |
|   // Set the_thread.
 | |
|   the_thread = static_cast<jthread>(env->NewGlobalRef(thread.get()));
 | |
| 
 | |
|   // Run agent thread.
 | |
|   CheckJvmtiError(jvmti, jvmti->RunAgentThread(thread.get(),
 | |
|                                                Test1919AgentThread,
 | |
|                                                nullptr,
 | |
|                                                JVMTI_THREAD_NORM_PRIORITY));
 | |
| }
 | |
| 
 | |
| static void JNICALL VMInitCallback(jvmtiEnv *jvmti, JNIEnv* env, jthread thread) {
 | |
|   EventList* list = nullptr;
 | |
|   CheckJvmtiError(jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list)));
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorEnter(list->events_mutex));
 | |
|   list->events.push_back({ "VMInit", env->NewGlobalRef(thread) });
 | |
|   // Create a new thread.
 | |
|   CreateAgentThread(jvmti, env);
 | |
|   // Wait for new thread to run.
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorWait(list->events_mutex, 0));
 | |
|   CheckJvmtiError(jvmti, jvmti->RawMonitorExit(list->events_mutex));
 | |
| }
 | |
| 
 | |
| static void InstallVMEvents(jvmtiEnv* env) {
 | |
|   jvmtiEventCallbacks callbacks;
 | |
|   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
 | |
|   callbacks.VMInit = VMInitCallback;
 | |
|   callbacks.ThreadStart = ThreadStartCallback;
 | |
|   jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
 | |
|   if (ret != JVMTI_ERROR_NONE) {
 | |
|     printf("Failed to install callbacks");
 | |
|   }
 | |
| 
 | |
|   EnableEvent(env, JVMTI_EVENT_VM_INIT);
 | |
|   EnableEvent(env, JVMTI_EVENT_THREAD_START);
 | |
| }
 | |
| 
 | |
| static void InstallEventList(jvmtiEnv* env) {
 | |
|   EventList* list = nullptr;
 | |
|   CheckJvmtiError(env, env->Allocate(sizeof(EventList), reinterpret_cast<unsigned char**>(&list)));
 | |
|   memset(list, 0, sizeof(EventList));
 | |
|   CheckJvmtiError(env, env->CreateRawMonitor("Test1919 Monitor", &list->events_mutex));
 | |
|   CheckJvmtiError(env, env->SetEnvironmentLocalStorage(list));
 | |
| }
 | |
| 
 | |
| jint OnLoad(JavaVM* vm,
 | |
|             char* options ATTRIBUTE_UNUSED,
 | |
|             void* reserved ATTRIBUTE_UNUSED) {
 | |
|   if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0) != 0) {
 | |
|     printf("Unable to get jvmti env!\n");
 | |
|     return 1;
 | |
|   }
 | |
|   InstallVMEvents(jvmti_env);
 | |
|   InstallEventList(jvmti_env);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1919_getEventNames(JNIEnv* env, jclass) {
 | |
|   EventList* list = nullptr;
 | |
|   if (JvmtiErrorToException(env,
 | |
|                             jvmti_env,
 | |
|                             jvmti_env->GetEnvironmentLocalStorage(
 | |
|                                 reinterpret_cast<void**>(&list)))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(list->events_mutex))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   jobjectArray ret = CreateObjectArray(env, list->events.size(), "java/lang/String",
 | |
|                                        [&](jint i) {
 | |
|                                          return env->NewStringUTF(list->events[i].event.c_str());
 | |
|                                        });
 | |
|   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(list->events_mutex))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1919_getEventThreads(JNIEnv* env, jclass) {
 | |
|   EventList* list = nullptr;
 | |
|   if (JvmtiErrorToException(env,
 | |
|                             jvmti_env,
 | |
|                             jvmti_env->GetEnvironmentLocalStorage(
 | |
|                                 reinterpret_cast<void**>(&list)))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(list->events_mutex))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   jobjectArray ret = CreateObjectArray(env, list->events.size(), "java/lang/Thread",
 | |
|                                        [&](jint i) {
 | |
|                                          return env->NewLocalRef(list->events[i].data);
 | |
|                                        });
 | |
|   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(list->events_mutex))) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jthread JNICALL Java_art_Test1919_getTestingThread(JNIEnv*, jclass) {
 | |
|   return the_thread;
 | |
| }
 | |
| 
 | |
| }  // namespace Test1919VMInitThreadStart
 | |
| }  // namespace art
 |