diff options
67 files changed, 1991 insertions, 933 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c9b2cc8b68..035ceadeb7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -6933,7 +6933,7 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { method_alignment_); const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0; auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( - class_linker_->GetAllocatorForClassLoader(klass_->GetClassLoader())->Realloc( + Runtime::Current()->GetLinearAlloc()->Realloc( self_, old_methods, old_methods_ptr_size, new_size)); CHECK(methods != nullptr); // Native allocation failure aborts. @@ -6953,13 +6953,19 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { StrideIterator<ArtMethod> out(methods->begin(method_size_, method_alignment_) + old_method_count); // Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and // we want the roots of the miranda methods to get visited. - for (ArtMethod* mir_method : miranda_methods_) { + for (size_t i = 0; i < miranda_methods_.size(); ++i) { + ArtMethod* mir_method = miranda_methods_[i]; ArtMethod& new_method = *out; new_method.CopyFrom(mir_method, pointer_size); new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied); DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u) << "Miranda method should be abstract!"; move_table_.emplace(mir_method, &new_method); + // Update the entry in the method array, as the array will be used for future lookups, + // where thread suspension is allowed. + // As such, the array should not contain locally allocated ArtMethod, otherwise the GC + // would not see them. + miranda_methods_[i] = &new_method; ++out; } // We need to copy the default methods into our own method table since the runtime requires that @@ -6968,9 +6974,10 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { // interface but will have different ArtMethod*s for them. This also means we cannot compare a // default method found on a class with one found on the declaring interface directly and must // look at the declaring class to determine if they are the same. - for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods_, - overriding_default_methods_}) { - for (ArtMethod* def_method : methods_vec) { + for (ScopedArenaVector<ArtMethod*>* methods_vec : {&default_methods_, + &overriding_default_methods_}) { + for (size_t i = 0; i < methods_vec->size(); ++i) { + ArtMethod* def_method = (*methods_vec)[i]; ArtMethod& new_method = *out; new_method.CopyFrom(def_method, pointer_size); // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been @@ -6981,12 +6988,18 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks; new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); move_table_.emplace(def_method, &new_method); + // Update the entry in the method array, as the array will be used for future lookups, + // where thread suspension is allowed. + // As such, the array should not contain locally allocated ArtMethod, otherwise the GC + // would not see them. + (*methods_vec)[i] = &new_method; ++out; } } - for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_conflict_methods_, - overriding_default_conflict_methods_}) { - for (ArtMethod* conf_method : methods_vec) { + for (ScopedArenaVector<ArtMethod*>* methods_vec : {&default_conflict_methods_, + &overriding_default_conflict_methods_}) { + for (size_t i = 0; i < methods_vec->size(); ++i) { + ArtMethod* conf_method = (*methods_vec)[i]; ArtMethod& new_method = *out; new_method.CopyFrom(conf_method, pointer_size); // This is a type of default method (there are default method impls, just a conflict) so @@ -7002,6 +7015,11 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { // that the compiler will not invoke the implementation of whatever method we copied from. EnsureThrowsInvocationError(class_linker_, &new_method); move_table_.emplace(conf_method, &new_method); + // Update the entry in the method array, as the array will be used for future lookups, + // where thread suspension is allowed. + // As such, the array should not contain locally allocated ArtMethod, otherwise the GC + // would not see them. + (*methods_vec)[i] = &new_method; ++out; } } @@ -7034,12 +7052,7 @@ ObjPtr<mirror::PointerArray> ClassLinker::LinkInterfaceMethodsHelper::UpdateVtab default_conflict_methods_, miranda_methods_}) { // These are the functions that are not already in the vtable! - for (ArtMethod* new_method : methods_vec) { - auto translated_method_it = move_table_.find(new_method); - CHECK(translated_method_it != move_table_.end()) - << "We must have a translation for methods added to the classes methods_ array! We " - << "could not find the ArtMethod added for " << ArtMethod::PrettyMethod(new_method); - ArtMethod* new_vtable_method = translated_method_it->second; + for (ArtMethod* new_vtable_method : methods_vec) { // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_ // fields are references into the dex file the method was defined in. Since the ArtMethod // does not store that information it uses declaring_class_->dex_cache_. @@ -7056,7 +7069,6 @@ ObjPtr<mirror::PointerArray> ClassLinker::LinkInterfaceMethodsHelper::UpdateVtab ArtMethod* translated_method = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size); // Try and find what we need to change this method to. auto translation_it = default_translations.find(i); - bool found_translation = false; if (translation_it != default_translations.end()) { if (translation_it->second.IsInConflict()) { // Find which conflict method we are to use for this method. @@ -7080,30 +7092,28 @@ ObjPtr<mirror::PointerArray> ClassLinker::LinkInterfaceMethodsHelper::UpdateVtab // Normal default method (changed from an older default or abstract interface method). DCHECK(translation_it->second.IsTranslation()); translated_method = translation_it->second.GetTranslation(); + auto it = move_table_.find(translated_method); + DCHECK(it != move_table_.end()); + translated_method = it->second; } - found_translation = true; + } else { + auto it = move_table_.find(translated_method); + translated_method = (it != move_table_.end()) ? it->second : nullptr; } - DCHECK(translated_method != nullptr); - auto it = move_table_.find(translated_method); - if (it != move_table_.end()) { - auto* new_method = it->second; - DCHECK(new_method != nullptr); + + if (translated_method != nullptr) { // Make sure the new_methods index is set. - if (new_method->GetMethodIndexDuringLinking() != i) { + if (translated_method->GetMethodIndexDuringLinking() != i) { if (kIsDebugBuild) { auto* methods = klass_->GetMethodsPtr(); CHECK_LE(reinterpret_cast<uintptr_t>(&*methods->begin(method_size_, method_alignment_)), - reinterpret_cast<uintptr_t>(new_method)); - CHECK_LT(reinterpret_cast<uintptr_t>(new_method), + reinterpret_cast<uintptr_t>(translated_method)); + CHECK_LT(reinterpret_cast<uintptr_t>(translated_method), reinterpret_cast<uintptr_t>(&*methods->end(method_size_, method_alignment_))); } - new_method->SetMethodIndex(0xFFFF & i); + translated_method->SetMethodIndex(0xFFFF & i); } - vtable->SetElementPtrSize(i, new_method, pointer_size); - } else { - // If it was not going to be updated we wouldn't have put it into the default_translations - // map. - CHECK(!found_translation) << "We were asked to update this vtable entry. Must not fail."; + vtable->SetElementPtrSize(i, translated_method, pointer_size); } } klass_->SetVTable(vtable.Ptr()); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index e1117e6ea3..7b86339663 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2406,16 +2406,29 @@ void ConcurrentCopying::FinishPhase() { } } -bool ConcurrentCopying::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) { +bool ConcurrentCopying::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field, + bool do_atomic_update) { mirror::Object* from_ref = field->AsMirrorPtr(); + if (from_ref == nullptr) { + return true; + } mirror::Object* to_ref = IsMarked(from_ref); if (to_ref == nullptr) { return false; } if (from_ref != to_ref) { - QuasiAtomic::ThreadFenceRelease(); - field->Assign(to_ref); - QuasiAtomic::ThreadFenceSequentiallyConsistent(); + if (do_atomic_update) { + do { + if (field->AsMirrorPtr() != from_ref) { + // Concurrently overwritten by a mutator. + break; + } + } while (!field->CasWeakRelaxed(from_ref, to_ref)); + } else { + QuasiAtomic::ThreadFenceRelease(); + field->Assign(to_ref); + QuasiAtomic::ThreadFenceSequentiallyConsistent(); + } } return true; } diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 5b8a557375..844bb450cc 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -183,7 +183,8 @@ class ConcurrentCopying : public GarbageCollector { REQUIRES_SHARED(Locks::mutator_lock_); bool IsMarkedInUnevacFromSpace(mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_); - virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) OVERRIDE + virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field, + bool do_atomic_update) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); void SweepSystemWeaks(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_); diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index 5b513991d1..0177e2a1ad 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -187,7 +187,10 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark // and will be used for reading system weaks while the GC is running. virtual mirror::Object* IsMarked(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) = 0; - virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj) + // Returns true if the given heap reference is null or is already marked. If it's already marked, + // update the reference (uses a CAS if do_atomic_update is true. Otherwise, returns false. + virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj, + bool do_atomic_update) REQUIRES_SHARED(Locks::mutator_lock_) = 0; // Used by reference processor. virtual void ProcessMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) = 0; diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index ddcb6c0698..85e6783599 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -472,9 +472,15 @@ mirror::Object* MarkCompact::IsMarked(mirror::Object* object) { return mark_bitmap_->Test(object) ? object : nullptr; } -bool MarkCompact::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref_ptr) { +bool MarkCompact::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref_ptr, + // MarkCompact does the GC in a pause. No CAS needed. + bool do_atomic_update ATTRIBUTE_UNUSED) { // Side effect free since we call this before ever moving objects. - return IsMarked(ref_ptr->AsMirrorPtr()) != nullptr; + mirror::Object* obj = ref_ptr->AsMirrorPtr(); + if (obj == nullptr) { + return true; + } + return IsMarked(obj) != nullptr; } void MarkCompact::SweepSystemWeaks() { diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 564f85b3f8..6d52d5d515 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -175,7 +175,8 @@ class MarkCompact : public GarbageCollector { virtual mirror::Object* IsMarked(mirror::Object* obj) OVERRIDE REQUIRES_SHARED(Locks::heap_bitmap_lock_) REQUIRES(Locks::mutator_lock_); - virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj) OVERRIDE + virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj, + bool do_atomic_update) OVERRIDE REQUIRES_SHARED(Locks::heap_bitmap_lock_) REQUIRES(Locks::mutator_lock_); void ForwardObject(mirror::Object* obj) REQUIRES(Locks::heap_bitmap_lock_, diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 06ed0290a9..f00da73458 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -390,8 +390,13 @@ inline void MarkSweep::MarkObjectNonNullParallel(mirror::Object* obj) { } } -bool MarkSweep::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref) { - return IsMarked(ref->AsMirrorPtr()); +bool MarkSweep::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref, + bool do_atomic_update ATTRIBUTE_UNUSED) { + mirror::Object* obj = ref->AsMirrorPtr(); + if (obj == nullptr) { + return true; + } + return IsMarked(obj); } class MarkSweep::MarkObjectSlowPath { diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 02cf462bd3..a6e2d61f6d 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -188,7 +188,8 @@ class MarkSweep : public GarbageCollector { void VerifyIsLive(const mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); - virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref) OVERRIDE + virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref, + bool do_atomic_update) OVERRIDE REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index f2aa5a7599..cb9e7e2c15 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -765,8 +765,13 @@ mirror::Object* SemiSpace::IsMarked(mirror::Object* obj) { return mark_bitmap_->Test(obj) ? obj : nullptr; } -bool SemiSpace::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* object) { +bool SemiSpace::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* object, + // SemiSpace does the GC in a pause. No CAS needed. + bool do_atomic_update ATTRIBUTE_UNUSED) { mirror::Object* obj = object->AsMirrorPtr(); + if (obj == nullptr) { + return true; + } mirror::Object* new_obj = IsMarked(obj); if (new_obj == nullptr) { return false; diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 4cebcc3044..52b5e5fe30 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -166,7 +166,8 @@ class SemiSpace : public GarbageCollector { REQUIRES(Locks::mutator_lock_) REQUIRES_SHARED(Locks::heap_bitmap_lock_); - virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* object) OVERRIDE + virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* object, + bool do_atomic_update) OVERRIDE REQUIRES(Locks::mutator_lock_) REQUIRES_SHARED(Locks::heap_bitmap_lock_); diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 081be968eb..c1548365c7 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -203,7 +203,9 @@ void ReferenceProcessor::DelayReferenceReferent(ObjPtr<mirror::Class> klass, DCHECK(klass != nullptr); DCHECK(klass->IsTypeOfReferenceClass()); mirror::HeapReference<mirror::Object>* referent = ref->GetReferentReferenceAddr(); - if (referent->AsMirrorPtr() != nullptr && !collector->IsMarkedHeapReference(referent)) { + // do_atomic_update needs to be true because this happens outside of the reference processing + // phase. + if (!collector->IsNullOrMarkedHeapReference(referent, /*do_atomic_update*/true)) { Thread* self = Thread::Current(); // TODO: Remove these locks, and use atomic stacks for storing references? // We need to check that the references haven't already been enqueued since we can end up diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc index a0eb197bd5..734caea371 100644 --- a/runtime/gc/reference_queue.cc +++ b/runtime/gc/reference_queue.cc @@ -129,8 +129,9 @@ void ReferenceQueue::ClearWhiteReferences(ReferenceQueue* cleared_references, while (!IsEmpty()) { ObjPtr<mirror::Reference> ref = DequeuePendingReference(); mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr(); - if (referent_addr->AsMirrorPtr() != nullptr && - !collector->IsMarkedHeapReference(referent_addr)) { + // do_atomic_update is false because this happens during the reference processing phase where + // Reference.clear() would block. + if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update*/false)) { // Referent is white, clear it. if (Runtime::Current()->IsActiveTransaction()) { ref->ClearReferent<true>(); @@ -147,8 +148,9 @@ void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue* cleared_referenc while (!IsEmpty()) { ObjPtr<mirror::FinalizerReference> ref = DequeuePendingReference()->AsFinalizerReference(); mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr(); - if (referent_addr->AsMirrorPtr() != nullptr && - !collector->IsMarkedHeapReference(referent_addr)) { + // do_atomic_update is false because this happens during the reference processing phase where + // Reference.clear() would block. + if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update*/false)) { ObjPtr<mirror::Object> forward_address = collector->MarkObject(referent_addr->AsMirrorPtr()); // Move the updated referent to the zombie field. if (Runtime::Current()->IsActiveTransaction()) { diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index f80c43d80c..e0f28adc4f 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -566,7 +566,10 @@ jweak JavaVMExt::AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) { return nullptr; } MutexLock mu(self, *Locks::jni_weak_globals_lock_); - while (UNLIKELY(!MayAccessWeakGlobals(self))) { + // CMS needs this to block for concurrent reference processing because an object allocated during + // the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak + // ref. But CC (kUseReadBarrier == true) doesn't because of the to-space invariant. + while (!kUseReadBarrier && UNLIKELY(!MayAccessWeakGlobals(self))) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. self->CheckEmptyCheckpoint(); diff --git a/runtime/mirror/object_reference-inl.h b/runtime/mirror/object_reference-inl.h index e70b93607e..22fb83cb5c 100644 --- a/runtime/mirror/object_reference-inl.h +++ b/runtime/mirror/object_reference-inl.h @@ -34,6 +34,15 @@ HeapReference<MirrorType> HeapReference<MirrorType>::FromObjPtr(ObjPtr<MirrorTyp return HeapReference<MirrorType>(ptr.Ptr()); } +template<class MirrorType> +bool HeapReference<MirrorType>::CasWeakRelaxed(MirrorType* expected_ptr, MirrorType* new_ptr) { + HeapReference<Object> expected_ref(HeapReference<Object>::FromMirrorPtr(expected_ptr)); + HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_ptr)); + Atomic<uint32_t>* atomic_reference = reinterpret_cast<Atomic<uint32_t>*>(&this->reference_); + return atomic_reference->CompareExchangeWeakRelaxed(expected_ref.reference_, + new_ref.reference_); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h index 71f34c66e2..a96a120d68 100644 --- a/runtime/mirror/object_reference.h +++ b/runtime/mirror/object_reference.h @@ -94,6 +94,9 @@ class MANAGED HeapReference : public ObjectReference<kPoisonHeapReferences, Mirr static HeapReference<MirrorType> FromObjPtr(ObjPtr<MirrorType> ptr) REQUIRES_SHARED(Locks::mutator_lock_); + bool CasWeakRelaxed(MirrorType* old_ptr, MirrorType* new_ptr) + REQUIRES_SHARED(Locks::mutator_lock_); + private: explicit HeapReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_) : ObjectReference<kPoisonHeapReferences, MirrorType>(mirror_ptr) {} diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 893abd5462..9c0927584e 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1361,8 +1361,10 @@ void MonitorList::BroadcastForNewMonitors() { void MonitorList::Add(Monitor* m) { Thread* self = Thread::Current(); MutexLock mu(self, monitor_list_lock_); - while (UNLIKELY((!kUseReadBarrier && !allow_new_monitors_) || - (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { + // CMS needs this to block for concurrent reference processing because an object allocated during + // the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak + // ref. But CC (kUseReadBarrier == true) doesn't because of the to-space invariant. + while (!kUseReadBarrier && UNLIKELY(!allow_new_monitors_)) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. self->CheckEmptyCheckpoint(); diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 36825cb870..268d71ac65 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -17,6 +17,7 @@ #include "dalvik_system_VMStack.h" #include "art_method-inl.h" +#include "gc/task_processor.h" #include "jni_internal.h" #include "nth_caller_visitor.h" #include "mirror/class-inl.h" @@ -31,9 +32,18 @@ namespace art { static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject peer) REQUIRES_SHARED(Locks::mutator_lock_) { jobject trace = nullptr; - if (soa.Decode<mirror::Object>(peer) == soa.Self()->GetPeer()) { + ObjPtr<mirror::Object> decoded_peer = soa.Decode<mirror::Object>(peer); + if (decoded_peer == soa.Self()->GetPeer()) { trace = soa.Self()->CreateInternalStackTrace<false>(soa); } else { + // Never allow suspending the heap task thread since it may deadlock if allocations are + // required for the stack trace. + Thread* heap_task_thread = + Runtime::Current()->GetHeap()->GetTaskProcessor()->GetRunningThread(); + // heap_task_thread could be null if the daemons aren't yet started. + if (heap_task_thread != nullptr && decoded_peer == heap_task_thread->GetPeer()) { + return nullptr; + } // Suspend thread to build stack trace. ScopedThreadSuspension sts(soa.Self(), kNative); ThreadList* thread_list = Runtime::Current()->GetThreadList(); diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index b757b2114f..4bd21b4c2f 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -29,6 +29,7 @@ cc_defaults { "ti_properties.cc", "ti_stack.cc", "ti_redefine.cc", + "ti_thread.cc", "transform.cc"], include_dirs: ["art/runtime"], shared_libs: [ diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index c52dd76b59..2629c9fc07 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -55,6 +55,7 @@ #include "ti_properties.h" #include "ti_redefine.h" #include "ti_stack.h" +#include "ti_thread.h" #include "transform.h" // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton @@ -117,11 +118,11 @@ class JvmtiFunctions { } static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::GetThreadState(env, thread, thread_state_ptr); } static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::GetCurrentThread(env, thread_ptr); } static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr) { @@ -159,7 +160,7 @@ class JvmtiFunctions { } static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadUtil::GetThreadInfo(env, thread, info_ptr); } static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env, @@ -237,7 +238,7 @@ class JvmtiFunctions { jint max_frame_count, jvmtiStackInfo** stack_info_ptr, jint* thread_count_ptr) { - return ERR(NOT_IMPLEMENTED); + return StackUtil::GetAllStackTraces(env, max_frame_count, stack_info_ptr, thread_count_ptr); } static jvmtiError GetThreadListStackTraces(jvmtiEnv* env, diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc index 579fb50ecc..098cedbffa 100644 --- a/runtime/openjdkjvmti/ti_stack.cc +++ b/runtime/openjdkjvmti/ti_stack.cc @@ -31,9 +31,15 @@ #include "ti_stack.h" +#include <list> +#include <unordered_map> +#include <vector> + #include "art_jvmti.h" #include "art_method-inl.h" +#include "base/bit_utils.h" #include "base/enums.h" +#include "base/mutex.h" #include "dex_file.h" #include "dex_file_annotations.h" #include "jni_env_ext.h" @@ -41,19 +47,19 @@ #include "mirror/class.h" #include "mirror/dex_cache.h" #include "scoped_thread_state_change-inl.h" +#include "ScopedLocalRef.h" #include "stack.h" -#include "thread.h" +#include "thread-inl.h" +#include "thread_list.h" #include "thread_pool.h" namespace openjdkjvmti { struct GetStackTraceVisitor : public art::StackVisitor { GetStackTraceVisitor(art::Thread* thread_in, - art::ScopedObjectAccessAlreadyRunnable& soa_, size_t start_, size_t stop_) : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), - soa(soa_), start(start_), stop(stop_) {} @@ -85,7 +91,6 @@ struct GetStackTraceVisitor : public art::StackVisitor { return true; } - art::ScopedObjectAccessAlreadyRunnable& soa; std::vector<jvmtiFrameInfo> frames; size_t start; size_t stop; @@ -99,10 +104,8 @@ struct GetStackTraceClosure : public art::Closure { start_result(0), stop_result(0) {} - void Run(art::Thread* self) OVERRIDE { - art::ScopedObjectAccess soa(art::Thread::Current()); - - GetStackTraceVisitor visitor(self, soa, start_input, stop_input); + void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + GetStackTraceVisitor visitor(self, start_input, stop_input); visitor.WalkStack(false); frames.swap(visitor.frames); @@ -118,6 +121,44 @@ struct GetStackTraceClosure : public art::Closure { size_t stop_result; }; +static jvmtiError TranslateFrameVector(const std::vector<jvmtiFrameInfo>& frames, + jint start_depth, + size_t start_result, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr) { + size_t collected_frames = frames.size(); + + // Assume we're here having collected something. + DCHECK_GT(max_frame_count, 0); + + // Frames from the top. + if (start_depth >= 0) { + if (start_result != 0) { + // Not enough frames. + return ERR(ILLEGAL_ARGUMENT); + } + DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count)); + if (frames.size() > 0) { + memcpy(frame_buffer, frames.data(), collected_frames * sizeof(jvmtiFrameInfo)); + } + *count_ptr = static_cast<jint>(frames.size()); + return ERR(NONE); + } + + // Frames from the bottom. + if (collected_frames < static_cast<size_t>(-start_depth)) { + return ERR(ILLEGAL_ARGUMENT); + } + + size_t count = std::min(static_cast<size_t>(-start_depth), static_cast<size_t>(max_frame_count)); + memcpy(frame_buffer, + &frames.data()[collected_frames + start_depth], + count * sizeof(jvmtiFrameInfo)); + *count_ptr = static_cast<jint>(count); + return ERR(NONE); +} + jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, jthread java_thread, jint start_depth, @@ -157,35 +198,179 @@ jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, } GetStackTraceClosure closure(start_depth >= 0 ? static_cast<size_t>(start_depth) : 0, - start_depth >= 0 ?static_cast<size_t>(max_frame_count) : 0); + start_depth >= 0 ? static_cast<size_t>(max_frame_count) : 0); thread->RequestSynchronousCheckpoint(&closure); - size_t collected_frames = closure.frames.size(); + return TranslateFrameVector(closure.frames, + start_depth, + closure.start_result, + max_frame_count, + frame_buffer, + count_ptr); +} - // Frames from the top. - if (start_depth >= 0) { - if (closure.start_result != 0) { - // Not enough frames. - return ERR(ILLEGAL_ARGUMENT); +struct GetAllStackTraceClosure : public art::Closure { + public: + explicit GetAllStackTraceClosure(size_t stop) + : start_input(0), + stop_input(stop), + frames_lock("GetAllStackTraceGuard", art::LockLevel::kAbortLock), + start_result(0), + stop_result(0) {} + + void Run(art::Thread* self) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!frames_lock) { + // self should be live here (so it could be suspended). No need to filter. + + art::Thread* current = art::Thread::Current(); + std::vector<jvmtiFrameInfo> self_frames; + + GetStackTraceVisitor visitor(self, start_input, stop_input); + visitor.WalkStack(false); + + self_frames.swap(visitor.frames); + + art::MutexLock mu(current, frames_lock); + frames.emplace(self, self_frames); + } + + const size_t start_input; + const size_t stop_input; + + art::Mutex frames_lock; + std::unordered_map<art::Thread*, std::vector<jvmtiFrameInfo>> frames GUARDED_BY(frames_lock); + size_t start_result; + size_t stop_result; +}; + + + +jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) { + if (max_frame_count < 0) { + return ERR(ILLEGAL_ARGUMENT); + } + if (stack_info_ptr == nullptr || thread_count_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + + art::Thread* current = art::Thread::Current(); + art::ScopedObjectAccess soa(current); // Now we know we have the shared lock. + art::ScopedThreadSuspension sts(current, art::kWaitingForDebuggerSuspension); + art::ScopedSuspendAll ssa("GetAllStackTraces"); + + std::vector<art::Thread*> threads; + std::vector<std::vector<jvmtiFrameInfo>> frames; + { + std::list<art::Thread*> thread_list; + { + art::MutexLock mu(current, *art::Locks::thread_list_lock_); + thread_list = art::Runtime::Current()->GetThreadList()->GetList(); } - DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count)); - if (closure.frames.size() > 0) { - memcpy(frame_buffer, closure.frames.data(), collected_frames * sizeof(jvmtiFrameInfo)); + + for (art::Thread* thread : thread_list) { + // Skip threads that are still starting. + if (thread->IsStillStarting()) { + continue; + } + + GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count)); + thread->RequestSynchronousCheckpoint(&closure); + + threads.push_back(thread); + frames.emplace_back(); + frames.back().swap(closure.frames); } - *count_ptr = static_cast<jint>(closure.frames.size()); - return ERR(NONE); } - // Frames from the bottom. - if (collected_frames < static_cast<size_t>(-start_depth)) { - return ERR(ILLEGAL_ARGUMENT); + // Convert the data into our output format. Note: we need to keep the threads suspended, + // as we need to access them for their peers. + + // Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to + // allocate one big chunk for this and the actual frames, which means we need + // to either be conservative or rearrange things later (the latter is implemented). + std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[frames.size()]); + std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos; + frame_infos.reserve(frames.size()); + + // Now run through and add data for each thread. + size_t sum_frames = 0; + for (size_t index = 0; index < frames.size(); ++index) { + jvmtiStackInfo& stack_info = stack_info_array.get()[index]; + memset(&stack_info, 0, sizeof(jvmtiStackInfo)); + + art::Thread* self = threads[index]; + const std::vector<jvmtiFrameInfo>& thread_frames = frames[index]; + + // For the time being, set the thread to null. We don't have good ScopedLocalRef + // infrastructure. + DCHECK(self->GetPeer() != nullptr); + stack_info.thread = nullptr; + stack_info.state = JVMTI_THREAD_STATE_SUSPENDED; + + size_t collected_frames = thread_frames.size(); + if (max_frame_count == 0 || collected_frames == 0) { + stack_info.frame_count = 0; + stack_info.frame_buffer = nullptr; + continue; + } + DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count)); + + jvmtiFrameInfo* frame_info = new jvmtiFrameInfo[collected_frames]; + frame_infos.emplace_back(frame_info); + + jint count; + jvmtiError translate_result = TranslateFrameVector(thread_frames, + 0, + 0, + static_cast<jint>(collected_frames), + frame_info, + &count); + DCHECK(translate_result == JVMTI_ERROR_NONE); + stack_info.frame_count = static_cast<jint>(collected_frames); + stack_info.frame_buffer = frame_info; + sum_frames += static_cast<size_t>(count); } - size_t count = std::min(static_cast<size_t>(-start_depth), static_cast<size_t>(max_frame_count)); - memcpy(frame_buffer, - &closure.frames.data()[collected_frames + start_depth], - count * sizeof(jvmtiFrameInfo)); - *count_ptr = static_cast<jint>(count); + // No errors, yet. Now put it all into an output buffer. + size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * frames.size(), + alignof(jvmtiFrameInfo)); + size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo); + unsigned char* chunk_data; + jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data); + if (alloc_result != ERR(NONE)) { + return alloc_result; + } + + jvmtiStackInfo* stack_info = reinterpret_cast<jvmtiStackInfo*>(chunk_data); + // First copy in all the basic data. + memcpy(stack_info, stack_info_array.get(), sizeof(jvmtiStackInfo) * frames.size()); + + // Now copy the frames and fix up the pointers. + jvmtiFrameInfo* frame_info = reinterpret_cast<jvmtiFrameInfo*>( + chunk_data + rounded_stack_info_size); + for (size_t i = 0; i < frames.size(); ++i) { + jvmtiStackInfo& old_stack_info = stack_info_array.get()[i]; + jvmtiStackInfo& new_stack_info = stack_info[i]; + + jthread thread_peer = current->GetJniEnv()->AddLocalReference<jthread>(threads[i]->GetPeer()); + new_stack_info.thread = thread_peer; + + if (old_stack_info.frame_count > 0) { + // Only copy when there's data - leave the nullptr alone. + size_t frames_size = static_cast<size_t>(old_stack_info.frame_count) * sizeof(jvmtiFrameInfo); + memcpy(frame_info, old_stack_info.frame_buffer, frames_size); + new_stack_info.frame_buffer = frame_info; + frame_info += old_stack_info.frame_count; + } + } + + *stack_info_ptr = stack_info; + *thread_count_ptr = static_cast<jint>(frames.size()); + return ERR(NONE); } diff --git a/runtime/openjdkjvmti/ti_stack.h b/runtime/openjdkjvmti/ti_stack.h index 1931ed3113..7619f98daf 100644 --- a/runtime/openjdkjvmti/ti_stack.h +++ b/runtime/openjdkjvmti/ti_stack.h @@ -32,12 +32,21 @@ #ifndef ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_ #define ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_ +#include "jni.h" #include "jvmti.h" +#include "base/mutex.h" + namespace openjdkjvmti { class StackUtil { public: + static jvmtiError GetAllStackTraces(jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) + REQUIRES(!art::Locks::thread_list_lock_); + static jvmtiError GetStackTrace(jvmtiEnv* env, jthread thread, jint start_depth, diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc new file mode 100644 index 0000000000..e20f5605d8 --- /dev/null +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -0,0 +1,357 @@ +/* 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_thread.h" + +#include "art_field.h" +#include "art_jvmti.h" +#include "base/logging.h" +#include "base/mutex.h" +#include "jni_internal.h" +#include "mirror/class.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "obj_ptr.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "well_known_classes.h" + +namespace openjdkjvmti { + +jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread* thread_ptr) { + art::Thread* self = art::Thread::Current(); + + art::ScopedObjectAccess soa(self); + + jthread thread_peer; + if (self->IsStillStarting()) { + thread_peer = nullptr; + } else { + thread_peer = soa.AddLocalReference<jthread>(self->GetPeer()); + } + + *thread_ptr = thread_peer; + return ERR(NONE); +} + +// Read the context classloader from a Java thread object. This is a lazy implementation +// that assumes GetThreadInfo isn't called too often. If we instead cache the ArtField, +// we will have to add synchronization as this can't be cached on startup (which is +// potentially runtime startup). +static art::ObjPtr<art::mirror::Object> GetContextClassLoader(art::ObjPtr<art::mirror::Object> peer) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (peer == nullptr) { + return nullptr; + } + art::ObjPtr<art::mirror::Class> klass = peer->GetClass(); + art::ArtField* cc_field = klass->FindDeclaredInstanceField("contextClassLoader", + "Ljava/lang/ClassLoader;"); + CHECK(cc_field != nullptr); + return cc_field->GetObject(peer); +} + +// Get the native thread. The spec says a null object denotes the current thread. +static art::Thread* GetNativeThread(jthread thread, + const art::ScopedObjectAccessAlreadyRunnable& soa) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (thread == nullptr) { + return art::Thread::Current(); + } + + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + return art::Thread::FromManagedThread(soa, thread); +} + +jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) { + if (info_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + + art::Thread* self = GetNativeThread(thread, soa); + if (self == nullptr && thread == nullptr) { + return ERR(INVALID_THREAD); + } + + JvmtiUniquePtr name_uptr; + if (self != nullptr) { + // Have a native thread object, this thread is alive. + std::string name; + self->GetThreadName(name); + jvmtiError name_result = CopyString( + env, name.c_str(), reinterpret_cast<unsigned char**>(&info_ptr->name)); + if (name_result != ERR(NONE)) { + return name_result; + } + name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name); + + info_ptr->priority = self->GetNativePriority(); + + info_ptr->is_daemon = self->IsDaemon(); + + art::ObjPtr<art::mirror::Object> peer = self->GetPeer(); + + // ThreadGroup. + if (peer != nullptr) { + art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + CHECK(f != nullptr); + art::ObjPtr<art::mirror::Object> group = f->GetObject(peer); + info_ptr->thread_group = group == nullptr + ? nullptr + : soa.AddLocalReference<jthreadGroup>(group); + } else { + info_ptr->thread_group = nullptr; + } + + // Context classloader. + art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer); + info_ptr->context_class_loader = ccl == nullptr + ? nullptr + : soa.AddLocalReference<jobject>(ccl); + } else { + // Only the peer. This thread has either not been started, or is dead. Read things from + // the Java side. + art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread); + + // Name. + { + art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_name); + CHECK(f != nullptr); + art::ObjPtr<art::mirror::Object> name = f->GetObject(peer); + std::string name_cpp; + const char* name_cstr; + if (name != nullptr) { + name_cpp = name->AsString()->ToModifiedUtf8(); + name_cstr = name_cpp.c_str(); + } else { + name_cstr = ""; + } + jvmtiError name_result = CopyString( + env, name_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name)); + if (name_result != ERR(NONE)) { + return name_result; + } + name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name); + } + + // Priority. + { + art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_priority); + CHECK(f != nullptr); + info_ptr->priority = static_cast<jint>(f->GetInt(peer)); + } + + // Daemon. + { + art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_daemon); + CHECK(f != nullptr); + info_ptr->is_daemon = f->GetBoolean(peer) == 0 ? JNI_FALSE : JNI_TRUE; + } + + // ThreadGroup. + { + art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + CHECK(f != nullptr); + art::ObjPtr<art::mirror::Object> group = f->GetObject(peer); + info_ptr->thread_group = group == nullptr + ? nullptr + : soa.AddLocalReference<jthreadGroup>(group); + } + + // Context classloader. + art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer); + info_ptr->context_class_loader = ccl == nullptr + ? nullptr + : soa.AddLocalReference<jobject>(ccl); + } + + name_uptr.release(); + + return ERR(NONE); +} + +// Return the thread's (or current thread, if null) thread state. Return kStarting in case +// there's no native counterpart (thread hasn't been started, yet, or is dead). +static art::ThreadState GetNativeThreadState(jthread thread, + const art::ScopedObjectAccessAlreadyRunnable& soa, + art::Thread** native_thread) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::Thread* self = nullptr; + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + if (thread == nullptr) { + self = art::Thread::Current(); + } else { + self = art::Thread::FromManagedThread(soa, thread); + } + *native_thread = self; + if (self == nullptr || self->IsStillStarting()) { + return art::ThreadState::kStarting; + } + return self->GetState(); +} + +static jint GetJvmtiThreadStateFromInternal(art::ThreadState internal_thread_state) { + jint jvmti_state = JVMTI_THREAD_STATE_ALIVE; + + if (internal_thread_state == art::ThreadState::kSuspended) { + jvmti_state |= JVMTI_THREAD_STATE_SUSPENDED; + // Note: We do not have data about the previous state. Otherwise we should load the previous + // state here. + } + + if (internal_thread_state == art::ThreadState::kNative) { + jvmti_state |= JVMTI_THREAD_STATE_IN_NATIVE; + } + + if (internal_thread_state == art::ThreadState::kRunnable || + internal_thread_state == art::ThreadState::kWaitingWeakGcRootRead || + internal_thread_state == art::ThreadState::kSuspended) { + jvmti_state |= JVMTI_THREAD_STATE_RUNNABLE; + } else if (internal_thread_state == art::ThreadState::kBlocked) { + jvmti_state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; + } else { + // Should be in waiting state. + jvmti_state |= JVMTI_THREAD_STATE_WAITING; + + if (internal_thread_state == art::ThreadState::kTimedWaiting || + internal_thread_state == art::ThreadState::kSleeping) { + jvmti_state |= JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT; + } else { + jvmti_state |= JVMTI_THREAD_STATE_WAITING_INDEFINITELY; + } + + if (internal_thread_state == art::ThreadState::kSleeping) { + jvmti_state |= JVMTI_THREAD_STATE_SLEEPING; + } + + if (internal_thread_state == art::ThreadState::kTimedWaiting || + internal_thread_state == art::ThreadState::kWaiting) { + jvmti_state |= JVMTI_THREAD_STATE_IN_OBJECT_WAIT; + } + + // TODO: PARKED. We'll have to inspect the stack. + } + + return jvmti_state; +} + +static jint GetJavaStateFromInternal(art::ThreadState internal_thread_state) { + switch (internal_thread_state) { + case art::ThreadState::kTerminated: + return JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED; + + case art::ThreadState::kRunnable: + case art::ThreadState::kNative: + case art::ThreadState::kWaitingWeakGcRootRead: + case art::ThreadState::kSuspended: + return JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE; + + case art::ThreadState::kTimedWaiting: + case art::ThreadState::kSleeping: + return JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING; + + case art::ThreadState::kBlocked: + return JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED; + + case art::ThreadState::kStarting: + return JVMTI_JAVA_LANG_THREAD_STATE_NEW; + + case art::ThreadState::kWaiting: + case art::ThreadState::kWaitingForGcToComplete: + case art::ThreadState::kWaitingPerformingGc: + case art::ThreadState::kWaitingForCheckPointsToRun: + case art::ThreadState::kWaitingForDebuggerSend: + case art::ThreadState::kWaitingForDebuggerToAttach: + case art::ThreadState::kWaitingInMainDebuggerLoop: + case art::ThreadState::kWaitingForDebuggerSuspension: + case art::ThreadState::kWaitingForDeoptimization: + case art::ThreadState::kWaitingForGetObjectsAllocated: + case art::ThreadState::kWaitingForJniOnLoad: + case art::ThreadState::kWaitingForSignalCatcherOutput: + case art::ThreadState::kWaitingInMainSignalCatcherLoop: + case art::ThreadState::kWaitingForMethodTracingStart: + case art::ThreadState::kWaitingForVisitObjects: + case art::ThreadState::kWaitingForGcThreadFlip: + return JVMTI_JAVA_LANG_THREAD_STATE_WAITING; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); +} + +jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread thread, + jint* thread_state_ptr) { + if (thread_state_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Thread* native_thread = nullptr; + art::ThreadState internal_thread_state = GetNativeThreadState(thread, soa, &native_thread); + + if (internal_thread_state == art::ThreadState::kStarting) { + if (thread == nullptr) { + // No native thread, and no Java thread? We must be starting up. Report as wrong phase. + return ERR(WRONG_PHASE); + } + + // Need to read the Java "started" field to know whether this is starting or terminated. + art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread); + art::ObjPtr<art::mirror::Class> klass = peer->GetClass(); + art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z"); + CHECK(started_field != nullptr); + bool started = started_field->GetBoolean(peer) != 0; + constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW; + constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED | + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED; + *thread_state_ptr = started ? kTerminatedState : kStartedState; + return ERR(NONE); + } + DCHECK(native_thread != nullptr); + + // Translate internal thread state to JVMTI and Java state. + jint jvmti_state = GetJvmtiThreadStateFromInternal(internal_thread_state); + if (native_thread->IsInterrupted()) { + jvmti_state |= JVMTI_THREAD_STATE_INTERRUPTED; + } + + // Java state is derived from nativeGetState. + // Note: Our implementation assigns "runnable" to suspended. As such, we will have slightly + // different mask. However, this is for consistency with the Java view. + jint java_state = GetJavaStateFromInternal(internal_thread_state); + + *thread_state_ptr = jvmti_state | java_state; + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h new file mode 100644 index 0000000000..b6ffbb5f65 --- /dev/null +++ b/runtime/openjdkjvmti/ti_thread.h @@ -0,0 +1,51 @@ +/* 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_THREAD_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class ThreadUtil { + public: + static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr); + + static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr); + + static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 2086d70791..df5fc5ce8a 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1364,6 +1364,39 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { return true; } +static bool EnsureJvmtiPlugin(Runtime* runtime, + std::vector<Plugin>* plugins, + std::string* error_msg) { + constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so"; + + // Is the plugin already loaded? + for (Plugin p : *plugins) { + if (p.GetLibrary() == plugin_name) { + return true; + } + } + + // Is the process debuggable? Otherwise, do not attempt to load the plugin. + if (!runtime->IsDebuggable()) { + *error_msg = "Process is not debuggable."; + return false; + } + + Plugin new_plugin = Plugin::Create(plugin_name); + + // Suspend all threads to protect ourself somewhat. + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); // Now we know we have the shared lock. + ScopedThreadSuspension sts(self, art::kWaitingForDebuggerToAttach); + ScopedSuspendAll ssa("EnsureJvmtiPlugin"); + if (!new_plugin.Load(error_msg)) { + return false; + } + + plugins->push_back(std::move(new_plugin)); + return true; +} + // Attach a new agent and add it to the list of runtime agents // // TODO: once we decide on the threading model for agents, @@ -1371,18 +1404,25 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // (and we synchronize access to any shared data structures like "agents_") // void Runtime::AttachAgent(const std::string& agent_arg) { + std::string error_msg; + if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) { + LOG(WARNING) << "Could not load plugin: " << error_msg; + ScopedObjectAccess soa(Thread::Current()); + ThrowIOException("%s", error_msg.c_str()); + return; + } + ti::Agent agent(agent_arg); int res = 0; - std::string err; - ti::Agent::LoadError result = agent.Attach(&res, &err); + ti::Agent::LoadError result = agent.Attach(&res, &error_msg); if (result == ti::Agent::kNoError) { agents_.push_back(std::move(agent)); } else { - LOG(ERROR) << "Agent attach failed (result=" << result << ") : " << err; + LOG(WARNING) << "Agent attach failed (result=" << result << ") : " << error_msg; ScopedObjectAccess soa(Thread::Current()); - ThrowWrappedIOException("%s", err.c_str()); + ThrowIOException("%s", error_msg.c_str()); } } diff --git a/test/129-ThreadGetId/expected.txt b/test/129-ThreadGetId/expected.txt index 134d8d0b47..aadf90d9d7 100644 --- a/test/129-ThreadGetId/expected.txt +++ b/test/129-ThreadGetId/expected.txt @@ -1 +1,2 @@ +HeapTaskDaemon depth 0 Finishing diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java index 9934bba95f..5aefd17f0e 100644 --- a/test/129-ThreadGetId/src/Main.java +++ b/test/129-ThreadGetId/src/Main.java @@ -22,6 +22,7 @@ public class Main implements Runnable { public static void main(String[] args) throws Exception { final Thread[] threads = new Thread[numberOfThreads]; + test_getStackTraces(); for (int t = 0; t < threads.length; t++) { threads[t] = new Thread(new Main()); threads[t].start(); @@ -32,6 +33,19 @@ public class Main implements Runnable { System.out.println("Finishing"); } + static void test_getStackTraces() { + // Check all the current threads for positive IDs. + Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); + for (Map.Entry<Thread, StackTraceElement[]> pair : map.entrySet()) { + Thread thread = pair.getKey(); + // Expect empty stack trace since we do not support suspending the GC thread for + // obtaining stack traces. See b/28261069. + if (thread.getName().equals("HeapTaskDaemon")) { + System.out.println(thread.getName() + " depth " + pair.getValue().length); + } + } + } + public void test_getId() { if (Thread.currentThread().getId() <= 0) { System.out.println("current thread's ID is not positive"); diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc index 60a31bdd2d..f74c1fc2ea 100644 --- a/test/903-hello-tagging/tagging.cc +++ b/test/903-hello-tagging/tagging.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "tagging.h" - #include <iostream> #include <pthread.h> #include <stdio.h> @@ -141,18 +139,6 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTaggedObjects(JNIEnv* env return resultArray; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test903HelloTagging } // namespace art diff --git a/test/903-hello-tagging/tagging.h b/test/903-hello-tagging/tagging.h deleted file mode 100644 index f062d44880..0000000000 --- a/test/903-hello-tagging/tagging.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_903_HELLO_TAGGING_TAGGING_H_ -#define ART_TEST_903_HELLO_TAGGING_TAGGING_H_ - -#include <jni.h> - -namespace art { -namespace Test903HelloTagging { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test903HelloTagging -} // namespace art - -#endif // ART_TEST_903_HELLO_TAGGING_TAGGING_H_ diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index f993606b42..95eab0c6cc 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "tracking.h" - #include <iostream> #include <pthread.h> #include <stdio.h> @@ -89,19 +87,6 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env } } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr); - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test904ObjectAllocation } // namespace art diff --git a/test/904-object-allocation/tracking.h b/test/904-object-allocation/tracking.h deleted file mode 100644 index 21c1837523..0000000000 --- a/test/904-object-allocation/tracking.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_904_OBJECT_ALLOCATION_TRACKING_H_ -#define ART_TEST_904_OBJECT_ALLOCATION_TRACKING_H_ - -#include <jni.h> - -namespace art { -namespace Test904ObjectAllocation { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test904ObjectAllocation -} // namespace art - -#endif // ART_TEST_904_OBJECT_ALLOCATION_TRACKING_H_ diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc index 7f295accb2..7b26d79edb 100644 --- a/test/905-object-free/tracking_free.cc +++ b/test/905-object-free/tracking_free.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "tracking_free.h" - #include <iostream> #include <pthread.h> #include <stdio.h> @@ -82,17 +80,5 @@ extern "C" JNIEXPORT jlongArray JNICALL Java_Main_getCollectedTags(JNIEnv* env, return ret; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test905ObjectFree } // namespace art diff --git a/test/905-object-free/tracking_free.h b/test/905-object-free/tracking_free.h deleted file mode 100644 index ba4aa43ffe..0000000000 --- a/test/905-object-free/tracking_free.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_905_OBJECT_FREE_TRACKING_FREE_H_ -#define ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_ - -#include <jni.h> - -namespace art { -namespace Test905ObjectFree { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test905ObjectFree -} // namespace art - -#endif // ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_ diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc index a2fd59128f..1362d470e6 100644 --- a/test/906-iterate-heap/iterate_heap.cc +++ b/test/906-iterate-heap/iterate_heap.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "iterate_heap.h" - #include <iostream> #include <pthread.h> #include <stdio.h> @@ -174,17 +172,5 @@ extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env AT Run(heap_filter, klass_filter, &config); } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test906IterateHeap } // namespace art diff --git a/test/906-iterate-heap/iterate_heap.h b/test/906-iterate-heap/iterate_heap.h deleted file mode 100644 index f25cdbaf49..0000000000 --- a/test/906-iterate-heap/iterate_heap.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_906_ITERATE_HEAP_ITERATE_HEAP_H_ -#define ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_ - -#include <jni.h> - -namespace art { -namespace Test906IterateHeap { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test906IterateHeap -} // namespace art - -#endif // ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_ diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc index 36d33b63cc..5bda7ebac8 100644 --- a/test/907-get-loaded-classes/get_loaded_classes.cc +++ b/test/907-get-loaded-classes/get_loaded_classes.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "get_loaded_classes.h" - #include <iostream> #include <pthread.h> #include <stdio.h> @@ -65,17 +63,5 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getLoadedClasses( return ret; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test907GetLoadedClasses } // namespace art diff --git a/test/907-get-loaded-classes/get_loaded_classes.h b/test/907-get-loaded-classes/get_loaded_classes.h deleted file mode 100644 index 4d27f898cc..0000000000 --- a/test/907-get-loaded-classes/get_loaded_classes.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_907_GET_LOADED_CLASSES_GET_LOADED_CLASSES_H_ -#define ART_TEST_907_GET_LOADED_CLASSES_GET_LOADED_CLASSES_H_ - -#include <jni.h> - -namespace art { -namespace Test907GetLoadedClasses { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test907GetLoadedClasses -} // namespace art - -#endif // ART_TEST_907_GET_LOADED_CLASSES_GET_LOADED_CLASSES_H_ diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc index 1fab79dcb1..59801ff648 100644 --- a/test/908-gc-start-finish/gc_callbacks.cc +++ b/test/908-gc-start-finish/gc_callbacks.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "gc_callbacks.h" - #include <stdio.h> #include <string.h> @@ -94,17 +92,5 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getGcFinishes(JNIEnv* env ATTRIBUTE_ return result; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test908GcStartFinish } // namespace art diff --git a/test/908-gc-start-finish/gc_callbacks.h b/test/908-gc-start-finish/gc_callbacks.h deleted file mode 100644 index 177a4eb7b2..0000000000 --- a/test/908-gc-start-finish/gc_callbacks.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_908_GC_START_FINISH_GC_CALLBACKS_H_ -#define ART_TEST_908_GC_START_FINISH_GC_CALLBACKS_H_ - -#include <jni.h> - -namespace art { -namespace Test908GcStartFinish { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test908GcStartFinish -} // namespace art - -#endif // ART_TEST_908_GC_START_FINISH_GC_CALLBACKS_H_ diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt index eacc595aaf..c0bccd6486 100644 --- a/test/909-attach-agent/expected.txt +++ b/test/909-attach-agent/expected.txt @@ -1,3 +1,11 @@ Hello, world! Attached Agent for test 909-attach-agent Goodbye! +Hello, world! +Attached Agent for test 909-attach-agent +Goodbye! +Hello, world! +java.io.IOException: Process is not debuggable. + at dalvik.system.VMDebug.attachAgent(Native Method) + at Main.main(Main.java:27) +Goodbye! diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run index aed6e83d67..985341bd4f 100755 --- a/test/909-attach-agent/run +++ b/test/909-attach-agent/run @@ -24,4 +24,14 @@ fi ./default-run "$@" --experimental agents \ --experimental runtime-plugins \ --android-runtime-option -Xplugin:${plugin} \ + --android-runtime-option -Xfully-deoptable \ + --args agent:${agent}=909-attach-agent + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --android-runtime-option -Xfully-deoptable \ + --args agent:${agent}=909-attach-agent + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ --args agent:${agent}=909-attach-agent diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc index fa9679db4b..f60fabb1df 100644 --- a/test/910-methods/methods.cc +++ b/test/910-methods/methods.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "methods.h" - #include <stdio.h> #include "base/macros.h" @@ -207,17 +205,5 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodSynthetic( return is_synthetic; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test910Methods } // namespace art diff --git a/test/910-methods/methods.h b/test/910-methods/methods.h deleted file mode 100644 index 93d18741ed..0000000000 --- a/test/910-methods/methods.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_910_METHODS_METHODS_H_ -#define ART_TEST_910_METHODS_METHODS_H_ - -#include <jni.h> - -namespace art { -namespace Test910Methods { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test910Methods -} // namespace art - -#endif // ART_TEST_910_METHODS_METHODS_H_ diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index f8c97ce475..e40698acc5 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -4,72 +4,72 @@ From top --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 - print (Ljava/lang/Thread;II)V 0 124 - printOrWait (IILMain$ControlData;)V 6 151 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - doTest ()V 38 34 - main ([Ljava/lang/String;)V 6 24 ---------- - print (Ljava/lang/Thread;II)V 0 124 - printOrWait (IILMain$ControlData;)V 6 151 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - doTest ()V 42 35 - main ([Ljava/lang/String;)V 6 24 + print (Ljava/lang/Thread;II)V 0 183 + printOrWait (IILMain$ControlData;)V 6 246 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + doTest ()V 38 41 + main ([Ljava/lang/String;)V 6 27 +--------- + print (Ljava/lang/Thread;II)V 0 183 + printOrWait (IILMain$ControlData;)V 6 246 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + doTest ()V 42 42 + main ([Ljava/lang/String;)V 6 27 --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 - print (Ljava/lang/Thread;II)V 0 124 - printOrWait (IILMain$ControlData;)V 6 151 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 ---------- - printOrWait (IILMain$ControlData;)V 6 151 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + print (Ljava/lang/Thread;II)V 0 183 + printOrWait (IILMain$ControlData;)V 6 246 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 +--------- + printOrWait (IILMain$ControlData;)V 6 246 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 From bottom --------- - main ([Ljava/lang/String;)V 6 24 + main ([Ljava/lang/String;)V 6 27 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - doTest ()V 65 41 - main ([Ljava/lang/String;)V 6 24 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + doTest ()V 65 48 + main ([Ljava/lang/String;)V 6 27 --------- - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 ################################ ### Other thread (suspended) ### @@ -77,132 +77,519 @@ From bottom From top --------- wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 157 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - run ()V 4 54 ---------- - printOrWait (IILMain$ControlData;)V 24 157 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - run ()V 4 54 ---------- - wait ()V -1 -2 - printOrWait (IILMain$ControlData;)V 24 157 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 ---------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 61 +--------- + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 61 +--------- + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 +--------- + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 From bottom --------- - run ()V 4 54 + run ()V 4 61 --------- - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - run ()V 4 54 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 61 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 ########################### ### Other thread (live) ### ########################### From top --------- - printOrWait (IILMain$ControlData;)V 44 164 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - run ()V 4 88 ---------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - run ()V 4 88 ---------- - printOrWait (IILMain$ControlData;)V 44 164 - baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 ---------- - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 + printOrWait (IILMain$ControlData;)V 44 259 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 95 +--------- + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 95 +--------- + printOrWait (IILMain$ControlData;)V 44 259 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 +--------- + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 From bottom --------- - run ()V 4 88 + run ()V 4 95 +--------- + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 95 +--------- + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + +################################ +### Other threads (suspended) ### +################################ +--------- +FinalizerDaemon +<not printed> +--------- +FinalizerWatchdogDaemon +<not printed> +--------- +HeapTaskDaemon +<not printed> +--------- +ReferenceQueueDaemon +<not printed> +--------- +Signal Catcher + +--------- +Thread-10 + +--------- +Thread-11 + +--------- +Thread-12 + +--------- +Thread-13 + +--------- +Thread-4 + +--------- +Thread-5 + +--------- +Thread-6 + +--------- +Thread-7 + +--------- +Thread-8 + +--------- +Thread-9 + +--------- +main + +--------- +FinalizerDaemon +<not printed> +--------- +FinalizerWatchdogDaemon +<not printed> +--------- +HeapTaskDaemon +<not printed> +--------- +ReferenceQueueDaemon +<not printed> +--------- +Signal Catcher + +--------- +Thread-10 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-11 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-12 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-13 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-4 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-5 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-6 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-7 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-8 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +Thread-9 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + +--------- +main + getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 + printAll (I)V 0 219 + doTestAllStackTraces ()V 107 156 + main ([Ljava/lang/String;)V 15 31 + +--------- +FinalizerDaemon +<not printed> +--------- +FinalizerWatchdogDaemon +<not printed> +--------- +HeapTaskDaemon +<not printed> +--------- +ReferenceQueueDaemon +<not printed> +--------- +Signal Catcher + +--------- +Thread-10 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-11 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + --------- - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - run ()V 4 88 +Thread-12 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 - foo (IIILMain$ControlData;)I 0 131 - baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 - bar (IIILMain$ControlData;)J 0 136 +Thread-13 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-4 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-5 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-6 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-7 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-8 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +Thread-9 + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 252 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239 + bar (IIILMain$ControlData;)J 0 231 + foo (IIILMain$ControlData;)I 0 226 + run ()V 4 144 + +--------- +main + getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 + printAll (I)V 0 219 + doTestAllStackTraces ()V 112 158 + main ([Ljava/lang/String;)V 15 31 + +Done diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java index 722bee8056..3479abbeae 100644 --- a/test/911-get-stack-trace/src/Main.java +++ b/test/911-get-stack-trace/src/Main.java @@ -14,7 +14,10 @@ * limitations under the License. */ +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CountDownLatch; public class Main { @@ -24,6 +27,10 @@ public class Main { doTest(); doTestOtherThreadWait(); doTestOtherThreadBusyLoop(); + + doTestAllStackTraces(); + + System.out.println("Done"); } public static void doTest() throws Exception { @@ -109,6 +116,58 @@ public class Main { t.join(); } + private final static List<Object> RETAIN = new ArrayList<Object>(); + + public static void doTestAllStackTraces() throws Exception { + System.out.println(); + System.out.println("################################"); + System.out.println("### Other threads (suspended) ###"); + System.out.println("################################"); + + // Also create an unstarted and a dead thread. + RETAIN.add(new Thread()); + Thread deadThread = new Thread(); + RETAIN.add(deadThread); + deadThread.start(); + deadThread.join(); + + final int N = 10; + + final ControlData data = new ControlData(N); + data.waitFor = new Object(); + + Thread threads[] = new Thread[N]; + + for (int i = 0; i < N; i++) { + Thread t = new Thread() { + public void run() { + Recurse.foo(4, 0, 0, data); + } + }; + t.start(); + threads[i] = t; + } + data.reached.await(); + Thread.yield(); + Thread.sleep(500); // A little bit of time... + + printAll(0); + + printAll(5); + + printAll(25); + + // Let the thread make progress and die. + synchronized(data.waitFor) { + data.waitFor.notifyAll(); + } + for (int i = 0; i < N; i++) { + threads[i].join(); + } + + RETAIN.clear(); + } + public static void print(String[][] stack) { System.out.println("---------"); for (String[] stackElement : stack) { @@ -124,6 +183,42 @@ public class Main { print(getStackTrace(t, start, max)); } + public static void printAll(Object[][] stacks) { + List<String> stringified = new ArrayList<String>(stacks.length); + + for (Object[] stackInfo : stacks) { + Thread t = (Thread)stackInfo[0]; + String name = (t != null) ? t.getName() : "null"; + String stackSerialization; + if (name.contains("Daemon")) { + // Do not print daemon stacks, as they're non-deterministic. + stackSerialization = "<not printed>"; + } else { + StringBuilder sb = new StringBuilder(); + for (String[] stackElement : (String[][])stackInfo[1]) { + for (String part : stackElement) { + sb.append(' '); + sb.append(part); + } + sb.append('\n'); + } + stackSerialization = sb.toString(); + } + stringified.add(name + "\n" + stackSerialization); + } + + Collections.sort(stringified); + + for (String s : stringified) { + System.out.println("---------"); + System.out.println(s); + } + } + + public static void printAll(int max) { + printAll(getAllStackTraces(max)); + } + // Wrap generated stack traces into a class to separate them nicely. public static class Recurse { @@ -170,10 +265,22 @@ public class Main { } public static class ControlData { - CountDownLatch reached = new CountDownLatch(1); + CountDownLatch reached; Object waitFor = null; volatile boolean stop = false; + + public ControlData() { + this(1); + } + + public ControlData(int latchCount) { + reached = new CountDownLatch(latchCount); + } } public static native String[][] getStackTrace(Thread thread, int start, int max); + // Get all stack traces. This will return an array with an element for each thread. The element + // is an array itself with the first element being the thread, and the second element a nested + // String array as in getStackTrace. + public static native Object[][] getAllStackTraces(int max); } diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index b3e8bc3b1f..57d4f6d2ee 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "stack_trace.h" - #include <inttypes.h> #include <memory> #include <stdio.h> @@ -52,22 +50,9 @@ static jint FindLineNumber(jint line_number_count, return line_number; } -extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( - JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) { - std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); - - jint count; - { - jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count); - if (result != JVMTI_ERROR_NONE) { - char* err; - jvmti_env->GetErrorName(result, &err); - printf("Failure running GetStackTrace: %s\n", err); - jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); - return nullptr; - } - } - +static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env, + jvmtiFrameInfo* frames, + jint count) { auto callback = [&](jint method_index) -> jobjectArray { char* name; char* sig; @@ -142,16 +127,57 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( return CreateObjectArray(env, count, "[Ljava/lang/String;", callback); } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) { + std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); + + jint count; + { + jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count); + if (result != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result, &err); + printf("Failure running GetStackTrace: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); + return nullptr; + } + } + + return TranslateJvmtiFrameInfoArray(env, frames.get(), count); +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllStackTraces( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint max) { + std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); + + jint thread_count; + jvmtiStackInfo* stack_infos; + { + jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count); + if (result != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result, &err); + printf("Failure running GetAllStackTraces: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); + return nullptr; + } } - SetAllCapabilities(jvmti_env); - return 0; + + auto callback = [&](jint thread_index) -> jobject { + auto inner_callback = [&](jint index) -> jobject { + if (index == 0) { + return stack_infos[thread_index].thread; + } else { + return TranslateJvmtiFrameInfoArray(env, + stack_infos[thread_index].frame_buffer, + stack_infos[thread_index].frame_count); + } + }; + return CreateObjectArray(env, 2, "java/lang/Object", inner_callback); + }; + jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos)); + return ret; } } // namespace Test911GetStackTrace diff --git a/test/911-get-stack-trace/stack_trace.h b/test/911-get-stack-trace/stack_trace.h deleted file mode 100644 index eba2a91da1..0000000000 --- a/test/911-get-stack-trace/stack_trace.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_911_GET_STACK_TRACE_STACK_TRACE_H_ -#define ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_ - -#include <jni.h> - -namespace art { -namespace Test911GetStackTrace { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test911GetStackTrace -} // namespace art - -#endif // ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_ diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index 38a4f0e337..69301c7925 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "classes.h" - #include <stdio.h> #include "base/macros.h" @@ -224,17 +222,5 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader( return classloader; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/classes.h b/test/912-classes/classes.h deleted file mode 100644 index 62fb203356..0000000000 --- a/test/912-classes/classes.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_912_CLASSES_CLASSES_H_ -#define ART_TEST_912_CLASSES_CLASSES_H_ - -#include <jni.h> - -namespace art { -namespace Test912Classes { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test912Classes -} // namespace art - -#endif // ART_TEST_912_CLASSES_CLASSES_H_ diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index 0b232af0df..67599192cf 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "heaps.h" - #include <inttypes.h> #include <stdio.h> #include <string.h> @@ -495,17 +493,5 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_followReferences(JNIEnv* env return ret; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test913Heaps } // namespace art diff --git a/test/913-heaps/heaps.h b/test/913-heaps/heaps.h deleted file mode 100644 index bd828aca33..0000000000 --- a/test/913-heaps/heaps.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_913_HEAPS_HEAPS_H_ -#define ART_TEST_913_HEAPS_HEAPS_H_ - -#include <jni.h> - -namespace art { -namespace Test913Heaps { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test913Heaps -} // namespace art - -#endif // ART_TEST_913_HEAPS_HEAPS_H_ diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc index 4d2b34b94e..7d29912f47 100644 --- a/test/918-fields/fields.cc +++ b/test/918-fields/fields.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "fields.h" - #include <stdio.h> #include "base/macros.h" @@ -132,17 +130,5 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isFieldSynthetic( return synth; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test918Fields } // namespace art diff --git a/test/918-fields/fields.h b/test/918-fields/fields.h deleted file mode 100644 index 89bd1614d5..0000000000 --- a/test/918-fields/fields.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_918_FIELDS_FIELDS_H_ -#define ART_TEST_918_FIELDS_FIELDS_H_ - -#include <jni.h> - -namespace art { -namespace Test918Fields { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test918Fields -} // namespace art - -#endif // ART_TEST_918_FIELDS_FIELDS_H_ diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc index 886dd0e673..0553a9d007 100644 --- a/test/920-objects/objects.cc +++ b/test/920-objects/objects.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "objects.h" - #include <stdio.h> #include "base/macros.h" @@ -61,17 +59,5 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getObjectHashCode( return hash; } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test920Objects } // namespace art diff --git a/test/920-objects/objects.h b/test/920-objects/objects.h deleted file mode 100644 index 5f21e7b7cb..0000000000 --- a/test/920-objects/objects.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_920_OBJECTS_OBJECTS_H_ -#define ART_TEST_920_OBJECTS_OBJECTS_H_ - -#include <jni.h> - -namespace art { -namespace Test920Objects { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test920Objects -} // namespace art - -#endif // ART_TEST_920_OBJECTS_OBJECTS_H_ diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc index b1e7fce3b5..cb732c74f1 100644 --- a/test/922-properties/properties.cc +++ b/test/922-properties/properties.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "properties.h" - #include <stdio.h> #include "base/macros.h" @@ -91,17 +89,5 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setSystemProperty( } } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test922Properties } // namespace art diff --git a/test/922-properties/properties.h b/test/922-properties/properties.h deleted file mode 100644 index 84feb10758..0000000000 --- a/test/922-properties/properties.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -#ifndef ART_TEST_922_PROPERTIES_PROPERTIES_H_ -#define ART_TEST_922_PROPERTIES_PROPERTIES_H_ - -#include <jni.h> - -namespace art { -namespace Test922Properties { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test922Properties -} // namespace art - -#endif // ART_TEST_922_PROPERTIES_PROPERTIES_H_ diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc index 2aa36cbdba..4baa530ec2 100644 --- a/test/923-monitors/monitors.cc +++ b/test/923-monitors/monitors.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "monitors.h" - #include <stdio.h> #include "base/macros.h" @@ -84,17 +82,5 @@ extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotifyAll( JvmtiErrorToException(env, result); } -// Don't do anything -jint OnLoad(JavaVM* vm, - char* options ATTRIBUTE_UNUSED, - void* reserved ATTRIBUTE_UNUSED) { - if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { - printf("Unable to get jvmti env!\n"); - return 1; - } - SetAllCapabilities(jvmti_env); - return 0; -} - } // namespace Test923Monitors } // namespace art diff --git a/test/923-monitors/monitors.h b/test/923-monitors/monitors.h deleted file mode 100644 index 14cd5cd633..0000000000 --- a/test/923-monitors/monitors.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -#ifndef ART_TEST_923_MONITORS_MONITORS_H_ -#define ART_TEST_923_MONITORS_MONITORS_H_ - -#include <jni.h> - -namespace art { -namespace Test923Monitors { - -jint OnLoad(JavaVM* vm, char* options, void* reserved); - -} // namespace Test923Monitors -} // namespace art - -#endif // ART_TEST_923_MONITORS_MONITORS_H_ diff --git a/test/924-threads/build b/test/924-threads/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/924-threads/build @@ -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-build "$@" --experimental agents diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt new file mode 100644 index 0000000000..54065223cf --- /dev/null +++ b/test/924-threads/expected.txt @@ -0,0 +1,30 @@ +currentThread OK +main +5 +false +java.lang.ThreadGroup[name=main,maxpri=10] +class dalvik.system.PathClassLoader +main +5 +false +java.lang.ThreadGroup[name=main,maxpri=10] +class dalvik.system.PathClassLoader +Daemon Thread +5 +true +java.lang.ThreadGroup[name=main,maxpri=10] +class dalvik.system.PathClassLoader +Daemon Thread +5 +true +java.lang.ThreadGroup[name=main,maxpri=10] +class dalvik.system.PathClassLoader +5 +5 +0 = NEW +191 = ALIVE|WAITING_INDEFINITELY|WAITING|IN_OBJECT_WAIT +1a1 = ALIVE|WAITING_WITH_TIMEOUT|WAITING|IN_OBJECT_WAIT +401 = ALIVE|BLOCKED_ON_MONITOR_ENTER +e1 = ALIVE|WAITING_WITH_TIMEOUT|SLEEPING|WAITING +5 = ALIVE|RUNNABLE +2 = TERMINATED diff --git a/test/924-threads/info.txt b/test/924-threads/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/924-threads/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/924-threads/run b/test/924-threads/run new file mode 100755 index 0000000000..4379349cb2 --- /dev/null +++ b/test/924-threads/run @@ -0,0 +1,19 @@ +#!/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 "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java new file mode 100644 index 0000000000..048766604f --- /dev/null +++ b/test/924-threads/src/Main.java @@ -0,0 +1,216 @@ +/* + * 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. + */ + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + Thread t1 = Thread.currentThread(); + Thread t2 = getCurrentThread(); + + if (t1 != t2) { + throw new RuntimeException("Expected " + t1 + " but got " + t2); + } + System.out.println("currentThread OK"); + + printThreadInfo(t1); + printThreadInfo(null); + + Thread t3 = new Thread("Daemon Thread"); + t3.setDaemon(true); + // Do not start this thread, yet. + printThreadInfo(t3); + // Start, and wait for it to die. + t3.start(); + t3.join(); + Thread.sleep(500); // Wait a little bit. + // Thread has died, check that we can still get info. + printThreadInfo(t3); + + doStateTests(); + } + + private static class Holder { + volatile boolean flag = false; + } + + private static void doStateTests() throws Exception { + System.out.println(Integer.toHexString(getThreadState(null))); + System.out.println(Integer.toHexString(getThreadState(Thread.currentThread()))); + + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + final CountDownLatch cdl3_1 = new CountDownLatch(1); + final CountDownLatch cdl3_2 = new CountDownLatch(1); + final CountDownLatch cdl4 = new CountDownLatch(1); + final CountDownLatch cdl5 = new CountDownLatch(1); + final Holder h = new Holder(); + Runnable r = new Runnable() { + @Override + public void run() { + try { + cdl1.countDown(); + synchronized(cdl1) { + cdl1.wait(); + } + + cdl2.countDown(); + synchronized(cdl2) { + cdl2.wait(1000); // Wait a second. + } + + cdl3_1.await(); + cdl3_2.countDown(); + synchronized(cdl3_2) { + // Nothing, just wanted to block on cdl3. + } + + cdl4.countDown(); + Thread.sleep(1000); + + cdl5.countDown(); + while (!h.flag) { + // Busy-loop. + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + + Thread t = new Thread(r); + printThreadState(t); + t.start(); + + // Waiting. + cdl1.await(); + Thread.yield(); + Thread.sleep(100); + printThreadState(t); + synchronized(cdl1) { + cdl1.notifyAll(); + } + + // Timed waiting. + cdl2.await(); + Thread.yield(); + Thread.sleep(100); + printThreadState(t); + synchronized(cdl2) { + cdl2.notifyAll(); + } + + // Blocked on monitor. + synchronized(cdl3_2) { + cdl3_1.countDown(); + cdl3_2.await(); + Thread.yield(); + Thread.sleep(100); + printThreadState(t); + } + + // Sleeping. + cdl4.await(); + Thread.yield(); + Thread.sleep(100); + printThreadState(t); + + // Running. + cdl5.await(); + Thread.yield(); + Thread.sleep(100); + printThreadState(t); + h.flag = true; + + // Dying. + t.join(); + Thread.yield(); + Thread.sleep(100); + + printThreadState(t); + } + + private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>(); + private final static List<Integer> STATE_KEYS = new ArrayList<Integer>(); + static { + STATE_NAMES.put(0x1, "ALIVE"); + STATE_NAMES.put(0x2, "TERMINATED"); + STATE_NAMES.put(0x4, "RUNNABLE"); + STATE_NAMES.put(0x400, "BLOCKED_ON_MONITOR_ENTER"); + STATE_NAMES.put(0x80, "WAITING"); + STATE_NAMES.put(0x10, "WAITING_INDEFINITELY"); + STATE_NAMES.put(0x20, "WAITING_WITH_TIMEOUT"); + STATE_NAMES.put(0x40, "SLEEPING"); + STATE_NAMES.put(0x100, "IN_OBJECT_WAIT"); + STATE_NAMES.put(0x200, "PARKED"); + STATE_NAMES.put(0x100000, "SUSPENDED"); + STATE_NAMES.put(0x200000, "INTERRUPTED"); + STATE_NAMES.put(0x400000, "IN_NATIVE"); + STATE_KEYS.addAll(STATE_NAMES.keySet()); + Collections.sort(STATE_KEYS); + } + + private static void printThreadState(Thread t) { + int state = getThreadState(t); + + StringBuilder sb = new StringBuilder(); + + for (Integer i : STATE_KEYS) { + if ((state & i) != 0) { + if (sb.length()>0) { + sb.append('|'); + } + sb.append(STATE_NAMES.get(i)); + } + } + + if (sb.length() == 0) { + sb.append("NEW"); + } + + System.out.println(Integer.toHexString(state) + " = " + sb.toString()); + } + + private static void printThreadInfo(Thread t) { + Object[] threadInfo = getThreadInfo(t); + if (threadInfo == null || threadInfo.length != 5) { + System.out.println(Arrays.toString(threadInfo)); + throw new RuntimeException("threadInfo length wrong"); + } + + System.out.println(threadInfo[0]); // Name + System.out.println(threadInfo[1]); // Priority + System.out.println(threadInfo[2]); // Daemon + System.out.println(threadInfo[3]); // Threadgroup + System.out.println(threadInfo[4] == null ? "null" : threadInfo[4].getClass()); // Context CL. + } + + private static native Thread getCurrentThread(); + private static native Object[] getThreadInfo(Thread t); + private static native int getThreadState(Thread t); +} diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc new file mode 100644 index 0000000000..4abf8fcf93 --- /dev/null +++ b/test/924-threads/threads.cc @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#include <stdio.h> + +#include "android-base/stringprintf.h" +#include "base/macros.h" +#include "base/logging.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedLocalRef.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test924Threads { + +// private static native Thread getCurrentThread(); +// private static native Object[] getThreadInfo(Thread t); + +extern "C" JNIEXPORT jthread JNICALL Java_Main_getCurrentThread( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jthread thread = nullptr; + jvmtiError result = jvmti_env->GetCurrentThread(&thread); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + return thread; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadInfo( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) { + jvmtiThreadInfo info; + memset(&info, 0, sizeof(jvmtiThreadInfo)); + + jvmtiError result = jvmti_env->GetThreadInfo(thread, &info); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint component_index) -> jobject { + switch (component_index) { + // The name. + case 0: + return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name); + + // The priority. Use a string for simplicity of construction. + case 1: + return env->NewStringUTF(android::base::StringPrintf("%d", info.priority).c_str()); + + // Whether it's a daemon. Use a string for simplicity of construction. + case 2: + return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false"); + + // The thread group; + case 3: + return env->NewLocalRef(info.thread_group); + + // The context classloader. + case 4: + return env->NewLocalRef(info.context_class_loader); + } + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + }; + jobjectArray ret = CreateObjectArray(env, 5, "java/lang/Object", callback); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name)); + if (info.thread_group != nullptr) { + env->DeleteLocalRef(info.thread_group); + } + if (info.context_class_loader != nullptr) { + env->DeleteLocalRef(info.context_class_loader); + } + + return ret; +} + +extern "C" JNIEXPORT jint JNICALL Java_Main_getThreadState( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) { + jint state; + jvmtiError result = jvmti_env->GetThreadState(thread, &state); + if (JvmtiErrorToException(env, result)) { + return 0; + } + return state; +} + +} // namespace Test924Threads +} // namespace art diff --git a/test/Android.bp b/test/Android.bp index a223c3aa29..b0f0e5a98d 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -264,6 +264,7 @@ art_cc_defaults { "920-objects/objects.cc", "922-properties/properties.cc", "923-monitors/monitors.cc", + "924-threads/threads.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index fd3a897dae..dd7876f09d 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -296,6 +296,7 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 921-hello-failure \ 922-properties \ 923-monitors \ + 924-threads \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -721,6 +722,16 @@ endif TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := +# Tests that check semantics for a non-debuggable app. +TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS := \ + 909-attach-agent \ + +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(PICTEST_TYPES),debuggable,$(TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES)) + +TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS := + # Tests incompatible with bisection bug search. Sorted by incompatibility reason. # 000 through 595 do not compile anything. 089 tests a build failure. 018 through 137 # run dalvikvm more than once. 115 and 088 assume they are always compiled. diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 33e132103e..8abd063a01 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -26,21 +26,7 @@ #include "common_helper.h" #include "901-hello-ti-agent/basics.h" -#include "903-hello-tagging/tagging.h" -#include "904-object-allocation/tracking.h" -#include "905-object-free/tracking_free.h" -#include "906-iterate-heap/iterate_heap.h" -#include "907-get-loaded-classes/get_loaded_classes.h" -#include "908-gc-start-finish/gc_callbacks.h" #include "909-attach-agent/attach.h" -#include "910-methods/methods.h" -#include "911-get-stack-trace/stack_trace.h" -#include "912-classes/classes.h" -#include "913-heaps/heaps.h" -#include "918-fields/fields.h" -#include "920-objects/objects.h" -#include "922-properties/properties.h" -#include "923-monitors/monitors.h" namespace art { @@ -55,31 +41,30 @@ struct AgentLib { OnAttach attach; }; -// A list of all the agents we have for testing. +// A trivial OnLoad implementation that only initializes the global jvmti_env. +static jint MinimalOnLoad(JavaVM* vm, + char* options ATTRIBUTE_UNUSED, + void* reserved ATTRIBUTE_UNUSED) { + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + SetAllCapabilities(jvmti_env); + return 0; +} + +// A list of all non-standard the agents we have for testing. All other agents will use +// MinimalOnLoad. AgentLib agents[] = { { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr }, { "902-hello-transformation", common_redefine::OnLoad, nullptr }, - { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr }, - { "904-object-allocation", Test904ObjectAllocation::OnLoad, nullptr }, - { "905-object-free", Test905ObjectFree::OnLoad, nullptr }, - { "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr }, - { "907-get-loaded-classes", Test907GetLoadedClasses::OnLoad, nullptr }, - { "908-gc-start-finish", Test908GcStartFinish::OnLoad, nullptr }, { "909-attach-agent", nullptr, Test909AttachAgent::OnAttach }, - { "910-methods", Test910Methods::OnLoad, nullptr }, - { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr }, - { "912-classes", Test912Classes::OnLoad, nullptr }, - { "913-heaps", Test913Heaps::OnLoad, nullptr }, { "914-hello-obsolescence", common_redefine::OnLoad, nullptr }, { "915-obsolete-2", common_redefine::OnLoad, nullptr }, { "916-obsolete-jit", common_redefine::OnLoad, nullptr }, { "917-fields-transformation", common_redefine::OnLoad, nullptr }, - { "918-fields", Test918Fields::OnLoad, nullptr }, { "919-obsolete-fields", common_redefine::OnLoad, nullptr }, - { "920-objects", Test920Objects::OnLoad, nullptr }, { "921-hello-failure", common_redefine::OnLoad, nullptr }, - { "922-properties", Test922Properties::OnLoad, nullptr }, - { "923-monitors", Test923Monitors::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { @@ -120,18 +105,21 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* printf("Unable to find agent name in options: %s\n", options); return -1; } + + SetIsJVM(remaining_options); + AgentLib* lib = FindAgent(name_option); + OnLoad fn = nullptr; if (lib == nullptr) { - printf("Unable to find agent named: %s, add it to the list in test/ti-agent/common_load.cc\n", - name_option); - return -2; - } - if (lib->load == nullptr) { - printf("agent: %s does not include an OnLoad method.\n", name_option); - return -3; + fn = &MinimalOnLoad; + } else { + if (lib->load == nullptr) { + printf("agent: %s does not include an OnLoad method.\n", name_option); + return -3; + } + fn = lib->load; } - SetIsJVM(remaining_options); - return lib->load(vm, remaining_options, reserved); + return fn(vm, remaining_options, reserved); } extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { |