More debugger support.

This wires up method-entry events and fixes a bug in exception events.

Change-Id: Ia7c46786a8073434fbb4546615072622f301ef84
diff --git a/src/debugger.cc b/src/debugger.cc
index 40405eb..6f381d3 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -797,7 +797,7 @@
   return gRegistry->Get<Class*>(instClassId)->InstanceOf(gRegistry->Get<Class*>(classId));
 }
 
-JDWP::FieldId ToFieldId(Field* f) {
+JDWP::FieldId ToFieldId(const Field* f) {
 #ifdef MOVING_GARBAGE_COLLECTOR
   UNIMPLEMENTED(FATAL);
 #else
@@ -805,7 +805,7 @@
 #endif
 }
 
-JDWP::MethodId ToMethodId(Method* m) {
+JDWP::MethodId ToMethodId(const Method* m) {
 #ifdef MOVING_GARBAGE_COLLECTOR
   UNIMPLEMENTED(FATAL);
 #else
@@ -830,11 +830,15 @@
 }
 
 void SetLocation(JDWP::JdwpLocation& location, Method* m, uintptr_t native_pc) {
-  Class* c = m->GetDeclaringClass();
-  location.typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
-  location.classId = gRegistry->Add(c);
-  location.methodId = ToMethodId(m);
-  location.idx = m->IsNative() ? -1 : m->ToDexPC(native_pc);
+  if (m == NULL) {
+    memset(&location, 0, sizeof(location));
+  } else {
+    Class* c = m->GetDeclaringClass();
+    location.typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+    location.classId = gRegistry->Add(c);
+    location.methodId = ToMethodId(m);
+    location.idx = m->IsNative() ? -1 : m->ToDexPC(native_pc);
+  }
 }
 
 std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
@@ -880,11 +884,12 @@
   return newSlot;
 }
 
-static uint16_t DemangleSlot(uint16_t slot, Frame& f) {
+static uint16_t DemangleSlot(uint16_t slot, Method* m) {
   if (slot == kEclipseWorkaroundSlot) {
     return 0;
   } else if (slot == 0) {
-    const DexFile::CodeItem* code_item = MethodHelper(f.GetMethod()).GetCodeItem();
+    const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
+    CHECK(code_item != NULL);
     return code_item->registers_size_ - code_item->ins_size_;
   }
   return slot;
@@ -1368,27 +1373,28 @@
   Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
 }
 
-bool Dbg::GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
-  Method** sp = reinterpret_cast<Method**>(frameId);
-  Frame f;
-  f.SetSP(sp);
+static Object* GetThis(Frame& f) {
   Method* m = f.GetMethod();
-
   Object* o = NULL;
   if (!m->IsNative() && !m->IsStatic()) {
-    uint16_t reg = DemangleSlot(0, f);
+    uint16_t reg = DemangleSlot(0, m);
     o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
   }
+  return o;
+}
+
+void Dbg::GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
+  Method** sp = reinterpret_cast<Method**>(frameId);
+  Frame f(sp);
+  Object* o = GetThis(f);
   *pThisId = gRegistry->Add(o);
-  return true;
 }
 
 void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
   Method** sp = reinterpret_cast<Method**>(frameId);
-  Frame f;
-  f.SetSP(sp);
-  uint16_t reg = DemangleSlot(slot, f);
+  Frame f(sp);
   Method* m = f.GetMethod();
+  uint16_t reg = DemangleSlot(slot, m);
 
   const VmapTable vmap_table(m->GetVmapTableRaw());
   uint32_t vmap_offset;
@@ -1476,10 +1482,9 @@
 
 void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width) {
   Method** sp = reinterpret_cast<Method**>(frameId);
-  Frame f;
-  f.SetSP(sp);
-  uint16_t reg = DemangleSlot(slot, f);
+  Frame f(sp);
   Method* m = f.GetMethod();
+  uint16_t reg = DemangleSlot(slot, m);
 
   const VmapTable vmap_table(m->GetVmapTableRaw());
   uint32_t vmap_offset;
@@ -1524,8 +1529,25 @@
   }
 }
 
