You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
7.2 KiB
202 lines
7.2 KiB
/*
|
|
* 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
|