Support for single-stepping by line.

Plus various other test fixes.

Change-Id: I2ef923e56a16a14380eda150685b5e3db944616e
diff --git a/src/debugger.cc b/src/debugger.cc
index 662757d..12d4034 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -133,8 +133,8 @@
   JDWP::JdwpStepDepth step_depth;
 
   const Method* method;
-  int line; // May be -1.
-  //const AddressSet* pAddressSet;    /* if non-null, address set for line */
+  int32_t line_number; // Or -1 for native methods.
+  std::set<uint32_t> dex_pcs;
   int stack_depth;
 };
 
@@ -547,7 +547,9 @@
 
 static Thread* DecodeThread(JDWP::ObjectId threadId) {
   Object* thread_peer = gRegistry->Get<Object*>(threadId);
-  CHECK(thread_peer != NULL);
+  if (thread_peer == NULL) {
+    return NULL;
+  }
   return Thread::FromManagedThread(thread_peer);
 }
 
@@ -649,16 +651,26 @@
   }
 }
 
-void Dbg::GetObjectType(JDWP::ObjectId objectId, JDWP::JdwpTypeTag* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
+JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId objectId, JDWP::ExpandBuf* pReply) {
   Object* o = gRegistry->Get<Object*>(objectId);
-  if (o->GetClass()->IsArrayClass()) {
-    *pRefTypeTag = JDWP::TT_ARRAY;
-  } else if (o->GetClass()->IsInterface()) {
-    *pRefTypeTag = JDWP::TT_INTERFACE;
-  } else {
-    *pRefTypeTag = JDWP::TT_CLASS;
+  if (o == NULL) {
+    return JDWP::ERR_INVALID_OBJECT;
   }
-  *pRefTypeId = gRegistry->Add(o->GetClass());
+
+  JDWP::JdwpTypeTag type_tag;
+  if (o->GetClass()->IsArrayClass()) {
+    type_tag = JDWP::TT_ARRAY;
+  } else if (o->GetClass()->IsInterface()) {
+    type_tag = JDWP::TT_INTERFACE;
+  } else {
+    type_tag = JDWP::TT_CLASS;
+  }
+  JDWP::RefTypeId type_id = gRegistry->Add(o->GetClass());
+
+  expandBufAdd1(pReply, type_tag);
+  expandBufAddRefTypeId(pReply, type_id);
+
+  return JDWP::ERR_NONE;
 }
 
 JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId refTypeId, std::string& signature) {
@@ -1017,10 +1029,10 @@
     int numItems;
     JDWP::ExpandBuf* pReply;
 
-    static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
+    static bool Callback(void* context, uint32_t address, uint32_t line_number) {
       DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
       expandBufAdd8BE(pContext->pReply, address);
-      expandBufAdd4BE(pContext->pReply, lineNum);
+      expandBufAdd4BE(pContext->pReply, line_number);
       pContext->numItems++;
       return true;
     }
@@ -1186,9 +1198,16 @@
   return true;
 }
 
-JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
+JDWP::JdwpError Dbg::GetThreadGroup(JDWP::ObjectId threadId, JDWP::ExpandBuf* pReply) {
   Object* thread = gRegistry->Get<Object*>(threadId);
-  CHECK(thread != NULL);
+  if (thread != NULL) {
+    return JDWP::ERR_INVALID_OBJECT;
+  }
+
+  // Okay, so it's an object, but is it actually a thread?
+  if (DecodeThread(threadId)) {
+    return JDWP::ERR_INVALID_THREAD;
+  }
 
   Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
   CHECK(c != NULL);
@@ -1196,7 +1215,10 @@
   CHECK(f != NULL);
   Object* group = f->GetObject(thread);
   CHECK(group != NULL);
-  return gRegistry->Add(group);
+  JDWP::ObjectId thread_group_id = gRegistry->Add(group);
+
+  expandBufAddObjectId(pReply, thread_group_id);
+  return JDWP::ERR_NONE;
 }
 
 std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
@@ -1269,8 +1291,13 @@
   return true;
 }
 
-uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
-  return DecodeThread(threadId)->GetSuspendCount();
+JDWP::JdwpError Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId, JDWP::ExpandBuf* pReply) {
+  Thread* thread = DecodeThread(threadId);
+  if (thread == NULL) {
+    return JDWP::ERR_INVALID_THREAD;
+  }
+  expandBufAdd4BE(pReply, thread->GetSuspendCount());
+  return JDWP::ERR_NONE;
 }
 
 bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
@@ -1682,9 +1709,9 @@
       } else if (gSingleStepControl.step_size == JDWP::SS_MIN) {
         event_flags |= kSingleStep;
         VLOG(jdwp) << "SS new instruction";
-//      } else if (!dvmAddressSetGet(gSingleStepControl.pAddressSet, pc - method->insns)) {
-//        event_flags |= kSingleStep;
-//        VLOG(jdwp) << "SS new line";
+      } else if (gSingleStepControl.dex_pcs.find(dex_pc) == gSingleStepControl.dex_pcs.end()) {
+        event_flags |= kSingleStep;
+        VLOG(jdwp) << "SS new line";
       }
     } else if (gSingleStepControl.step_depth == JDWP::SD_OVER) {
       // Step over method calls.  We break when the line number is
@@ -1705,9 +1732,9 @@
         if (gSingleStepControl.step_size == JDWP::SS_MIN) {
           event_flags |= kSingleStep;
           VLOG(jdwp) << "SS new instruction";
-//        } else if (!dvmAddressSetGet(gSingleStepControl.pAddressSet, pc - method->insns)) {
-//          event_flags |= kSingleStep;
-//          VLOG(jdwp) << "SS new line";
+        } else if (gSingleStepControl.dex_pcs.find(dex_pc) == gSingleStepControl.dex_pcs.end()) {
+          event_flags |= kSingleStep;
+          VLOG(jdwp) << "SS new line";
         }
       }
     } else {
@@ -1771,8 +1798,11 @@
   }
 }
 
