Merge "Dexfuzz: Added --divergence-retry option."
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index d5c6520..acdd0d3 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@
"ti_method.cc",
"ti_monitor.cc",
"ti_object.cc",
+ "ti_phase.cc",
"ti_properties.cc",
"ti_search.cc",
"ti_stack.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index e9b7cf5..fcedd4e 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -55,6 +55,7 @@
#include "ti_method.h"
#include "ti_monitor.h"
#include "ti_object.h"
+#include "ti_phase.h"
#include "ti_properties.h"
#include "ti_redefine.h"
#include "ti_search.h"
@@ -198,11 +199,11 @@
}
static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::SetThreadLocalStorage(env, thread, data);
}
static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr);
}
static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
@@ -593,7 +594,7 @@
jclass klass,
jint* minor_version_ptr,
jint* major_version_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassVersionNumbers(env, klass, minor_version_ptr, major_version_ptr);
}
static jvmtiError GetConstantPool(jvmtiEnv* env,
@@ -1099,11 +1100,12 @@
}
static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return PhaseUtil::GetPhase(env, phase_ptr);
}
static jvmtiError DisposeEnvironment(jvmtiEnv* env) {
ENSURE_VALID_ENV(env);
+ gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env));
delete env;
return OK;
}
@@ -1300,8 +1302,17 @@
// The plugin initialization function. This adds the jvmti environment.
extern "C" bool ArtPlugin_Initialize() {
art::Runtime* runtime = art::Runtime::Current();
+
+ if (runtime->IsStarted()) {
+ PhaseUtil::SetToLive();
+ } else {
+ PhaseUtil::SetToOnLoad();
+ }
+ PhaseUtil::Register(&gEventHandler);
+
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
runtime->AddSystemWeakHolder(&gObjectTagTable);
+
return true;
}
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index f38aa86..7182055 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -144,6 +144,18 @@
envs.push_back(env);
}
+void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+ auto it = std::find(envs.begin(), envs.end(), env);
+ if (it != envs.end()) {
+ envs.erase(it);
+ for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
+ i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
+ ++i) {
+ RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i));
+ }
+ }
+}
+
static bool IsThreadControllable(ArtJvmtiEvent event) {
switch (event) {
case ArtJvmtiEvent::kVmInit:
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 08a8765..8e246de 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -141,6 +141,9 @@
// enabled, yet.
void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
+ // Remove an env.
+ void RemoveArtJvmTiEnv(ArtJvmTiEnv* env);
+
bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
if (!EventMask::EventIsInRange(event)) {
return false;
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index d1324bc..abcc849 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -417,4 +417,35 @@
return ERR(NONE);
}
+jvmtiError ClassUtil::GetClassVersionNumbers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (jklass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass);
+ if (!jklass_obj->IsClass()) {
+ return ERR(INVALID_CLASS);
+ }
+ art::ObjPtr<art::mirror::Class> klass = jklass_obj->AsClass();
+ if (klass->IsPrimitive() || klass->IsArrayClass()) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (minor_version_ptr == nullptr || major_version_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ // Note: proxies will show the dex file version of java.lang.reflect.Proxy, as that is
+ // what their dex cache copies from.
+ uint32_t version = klass->GetDexFile().GetHeader().GetVersion();
+
+ *major_version_ptr = static_cast<jint>(version);
+ *minor_version_ptr = 0;
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index 7a0fafb..9558894 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -72,6 +72,11 @@
static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
+
+ static jvmtiError GetClassVersionNumbers(jvmtiEnv* env,
+ jclass klass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
new file mode 100644
index 0000000..85d6b72
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -0,0 +1,129 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_phase.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "events-inl.h"
+#include "runtime.h"
+#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace openjdkjvmti {
+
+jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0);
+
+struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
+ inline static JNIEnv* GetJniEnv() {
+ return reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv());
+ }
+
+ inline static jthread GetCurrentJThread() {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ return soa.AddLocalReference<jthread>(soa.Self()->GetPeer());
+ }
+
+ void NextRuntimePhase(RuntimePhase phase) OVERRIDE {
+ // TODO: Events.
+ switch (phase) {
+ case RuntimePhase::kInitialAgents:
+ PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL;
+ break;
+ case RuntimePhase::kStart:
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_START;
+ break;
+ case RuntimePhase::kInit:
+ {
+ ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
+ event_handler->DispatchEvent(nullptr,
+ ArtJvmtiEvent::kVmInit,
+ GetJniEnv(),
+ thread.get());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+ }
+ break;
+ case RuntimePhase::kDeath:
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
+ // TODO: Block events now.
+ break;
+ }
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+PhaseUtil::PhaseCallback gPhaseCallback;
+
+jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase_ptr) {
+ if (phase_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ jvmtiPhase now = PhaseUtil::current_phase_;
+ DCHECK(now == JVMTI_PHASE_ONLOAD ||
+ now == JVMTI_PHASE_PRIMORDIAL ||
+ now == JVMTI_PHASE_START ||
+ now == JVMTI_PHASE_LIVE ||
+ now == JVMTI_PHASE_DEAD);
+ *phase_ptr = now;
+ return ERR(NONE);
+}
+
+void PhaseUtil::SetToOnLoad() {
+ DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_));
+ PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
+}
+
+void PhaseUtil::SetToPrimordial() {
+ DCHECK_EQ(static_cast<size_t>(JVMTI_PHASE_ONLOAD), static_cast<size_t>(PhaseUtil::current_phase_));
+ PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
+}
+
+void PhaseUtil::SetToLive() {
+ DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_));
+ PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+}
+
+void PhaseUtil::Register(EventHandler* handler) {
+ gPhaseCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add phase callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback);
+}
+
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
new file mode 100644
index 0000000..054652a
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class EventHandler;
+
+class PhaseUtil {
+ public:
+ static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr);
+
+ static void Register(EventHandler* event_handler);
+
+ // Move the phase from unitialized to LOAD.
+ static void SetToOnLoad();
+
+ // Move the phase from LOAD to PRIMORDIAL.
+ static void SetToPrimordial();
+
+ // Move the phase from unitialized to LIVE.
+ static void SetToLive();
+
+ struct PhaseCallback;
+
+ private:
+ static jvmtiPhase current_phase_;
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8b355c8..4936a2f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1383,6 +1383,10 @@
LOG(ERROR) << "Unable to load an agent: " << err;
}
}
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents);
+ }
VLOG(startup) << "Runtime::Init exiting";
@@ -1395,7 +1399,7 @@
constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so";
// Is the plugin already loaded?
- for (Plugin p : *plugins) {
+ for (const Plugin& p : *plugins) {
if (p.GetLibrary() == plugin_name) {
return true;
}
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 6344c69..e580e78 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -58,9 +58,10 @@
class RuntimePhaseCallback {
public:
enum RuntimePhase {
- kStart, // The runtime is started.
- kInit, // The runtime is initialized (and will run user code soon).
- kDeath, // The runtime just died.
+ kInitialAgents, // Initial agent loading is done.
+ kStart, // The runtime is started.
+ kInit, // The runtime is initialized (and will run user code soon).
+ kDeath, // The runtime just died.
};
virtual ~RuntimePhaseCallback() {}
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index c379b5c..8974b59 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -351,8 +351,13 @@
struct Callback : public RuntimePhaseCallback {
void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE {
- if (p == RuntimePhaseCallback::RuntimePhase::kStart) {
- if (init_seen > 0) {
+ if (p == RuntimePhaseCallback::RuntimePhase::kInitialAgents) {
+ if (start_seen > 0 || init_seen > 0 || death_seen > 0) {
+ LOG(FATAL) << "Unexpected order";
+ }
+ ++initial_agents_seen;
+ } else if (p == RuntimePhaseCallback::RuntimePhase::kStart) {
+ if (init_seen > 0 || death_seen > 0) {
LOG(FATAL) << "Init seen before start.";
}
++start_seen;
@@ -365,6 +370,7 @@
}
}
+ size_t initial_agents_seen = 0;
size_t start_seen = 0;
size_t init_seen = 0;
size_t death_seen = 0;
@@ -374,6 +380,7 @@
};
TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) {
+ ASSERT_EQ(0u, cb_.initial_agents_seen);
ASSERT_EQ(0u, cb_.start_seen);
ASSERT_EQ(0u, cb_.init_seen);
ASSERT_EQ(0u, cb_.death_seen);
@@ -386,6 +393,7 @@
ASSERT_TRUE(started);
}
+ ASSERT_EQ(0u, cb_.initial_agents_seen);
ASSERT_EQ(1u, cb_.start_seen);
ASSERT_EQ(1u, cb_.init_seen);
ASSERT_EQ(0u, cb_.death_seen);
@@ -393,6 +401,7 @@
// Delete the runtime.
runtime_.reset();
+ ASSERT_EQ(0u, cb_.initial_agents_seen);
ASSERT_EQ(1u, cb_.start_seen);
ASSERT_EQ(1u, cb_.init_seen);
ASSERT_EQ(1u, cb_.death_seen);
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 052fb9a..0b17656 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -28,6 +28,46 @@
namespace art {
namespace Test901HelloTi {
+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 VMStartCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+ printf("VMStart\n");
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jthread thread ATTRIBUTE_UNUSED) {
+ printf("VMInit\n");
+}
+
+static void JNICALL VMDeatchCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+ printf("VMDeath\n");
+}
+
+
+static void InstallVMEvents(jvmtiEnv* env) {
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMStart = VMStartCallback;
+ callbacks.VMInit = VMInitCallback;
+ callbacks.VMDeath = VMDeatchCallback;
+ jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (ret != JVMTI_ERROR_NONE) {
+ printf("Failed to install callbacks");
+ }
+
+ EnableEvent(env, JVMTI_EVENT_VM_START);
+ EnableEvent(env, JVMTI_EVENT_VM_INIT);
+ EnableEvent(env, JVMTI_EVENT_VM_DEATH);
+}
+
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
@@ -72,6 +112,10 @@
printf("Unexpected version number!\n");
return -1;
}
+
+ InstallVMEvents(env);
+ InstallVMEvents(env2);
+
CHECK_CALL_SUCCESS(env->DisposeEnvironment());
CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
#undef CHECK_CALL_SUCCESS
@@ -82,6 +126,19 @@
}
SetAllCapabilities(jvmti_env);
+ jvmtiPhase current_phase;
+ jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase);
+ if (phase_result != JVMTI_ERROR_NONE) {
+ printf("Could not get phase");
+ return 1;
+ }
+ if (current_phase != JVMTI_PHASE_ONLOAD) {
+ printf("Wrong phase");
+ return 1;
+ }
+
+ InstallVMEvents(jvmti_env);
+
return JNI_OK;
}
@@ -92,5 +149,15 @@
JvmtiErrorToException(env, result);
}
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ jvmtiPhase current_phase;
+ jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase);
+ if (JvmtiErrorToException(env, phase_result)) {
+ return JNI_FALSE;
+ }
+ return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE;
+}
+
} // namespace Test901HelloTi
} // namespace art
diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt
index 2aee99b..c4b24cb 100644
--- a/test/901-hello-ti-agent/expected.txt
+++ b/test/901-hello-ti-agent/expected.txt
@@ -1,8 +1,12 @@
Loaded Agent for test 901-hello-ti-agent
+VMStart
+VMInit
Hello, world!
+Agent in live phase.
0
1
2
4
8
JVMTI_ERROR_ILLEGAL_ARGUMENT
+VMDeath
diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java
index 775e5c2..faf2dc2 100644
--- a/test/901-hello-ti-agent/src/Main.java
+++ b/test/901-hello-ti-agent/src/Main.java
@@ -20,6 +20,10 @@
System.out.println("Hello, world!");
+ if (checkLivePhase()) {
+ System.out.println("Agent in live phase.");
+ }
+
set(0); // OTHER
set(1); // GC
set(2); // CLASS
@@ -37,5 +41,6 @@
}
}
+ private static native boolean checkLivePhase();
private static native void setVerboseFlag(int flag, boolean value);
}
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index a22d1d7..29eeff6 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -241,5 +241,23 @@
return ret;
}
+extern "C" JNIEXPORT jintArray JNICALL Java_Main_getClassVersion(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint major, minor;
+ jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ jintArray int_array = env->NewIntArray(2);
+ if (int_array == nullptr) {
+ return nullptr;
+ }
+ jint buf[2] = { major, minor };
+ env->SetIntArrayRegion(int_array, 0, 2, buf);
+
+ return int_array;
+}
+
} // namespace Test912Classes
} // namespace art
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index a95a465..f3cb261 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -59,3 +59,5 @@
boot <- src+src-ex (A,B)
912-classes.jar+ ->
[class A, class B, class java.lang.Object]
+
+[37, 0]
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index ea3c49c..cbf2392 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -80,6 +80,10 @@
testClassLoader(getProxyClass());
testClassLoaderClasses();
+
+ System.out.println();
+
+ testClassVersion();
}
private static Class<?> proxyClass = null;
@@ -202,6 +206,10 @@
}
}
+ private static void testClassVersion() {
+ System.out.println(Arrays.toString(getClassVersion(Main.class)));
+ }
+
private static void printClassLoaderClasses(ClassLoader cl) {
for (;;) {
if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
@@ -262,6 +270,8 @@
private static native Class<?>[] getClassLoaderClasses(ClassLoader cl);
+ private static native int[] getClassVersion(Class<?> c);
+
private static class TestForNonInit {
public static double dummy = Math.random(); // So it can't be compile-time initialized.
}
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
index 32e3368..3b7fb24 100644
--- a/test/924-threads/expected.txt
+++ b/test/924-threads/expected.txt
@@ -29,3 +29,5 @@
5 = ALIVE|RUNNABLE
2 = TERMINATED
[Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]]
+JVMTI_ERROR_THREAD_NOT_ALIVE
+JVMTI_ERROR_THREAD_NOT_ALIVE
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index 492a7ac..dec49a8 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -56,6 +56,8 @@
doStateTests();
doAllThreadsTests();
+
+ doTLSTests();
}
private static class Holder {
@@ -164,6 +166,68 @@
System.out.println(Arrays.toString(threads));
}
+ private static void doTLSTests() throws Exception {
+ doTLSNonLiveTests();
+ doTLSLiveTests();
+ }
+
+ private static void doTLSNonLiveTests() throws Exception {
+ Thread t = new Thread();
+ try {
+ setTLS(t, 1);
+ System.out.println("Expected failure setting TLS for non-live thread");
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ t.start();
+ t.join();
+ try {
+ setTLS(t, 1);
+ System.out.println("Expected failure setting TLS for non-live thread");
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ private static void doTLSLiveTests() throws Exception {
+ setTLS(Thread.currentThread(), 1);
+
+ long l = getTLS(Thread.currentThread());
+ if (l != 1) {
+ throw new RuntimeException("Unexpected TLS value: " + l);
+ };
+
+ final CountDownLatch cdl1 = new CountDownLatch(1);
+ final CountDownLatch cdl2 = new CountDownLatch(1);
+
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ cdl1.countDown();
+ cdl2.await();
+ setTLS(Thread.currentThread(), 2);
+ if (getTLS(Thread.currentThread()) != 2) {
+ throw new RuntimeException("Different thread issue");
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ Thread t = new Thread(r);
+ t.start();
+ cdl1.await();
+ setTLS(Thread.currentThread(), 1);
+ cdl2.countDown();
+
+ t.join();
+ if (getTLS(Thread.currentThread()) != 1) {
+ throw new RuntimeException("Got clobbered");
+ }
+ }
+
private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
public int compare(Thread o1, Thread o2) {
return o1.getName().compareTo(o2.getName());
@@ -229,4 +293,6 @@
private static native Object[] getThreadInfo(Thread t);
private static native int getThreadState(Thread t);
private static native Thread[] getAllThreads();
+ private static native void setTLS(Thread t, long l);
+ private static native long getTLS(Thread t);
}
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index 1487b7c..d35eaa8 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -120,5 +120,22 @@
return ret;
}
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getTLS(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
+ void* tls;
+ jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
+ if (JvmtiErrorToException(env, result)) {
+ return 0;
+ }
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setTLS(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
+ const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
+ jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
+ JvmtiErrorToException(env, result);
+}
+
} // namespace Test924Threads
} // namespace art