Various things.
This patch:
1. adds good-enough implementations for a couple of Class native methods we
need.
2. removes a fixed TODO from image_test.
3. adds missing null checks to the Thread native methods, and forwards
Thread.nativeCreate to Thread::Create.
4. stops jni_compiler from overwriting already-registered native methods
with the dynamic lookup stub.
5. adds workarounds to Thread::CreatePeer so we can cope better without
code, attempts to initialize thread groups correctly, dumps thread state
from the managed peer (where one exists), and implements Thread::DumpStack
for debugging (and SIGQUIT dumps).
Change-Id: I3281226dd22ec852dca5165748fc75a254904ac0
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 046fbbe..b4fb7aa 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -76,6 +76,7 @@
src/image_writer.cc \
src/indirect_reference_table.cc \
src/intern_table.cc \
+ src/java_lang_Class.cc \
src/java_lang_Object.cc \
src/java_lang_Runtime.cc \
src/java_lang_String.cc \
diff --git a/src/image_test.cc b/src/image_test.cc
index 0bd79db..02a7d69 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -71,8 +71,7 @@
ASSERT_TRUE(boot_space != NULL);
// enable to display maps to debug boot_base and boot_limit checking problems below
- // TODO: why does this dump show two attached threads?
- if (true) {
+ if (false) {
SignalCatcher::HandleSigQuit();
}
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
new file mode 100644
index 0000000..e068d2e
--- /dev/null
+++ b/src/java_lang_Class.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 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 "jni_internal.h"
+#include "class_linker.h"
+#include "object.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+namespace {
+
+jclass Class_getComponentType(JNIEnv* env, jobject javaThis) {
+ Class* c = Decode<Class*>(env, javaThis);
+ if (!c->IsArrayClass()) {
+ return NULL;
+ }
+
+ /*
+ * We can't just return c->GetComponentType(), because that gives
+ * us the base type (e.g. X[][][] returns X). If this is a multi-
+ * dimensional array, we have to do the lookup by name.
+ */
+ Class* result;
+ std::string descriptor(c->GetDescriptor()->ToModifiedUtf8());
+ if (descriptor[1] == '[') {
+ result = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str() + 1, c->GetClassLoader());
+ } else {
+ result = c->GetComponentType();
+ }
+ return AddLocalReference<jclass>(env, result);
+
+}
+
+jobjectArray Class_getDeclaredClasses(JNIEnv* env, jclass java_lang_Class_class, jclass c, jboolean publicOnly) {
+ UNIMPLEMENTED(WARNING);
+ return env->NewObjectArray(0, java_lang_Class_class, NULL);
+}
+
+static JNINativeMethod gMethods[] = {
+ //NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, desiredAssertionStatus, "()Z"),
+ //NATIVE_METHOD(Class, getClassLoader, "(Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
+ NATIVE_METHOD(Class, getComponentType, "()Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, getDeclaredAnnotation, "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+ //NATIVE_METHOD(Class, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"),
+ NATIVE_METHOD(Class, getDeclaredClasses, "(Ljava/lang/Class;Z)[Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, getDeclaredConstructorOrMethod, "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Member;"),
+ //NATIVE_METHOD(Class, getDeclaredConstructors, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;"),
+ //NATIVE_METHOD(Class, getDeclaredField, "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;"),
+ //NATIVE_METHOD(Class, getDeclaredFields, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;"),
+ //NATIVE_METHOD(Class, getDeclaredMethods, "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;"),
+ //NATIVE_METHOD(Class, getDeclaringClass, "()Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, getEnclosingClass, "()Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, getEnclosingConstructor, "()Ljava/lang/reflect/Constructor;"),
+ //NATIVE_METHOD(Class, getEnclosingMethod, "()Ljava/lang/reflect/Method;"),
+ //NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"),
+ //NATIVE_METHOD(Class, getInterfaces, "()[Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, getModifiers, "(Ljava/lang/Class;Z)I"),
+ //NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
+ //NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/Object;"),
+ //NATIVE_METHOD(Class, getSuperclass, "()Ljava/lang/Class;"),
+ //NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
+ //NATIVE_METHOD(Class, isAssignableFrom, "(Ljava/lang/Class;)Z"),
+ //NATIVE_METHOD(Class, isDeclaredAnnotationPresent, "(Ljava/lang/Class;)Z"),
+ //NATIVE_METHOD(Class, isInstance, "(Ljava/lang/Object;)Z"),
+ //NATIVE_METHOD(Class, isInterface, "()Z"),
+ //NATIVE_METHOD(Class, isPrimitive, "()Z"),
+ //NATIVE_METHOD(Class, newInstanceImpl, "()Ljava/lang/Object;"),
+};
+
+} // namespace
+
+void register_java_lang_Class(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "java/lang/Class", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/java_lang_Thread.cc b/src/java_lang_Thread.cc
index 44578ee..286a211 100644
--- a/src/java_lang_Thread.cc
+++ b/src/java_lang_Thread.cc
@@ -26,8 +26,7 @@
namespace {
jobject Thread_currentThread(JNIEnv* env, jclass) {
- Object* peer = Decode<Object*>(env, Thread::Current()->GetPeer());
- return AddLocalReference<jobject>(env, peer);
+ return AddLocalReference<jobject>(env, Thread::Current()->GetPeer());
}
jboolean Thread_interrupted(JNIEnv* env, jclass) {
@@ -37,19 +36,19 @@
jboolean Thread_isInterrupted(JNIEnv* env, jobject javaThread) {
ThreadListLock lock;
Thread* thread = Thread::FromManagedThread(env, javaThread);
- return thread->IsInterrupted();
+ return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
}
void Thread_nativeCreate(JNIEnv* env, jclass, jobject javaThread, jlong stackSize) {
- UNIMPLEMENTED(FATAL);
- //Object* threadObj = dvmDecodeIndirectRef(env, javaThread);
- //dvmCreateInterpThread(threadObj, (int) stackSize);
+ Object* managedThread = Decode<Object*>(env, javaThread);
+ Thread::Create(managedThread, stackSize);
}
jint Thread_nativeGetStatus(JNIEnv* env, jobject javaThread) {
ThreadListLock lock;
Thread* thread = Thread::FromManagedThread(env, javaThread);
- return static_cast<jint>(thread->GetState());
+ Thread::State state = (thread != NULL) ? thread->GetState() : Thread::kUnknown;
+ return static_cast<jint>(state);
}
jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject javaThread, jobject javaObject) {
@@ -93,7 +92,9 @@
void Thread_nativeSetPriority(JNIEnv* env, jobject javaThread, jint newPriority) {
ThreadListLock lock;
Thread* thread = Thread::FromManagedThread(env, javaThread);
- thread->SetNativePriority(newPriority);
+ if (thread != NULL) {
+ thread->SetNativePriority(newPriority);
+ }
}
/*
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 9da1ab0..11b99c8 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -76,7 +76,9 @@
jni_stub_ = x86::CreateJniStub();
}
}
- native_method->RegisterNative(jni_stub_->GetData());
+ if (!native_method->IsRegistered()) {
+ native_method->RegisterNative(jni_stub_->GetData());
+ }
// TODO: Need to make sure that the stub is copied into the image. I.e.,
// ByteArray* needs to be reachable either as a root or from the object graph.
diff --git a/src/object.h b/src/object.h
index 94adf60..70a9b33 100644
--- a/src/object.h
+++ b/src/object.h
@@ -903,6 +903,10 @@
return_pc_offset_in_bytes, false);
}
+ bool IsRegistered() {
+ return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_), false) != NULL;
+ }
+
void RegisterNative(const void* native_method) {
CHECK(IsNative());
CHECK(native_method != NULL);
diff --git a/src/runtime.cc b/src/runtime.cc
index 454a38c..a1157e6 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -413,7 +413,7 @@
//REGISTER(register_dalvik_system_VMRuntime);
REGISTER(register_dalvik_system_VMStack);
//REGISTER(register_dalvik_system_Zygote);
- //REGISTER(register_java_lang_Class);
+ REGISTER(register_java_lang_Class);
REGISTER(register_java_lang_Object);
REGISTER(register_java_lang_Runtime);
REGISTER(register_java_lang_String);
diff --git a/src/thread.cc b/src/thread.cc
index 312bb30..05b4b9b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -26,7 +26,7 @@
pthread_key_t Thread::pthread_key_self_;
// Temporary debugging hook for compiler.
-static void DebugMe(Method* method, uint32_t info) {
+void DebugMe(Method* method, uint32_t info) {
LOG(INFO) << "DebugMe";
if (method != NULL)
LOG(INFO) << PrettyMethod(method);
@@ -40,8 +40,7 @@
* callsite of the failed invoke-interface. See comments in
* compiler/runtime_support.S
*/
-extern "C" void artFailedInvokeInterface()
-{
+extern "C" void artFailedInvokeInterface() {
UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
}
@@ -62,19 +61,19 @@
}
// TODO: placeholder. This is what generated code will call to throw
-static void ThrowException(Thread* thread, Throwable* exception) {
- /*
- * exception may be NULL, in which case this routine should
- * throw NPE. NOTE: this is a convenience for generated code,
- * which previuosly did the null check inline and constructed
- * and threw a NPE if NULL. This routine responsible for setting
- * exception_ in thread.
- */
- UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
+void ThrowException(Thread* thread, Throwable* exception) {
+ /*
+ * exception may be NULL, in which case this routine should
+ * throw NPE. NOTE: this is a convenience for generated code,
+ * which previously did the null check inline and constructed
+ * and threw a NPE if NULL. This routine responsible for setting
+ * exception_ in thread.
+ */
+ UNIMPLEMENTED(FATAL) << "Unimplemented exception throw: " << PrettyType(exception);
}
// TODO: placeholder. Helper function to type
-static Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
+Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
/*
* Should initialize & fix up method->dex_cache_resolved_types_[].
* Returns initialized type. Does not return normally if an exception
@@ -86,7 +85,7 @@
}
// TODO: placeholder. Helper function to resolve virtual method
-static void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
+void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
/*
* Slow-path handler on invoke virtual method path in which
* base method is unresolved at compile-time. Doesn't need to
@@ -98,9 +97,7 @@
}
// TODO: placeholder. Helper function to alloc array for OP_FILLED_NEW_ARRAY
-static Array* CheckAndAllocFromCode(uint32_t type_index, Method* method,
- int32_t component_count)
-{
+Array* CheckAndAllocFromCode(uint32_t type_index, Method* method, int32_t component_count) {
/*
* Just a wrapper around Array::AllocFromCode() that additionally
* throws a runtime exception "bad Filled array req" for 'D' and 'J'.
@@ -110,7 +107,7 @@
}
// TODO: placeholder (throw on failure)
-static void CheckCastFromCode(const Class* a, const Class* b) {
+void CheckCastFromCode(const Class* a, const Class* b) {
if (a->IsAssignableFrom(b)) {
return;
}
@@ -118,19 +115,19 @@
}
// TODO: placeholder
-static void UnlockObjectFromCode(Thread* thread, Object* obj) {
+void UnlockObjectFromCode(Thread* thread, Object* obj) {
// TODO: throw and unwind if lock not held
// TODO: throw and unwind on NPE
obj->MonitorExit(thread);
}
// TODO: placeholder
-static void LockObjectFromCode(Thread* thread, Object* obj) {
+void LockObjectFromCode(Thread* thread, Object* obj) {
obj->MonitorEnter(thread);
}
// TODO: placeholder
-static void CheckSuspendFromCode(Thread* thread) {
+void CheckSuspendFromCode(Thread* thread) {
/*
* Code is at a safe point, suspend if needed.
* Also, this is where a pending safepoint callback
@@ -139,51 +136,51 @@
}
// TODO: placeholder
-static void StackOverflowFromCode(Method* method) {
- //NOTE: to save code space, this handler needs to look up its own Thread*
- UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
+void StackOverflowFromCode(Method* method) {
+ //NOTE: to save code space, this handler needs to look up its own Thread*
+ UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
}
// TODO: placeholder
-static void ThrowNullPointerFromCode() {
- //NOTE: to save code space, this handler must look up caller's Method*
- UNIMPLEMENTED(FATAL) << "Null pointer exception";
+void ThrowNullPointerFromCode() {
+ Thread::Current()->Dump(std::cerr);
+ //NOTE: to save code space, this handler must look up caller's Method*
+ UNIMPLEMENTED(FATAL) << "Null pointer exception";
}
// TODO: placeholder
-static void ThrowDivZeroFromCode() {
- UNIMPLEMENTED(FATAL) << "Divide by zero";
+void ThrowDivZeroFromCode() {
+ UNIMPLEMENTED(FATAL) << "Divide by zero";
}
// TODO: placeholder
-static void ThrowArrayBoundsFromCode(int32_t index, int32_t limit) {
- UNIMPLEMENTED(FATAL) << "Bound check exception, idx: " << index <<
- ", limit: " << limit;
+void ThrowArrayBoundsFromCode(int32_t index, int32_t limit) {
+ UNIMPLEMENTED(FATAL) << "Bound check exception, idx: " << index << ", limit: " << limit;
}
// TODO: placeholder
-static void ThrowVerificationErrorFromCode(int32_t src1, int32_t ref) {
+void ThrowVerificationErrorFromCode(int32_t src1, int32_t ref) {
UNIMPLEMENTED(FATAL) << "Verification error, src1: " << src1 <<
" ref: " << ref;
}
// TODO: placeholder
-static void ThrowNegArraySizeFromCode(int32_t index) {
+void ThrowNegArraySizeFromCode(int32_t index) {
UNIMPLEMENTED(FATAL) << "Negative array size: " << index;
}
// TODO: placeholder
-static void ThrowInternalErrorFromCode(int32_t errnum) {
+void ThrowInternalErrorFromCode(int32_t errnum) {
UNIMPLEMENTED(FATAL) << "Internal error: " << errnum;
}
// TODO: placeholder
-static void ThrowRuntimeExceptionFromCode(int32_t errnum) {
+void ThrowRuntimeExceptionFromCode(int32_t errnum) {
UNIMPLEMENTED(FATAL) << "Internal error: " << errnum;
}
// TODO: placeholder
-static void ThrowNoSuchMethodFromCode(int32_t method_idx) {
+void ThrowNoSuchMethodFromCode(int32_t method_idx) {
UNIMPLEMENTED(FATAL) << "No such method, idx: " << method_idx;
}
@@ -204,8 +201,7 @@
* ubyte data[size*width] table of data values (may contain a single-byte
* padding at the end)
*/
-static void HandleFillArrayDataFromCode(Array* array, const uint16_t* table)
-{
+void HandleFillArrayDataFromCode(Array* array, const uint16_t* table) {
uint32_t size = (uint32_t)table[2] | (((uint32_t)table[3]) << 16);
uint32_t size_in_bytes = size * table[1];
if (static_cast<int32_t>(size) > array->GetLength()) {
@@ -220,8 +216,7 @@
* Float/double conversion requires clamping to min and max of integer form. If
* target doesn't support this normally, use these.
*/
-static int64_t D2L(double d)
-{
+int64_t D2L(double d) {
static const double kMaxLong = (double)(int64_t)0x7fffffffffffffffULL;
static const double kMinLong = (double)(int64_t)0x8000000000000000ULL;
if (d >= kMaxLong)
@@ -234,8 +229,7 @@
return (int64_t)d;
}
-static int64_t F2L(float f)
-{
+int64_t F2L(float f) {
static const float kMaxLong = (float)(int64_t)0x7fffffffffffffffULL;
static const float kMinLong = (float)(int64_t)0x8000000000000000ULL;
if (f >= kMaxLong)
@@ -335,12 +329,15 @@
return NULL;
}
-Thread* Thread::Create(const Runtime* runtime) {
- UNIMPLEMENTED(FATAL) << "need to pass in a java.lang.Thread";
+void Thread::Create(Object* peer, size_t stack_size) {
+ CHECK(peer != NULL);
- size_t stack_size = runtime->GetDefaultStackSize();
+ if (stack_size == 0) {
+ stack_size = Runtime::Current()->GetDefaultStackSize();
+ }
- Thread* new_thread = new Thread;
+ Thread* self = new Thread;
+ self->peer_ = peer;
pthread_attr_t attr;
errno = pthread_attr_init(&attr);
@@ -358,7 +355,7 @@
PLOG(FATAL) << "pthread_attr_setstacksize(" << stack_size << ") failed";
}
- errno = pthread_create(&new_thread->pthread_, &attr, ThreadStart, new_thread);
+ errno = pthread_create(&self->pthread_, &attr, ThreadStart, self);
if (errno != 0) {
PLOG(FATAL) << "pthread_create failed";
}
@@ -367,11 +364,6 @@
if (errno != 0) {
PLOG(FATAL) << "pthread_attr_destroy failed";
}
-
- // TODO: get the "daemon" field from the java.lang.Thread.
- // new_thread->is_daemon_ = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
-
- return new_thread;
}
Thread* Thread::Attach(const Runtime* runtime, const char* name, bool as_daemon) {
@@ -379,7 +371,6 @@
self->tid_ = ::art::GetTid();
self->pthread_ = pthread_self();
- self->is_daemon_ = as_daemon;
self->InitStackHwm();
@@ -405,12 +396,22 @@
return self;
}
+jobject GetWellKnownThreadGroup(JNIEnv* env, const char* field_name) {
+ jclass thread_group_class = env->FindClass("java/lang/ThreadGroup");
+ jfieldID fid = env->GetStaticFieldID(thread_group_class, field_name, "Ljava/lang/ThreadGroup;");
+ jobject thread_group = env->GetStaticObjectField(thread_group_class, fid);
+ // This will be null in the compiler (and tests), but never in a running system.
+ //CHECK(thread_group != NULL) << "java.lang.ThreadGroup." << field_name << " not initialized";
+ return thread_group;
+}
+
void Thread::CreatePeer(const char* name, bool as_daemon) {
ScopedThreadStateChange tsc(Thread::Current(), Thread::kNative);
JNIEnv* env = jni_env_;
- jobject thread_group = NULL;
+ const char* field_name = (GetThinLockId() == ThreadList::kMainId) ? "mMain" : "mSystem";
+ jobject thread_group = GetWellKnownThreadGroup(env, field_name);
jobject thread_name = env->NewStringUTF(name);
jint thread_priority = GetNativePriority();
jboolean thread_is_daemon = as_daemon;
@@ -419,7 +420,21 @@
jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
jobject peer = env->NewObject(c, mid, thread_group, thread_name, thread_priority, thread_is_daemon);
- peer_ = env->NewGlobalRef(peer);
+
+ // Because we mostly run without code available (in the compiler, in tests), we
+ // manually assign the fields the constructor should have set.
+ // TODO: lose this.
+ jfieldID fid;
+ fid = env->GetFieldID(c, "group", "Ljava/lang/ThreadGroup;");
+ env->SetObjectField(peer, fid, thread_group);
+ fid = env->GetFieldID(c, "name", "Ljava/lang/String;");
+ env->SetObjectField(peer, fid, thread_name);
+ fid = env->GetFieldID(c, "priority", "I");
+ env->SetIntField(peer, fid, thread_priority);
+ fid = env->GetFieldID(c, "daemon", "Z");
+ env->SetBooleanField(peer, fid, thread_is_daemon);
+
+ peer_ = DecodeJObject(peer);
}
void Thread::InitStackHwm() {
@@ -456,33 +471,8 @@
}
void Thread::Dump(std::ostream& os) const {
- /*
- * Get the java.lang.Thread object. This function gets called from
- * some weird debug contexts, so it's possible that there's a GC in
- * progress on some other thread. To decrease the chances of the
- * thread object being moved out from under us, we add the reference
- * to the tracked allocation list, which pins it in place.
- *
- * If threadObj is NULL, the thread is still in the process of being
- * attached to the VM, and there's really nothing interesting to
- * say about it yet.
- */
- os << "TODO: pin Thread before dumping\n";
-#if 0
- // TODO: dalvikvm had this limitation, but we probably still want to do our best.
- if (peer_ == NULL) {
- LOGI("Can't dump thread %d: threadObj not set", threadId);
- return;
- }
- dvmAddTrackedAlloc(peer_, NULL);
-#endif
-
DumpState(os);
DumpStack(os);
-
-#if 0
- dvmReleaseTrackedAlloc(peer_, NULL);
-#endif
}
std::string GetSchedulerGroup(pid_t tid) {
@@ -511,25 +501,46 @@
}
void Thread::DumpState(std::ostream& os) const {
- std::string thread_name("unknown");
- int priority = -1;
+ std::string thread_name("<native thread without managed peer>");
+ std::string group_name;
+ int priority;
+ bool is_daemon = false;
-#if 0 // TODO
- nameStr = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
- threadName = dvmCreateCstrFromString(nameStr);
- priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
-#else
- {
- // TODO: this may be truncated; we should use the java.lang.Thread 'name' field instead.
+ if (peer_ != NULL) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ Class* boolean_class = class_linker->FindPrimitiveClass('Z');
+ Class* int_class = class_linker->FindPrimitiveClass('I');
+ Class* string_class = class_linker->FindSystemClass("Ljava/lang/String;");
+ Class* thread_class = class_linker->FindSystemClass("Ljava/lang/Thread;");
+ Class* thread_group_class = class_linker->FindSystemClass("Ljava/lang/ThreadGroup;");
+
+ Field* name_field = thread_class->FindDeclaredInstanceField("name", string_class);
+ Field* priority_field = thread_class->FindDeclaredInstanceField("priority", int_class);
+ Field* daemon_field = thread_class->FindDeclaredInstanceField("daemon", boolean_class);
+ Field* thread_group_field = thread_class->FindDeclaredInstanceField("group", thread_group_class);
+
+ String* thread_name_string = reinterpret_cast<String*>(name_field->GetObject(peer_));
+ thread_name = (thread_name_string != NULL) ? thread_name_string->ToModifiedUtf8() : "<null>";
+ priority = priority_field->GetInt(peer_);
+ is_daemon = daemon_field->GetBoolean(peer_);
+
+ Object* thread_group = thread_group_field->GetObject(peer_);
+ if (thread_group != NULL) {
+ Field* name_field = thread_group_class->FindDeclaredInstanceField("name", string_class);
+ String* group_name_string = reinterpret_cast<String*>(name_field->GetObject(thread_group));
+ group_name = (group_name_string != NULL) ? group_name_string->ToModifiedUtf8() : "<null>";
+ }
+ } else {
+ // This name may be truncated, but it's the best we can do in the absence of a managed peer.
std::string stats;
if (ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
size_t start = stats.find('(') + 1;
size_t end = stats.find(')') - start;
thread_name = stats.substr(start, end);
}
+ priority = GetNativePriority();
}
- priority = -1;
-#endif
int policy;
sched_param sp;
@@ -543,19 +554,8 @@
scheduler_group = "default";
}
- std::string group_name("(null; initializing?)");
-#if 0
- groupObj = (Object*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
- if (groupObj != NULL) {
- nameStr = (StringObject*) dvmGetFieldObject(groupObj, gDvm.offJavaLangThreadGroup_name);
- groupName = dvmCreateCstrFromString(nameStr);
- }
-#else
- group_name = "TODO";
-#endif
-
os << '"' << thread_name << '"';
- if (is_daemon_) {
+ if (is_daemon) {
os << " daemon";
}
os << " prio=" << priority
@@ -605,8 +605,36 @@
<< " HZ=" << sysconf(_SC_CLK_TCK) << "\n";
}
+struct StackDumpVisitor : public Thread::StackVisitor {
+ StackDumpVisitor(std::ostream& os) : os(os) {
+ }
+
+ ~StackDumpVisitor() {
+ }
+
+ void VisitFrame(const Frame& frame) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ Method* m = frame.GetMethod();
+ Class* c = m->GetDeclaringClass();
+ const DexFile& dex_file = class_linker->FindDexFile(c->GetDexCache());
+
+ os << " at " << PrettyMethod(m, false);
+ if (m->IsNative()) {
+ os << "(Native method)";
+ } else {
+ int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(frame.GetPC()));
+ os << "(" << c->GetSourceFile()->ToModifiedUtf8() << ":" << line_number << ")";
+ }
+ os << "\n";
+ }
+
+ std::ostream& os;
+};
+
void Thread::DumpStack(std::ostream& os) const {
- os << "UNIMPLEMENTED: Thread::DumpStack\n";
+ StackDumpVisitor dumper(os);
+ WalkStack(&dumper);
}
void Thread::ThreadExitCallback(void* arg) {
@@ -687,10 +715,6 @@
//dvmUnlockObject(self, lock);
//lock = NULL;
- // Delete our global reference to the java.lang.Thread.
- jni_env_->DeleteGlobalRef(peer_);
- peer_ = NULL;
-
delete jni_env_;
jni_env_ = NULL;
@@ -783,9 +807,9 @@
class CountStackDepthVisitor : public Thread::StackVisitor {
public:
CountStackDepthVisitor() : depth_(0) {}
- virtual bool VisitFrame(const Frame&) {
+
+ virtual void VisitFrame(const Frame&) {
++depth_;
- return true;
}
int GetDepth() const {
@@ -817,11 +841,10 @@
virtual ~BuildInternalStackTraceVisitor() {}
- virtual bool VisitFrame(const Frame& frame) {
+ virtual void VisitFrame(const Frame& frame) {
method_trace_->Set(count_, frame.GetMethod());
pc_trace_->Set(count_, frame.GetPC());
++count_;
- return true;
}
jobject GetInternalStackTrace() const {
@@ -841,7 +864,7 @@
};
void Thread::WalkStack(StackVisitor* visitor) const {
- Frame frame = Thread::Current()->GetTopOfStack();
+ Frame frame = GetTopOfStack();
// TODO: enable this CHECK after native_to_managed_record_ is initialized during startup.
// CHECK(native_to_managed_record_ != NULL);
NativeToManagedRecord* record = native_to_managed_record_;
@@ -1005,8 +1028,12 @@
}
void Thread::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
- //(*visitor)(&thread->threadObj, threadId, ROOT_THREAD_OBJECT, arg);
- //(*visitor)(&thread->exception, threadId, ROOT_NATIVE_STACK, arg);
+ if (exception_ != NULL) {
+ visitor(exception_, arg);
+ }
+ if (peer_ != NULL) {
+ visitor(peer_, arg);
+ }
jni_env_->locals.VisitRoots(visitor, arg);
jni_env_->monitors.VisitRoots(visitor, arg);
// visitThreadStack(visitor, thread, arg);
diff --git a/src/thread.h b/src/thread.h
index ed4b1e4..9717a7c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -208,11 +208,11 @@
class StackVisitor {
public:
virtual ~StackVisitor() {}
- virtual bool VisitFrame(const Frame& frame) = 0;
+ virtual void VisitFrame(const Frame& frame) = 0;
};
// Creates a new thread.
- static Thread* Create(const Runtime* runtime);
+ static void Create(Object* peer, size_t stack_size);
// Creates a new thread from the calling thread.
static Thread* Attach(const Runtime* runtime, const char* name, bool as_daemon);
@@ -274,7 +274,7 @@
return pthread_;
}
- jobject GetPeer() const {
+ Object* GetPeer() const {
return peer_;
}
@@ -495,10 +495,8 @@
// Native thread handle.
pthread_t pthread_;
- bool is_daemon_;
-
// Our managed peer (an instance of java.lang.Thread).
- jobject peer_;
+ Object* peer_;
// Guards the 'interrupted_' and 'wait_monitor_' members.
mutable Mutex wait_mutex_;