Merge "Revert "Revert "Refactor ClassLinker::LinkInterfaceMethods()."""
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index e1117e6..7b86339 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2406,16 +2406,29 @@
}
}
-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 5b8a557..844bb45 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -183,7 +183,8 @@
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 5b51399..0177e2a 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -187,7 +187,10 @@
// 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 ddcb6c0..85e6783 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -472,9 +472,15 @@
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 564f85b..6d52d5d 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -175,7 +175,8 @@
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 06ed029..f00da73 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -390,8 +390,13 @@
}
}
-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 02cf462..a6e2d61 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -188,7 +188,8 @@
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 f2aa5a7..cb9e7e2 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -765,8 +765,13 @@
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 4cebcc3..52b5e5f 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -166,7 +166,8 @@
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 081be96..c154836 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -203,7 +203,9 @@
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 a0eb197..734caea 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -129,8 +129,9 @@
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 @@
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 f80c43d..e0f28ad 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -566,7 +566,10 @@
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 e70b936..22fb83c 100644
--- a/runtime/mirror/object_reference-inl.h
+++ b/runtime/mirror/object_reference-inl.h
@@ -34,6 +34,15 @@
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 71f34c6..a96a120 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -94,6 +94,9 @@
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 893abd5..9c09275 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1361,8 +1361,10 @@
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 36825cb..268d71a 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 @@
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 b757b21..4bd21b4 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -29,6 +29,7 @@
"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 c52dd76..2629c9f 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 @@
}
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 @@
}
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 @@
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 579fb50..098cedb 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 @@
return true;
}
- art::ScopedObjectAccessAlreadyRunnable& soa;
std::vector<jvmtiFrameInfo> frames;
size_t start;
size_t stop;
@@ -99,10 +104,8 @@
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 @@
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 @@
}
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);
- }
- 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));
- }
- *count_ptr = static_cast<jint>(closure.frames.size());
- return ERR(NONE);
+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);
}
- // Frames from the bottom.
- if (collected_frames < static_cast<size_t>(-start_depth)) {
+ 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);
+ }
- 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);
+
+ 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();
+ }
+
+ 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);
+ }
+ }
+
+ // 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);
+ }
+
+ // 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 1931ed3..7619f98 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 0000000..e20f560
--- /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 0000000..b6ffbb5
--- /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 2086d70..df5fc5c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1364,6 +1364,39 @@
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 @@
// (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 134d8d0..aadf90d 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 9934bba..5aefd17 100644
--- a/test/129-ThreadGetId/src/Main.java
+++ b/test/129-ThreadGetId/src/Main.java
@@ -22,6 +22,7 @@
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 @@
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/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt
index eacc595..c0bccd6 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 aed6e83..985341b 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -24,4 +24,14 @@
./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/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index f8c97ce..e40698a 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 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 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 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
+ 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 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
+ 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 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 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 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 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 157
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
+ 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 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
+ 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
+ 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 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 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 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
+ 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 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
From bottom
---------
- run ()V 4 88
+ run ()V 4 95
---------
- 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
+ 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 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 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
+
+---------
+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
+
+---------
+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 722bee8..3479abb 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 @@
doTest();
doTestOtherThreadWait();
doTestOtherThreadBusyLoop();
+
+ doTestAllStackTraces();
+
+ System.out.println("Done");
}
public static void doTest() throws Exception {
@@ -109,6 +116,58 @@
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 @@
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 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 cca163b..57d4f6d 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -50,22 +50,9 @@
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;
@@ -140,5 +127,58 @@
return CreateObjectArray(env, count, "[Ljava/lang/String;", callback);
}
+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;
+ }
+ }
+
+ 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
} // namespace art
diff --git a/test/924-threads/build b/test/924-threads/build
new file mode 100755
index 0000000..898e2e5
--- /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 0000000..5406522
--- /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 0000000..875a5f6
--- /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 0000000..4379349
--- /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 0000000..0487666
--- /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 0000000..4abf8fc
--- /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 a223c3a..b0f0e5a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -264,6 +264,7 @@
"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 fd3a897..dd7876f 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -296,6 +296,7 @@
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 @@
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.