-void Dbg::PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags) {
-  UNIMPLEMENTED(FATAL);
+void Dbg::PostLocationEvent(const Method* m, int dex_pc, Object* this_object, int event_flags) {
+  Class* c = m->GetDeclaringClass();
+
+  JDWP::JdwpLocation location;
+  location.typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+  location.classId = gRegistry->Add(c);
+  location.methodId = ToMethodId(m);
+  location.idx = m->IsNative() ? -1 : dex_pc;
+
+  // Note we use "NoReg" so we don't keep track of references that are
+  // never actually sent to the debugger. 'this_id' is only used to
+  // compare against registered events...
+  JDWP::ObjectId this_id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(this_object));
+  if (gJdwpState->PostLocationEvent(&location, this_id, event_flags)) {
+    // ...unless there's a registered event, in which case we
+    // need to really track the class and 'this'.
+    gRegistry->Add(c);
+    gRegistry->Add(this_object);
+  }
 }
 
 void Dbg::PostException(Method** sp, Method* throwMethod, uintptr_t throwNativePc, Method* catchMethod, uintptr_t catchNativePc, Object* exception) {
@@ -1570,6 +1592,127 @@
   gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state);
 }
 
+void Dbg::UpdateDebugger(int32_t dex_pc, Thread* self, Method** sp) {
+  if (!gDebuggerActive) {
+    return;
+  }
+
+  int event_flags = 0;
+
+  // Update xtra.currentPc on every instruction.  We need to do this if
+  // there's a chance that we could get suspended.  This can happen if
+  // event_flags != 0 here, or somebody manually requests a suspend
+  // (which gets handled at PERIOD_CHECKS time).  One place where this
+  // needs to be correct is in dvmAddSingleStep().
+  //dvmExportPC(pc, fp);
+
+  // We use a pc of -1 to represent method entry, since we might branch back to pc 0 later.
+  if (dex_pc == -1) {
+    event_flags |= kMethodEntry;
+  }
+
+  /*
+  // See if we have a breakpoint here.
+  // Depending on the "mods" associated with event(s) on this address,
+  // we may or may not actually send a message to the debugger.
+  if (GET_OPCODE(*pc) == OP_BREAKPOINT) {
+    ALOGV("+++ breakpoint hit at %p", pc);
+    event_flags |= kBreakPoint;
+  }
+  */
+
+  /*
+  // If the debugger is single-stepping one of our threads, check to
+  // see if we're that thread and we've reached a step point.
+  const StepControl* pCtrl = &gDvm.stepControl;
+  if (pCtrl->active && pCtrl->thread == self) {
+    int frameDepth;
+    bool doStop = false;
+    const char* msg = NULL;
+
+    assert(!dvmIsNativeMethod(method));
+
+    if (pCtrl->depth == SD_INTO) {
+      // Step into method calls.  We break when the line number
+      // or method pointer changes.  If we're in SS_MIN mode, we
+      // always stop.
+      if (pCtrl->method != method) {
+        doStop = true;
+        msg = "new method";
+      } else if (pCtrl->size == SS_MIN) {
+        doStop = true;
+        msg = "new instruction";
+      } else if (!dvmAddressSetGet(pCtrl->pAddressSet, pc - method->insns)) {
+        doStop = true;
+        msg = "new line";
+      }
+    } else if (pCtrl->depth == SD_OVER) {
+      // Step over method calls.  We break when the line number is
+      // different and the frame depth is <= the original frame
+      // depth.  (We can't just compare on the method, because we
+      // might get unrolled past it by an exception, and it's tricky
+      // to identify recursion.)
+      frameDepth = dvmComputeVagueFrameDepth(self, fp);
+      if (frameDepth < pCtrl->frameDepth) {
+        // popped up one or more frames, always trigger
+        doStop = true;
+        msg = "method pop";
+      } else if (frameDepth == pCtrl->frameDepth) {
+        // same depth, see if we moved
+        if (pCtrl->size == SS_MIN) {
+          doStop = true;
+          msg = "new instruction";
+        } else if (!dvmAddressSetGet(pCtrl->pAddressSet, pc - method->insns)) {
+          doStop = true;
+          msg = "new line";
+        }
+      }
+    } else {
+      assert(pCtrl->depth == SD_OUT);
+      // Return from the current method.  We break when the frame
+      // depth pops up.
+
+      // This differs from the "method exit" break in that it stops
+      // with the PC at the next instruction in the returned-to
+      // function, rather than the end of the returning function.
+      frameDepth = dvmComputeVagueFrameDepth(self, fp);
+      if (frameDepth < pCtrl->frameDepth) {
+        doStop = true;
+        msg = "method pop";
+      }
+    }
+
+    if (doStop) {
+      ALOGV("#####S %s", msg);
+      event_flags |= kSingleStep;
+    }
+  }
+  */
+
+  /*
+  // Check to see if this is a "return" instruction.  JDWP says we should
+  // send the event *after* the code has been executed, but it also says
+  // the location we provide is the last instruction.  Since the "return"
+  // instruction has no interesting side effects, we should be safe.
+  // (We can't just move this down to the returnFromMethod label because
+  // we potentially need to combine it with other events.)
+  // We're also not supposed to generate a method exit event if the method
+  // terminates "with a thrown exception".
+  u2 opcode = GET_OPCODE(*pc);
+  if (opcode == OP_RETURN_VOID || opcode == OP_RETURN || opcode == OP_RETURN_WIDE ||opcode == OP_RETURN_OBJECT) {
+    event_flags |= kMethodExit;
+  }
+  */
+
+  // If there's something interesting going on, see if it matches one
+  // of the debugger filters.
+  if (event_flags != 0) {
+    Frame f(sp);
+    f.Next(); // Skip callee save frame.
+    Dbg::PostLocationEvent(f.GetMethod(), dex_pc, GetThis(f), event_flags);
+  }
+}
+
 bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
   UNIMPLEMENTED(FATAL);
   return false;
