Merge "ART: Add GetClassVersionNumbers."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index f708e26..d8b780a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -121,14 +121,14 @@
ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
$(HOST_CORE_IMAGE_optimizing_pic_64) \
$(HOST_CORE_IMAGE_optimizing_pic_32) \
- $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
- $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+ $(HOST_CORE_IMAGE_interpreter_pic_64) \
+ $(HOST_CORE_IMAGE_interpreter_pic_32) \
$(HOST_OUT_EXECUTABLES)/patchoatd
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_optimizing_pic_64) \
$(TARGET_CORE_IMAGE_optimizing_pic_32) \
- $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
- $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+ $(TARGET_CORE_IMAGE_interpreter_pic_64) \
+ $(TARGET_CORE_IMAGE_interpreter_pic_32) \
$(TARGET_OUT_EXECUTABLES)/patchoatd
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 4109345..b3b1903 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -2341,6 +2341,16 @@
void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
ArtMethod* copy,
const ImageInfo& image_info) {
+ if (orig->IsAbstract()) {
+ // Ignore the single-implementation info for abstract method.
+ // Do this on orig instead of copy, otherwise there is a crash due to methods
+ // are copied before classes.
+ // TODO: handle fixup of single-implementation method for abstract method.
+ orig->SetHasSingleImplementation(false);
+ orig->SetSingleImplementation(
+ nullptr, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ }
+
memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 5d40f75..7772e8f 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -304,7 +304,8 @@
// We do not support HDeoptimize in OSR methods.
return nullptr;
}
- return resolved_method->GetSingleImplementation();
+ PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize();
+ return resolved_method->GetSingleImplementation(pointer_size);
}
bool HInliner::TryInline(HInvoke* invoke_instruction) {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 4f06a91..5fc9972 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -1414,7 +1414,14 @@
ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
- {
+ // The two tests below make sure that fiddling with the method kind
+ // (static, virtual, interface) is detected by `ValidateDependencies`.
+
+ // An interface method lookup can succeed with a virtual method lookup on the same class.
+ // That's OK, as we only want to make sure there is a method being defined with the right
+ // flags. Therefore, polluting the interface methods with virtual methods does not have
+ // to fail verification.
+ if (resolution_kind != kVirtualMethodResolution) {
VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
bool found = false;
@@ -1433,7 +1440,8 @@
ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
- {
+ // See comment above that applies the same way.
+ if (resolution_kind != kInterfaceMethodResolution) {
VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
bool found = false;
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index d7d39af..92f3727 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -55,15 +55,13 @@
extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
const char*);
-ArtMethod* ArtMethod::GetSingleImplementation() {
+ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) {
DCHECK(!IsNative());
if (!IsAbstract()) {
// A non-abstract's single implementation is itself.
return this;
}
- // TODO: add single-implementation logic for abstract method by storing it
- // in ptr_sized_fields_.
- return nullptr;
+ return reinterpret_cast<ArtMethod*>(GetDataPtrSize(pointer_size));
}
ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 17f343d..a33111a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -456,7 +456,7 @@
}
}
- ArtMethod* GetSingleImplementation()
+ ArtMethod* GetSingleImplementation(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) {
@@ -684,7 +684,8 @@
ArtMethod** dex_cache_resolved_methods_;
// Pointer to JNI function registered to this method, or a function to resolve the JNI function,
- // or the profiling data for non-native methods, or an ImtConflictTable.
+ // or the profiling data for non-native methods, or an ImtConflictTable, or the
+ // single-implementation of an abstract method.
void* data_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
diff --git a/runtime/cha.cc b/runtime/cha.cc
index d94b091..a1ad585 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -185,7 +185,8 @@
};
void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class,
- uint16_t verify_index) {
+ uint16_t verify_index,
+ ArtMethod* excluded_method) {
// Grab cha_lock_ to make sure all single-implementation updates are seen.
PointerSize image_pointer_size =
Runtime::Current()->GetClassLinker()->GetImagePointerSize();
@@ -195,9 +196,11 @@
return;
}
ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size);
- DCHECK(!verify_method->HasSingleImplementation())
- << "class: " << verify_class->PrettyClass()
- << " verify_method: " << verify_method->PrettyMethod(true);
+ if (verify_method != excluded_method) {
+ DCHECK(!verify_method->HasSingleImplementation())
+ << "class: " << verify_class->PrettyClass()
+ << " verify_method: " << verify_method->PrettyMethod(true);
+ }
verify_class = verify_class->GetSuperClass();
}
}
@@ -206,41 +209,152 @@
Handle<mirror::Class> klass,
ArtMethod* virtual_method,
ArtMethod* method_in_super,
- std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) {
+ std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
+ PointerSize pointer_size) {
// TODO: if klass is not instantiable, virtual_method isn't invocable yet so
// even if it overrides, it doesn't invalidate single-implementation
// assumption.
- DCHECK_NE(virtual_method, method_in_super);
+ DCHECK((virtual_method != method_in_super) || virtual_method->IsAbstract());
DCHECK(method_in_super->GetDeclaringClass()->IsResolved()) << "class isn't resolved";
// If virtual_method doesn't come from a default interface method, it should
// be supplied by klass.
- DCHECK(virtual_method->IsCopied() ||
+ DCHECK(virtual_method == method_in_super ||
+ virtual_method->IsCopied() ||
virtual_method->GetDeclaringClass() == klass.Get());
- // A new virtual_method should set method_in_super to
- // non-single-implementation (if not set already).
- // We don't grab cha_lock_. Single-implementation flag won't be set to true
- // again once it's set to false.
+ // To make updating single-implementation flags simple, we always maintain the following
+ // invariant:
+ // Say all virtual methods in the same vtable slot, starting from the bottom child class
+ // to super classes, is a sequence of unique methods m3, m2, m1, ... (after removing duplicate
+ // methods for inherited methods).
+ // For example for the following class hierarchy,
+ // class A { void m() { ... } }
+ // class B extends A { void m() { ... } }
+ // class C extends B {}
+ // class D extends C { void m() { ... } }
+ // the sequence is D.m(), B.m(), A.m().
+ // The single-implementation status for that sequence of methods begin with one or two true's,
+ // then become all falses. The only case where two true's are possible is for one abstract
+ // method m and one non-abstract method mImpl that overrides method m.
+ // With the invariant, when linking in a new class, we only need to at most update one or
+ // two methods in the sequence for their single-implementation status, in order to maintain
+ // the invariant.
+
if (!method_in_super->HasSingleImplementation()) {
// method_in_super already has multiple implementations. All methods in the
// same vtable slots in its super classes should have
// non-single-implementation already.
if (kIsDebugBuild) {
VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(),
- method_in_super->GetMethodIndex());
+ method_in_super->GetMethodIndex(),
+ nullptr /* excluded_method */);
}
return;
}
// Native methods don't have single-implementation flag set.
DCHECK(!method_in_super->IsNative());
- // Invalidate method_in_super's single-implementation status.
- invalidated_single_impl_methods.insert(method_in_super);
+
+ uint16_t method_index = method_in_super->GetMethodIndex();
+ if (method_in_super->IsAbstract()) {
+ if (kIsDebugBuild) {
+ // An abstract method should have made all methods in the same vtable
+ // slot above it in the class hierarchy having non-single-implementation.
+ mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass();
+ VerifyNonSingleImplementation(super_super,
+ method_index,
+ method_in_super);
+ }
+
+ if (virtual_method->IsAbstract()) {
+ // SUPER: abstract, VIRTUAL: abstract.
+ if (method_in_super == virtual_method) {
+ DCHECK(klass->IsInstantiable());
+ // An instantiable subclass hasn't provided a concrete implementation of
+ // the abstract method. Invoking method_in_super may throw AbstractMethodError.
+ // This is an uncommon case, so we simply treat method_in_super as not
+ // having single-implementation.
+ invalidated_single_impl_methods.insert(method_in_super);
+ return;
+ } else {
+ // One abstract method overrides another abstract method. This is an uncommon
+ // case. We simply treat method_in_super as not having single-implementation.
+ invalidated_single_impl_methods.insert(method_in_super);
+ return;
+ }
+ } else {
+ // SUPER: abstract, VIRTUAL: non-abstract.
+ // A non-abstract method overrides an abstract method.
+ if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) {
+ // Abstract method_in_super gets its first implementation. Keep it as having
+ // single-implementation and record that single implementation.
+ DCHECK(method_in_super->HasSingleImplementation());
+ method_in_super->SetSingleImplementation(virtual_method, pointer_size);
+ return;
+ } else {
+ // Abstract method_in_super already got one implementation.
+ // Invalidate method_in_super's single-implementation status.
+ invalidated_single_impl_methods.insert(method_in_super);
+ return;
+ }
+ }
+ } else {
+ if (virtual_method->IsAbstract()) {
+ // SUPER: non-abstract, VIRTUAL: abstract.
+ // An abstract method overrides a non-abstract method. This is an uncommon
+ // case, we simply treat both methods as not having single-implementation.
+ invalidated_single_impl_methods.insert(virtual_method);
+ // Fall-through to handle invalidating method_in_super of its
+ // single-implementation status.
+ }
+
+ // SUPER: non-abstract, VIRTUAL: non-abstract/abstract(fall-through from previous if).
+ // Invalidate method_in_super's single-implementation status.
+ invalidated_single_impl_methods.insert(method_in_super);
+
+ // method_in_super might be the single-implementation of another abstract method,
+ // which should be also invalidated of its single-implementation status.
+ mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass();
+ while (super_super != nullptr &&
+ method_index < super_super->GetVTableLength()) {
+ ArtMethod* method_in_super_super = super_super->GetVTableEntry(method_index, pointer_size);
+ if (method_in_super_super != method_in_super) {
+ if (method_in_super_super->IsAbstract()) {
+ if (method_in_super_super->HasSingleImplementation()) {
+ // Invalidate method_in_super's single-implementation status.
+ invalidated_single_impl_methods.insert(method_in_super_super);
+ // No need to further traverse up the class hierarchy since if there
+ // are cases that one abstract method overrides another method, we
+ // should have made that method having non-single-implementation already.
+ } else {
+ // method_in_super_super is already non-single-implementation.
+ // No need to further traverse up the class hierarchy.
+ }
+ } else {
+ DCHECK(!method_in_super_super->HasSingleImplementation());
+ // No need to further traverse up the class hierarchy since two non-abstract
+ // methods (method_in_super and method_in_super_super) should have set all
+ // other methods (abstract or not) in the vtable slot to be non-single-implementation.
+ }
+
+ if (kIsDebugBuild) {
+ VerifyNonSingleImplementation(super_super->GetSuperClass(),
+ method_index,
+ method_in_super_super);
+ }
+ // No need to go any further.
+ return;
+ } else {
+ super_super = super_super->GetSuperClass();
+ }
+ }
+ }
}
void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass,
- ArtMethod* method) {
+ ArtMethod* method,
+ PointerSize pointer_size) {
DCHECK(method->IsCopied() || method->GetDeclaringClass() == klass.Get());
if (klass->IsFinal() || method->IsFinal()) {
// Final classes or methods do not need CHA for devirtualization.
@@ -253,16 +367,21 @@
// cannot be inlined. It's not worthwhile to devirtualize the
// call which can add a deoptimization point.
DCHECK(!method->HasSingleImplementation());
+ } else if (method->IsAbstract()) {
+ if (method->GetDeclaringClass()->IsInstantiable()) {
+ // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali).
+ // Do not attempt to devirtualize it.
+ method->SetHasSingleImplementation(false);
+ } else {
+ // Abstract method starts with single-implementation flag set and null
+ // implementation method.
+ method->SetHasSingleImplementation(true);
+ DCHECK(method->GetSingleImplementation(pointer_size) == nullptr);
+ }
} else {
method->SetHasSingleImplementation(true);
- if (method->IsAbstract()) {
- // There is no real implementation yet.
- // TODO: implement single-implementation logic for abstract methods.
- DCHECK(method->GetSingleImplementation() == nullptr);
- } else {
- // Single implementation of non-abstract method is itself.
- DCHECK_EQ(method->GetSingleImplementation(), method);
- }
+ // Single implementation of non-abstract method is itself.
+ DCHECK_EQ(method->GetSingleImplementation(pointer_size), method);
}
}
@@ -286,19 +405,29 @@
ArtMethod* method_in_super = super_class->GetVTableEntry(i, image_pointer_size);
if (method == method_in_super) {
// vtable slot entry is inherited from super class.
+ if (method->IsAbstract() && klass->IsInstantiable()) {
+ // An instantiable class that inherits an abstract method is treated as
+ // supplying an implementation that throws AbstractMethodError.
+ CheckSingleImplementationInfo(klass,
+ method,
+ method_in_super,
+ invalidated_single_impl_methods,
+ image_pointer_size);
+ }
continue;
}
- InitSingleImplementationFlag(klass, method);
+ InitSingleImplementationFlag(klass, method, image_pointer_size);
CheckSingleImplementationInfo(klass,
method,
method_in_super,
- invalidated_single_impl_methods);
+ invalidated_single_impl_methods,
+ image_pointer_size);
}
// For new virtual methods that don't override.
for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) {
ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size);
- InitSingleImplementationFlag(klass, method);
+ InitSingleImplementationFlag(klass, method, image_pointer_size);
}
Runtime* const runtime = Runtime::Current();
diff --git a/runtime/cha.h b/runtime/cha.h
index ada5c89..a56a752 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -112,7 +112,9 @@
void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void InitSingleImplementationFlag(Handle<mirror::Class> klass, ArtMethod* method)
+ void InitSingleImplementationFlag(Handle<mirror::Class> klass,
+ ArtMethod* method,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
// `virtual_method` in `klass` overrides `method_in_super`.
@@ -123,12 +125,16 @@
Handle<mirror::Class> klass,
ArtMethod* virtual_method,
ArtMethod* method_in_super,
- std::unordered_set<ArtMethod*>& invalidated_single_impl_methods)
+ std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Verify all methods in the same vtable slot from verify_class and its supers
- // don't have single-implementation.
- void VerifyNonSingleImplementation(mirror::Class* verify_class, uint16_t verify_index)
+ // For all methods in vtable slot at `verify_index` of `verify_class` and its
+ // superclasses, single-implementation status should be false, except if the
+ // method is `excluded_method`.
+ void VerifyNonSingleImplementation(mirror::Class* verify_class,
+ uint16_t verify_index,
+ ArtMethod* excluded_method)
REQUIRES_SHARED(Locks::mutator_lock_);
// A map that maps a method to a set of compiled code that assumes that method has a
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index b0c4597..7ae9f03 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,7 +160,7 @@
// image at GetImageLocation(). This is used for testing mismatched
// image checksums in the oat_file_assistant_tests.
std::string GetImageLocation2() const {
- return GetImageDirectory() + "/core-npic.art";
+ return GetImageDirectory() + "/core-interpreter.art";
}
std::string GetDexSrc1() const {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 76777d9..28bcb97 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -596,7 +596,7 @@
// Get the register arguments for the invoke.
inst->GetVarArgs(args, inst_data);
// Drop the first register which is the method handle performing the invoke.
- memcpy(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
+ memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
args[Instruction::kMaxVarArgRegs - 1] = 0;
return DoInvokePolymorphic<is_range, do_access_check>(self,
invoke_method,
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index afa804c..84eacde 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -152,15 +152,17 @@
}
}
- if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
- if (relocate) {
- EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
- } else {
- EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ if (!with_alternate_image) {
+ if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+ if (relocate) {
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ } else {
+ EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ }
}
}
}
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 a55f662..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,
@@ -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_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/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 15cc566..1131607 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -963,20 +963,25 @@
// Check recorded fields are resolved the same way, have the same recorded class,
// and have the same recorded flags.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false)));
for (const auto& entry : fields) {
- ArtField* field = class_linker->ResolveFieldJLS(
- dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader);
-
- if (field == nullptr) {
- DCHECK(self->IsExceptionPending());
- self->ClearException();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
+ StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
+ StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
+ // Only use field_id.class_idx_ when the entry is unresolved, which is rare.
+ // Otherwise, we might end up resolving an application class, which is expensive.
+ std::string expected_decl_klass = entry.IsResolved()
+ ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+ : dex_file.StringByTypeIdx(field_id.class_idx_);
+ mirror::Class* cls = FindClassAndClearException(
+ class_linker, self, expected_decl_klass.c_str(), class_loader);
+ if (cls == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+ return false;
}
+ DCHECK(cls->IsResolved());
+ ArtField* field = mirror::Class::FindField(self, cls, name, type);
if (entry.IsResolved()) {
- std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
std::string temp;
if (field == nullptr) {
LOG(INFO) << "VerifierDeps: Could not resolve field "
@@ -1025,11 +1030,16 @@
const char* name = dex_file.GetMethodName(method_id);
const Signature signature = dex_file.GetMethodSignature(method_id);
- const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+ // Only use method_id.class_idx_ when the entry is unresolved, which is rare.
+ // Otherwise, we might end up resolving an application class, which is expensive.
+ std::string expected_decl_klass = entry.IsResolved()
+ ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+ : dex_file.StringByTypeIdx(method_id.class_idx_);
- mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader);
+ mirror::Class* cls = FindClassAndClearException(
+ class_linker, self, expected_decl_klass.c_str(), class_loader);
if (cls == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
return false;
}
DCHECK(cls->IsResolved());
@@ -1045,7 +1055,6 @@
if (entry.IsResolved()) {
std::string temp;
- std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
if (method == nullptr) {
LOG(INFO) << "VerifierDeps: Could not resolve "
<< kind
diff --git a/test/616-cha-abstract/expected.txt b/test/616-cha-abstract/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-abstract/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-abstract/info.txt b/test/616-cha-abstract/info.txt
new file mode 100644
index 0000000..4f7e013
--- /dev/null
+++ b/test/616-cha-abstract/info.txt
@@ -0,0 +1 @@
+Test for Class Hierarchy Analysis (CHA) on abstract method.
diff --git a/test/616-cha-abstract/run b/test/616-cha-abstract/run
new file mode 100644
index 0000000..d8b4f0d
--- /dev/null
+++ b/test/616-cha-abstract/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Run without an app image to prevent the classes to be loaded at startup.
+exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java
new file mode 100644
index 0000000..e1d7db1
--- /dev/null
+++ b/test/616-cha-abstract/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+abstract class Base {
+ abstract void foo(int i);
+
+ void printError(String msg) {
+ System.out.println(msg);
+ }
+}
+
+class Main1 extends Base {
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
+ }
+}
+
+class Main2 extends Main1 {
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
+ }
+}
+
+public class Main {
+ static Main1 sMain1;
+ static Main1 sMain2;
+
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
+
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
+ }
+ }
+
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Dummy.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Dummy.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Dummy.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
+ }
+
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
+
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ testOverride(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
+ }
+
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+ static Main1 createMain2() {
+ return new Main2();
+ }
+}
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/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
diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt
index fd3c331..c148ad0 100644
--- a/test/valgrind-suppressions.txt
+++ b/test/valgrind-suppressions.txt
@@ -22,3 +22,27 @@
...
fun:_ZN3art7Runtime17InitNativeMethodsEv
}
+
+# SigQuit runs libbacktrace
+{
+ BackTraceReading64
+ Memcheck:Addr8
+ fun:access_mem_unrestricted
+ fun:_Uelf64_memory_read
+ fun:_Uelf64_valid_object_memory
+ fun:map_create_list
+ fun:unw_map_local_create
+ fun:_ZN14UnwindMapLocal5BuildEv
+ fun:_ZN12BacktraceMap6CreateEib
+}
+{
+ BackTraceReading32
+ Memcheck:Addr4
+ fun:access_mem_unrestricted
+ fun:_Uelf32_memory_read
+ fun:_Uelf32_valid_object_memory
+ fun:map_create_list
+ fun:unw_map_local_create
+ fun:_ZN14UnwindMapLocal5BuildEv
+ fun:_ZN12BacktraceMap6CreateEib
+}