text: /* code in here is only included in portable-debug interpreter */
  
  /*
   * Determine if an address is "interesting" to the debugger.  This allows
   * us to avoid scanning the entire event list before every instruction.
   *
   * The "debugBreakAddr" table is global and not synchronized.
   */
  static bool isInterestingAddr(const u2* pc)
  {
      const u2** ptr = gDvm.debugBreakAddr;
      int i;
  
      for (i = 0; i < MAX_BREAKPOINTS; i++, ptr++) {
          if (*ptr == pc) {
              LOGV("BKP: hit on %p\n", pc);
              return true;
          }
      }
      return false;
  }
  
  /*
   * Update the debugger on interesting events, such as hitting a breakpoint
   * or a single-step point.  This is called from the top of the interpreter
   * loop, before the current instruction is processed.
   *
   * Set "methodEntry" if we've just entered the method.  This detects
   * method exit by checking to see if the next instruction is "return".
   *
   * This can't catch native method entry/exit, so we have to handle that
   * at the point of invocation.  We also need to catch it in dvmCallMethod
   * if we want to capture native->native calls made through JNI.
   *
   * Notes to self:
   * - Don't want to switch to VMWAIT while posting events to the debugger.
   *   Let the debugger code decide if we need to change state.
   * - We may want to check for debugger-induced thread suspensions on
   *   every instruction.  That would make a "suspend all" more responsive
   *   and reduce the chances of multiple simultaneous events occurring.
   *   However, it could change the behavior some.
   *
   * TODO: method entry/exit events are probably less common than location
   * breakpoints.  We may be able to speed things up a bit if we don't query
   * the event list unless we know there's at least one lurking within.
   */
  static void updateDebugger(const Method* method, const u2* pc, const u4* fp,
      bool methodEntry, Thread* self)
  {
      int eventFlags = 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
       * eventFlags != 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().
       */
      EXPORT_PC();
  
      if (methodEntry)
          eventFlags |= DBG_METHOD_ENTRY;
  
      /*
       * 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.
       *
       * Checking method->debugBreakpointCount is slower on the device than
       * just scanning the table (!).  We could probably work something out
       * where we just check it on method entry/exit and remember the result,
       * but that's more fragile and requires passing more stuff around.
       */
  #ifdef WITH_DEBUGGER
      if (method->debugBreakpointCount > 0 && isInterestingAddr(pc)) {
          eventFlags |= DBG_BREAKPOINT;
      }
  #endif
  
      /*
       * 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 line, 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) {
              LOGV("#####S %s\n", msg);
              eventFlags |= DBG_SINGLE_STEP;
          }
      }
  
      /*
       * 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 inst = INST_INST(FETCH(0));
      if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE ||
          inst == OP_RETURN_OBJECT)
      {
          eventFlags |= DBG_METHOD_EXIT;
      }
  
      /*
       * If there's something interesting going on, see if it matches one
       * of the debugger filters.
       */
      if (eventFlags != 0) {
          Object* thisPtr = dvmGetThisPtr(method, fp);
          if (thisPtr != NULL && !dvmIsValidObject(thisPtr)) {
              /*
               * TODO: remove this check if we're confident that the "this"
               * pointer is where it should be -- slows us down, especially
               * during single-step.
               */
              char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
              LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr,
                  method->clazz->descriptor, method->name, desc);
              free(desc);
              dvmAbort();
          }
          dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
              eventFlags);
      }
  }
  
  /*
   * Perform some operations at the "top" of the interpreter loop.
   * This stuff is required to support debugging and profiling.
   *
   * Using" __attribute__((noinline))" seems to do more harm than good.  This
   * is best when inlined due to the large number of parameters, most of
   * which are local vars in the main interp loop.
   */
  static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self,
      const Method* method, bool* pIsMethodEntry)
  {
      /* check to see if we've run off end of method */
      assert(pc >= method->insns && pc <
              method->insns + dvmGetMethodInsnsSize(method));
  
  #if 0
      /*
       * When we hit a specific method, enable verbose instruction logging.
       * Sometimes it's helpful to use the debugger attach as a trigger too.
       */
      if (*pIsMethodEntry) {
          static const char* cd = "Landroid/test/Arithmetic;";
          static const char* mn = "shiftTest2";
          static const char* sg = "()V";
  
          if (/*gDvm.debuggerActive &&*/
              strcmp(method->clazz->descriptor, cd) == 0 &&
              strcmp(method->name, mn) == 0 &&
              strcmp(method->shorty, sg) == 0)
          {
              LOGW("Reached %s.%s, enabling verbose mode\n",
                  method->clazz->descriptor, method->name);
              android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
              dumpRegs(method, fp, true);
          }
  
          if (!gDvm.debuggerActive)
              *pIsMethodEntry = false;
      }
  #endif
  
      /*
       * If the debugger is attached, check for events.  If the profiler is
       * enabled, update that too.
       *
       * This code is executed for every instruction we interpret, so for
       * performance we use a couple of #ifdef blocks instead of runtime tests.
       */
  #ifdef WITH_PROFILER
      /* profiler and probably debugger */
      bool isEntry = *pIsMethodEntry;
      if (isEntry) {
          *pIsMethodEntry = false;
          TRACE_METHOD_ENTER(self, method);
      }
      if (gDvm.debuggerActive) {
          updateDebugger(method, pc, fp, isEntry, self);
      }
      if (gDvm.instructionCountEnableCount != 0) {
          /*
           * Count up the #of executed instructions.  This isn't synchronized
           * for thread-safety; if we need that we should make this
           * thread-local and merge counts into the global area when threads
           * exit (perhaps suspending all other threads GC-style and pulling
           * the data out of them).
           */
          int inst = *pc & 0xff;
          gDvm.executedInstrCounts[inst]++;
      }
  #else
      /* debugger only */
      if (gDvm.debuggerActive) {
          bool isEntry = *pIsMethodEntry;
          updateDebugger(method, pc, fp, isEntry, self);
          if (isEntry)
              *pIsMethodEntry = false;
      }
  #endif
  }
  
  