diff --git a/src/debugger.h b/src/debugger.h
index 8a702a1..dbb99ff 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -201,7 +201,7 @@
   static void ResumeThread(JDWP::ObjectId threadId);
   static void SuspendSelf();
 
-  static bool GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId);
+  static void GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId);
   static void GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen);
   static void SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width);
 
@@ -220,6 +220,8 @@
   static void PostThreadDeath(Thread* t);
   static void PostClassPrepare(Class* c);
 
+  static void UpdateDebugger(int32_t dex_pc, Thread* self, Method** sp);
+
   static bool WatchLocation(const JDWP::JdwpLocation* pLoc);
   static void UnwatchLocation(const JDWP::JdwpLocation* pLoc);
   static bool ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth);
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index f3e98f5..c0e6295 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -352,12 +352,8 @@
  */
 static bool PatternMatch(const char* pattern, const std::string& target) {
   size_t patLen = strlen(pattern);
-
   if (pattern[0] == '*') {
     patLen--;
-    // TODO: remove printf when we find a test case to verify this
-    LOG(ERROR) << ">>> comparing '" << (pattern + 1) << "' to '" << (target.c_str() + (target.size()-patLen)) << "'";
-
     if (target.size() < patLen) {
       return false;
     }
@@ -701,12 +697,6 @@
   return true;
 }
 
-// TODO: This doesn't behave like the real dvmDescriptorToName.
-// I'm hoping this isn't used to communicate with the debugger, and we can just use descriptors.
-std::string dvmDescriptorToName(const std::string& descriptor) {
-  return descriptor;
-}
-
 /*
  * A location of interest has been reached.  This handles:
  *   Breakpoint
@@ -736,7 +726,7 @@
   basket.classId = pLoc->classId;
   basket.thisPtr = thisPtr;
   basket.threadId = Dbg::GetThreadSelfId();
-  basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(pLoc->classId));
+  basket.className = DescriptorToName(Dbg::GetClassDescriptor(pLoc->classId).c_str());
 
   /*
    * On rare occasions we may need to execute interpreted code in the VM
@@ -931,7 +921,7 @@
   basket.pLoc = pThrowLoc;
   basket.classId = pThrowLoc->classId;
   basket.threadId = Dbg::GetThreadSelfId();
-  basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
+  basket.className = DescriptorToName(Dbg::GetClassDescriptor(basket.classId).c_str());
   basket.excepClassId = exceptionClassId;
   basket.caught = (pCatchLoc->classId != 0);
   basket.thisPtr = thisPtr;
@@ -1014,7 +1004,7 @@
   memset(&basket, 0, sizeof(basket));
   basket.classId = refTypeId;
   basket.threadId = Dbg::GetThreadSelfId();
-  basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
+  basket.className = DescriptorToName(Dbg::GetClassDescriptor(basket.classId).c_str());
 
   /* suppress class prep caused by debugger */
   if (InvokeInProgress()) {
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 7914aa7..3dbf437 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -1482,9 +1482,7 @@
   FrameId frameId = ReadFrameId(&buf);
 
   ObjectId objectId;
-  if (!Dbg::GetThisObject(frameId, &objectId)) {
-    return ERR_INVALID_FRAMEID;
-  }
+  Dbg::GetThisObject(frameId, &objectId);
 
   uint8_t objectTag = Dbg::GetObjectTag(objectId);
   VLOG(jdwp) << StringPrintf("  Req for 'this' in frame=%llx --> %llx '%c'", frameId, objectId, (char)objectTag);
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index eb06df5..670049a 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -16,6 +16,7 @@
 
 #include "runtime_support.h"
 
+#include "debugger.h"
 #include "dex_cache.h"
 #include "dex_verifier.h"
 #include "macros.h"
@@ -100,14 +101,14 @@
 }
 
 /*
- * Report location to debugger.  Note: dalvikPC is the current offset within
+ * Report location to debugger.  Note: dex_pc is the current offset within
  * the method.  However, because the offset alone cannot distinguish between
  * method entry and offset 0 within the method, we'll use an offset of -1
  * to denote method entry.
  */
-extern "C" void artUpdateDebuggerFromCode(int32_t dalvikPC, Thread* self, Method** sp) {
+extern "C" void artUpdateDebuggerFromCode(int32_t dex_pc, Thread* self, Method** sp) {
   FinishCalleeSaveFrameSetup(self, sp,  Runtime::kRefsAndArgs);
-    // TODO: fill this out similar to old "updateDebugger"
+  Dbg::UpdateDebugger(dex_pc, self, sp);
 }
 
 // Temporary debugging hook for compiler.
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 954c362..a5e4c87 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -28,7 +28,7 @@
 extern Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
                                          Thread* self, bool access_check);
 extern void DebugMe(Method* method, uint32_t info);
-extern void UpdateDebuggerFromCode(Method* method, Thread* thread , int32_t dalvikPC, Method** sp);
+extern void UpdateDebuggerFromCode(Method* method, Thread* thread , int32_t dex_pc, Method** sp);
 extern Object* DecodeJObjectInThread(Thread* thread, jobject obj);
 extern Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer, Thread* self,
                                 bool is_static, bool is_primitive, size_t expected_size);
