Uncaught exception handlers and automatic removal from thread groups.
Also fix exception stack traces to only include what they should.
Change-Id: I9aebc48515b60bfb2b93710192d9a1407936e04a
diff --git a/src/thread.cc b/src/thread.cc
index 3495438..68020d1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -41,14 +41,18 @@
pthread_key_t Thread::pthread_key_self_;
+static Class* gThrowable = NULL;
static Field* gThread_daemon = NULL;
static Field* gThread_group = NULL;
static Field* gThread_lock = NULL;
static Field* gThread_name = NULL;
static Field* gThread_priority = NULL;
+static Field* gThread_uncaughtHandler = NULL;
static Field* gThread_vmData = NULL;
static Field* gThreadGroup_name = NULL;
static Method* gThread_run = NULL;
+static Method* gThreadGroup_removeThread = NULL;
+static Method* gUncaughtExceptionHandler_uncaughtException = NULL;
// Temporary debugging hook for compiler.
void DebugMe(Method* method, uint32_t info) {
@@ -835,14 +839,20 @@
Class* Thread_class = class_linker->FindSystemClass("Ljava/lang/Thread;");
Class* ThreadGroup_class = class_linker->FindSystemClass("Ljava/lang/ThreadGroup;");
Class* ThreadLock_class = class_linker->FindSystemClass("Ljava/lang/ThreadLock;");
+ Class* UncaughtExceptionHandler_class = class_linker->FindSystemClass("Ljava/lang/Thread$UncaughtExceptionHandler;");
+ gThrowable = class_linker->FindSystemClass("Ljava/lang/Throwable;");
gThread_daemon = Thread_class->FindDeclaredInstanceField("daemon", boolean_class);
gThread_group = Thread_class->FindDeclaredInstanceField("group", ThreadGroup_class);
gThread_lock = Thread_class->FindDeclaredInstanceField("lock", ThreadLock_class);
gThread_name = Thread_class->FindDeclaredInstanceField("name", String_class);
gThread_priority = Thread_class->FindDeclaredInstanceField("priority", int_class);
gThread_run = Thread_class->FindVirtualMethod("run", "()V");
+ gThread_uncaughtHandler = Thread_class->FindDeclaredInstanceField("uncaughtHandler", UncaughtExceptionHandler_class);
gThread_vmData = Thread_class->FindDeclaredInstanceField("vmData", int_class);
gThreadGroup_name = ThreadGroup_class->FindDeclaredInstanceField("name", String_class);
+ gThreadGroup_removeThread = ThreadGroup_class->FindVirtualMethod("removeThread", "(Ljava/lang/Thread;)V");
+ gUncaughtExceptionHandler_uncaughtException =
+ UncaughtExceptionHandler_class->FindVirtualMethod("uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
}
void Thread::Shutdown() {
@@ -883,24 +893,48 @@
jni_env_->monitors.VisitRoots(MonitorExitVisitor, NULL);
}
- if (IsExceptionPending()) {
- UNIMPLEMENTED(FATAL) << "threadExitUncaughtException()";
- }
-
- // TODO: ThreadGroup.removeThread(this);
-
if (peer_ != NULL) {
+ Object* group = gThread_group->GetObject(peer_);
+
+ // Handle any pending exception.
+ if (IsExceptionPending()) {
+ // Get and clear the exception.
+ Object* exception = GetException();
+ ClearException();
+
+ // If the thread has its own handler, use that.
+ Object* handler = gThread_uncaughtHandler->GetObject(peer_);
+ if (handler == NULL) {
+ // Otherwise use the thread group's default handler.
+ handler = group;
+ }
+
+ // Call the handler.
+ Method* m = handler->GetClass()->FindVirtualMethodForVirtualOrInterface(gUncaughtExceptionHandler_uncaughtException);
+ Object* args[2];
+ args[0] = peer_;
+ args[1] = exception;
+ m->Invoke(this, handler, reinterpret_cast<byte*>(&args), NULL);
+
+ // If the handler threw, clear that exception too.
+ ClearException();
+ }
+
+ // this.group.removeThread(this);
+ Method* m = group->GetClass()->FindVirtualMethodForVirtualOrInterface(gThreadGroup_removeThread);
+ Object* args = peer_;
+ m->Invoke(this, group, reinterpret_cast<byte*>(&args), NULL);
+
+ // this.vmData = 0;
SetVmData(peer_, NULL);
- }
- // TODO: say "bye" to the debugger.
- //if (gDvm.debuggerConnected) {
- // dvmDbgPostThreadDeath(self);
- //}
+ // TODO: say "bye" to the debugger.
+ //if (gDvm.debuggerConnected) {
+ // dvmDbgPostThreadDeath(self);
+ //}
- // Thread.join() is implemented as an Object.wait() on the Thread.lock
- // object. Signal anyone who is waiting.
- if (peer_ != NULL) {
+ // Thread.join() is implemented as an Object.wait() on the Thread.lock
+ // object. Signal anyone who is waiting.
Thread* self = Thread::Current();
Object* lock = gThread_lock->GetObject(peer_);
// (This conditional is only needed for tests, where Thread.lock won't have been set.)
@@ -1012,24 +1046,38 @@
class CountStackDepthVisitor : public Thread::StackVisitor {
public:
- CountStackDepthVisitor() : depth_(0) {}
+ CountStackDepthVisitor() : depth_(0), skip_depth_(0), skipping_(true) {}
- virtual void VisitFrame(const Frame&, uintptr_t pc) {
- ++depth_;
+ virtual void VisitFrame(const Frame& frame, uintptr_t pc) {
+ // We want to skip frames up to and including the exception's constructor.
+ if (skipping_ && !gThrowable->IsAssignableFrom(frame.GetMethod()->GetDeclaringClass())) {
+ skipping_ = false;
+ }
+ if (!skipping_) {
+ ++depth_;
+ } else {
+ ++skip_depth_;
+ }
}
int GetDepth() const {
return depth_;
}
+ int GetSkipDepth() const {
+ return skip_depth_;
+ }
+
private:
uint32_t depth_;
+ uint32_t skip_depth_;
+ bool skipping_;
};
-//
class BuildInternalStackTraceVisitor : public Thread::StackVisitor {
public:
- explicit BuildInternalStackTraceVisitor(int depth, ScopedJniThreadState& ts) : count_(0) {
+ 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);
@@ -1048,6 +1096,10 @@
virtual ~BuildInternalStackTraceVisitor() {}
virtual void VisitFrame(const Frame& frame, uintptr_t pc) {
+ if (skip_depth_ > 0) {
+ skip_depth_--;
+ return;
+ }
method_trace_->Set(count_, frame.GetMethod());
pc_trace_->Set(count_, pc);
++count_;
@@ -1058,6 +1110,8 @@
}
private:
+ // How many more frames to skip.
+ int32_t skip_depth_;
// Current position down stack trace
uint32_t count_;
// Array of return PC values
@@ -1113,12 +1167,13 @@
CountStackDepthVisitor count_visitor;
WalkStack(&count_visitor);
int32_t depth = count_visitor.GetDepth();
+ int32_t skip_depth = count_visitor.GetSkipDepth();
// Transition into runnable state to work on Object*/Array*
ScopedJniThreadState ts(jni_env_);
// Build internal stack trace
- BuildInternalStackTraceVisitor build_trace_visitor(depth, ts);
+ BuildInternalStackTraceVisitor build_trace_visitor(depth, skip_depth, ts);
WalkStack(&build_trace_visitor);
return build_trace_visitor.GetInternalStackTrace();