Fix debugger to properly return TS_SLEEPING.

Thread.sleep is implemented with Object.wait, so the code can't
distinguish the two. I added a check to see if Thread.sleep
is somewhere on the stack, indicating that the thread is actually
sleeping instead of waiting.

Change-Id: I288befc1b3dc76e30c0620ab9c850c66b81c7a6d
diff --git a/src/debugger.cc b/src/debugger.cc
index 77a9252..158be0b 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -1420,8 +1420,6 @@
 
   MutexLock mu2(soa.Self(), *Locks::thread_suspend_count_lock_);
 
-  // TODO: if we're in Thread.sleep(long), we should return TS_SLEEPING,
-  // even if it's implemented using Object.wait(long).
   switch (thread->GetState()) {
     case kTerminated:   *pThreadStatus = JDWP::TS_ZOMBIE;   break;
     case kRunnable:     *pThreadStatus = JDWP::TS_RUNNING;  break;
@@ -1444,6 +1442,34 @@
     // Don't add a 'default' here so the compiler can spot incompatible enum changes.
   }
 
+  if (thread->GetState() == kTimedWaiting) {
+    // Since Thread.sleep is implemented using Object.wait, see if Thread.sleep
+    // is on the stack and change state to TS_SLEEPING if it is.
+    struct SleepMethodVisitor : public StackVisitor {
+      SleepMethodVisitor(const ManagedStack* stack,
+                         const std::deque<InstrumentationStackFrame>* instrumentation_stack)
+          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+          : StackVisitor(stack, instrumentation_stack, NULL), found_(false) {}
+
+      virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+        std::string name(PrettyMethod(GetMethod(), false));
+        if (name == "java.lang.Thread.sleep") {
+          found_ = true;
+          return false;
+        }
+        return true;
+      }
+
+      bool found_;
+    };
+
+    SleepMethodVisitor visitor(thread->GetManagedStack(), thread->GetInstrumentationStack());
+    visitor.WalkStack(false);
+    if (visitor.found_) {
+      *pThreadStatus = JDWP::TS_SLEEPING;
+    }
+  }
+
   *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : JDWP::SUSPEND_STATUS_NOT_SUSPENDED);
 
   return true;