diff --git a/src/runtime_support_arm.S b/src/runtime_support_arm.S
index 7456853..69e908d 100644
--- a/src/runtime_support_arm.S
+++ b/src/runtime_support_arm.S
@@ -67,15 +67,15 @@
     .global art_update_debugger
     .extern artUpdateDebuggerFromCode
     /*
-     * On entry, r0 and r1 must be preserved, r2 is DalvikPC
+     * On entry, r0 and r1 must be preserved, r2 is dex PC
      */
 art_update_debugger:
     mov    r3, r0         @ stash away r0 so that it's saved as if it were an argument
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
-    mov    r0, r2         @ arg0 is DalvikPC
+    mov    r0, r2         @ arg0 is dex PC
     mov    r1, rSELF      @ arg1 is Thread*
     mov    r2, sp         @ arg2 is sp
-    bl     artUpdateDebuggerFromCode      @ artUpdateDebuggerFromCode(Method*, Thread*, dPC, sp)
+    bl     artUpdateDebuggerFromCode      @ artUpdateDebuggerFromCode(int32_t, Thread*, Method**)
     RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
     mov    r0, r3         @ restore original r0
     bx     lr
diff --git a/src/stack.h b/src/stack.h
index 3a0903f..382ad24 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -41,6 +41,8 @@
  public:
   Frame() : sp_(NULL) {}
 
+  Frame(Method** sp) : sp_(sp) {}
+
   Method* GetMethod() const {
     return (sp_ != NULL) ? *sp_ : NULL;
   }
@@ -68,8 +70,6 @@
     return sp_;
   }
 
-  // TODO: this is here for testing, remove when we have exception unit tests
-  // that use the real stack
   void SetSP(Method** sp) {
     sp_ = sp;
   }
diff --git a/src/utils.cc b/src/utils.cc
index 09a01c6..f599c0e 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -375,13 +375,18 @@
 }
 
 std::string DescriptorToDot(const char* descriptor) {
+  std::string result(DescriptorToName(descriptor));
+  std::replace(result.begin(), result.end(), '/', '.');
+  return result;
+}
+
+std::string DescriptorToName(const char* descriptor) {
   size_t length = strlen(descriptor);
   DCHECK_GT(length, 0U);
   DCHECK_EQ(descriptor[0], 'L');
   DCHECK_EQ(descriptor[length - 1], ';');
-  std::string dot(descriptor + 1, length - 2);
-  std::replace(dot.begin(), dot.end(), '/', '.');
-  return dot;
+  std::string result(descriptor + 1, length - 2);
+  return result;
 }
 
 std::string JniShortName(const Method* m) {
diff --git a/src/utils.h b/src/utils.h
index 39a5d4f..e774a3b 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -218,6 +218,9 @@
 // Turn "Ljava/lang/String;" into "java.lang.String".
 std::string DescriptorToDot(const char* descriptor);
 
+// Turn "Ljava/lang/String;" into "java/lang/String".
+std::string DescriptorToName(const char* descriptor);
+
 // Tests for whether 's' is a valid class name in the three common forms:
 bool IsValidBinaryClassName(const char* s);  // "java.lang.String"
 bool IsValidJniClassName(const char* s);     // "java/lang/String"