-bool Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize step_size, JDWP::JdwpStepDepth step_depth) {
+JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize step_size, JDWP::JdwpStepDepth step_depth) {
   Thread* thread = DecodeThread(threadId);
+  if (thread == NULL) {
+    return JDWP::ERR_INVALID_THREAD;
+  }
 
   // TODO: there's no theoretical reason why we couldn't support single-stepping
   // of multiple threads at once, but we never did so historically.
@@ -1781,17 +1811,29 @@
                  << "; switching to " << *thread;
   }
 
+  //
+  // Work out what Method* we're in, the current line number, and how deep the stack currently
+  // is for step-out.
+  //
+
   struct SingleStepStackVisitor : public Thread::StackVisitor {
     SingleStepStackVisitor() {
       gSingleStepControl.method = NULL;
       gSingleStepControl.stack_depth = 0;
     }
-    virtual void VisitFrame(const Frame& f, uintptr_t) {
+    virtual void VisitFrame(const Frame& f, uintptr_t pc) {
       // TODO: we'll need to skip callee-save frames too.
       if (f.HasMethod()) {
         ++gSingleStepControl.stack_depth;
         if (gSingleStepControl.method == NULL) {
-          gSingleStepControl.method = f.GetMethod();
+          const Method* m = f.GetMethod();
+          const DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
+          gSingleStepControl.method = m;
+          gSingleStepControl.line_number = -1;
+          if (dex_cache != NULL) {
+            const DexFile& dex_file = Runtime::Current()->GetClassLinker()->FindDexFile(dex_cache);
+            gSingleStepControl.line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc));
+          }
         }
       }
     }
@@ -1799,17 +1841,85 @@
   SingleStepStackVisitor visitor;
   thread->WalkStack(&visitor);
 
+  //
+  // Find the dex_pc values that correspond to the current line, for line-based single-stepping.
+  //
+
+  struct DebugCallbackContext {
+    DebugCallbackContext() {
+      last_pc_valid = false;
+      last_pc = 0;
+      gSingleStepControl.dex_pcs.clear();
+    }
+
+    static bool Callback(void* raw_context, uint32_t address, uint32_t line_number) {
+      DebugCallbackContext* context = reinterpret_cast<DebugCallbackContext*>(raw_context);
+      if (static_cast<int32_t>(line_number) == gSingleStepControl.line_number) {
+        if (!context->last_pc_valid) {
+          // Everything from this address until the next line change is ours.
+          context->last_pc = address;
+          context->last_pc_valid = true;
+        }
+        // Otherwise, if we're already in a valid range for this line,
+        // just keep going (shouldn't really happen)...
+      } else if (context->last_pc_valid) { // and the line number is new
+        // Add everything from the last entry up until here to the set
+        for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) {
+          gSingleStepControl.dex_pcs.insert(dex_pc);
+        }
+        context->last_pc_valid = false;
+      }
+      return false; // There may be multiple entries for any given line.
+    }
+
+    ~DebugCallbackContext() {
+      // If the line number was the last in the position table...
+      if (last_pc_valid) {
+        size_t end = MethodHelper(gSingleStepControl.method).GetCodeItem()->insns_size_in_code_units_;
+        for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) {
+          gSingleStepControl.dex_pcs.insert(dex_pc);
+        }
+      }
+    }
+
+    bool last_pc_valid;
+    uint32_t last_pc;
+  };
+  DebugCallbackContext context;
+  const Method* m = gSingleStepControl.method;
+  MethodHelper mh(m);
+  mh.GetDexFile().DecodeDebugInfo(mh.GetCodeItem(), m->IsStatic(), m->GetDexMethodIndex(),
+                                  DebugCallbackContext::Callback, NULL, &context);
+
+  //
+  // Everything else...
+  //
+
   gSingleStepControl.thread = thread;
   gSingleStepControl.step_size = step_size;
   gSingleStepControl.step_depth = step_depth;
   gSingleStepControl.is_active = true;
 
-  return true;
+  if (VLOG_IS_ON(jdwp)) {
+    VLOG(jdwp) << "Single-step thread: " << *gSingleStepControl.thread;
+    VLOG(jdwp) << "Single-step step size: " << gSingleStepControl.step_size;
+    VLOG(jdwp) << "Single-step step depth: " << gSingleStepControl.step_depth;
+    VLOG(jdwp) << "Single-step current method: " << PrettyMethod(gSingleStepControl.method);
+    VLOG(jdwp) << "Single-step current line: " << gSingleStepControl.line_number;
+    VLOG(jdwp) << "Single-step current stack depth: " << gSingleStepControl.stack_depth;
+    VLOG(jdwp) << "Single-step dex_pc values:";
+    for (std::set<uint32_t>::iterator it = gSingleStepControl.dex_pcs.begin() ; it != gSingleStepControl.dex_pcs.end(); ++it) {
+      VLOG(jdwp) << " " << *it;
+    }
+  }
+
+  return JDWP::ERR_NONE;
 }
 
 void Dbg::UnconfigureStep(JDWP::ObjectId threadId) {
   gSingleStepControl.is_active = false;
   gSingleStepControl.thread = NULL;
+  gSingleStepControl.dex_pcs.clear();
 }
 
 JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptionId) {