Implement most of VMStack and some of Zygote.
Change-Id: I07e18259a0452a2a9b077148f4f1ca67d3f63427
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 0f12340..b115e2e 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -86,8 +86,10 @@
src/compiler/codegen/arm/Assemble.cc \
src/compiler/codegen/arm/LocalOptimizations.cc \
src/compiler/codegen/arm/armv7-a/Codegen.cc \
+ src/dalvik_system_VMDebug.cc \
src/dalvik_system_VMRuntime.cc \
src/dalvik_system_VMStack.cc \
+ src/dalvik_system_Zygote.cc \
src/dex_cache.cc \
src/dex_file.cc \
src/dex_instruction.cc \
@@ -99,7 +101,6 @@
src/image_writer.cc \
src/indirect_reference_table.cc \
src/intern_table.cc \
- src/dalvik_system_VMDebug.cc \
src/java_lang_Class.cc \
src/java_lang_Object.cc \
src/java_lang_Runtime.cc \
diff --git a/src/dalvik_system_VMRuntime.cc b/src/dalvik_system_VMRuntime.cc
index 1e95637..1bc1e39 100644
--- a/src/dalvik_system_VMRuntime.cc
+++ b/src/dalvik_system_VMRuntime.cc
@@ -88,7 +88,7 @@
jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) {
// TODO: debugger!
- return false;
+ return JNI_FALSE;
}
jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
diff --git a/src/dalvik_system_VMStack.cc b/src/dalvik_system_VMStack.cc
index 2a3f18b..331a64f 100644
--- a/src/dalvik_system_VMStack.cc
+++ b/src/dalvik_system_VMStack.cc
@@ -15,7 +15,9 @@
*/
#include "jni_internal.h"
+#include "class_loader.h"
#include "object.h"
+#include "thread_list.h"
#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
@@ -23,14 +25,58 @@
namespace {
-jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject targetThread, jobjectArray javaSteArray) {
- UNIMPLEMENTED(FATAL);
- return 0;
+class StackGetter {
+ public:
+ StackGetter(JNIEnv* env, Thread* thread) : env_(env), thread_(thread), trace_(NULL) {
+ }
+
+ static void Callback(void* arg) {
+ reinterpret_cast<StackGetter*>(arg)->Callback();
+ }
+
+ jobject GetTrace() {
+ return trace_;
+ }
+
+ private:
+ void Callback() {
+ trace_ = thread_->CreateInternalStackTrace(env_);
+ }
+
+ JNIEnv* env_;
+ Thread* thread_;
+ jobject trace_;
+};
+
+jobject GetThreadStack(JNIEnv* env, jobject javaThread) {
+ Thread* thread = Thread::FromManagedThread(env, javaThread);
+ if (thread == NULL) {
+ return NULL;
+ }
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+ StackGetter stack_getter(env, thread);
+ thread_list->RunWhileSuspended(thread, StackGetter::Callback, &stack_getter);
+ return stack_getter.GetTrace();
+}
+
+jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread, jobjectArray javaSteArray) {
+ jobject trace = GetThreadStack(env, javaThread);
+ if (trace == NULL) {
+ return 0;
+ }
+ int32_t depth;
+ Thread::InternalStackTraceToStackTraceElementArray(env, trace, javaSteArray, &depth);
+ return depth;
}
jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
- UNIMPLEMENTED(WARNING);
- return NULL;
+ // Returns the defining class loader of the caller's caller.
+ Frame frame = Thread::Current()->GetTopOfStack();
+ frame.Next();
+ frame.Next();
+ Method* callerCaller = frame.GetMethod();
+ const Object* cl = callerCaller->GetDeclaringClass()->GetClassLoader();
+ return AddLocalReference<jobject>(env, cl);
}
jobjectArray VMStack_getClasses(JNIEnv* env, jclass, jint maxDepth) {
@@ -39,13 +85,22 @@
}
jclass VMStack_getStackClass2(JNIEnv* env, jclass) {
- UNIMPLEMENTED(FATAL);
- return NULL;
+ // Returns the class of the caller's caller's caller.
+ Frame frame = Thread::Current()->GetTopOfStack();
+ frame.Next();
+ frame.Next();
+ frame.Next();
+ Method* callerCallerCaller = frame.GetMethod();
+ Class* c = callerCallerCaller->GetDeclaringClass();
+ return AddLocalReference<jclass>(env, c);
}
-jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject targetThread) {
- UNIMPLEMENTED(FATAL);
- return NULL;
+jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject javaThread) {
+ jobject trace = GetThreadStack(env, javaThread);
+ if (trace == NULL) {
+ return NULL;
+ }
+ return Thread::InternalStackTraceToStackTraceElementArray(env, trace);
}
static JNINativeMethod gMethods[] = {
diff --git a/src/dalvik_system_Zygote.cc b/src/dalvik_system_Zygote.cc
new file mode 100644
index 0000000..1b98e9b
--- /dev/null
+++ b/src/dalvik_system_Zygote.cc
@@ -0,0 +1,56 @@
+/*
+ * 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 "JNIHelp.h"
+#include "ScopedUtfChars.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+#include <paths.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+namespace art {
+
+namespace {
+
+void Zygote_nativeExecShell(JNIEnv* env, jclass, jstring javaCommand) {
+ ScopedUtfChars command(env, javaCommand);
+ if (command.c_str() == NULL) {
+ return;
+ }
+ const char *argp[] = {_PATH_BSHELL, "-c", command.c_str(), NULL};
+ LOG(INFO) << "Exec: " << argp[0] << ' ' << argp[1] << ' ' << argp[2];
+
+ execv(_PATH_BSHELL, (char**)argp);
+ exit(127);
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(Zygote, nativeExecShell, "(Ljava/lang/String;)V"),
+ //NATIVE_METHOD(Zygote, nativeFork, "()I"),
+ //NATIVE_METHOD(Zygote, nativeForkAndSpecialize, "(II[II[[I)I"),
+ //NATIVE_METHOD(Zygote, nativeForkSystemServer, "(II[II[[IJJ)I"),
+};
+
+} // namespace
+
+void register_dalvik_system_Zygote(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "dalvik/system/Zygote", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/exception_test.cc b/src/exception_test.cc
index 6c48f5f..ee6c1a0d 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -160,13 +160,11 @@
Thread* thread = Thread::Current();
thread->SetTopOfStack(fake_stack, reinterpret_cast<uintptr_t>(method_g_->GetCode()) + 3);
- jobject internal = thread->CreateInternalStackTrace();
- jobjectArray ste_array =
- Thread::InternalStackTraceToStackTraceElementArray(internal,
- thread->GetJniEnv());
+ JNIEnv* env = thread->GetJniEnv();
+ jobject internal = thread->CreateInternalStackTrace(env);
+ jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(env, internal);
ObjectArray<StackTraceElement>* trace_array =
- Decode<ObjectArray<StackTraceElement>*>(thread->GetJniEnv(), ste_array);
-
+ Decode<ObjectArray<StackTraceElement>*>(env, ste_array);
ASSERT_TRUE(trace_array->Get(0) != NULL);
EXPECT_STREQ("java.lang.MyClass",
diff --git a/src/java_lang_Throwable.cc b/src/java_lang_Throwable.cc
index 4c3bfcf..e8a3891 100644
--- a/src/java_lang_Throwable.cc
+++ b/src/java_lang_Throwable.cc
@@ -25,11 +25,11 @@
jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) {
JNIEnvExt* env_ext = reinterpret_cast<JNIEnvExt*>(env);
- return env_ext->self->CreateInternalStackTrace();
+ return env_ext->self->CreateInternalStackTrace(env);
}
jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject javaStackState) {
- return Thread::InternalStackTraceToStackTraceElementArray(javaStackState, env);
+ return Thread::InternalStackTraceToStackTraceElementArray(env, javaStackState);
}
JNINativeMethod gMethods[] = {
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 4f862d6..fbdf2bf 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -464,9 +464,8 @@
ScopedJniThreadState ts(env);
// Build stack trace
- jobject internal = Thread::Current()->CreateInternalStackTrace();
- jobjectArray ste_array =
- Thread::InternalStackTraceToStackTraceElementArray(internal, env);
+ jobject internal = Thread::Current()->CreateInternalStackTrace(env);
+ jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(env, internal);
ObjectArray<StackTraceElement>* trace_array =
Decode<ObjectArray<StackTraceElement>*>(env, ste_array);
EXPECT_TRUE(trace_array != NULL);
diff --git a/src/runtime.cc b/src/runtime.cc
index 68f42af..5dc057e 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -455,7 +455,7 @@
REGISTER(register_dalvik_system_VMDebug);
REGISTER(register_dalvik_system_VMRuntime);
REGISTER(register_dalvik_system_VMStack);
- //REGISTER(register_dalvik_system_Zygote);
+ REGISTER(register_dalvik_system_Zygote);
REGISTER(register_java_lang_Class);
REGISTER(register_java_lang_Object);
REGISTER(register_java_lang_Runtime);
diff --git a/src/thread.cc b/src/thread.cc
index 6eac92b..97f5710 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -56,10 +56,11 @@
// Temporary debugging hook for compiler.
void DebugMe(Method* method, uint32_t info) {
- LOG(INFO) << "DebugMe";
- if (method != NULL)
- LOG(INFO) << PrettyMethod(method);
- LOG(INFO) << "Info: " << info;
+ LOG(INFO) << "DebugMe";
+ if (method != NULL) {
+ LOG(INFO) << PrettyMethod(method);
+ }
+ LOG(INFO) << "Info: " << info;
}
} // namespace art
@@ -479,6 +480,11 @@
gThread_vmData->SetInt(managed_thread, reinterpret_cast<uintptr_t>(native_thread));
}
+Thread* Thread::FromManagedThread(JNIEnv* env, jobject java_thread) {
+ Object* thread = Decode<Object*>(env, java_thread);
+ return reinterpret_cast<Thread*>(static_cast<uintptr_t>(gThread_vmData->GetInt(thread)));
+}
+
void Thread::Create(Object* peer, size_t stack_size) {
CHECK(peer != NULL);
@@ -549,7 +555,8 @@
}
void Thread::CreatePeer(const char* name, bool as_daemon) {
- ScopedThreadStateChange tsc(Thread::Current(), Thread::kNative);
+ Thread* self = Thread::Current();
+ ScopedThreadStateChange tsc(self, Thread::kNative);
JNIEnv* env = jni_env_;
@@ -563,21 +570,16 @@
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_ = DecodeJObject(peer);
+ SetVmData(peer_, self);
// 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);
+ gThread_daemon->SetBoolean(peer_, thread_is_daemon);
+ gThread_group->SetObject(peer_, Decode<Object*>(env, thread_group));
+ gThread_name->SetObject(peer_, Decode<Object*>(env, thread_name));
+ gThread_priority->SetInt(peer_, thread_priority);
}
void Thread::InitStackHwm() {
@@ -867,9 +869,6 @@
}
void Thread::FinishStartup() {
- // Finish attaching the main thread.
- Thread::Current()->CreatePeer("main", false);
-
// Now the ClassLinker is ready, we can find the various Class*, Field*, and Method*s we need.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Class* boolean_class = class_linker->FindPrimitiveClass('Z');
@@ -892,6 +891,9 @@
gThreadGroup_removeThread = ThreadGroup_class->FindVirtualMethod("removeThread", "(Ljava/lang/Thread;)V");
gUncaughtExceptionHandler_uncaughtException =
UncaughtExceptionHandler_class->FindVirtualMethod("uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+ // Finish attaching the main thread.
+ Thread::Current()->CreatePeer("main", false);
}
void Thread::Shutdown() {
@@ -1124,8 +1126,7 @@
explicit BuildInternalStackTraceVisitor(int depth, int skip_depth, ScopedJniThreadState& ts)
: skip_depth_(skip_depth), count_(0) {
// Allocate method trace with an extra slot that will hold the PC trace
- method_trace_ = Runtime::Current()->GetClassLinker()->
- AllocObjectArray<Object>(depth + 1);
+ method_trace_ = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(depth + 1);
// Register a local reference as IntArray::Alloc may trigger GC
local_ref_ = AddLocalReference<jobject>(ts.Env(), method_trace_);
pc_trace_ = IntArray::Alloc(depth);
@@ -1207,7 +1208,7 @@
}
}
-jobject Thread::CreateInternalStackTrace() const {
+jobject Thread::CreateInternalStackTrace(JNIEnv* env) const {
// Compute depth of stack
CountStackDepthVisitor count_visitor;
WalkStack(&count_visitor);
@@ -1215,7 +1216,7 @@
int32_t skip_depth = count_visitor.GetSkipDepth();
// Transition into runnable state to work on Object*/Array*
- ScopedJniThreadState ts(jni_env_);
+ ScopedJniThreadState ts(env);
// Build internal stack trace
BuildInternalStackTraceVisitor build_trace_visitor(depth, skip_depth, ts);
@@ -1224,8 +1225,8 @@
return build_trace_visitor.GetInternalStackTrace();
}
-jobjectArray Thread::InternalStackTraceToStackTraceElementArray(jobject internal,
- JNIEnv* env) {
+jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal,
+ jobjectArray output_array, int* stack_depth) {
// Transition into runnable state to work on Object*/Array*
ScopedJniThreadState ts(env);
@@ -1237,10 +1238,24 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- // Create java_trace array and place in local reference table
- ObjectArray<StackTraceElement>* java_traces =
- class_linker->AllocStackTraceElementArray(depth);
- jobjectArray result = AddLocalReference<jobjectArray>(ts.Env(), java_traces);
+ jobjectArray result;
+ ObjectArray<StackTraceElement>* java_traces;
+ if (output_array != NULL) {
+ // Reuse the array we were given.
+ result = output_array;
+ java_traces = reinterpret_cast<ObjectArray<StackTraceElement>*>(Decode<Array*>(env,
+ output_array));
+ // ...adjusting the number of frames we'll write to not exceed the array length.
+ depth = std::min(depth, java_traces->GetLength());
+ } else {
+ // Create java_trace array and place in local reference table
+ java_traces = class_linker->AllocStackTraceElementArray(depth);
+ result = AddLocalReference<jobjectArray>(ts.Env(), java_traces);
+ }
+
+ if (stack_depth != NULL) {
+ *stack_depth = depth;
+ }
for (int32_t i = 0; i < depth; ++i) {
// Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
diff --git a/src/thread.h b/src/thread.h
index 4e1db6f..446a8ec 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -254,12 +254,7 @@
return reinterpret_cast<Thread*>(thread);
}
- static Thread* FromManagedThread(JNIEnv* env, jobject thread) {
- // TODO: make these more generally available, and cached.
- jclass java_lang_Thread = env->FindClass("java/lang/Thread");
- jfieldID fid = env->GetFieldID(java_lang_Thread, "vmData", "I");
- return reinterpret_cast<Thread*>(static_cast<uintptr_t>(env->GetIntField(thread, fid)));
- }
+ static Thread* FromManagedThread(JNIEnv* env, jobject thread);
void Dump(std::ostream& os) const;
@@ -456,11 +451,14 @@
// Create the internal representation of a stack trace, that is more time
// and space efficient to compute than the StackTraceElement[]
- jobject CreateInternalStackTrace() const;
+ jobject CreateInternalStackTrace(JNIEnv* env) const;
- // Convert an internal stack trace representation to a StackTraceElement[]
- static jobjectArray
- InternalStackTraceToStackTraceElementArray(jobject internal, JNIEnv* env);
+ // Convert an internal stack trace representation (returned by CreateInternalStackTrace) to a
+ // StackTraceElement[]. If output_array is NULL, a new array is created, otherwise as many
+ // frames as will fit are written into the given array. If stack_depth is non-NULL, it's updated
+ // with the number of valid frames in the returned array.
+ static jobjectArray InternalStackTraceToStackTraceElementArray(JNIEnv* env, jobject internal,
+ jobjectArray output_array = NULL, int* stack_depth = NULL);
void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
diff --git a/src/thread_list.cc b/src/thread_list.cc
index f17414c..8c6185d 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -122,6 +122,29 @@
//LOG(INFO) << *self << " SuspendAll complete";
}
+void ThreadList::Suspend(Thread* thread) {
+ DCHECK(thread != Thread::Current());
+
+ // TODO: add another thread_suspend_lock_ to avoid GC/debugger races.
+
+ //LOG(INFO) << "Suspend(" << *thread << ") starting...";
+
+ MutexLock mu(thread_list_lock_);
+ if (!Contains(thread)) {
+ return;
+ }
+
+ {
+ MutexLock mu(thread_suspend_count_lock_);
+ ++thread->suspend_count_;
+ }
+
+ thread->WaitUntilSuspended();
+
+ //LOG(INFO) << "Suspend(" << *thread << ") complete";
+}
+
+
void ThreadList::ResumeAll() {
Thread* self = Thread::Current();
@@ -156,6 +179,45 @@
//LOG(INFO) << *self << " ResumeAll complete";
}
+void ThreadList::Resume(Thread* thread) {
+ DCHECK(thread != Thread::Current());
+
+ //LOG(INFO) << "Resume(" << *thread << ") starting...";
+
+ {
+ MutexLock mu1(thread_list_lock_);
+ MutexLock mu2(thread_suspend_count_lock_);
+ if (!Contains(thread)) {
+ return;
+ }
+ if (thread->suspend_count_ > 0) {
+ --thread->suspend_count_;
+ } else {
+ LOG(WARNING) << *thread << " suspend count already zero";
+ }
+ }
+
+ {
+ //LOG(INFO) << "Resume(" << *thread << ") waking others";
+ MutexLock mu(thread_suspend_count_lock_);
+ thread_suspend_count_cond_.Broadcast();
+ }
+
+ //LOG(INFO) << "Resume(" << *thread << ") complete";
+}
+
+void ThreadList::RunWhileSuspended(Thread* thread, void (*callback)(void*), void* arg) {
+ DCHECK(thread != NULL);
+ Thread* self = Thread::Current();
+ if (thread != self) {
+ Suspend(thread);
+ }
+ callback(arg);
+ if (thread != self) {
+ Resume(thread);
+ }
+}
+
void ThreadList::Register(Thread* thread) {
//LOG(INFO) << "ThreadList::Register() " << *thread;
MutexLock mu(thread_list_lock_);
diff --git a/src/thread_list.h b/src/thread_list.h
index 32bcf20..7840f71 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -31,14 +31,13 @@
ThreadList();
~ThreadList();
- bool Contains(Thread* thread);
-
void Dump(std::ostream& os);
// Thread suspension support.
void FullSuspendCheck(Thread* thread);
void ResumeAll();
void SuspendAll();
+ void RunWhileSuspended(Thread* thread, void (*callback)(void*), void* arg);
void Register(Thread* thread);
void Unregister();
@@ -54,7 +53,10 @@
bool AllThreadsAreDaemons();
uint32_t AllocThreadId();
+ bool Contains(Thread* thread);
void ReleaseThreadId(uint32_t id);
+ void Resume(Thread* thread);
+ void Suspend(Thread* thread);
void SuspendAllDaemonThreads();
void WaitForNonDaemonThreadsToExit();