diff options
l--------- | openjdkjvmti/generate-operator-out.py | 1 | ||||
-rw-r--r-- | openjdkjvmti/ti_phase.cc | 13 | ||||
-rw-r--r-- | openjdkjvmti/ti_thread.cc | 24 | ||||
-rw-r--r-- | openjdkjvmti/ti_thread.h | 7 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/expected.txt | 4 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/info.txt | 3 | ||||
-rwxr-xr-x | test/1919-vminit-thread-start-timing/run | 17 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/src/Main.java | 21 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/src/art/Main.java | 28 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/src/art/Test1919.java | 53 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/vminit.cc | 192 | ||||
-rw-r--r-- | test/1919-vminit-thread-start-timing/vminit.h | 30 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/ti-agent/common_load.cc | 2 |
14 files changed, 380 insertions, 16 deletions
diff --git a/openjdkjvmti/generate-operator-out.py b/openjdkjvmti/generate-operator-out.py new file mode 120000 index 0000000000..cc291d20c1 --- /dev/null +++ b/openjdkjvmti/generate-operator-out.py @@ -0,0 +1 @@ +../tools/generate-operator-out.py
\ No newline at end of file diff --git a/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc index e8c1ca7335..07cf31c354 100644 --- a/openjdkjvmti/ti_phase.cc +++ b/openjdkjvmti/ti_phase.cc @@ -72,9 +72,16 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { { ThreadUtil::CacheData(); PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; - ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread()); - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(nullptr, GetJniEnv(), thread.get()); + { + ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread()); + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>( + nullptr, GetJniEnv(), thread.get()); + } + // We need to have these events be ordered to match behavior expected by some real-world + // agents. The spec does not really require this but compatibility is a useful property to + // maintain. + ThreadUtil::VMInitEventSent(); } break; case RuntimePhase::kDeath: diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 6fa73f8a8c..b0a1a8556a 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -57,13 +57,14 @@ namespace openjdkjvmti { art::ArtField* ThreadUtil::context_class_loader_ = nullptr; -struct ThreadCallback : public art::ThreadLifecycleCallback, public art::RuntimePhaseCallback { +struct ThreadCallback : public art::ThreadLifecycleCallback { jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { if (self->GetPeer() == nullptr) { return nullptr; } return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer()); } + template <ArtJvmtiEvent kEvent> void Post(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { DCHECK_EQ(self, art::Thread::Current()); @@ -96,15 +97,6 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime Post<ArtJvmtiEvent::kThreadEnd>(self); } - void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { - if (phase == RuntimePhase::kInit) { - // We moved to VMInit. Report the main thread as started (it was attached early, and must - // not be reported until Init. - started = true; - Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current()); - } - } - EventHandler* event_handler = nullptr; bool started = false; }; @@ -121,10 +113,19 @@ void ThreadUtil::Register(EventHandler* handler) { art::ThreadState::kWaitingForDebuggerToAttach); art::ScopedSuspendAll ssa("Add thread callback"); runtime->GetRuntimeCallbacks()->AddThreadLifecycleCallback(&gThreadCallback); - runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gThreadCallback); +} + +void ThreadUtil::VMInitEventSent() { + // We should have already started. + DCHECK(gThreadCallback.started); + // We moved to VMInit. Report the main thread as started (it was attached early, and must not be + // reported until Init. + gThreadCallback.Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current()); } void ThreadUtil::CacheData() { + // We must have started since it is now safe to cache our data; + gThreadCallback.started = true; art::ScopedObjectAccess soa(art::Thread::Current()); art::ObjPtr<art::mirror::Class> thread_class = soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread); @@ -140,7 +141,6 @@ void ThreadUtil::Unregister() { art::ScopedSuspendAll ssa("Remove thread callback"); art::Runtime* runtime = art::Runtime::Current(); runtime->GetRuntimeCallbacks()->RemoveThreadLifecycleCallback(&gThreadCallback); - runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gThreadCallback); } jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread* thread_ptr) { diff --git a/openjdkjvmti/ti_thread.h b/openjdkjvmti/ti_thread.h index 03c49d7788..a19974aa16 100644 --- a/openjdkjvmti/ti_thread.h +++ b/openjdkjvmti/ti_thread.h @@ -53,9 +53,14 @@ class ThreadUtil { static void Register(EventHandler* event_handler); static void Unregister(); - // To be called when it is safe to cache data. + // To be called when it is safe to cache data. This means that we have at least entered the + // RuntimePhase::kInit but we might or might not have already called VMInit event. static void CacheData(); + // Called just after we have sent the VMInit callback so that ThreadUtil can do final setup. This + // ensures that there are no timing issues between the two callbacks. + static void VMInitEventSent() REQUIRES_SHARED(art::Locks::mutator_lock_); + // Handle a jvmtiEnv going away. static void RemoveEnvironment(jvmtiEnv* env); diff --git a/test/1919-vminit-thread-start-timing/expected.txt b/test/1919-vminit-thread-start-timing/expected.txt new file mode 100644 index 0000000000..63614512cf --- /dev/null +++ b/test/1919-vminit-thread-start-timing/expected.txt @@ -0,0 +1,4 @@ +VMInit: main +ThreadStart: JVMTI_THREAD-Test1919 +Test1919AgentThread: JVMTI_THREAD-Test1919 +ThreadStart: main diff --git a/test/1919-vminit-thread-start-timing/info.txt b/test/1919-vminit-thread-start-timing/info.txt new file mode 100644 index 0000000000..995f0a1209 --- /dev/null +++ b/test/1919-vminit-thread-start-timing/info.txt @@ -0,0 +1,3 @@ +Tests basic functions in the jvmti plugin. + +Test the interaction of VMInit events and thread starts. diff --git a/test/1919-vminit-thread-start-timing/run b/test/1919-vminit-thread-start-timing/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1919-vminit-thread-start-timing/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 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. + +./default-run "$@" --jvmti diff --git a/test/1919-vminit-thread-start-timing/src/Main.java b/test/1919-vminit-thread-start-timing/src/Main.java new file mode 100644 index 0000000000..65781b8484 --- /dev/null +++ b/test/1919-vminit-thread-start-timing/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2011 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. + */ + +public class Main { + public static void main(String[] args) { + art.Test1919.run(); + } +} diff --git a/test/1919-vminit-thread-start-timing/src/art/Main.java b/test/1919-vminit-thread-start-timing/src/art/Main.java new file mode 100644 index 0000000000..8b01920638 --- /dev/null +++ b/test/1919-vminit-thread-start-timing/src/art/Main.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 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. + */ + +package art; + +// Binder class so the agent's C code has something that can be bound and exposed to tests. +// In a package to separate cleanly and work around CTS reference issues (though this class +// should be replaced in the CTS version). +public class Main { + // Load the given class with the given classloader, and bind all native methods to corresponding + // C methods in the agent. Will abort if any of the steps fail. + public static native void bindAgentJNI(String className, ClassLoader classLoader); + // Same as above, giving the class directly. + public static native void bindAgentJNIForClass(Class<?> klass); +} diff --git a/test/1919-vminit-thread-start-timing/src/art/Test1919.java b/test/1919-vminit-thread-start-timing/src/art/Test1919.java new file mode 100644 index 0000000000..3d5c079f74 --- /dev/null +++ b/test/1919-vminit-thread-start-timing/src/art/Test1919.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 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. + */ + +package art; + +public class Test1919 { + public static final boolean PRINT_ALL_THREADS = false; + + public static void run() { + for (Event e : getEvents()) { + if (PRINT_ALL_THREADS || + e.thr.equals(Thread.currentThread()) || + e.thr.getName().equals("JVMTI_THREAD-Test1919")) { + System.out.println(e.name + ": " + e.thr.getName()); + } + } + } + + static class Event { + public final String name; + public final Thread thr; + public Event(String name, Thread thr) { + this.name = name; + this.thr = thr; + } + } + + public static Event[] getEvents() { + String[] ns = getEventNames(); + Thread[] ts = getEventThreads(); + Event[] es = new Event[Math.min(ns.length, ts.length)]; + for (int i = 0; i < es.length; i++) { + es[i] = new Event(ns[i], ts[i]); + } + return es; + } + + public static native String[] getEventNames(); + public static native Thread[] getEventThreads(); +} diff --git a/test/1919-vminit-thread-start-timing/vminit.cc b/test/1919-vminit-thread-start-timing/vminit.cc new file mode 100644 index 0000000000..109c61f05c --- /dev/null +++ b/test/1919-vminit-thread-start-timing/vminit.cc @@ -0,0 +1,192 @@ +/* + * 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; +}; + + +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()); + + // 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; +} + +} // namespace Test1919VMInitThreadStart +} // namespace art diff --git a/test/1919-vminit-thread-start-timing/vminit.h b/test/1919-vminit-thread-start-timing/vminit.h new file mode 100644 index 0000000000..c4a5ea8ef1 --- /dev/null +++ b/test/1919-vminit-thread-start-timing/vminit.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#ifndef ART_TEST_1919_VMINIT_THREAD_START_TIMING_VMINIT_H_ +#define ART_TEST_1919_VMINIT_THREAD_START_TIMING_VMINIT_H_ + +#include <jni.h> + +namespace art { +namespace Test1919VMInitThreadStart { + +jint OnLoad(JavaVM* vm, char* options, void* reserved); + +} // namespace Test1919VMInitThreadStart +} // namespace art + +#endif // ART_TEST_1919_VMINIT_THREAD_START_TIMING_VMINIT_H_ diff --git a/test/Android.bp b/test/Android.bp index fab664a3e2..9b9ab108e6 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -295,6 +295,7 @@ art_cc_defaults { "1908-suspend-native-resume-self/native_suspend_resume.cc", "1909-per-agent-tls/agent_tls.cc", "1914-get-local-instance/local_instance.cc", + "1919-vminit-thread-start-timing/vminit.cc", ], shared_libs: [ "libbase", diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 1d13c620e3..d85f33a05d 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -28,6 +28,7 @@ #include "909-attach-agent/attach.h" #include "936-search-onload/search_onload.h" #include "983-source-transform-verify/source_transform.h" +#include "1919-vminit-thread-start-timing/vminit.h" namespace art { @@ -82,6 +83,7 @@ static AgentLib agents[] = { { "941-recursive-obsolete-jit", common_redefine::OnLoad, nullptr }, { "943-private-recursive-jit", common_redefine::OnLoad, nullptr }, { "983-source-transform-verify", Test983SourceTransformVerify::OnLoad, nullptr }, + { "1919-vminit-thread-start-timing", Test1919VMInitThreadStart::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { |