Home | History | Annotate | Download | only in portable
      1 /* code in here is only included in portable-debug interpreter */
      2 
      3 /*
      4  * Update the debugger on interesting events, such as hitting a breakpoint
      5  * or a single-step point.  This is called from the top of the interpreter
      6  * loop, before the current instruction is processed.
      7  *
      8  * Set "methodEntry" if we've just entered the method.  This detects
      9  * method exit by checking to see if the next instruction is "return".
     10  *
     11  * This can't catch native method entry/exit, so we have to handle that
     12  * at the point of invocation.  We also need to catch it in dvmCallMethod
     13  * if we want to capture native->native calls made through JNI.
     14  *
     15  * Notes to self:
     16  * - Don't want to switch to VMWAIT while posting events to the debugger.
     17  *   Let the debugger code decide if we need to change state.
     18  * - We may want to check for debugger-induced thread suspensions on
     19  *   every instruction.  That would make a "suspend all" more responsive
     20  *   and reduce the chances of multiple simultaneous events occurring.
     21  *   However, it could change the behavior some.
     22  *
     23  * TODO: method entry/exit events are probably less common than location
     24  * breakpoints.  We may be able to speed things up a bit if we don't query
     25  * the event list unless we know there's at least one lurking within.
     26  */
     27 static void updateDebugger(const Method* method, const u2* pc, const u4* fp,
     28     bool methodEntry, Thread* self)
     29 {
     30     int eventFlags = 0;
     31 
     32     /*
     33      * Update xtra.currentPc on every instruction.  We need to do this if
     34      * there's a chance that we could get suspended.  This can happen if
     35      * eventFlags != 0 here, or somebody manually requests a suspend
     36      * (which gets handled at PERIOD_CHECKS time).  One place where this
     37      * needs to be correct is in dvmAddSingleStep().
     38      */
     39     EXPORT_PC();
     40 
     41     if (methodEntry)
     42         eventFlags |= DBG_METHOD_ENTRY;
     43 
     44     /*
     45      * See if we have a breakpoint here.
     46      *
     47      * Depending on the "mods" associated with event(s) on this address,
     48      * we may or may not actually send a message to the debugger.
     49      */
     50     if (INST_INST(*pc) == OP_BREAKPOINT) {
     51         LOGV("+++ breakpoint hit at %p\n", pc);
     52         eventFlags |= DBG_BREAKPOINT;
     53     }
     54 
     55     /*
     56      * If the debugger is single-stepping one of our threads, check to
     57      * see if we're that thread and we've reached a step point.
     58      */
     59     const StepControl* pCtrl = &gDvm.stepControl;
     60     if (pCtrl->active && pCtrl->thread == self) {
     61         int frameDepth;
     62         bool doStop = false;
     63         const char* msg = NULL;
     64 
     65         assert(!dvmIsNativeMethod(method));
     66 
     67         if (pCtrl->depth == SD_INTO) {
     68             /*
     69              * Step into method calls.  We break when the line number
     70              * or method pointer changes.  If we're in SS_MIN mode, we
     71              * always stop.
     72              */
     73             if (pCtrl->method != method) {
     74                 doStop = true;
     75                 msg = "new method";
     76             } else if (pCtrl->size == SS_MIN) {
     77                 doStop = true;
     78                 msg = "new instruction";
     79             } else if (!dvmAddressSetGet(
     80                     pCtrl->pAddressSet, pc - method->insns)) {
     81                 doStop = true;
     82                 msg = "new line";
     83             }
     84         } else if (pCtrl->depth == SD_OVER) {
     85             /*
     86              * Step over method calls.  We break when the line number is
     87              * different and the frame depth is <= the original frame
     88              * depth.  (We can't just compare on the method, because we
     89              * might get unrolled past it by an exception, and it's tricky
     90              * to identify recursion.)
     91              */
     92             frameDepth = dvmComputeVagueFrameDepth(self, fp);
     93             if (frameDepth < pCtrl->frameDepth) {
     94                 /* popped up one or more frames, always trigger */
     95                 doStop = true;
     96                 msg = "method pop";
     97             } else if (frameDepth == pCtrl->frameDepth) {
     98                 /* same depth, see if we moved */
     99                 if (pCtrl->size == SS_MIN) {
    100                     doStop = true;
    101                     msg = "new instruction";
    102                 } else if (!dvmAddressSetGet(pCtrl->pAddressSet,
    103                             pc - method->insns)) {
    104                     doStop = true;
    105                     msg = "new line";
    106                 }
    107             }
    108         } else {
    109             assert(pCtrl->depth == SD_OUT);
    110             /*
    111              * Return from the current method.  We break when the frame
    112              * depth pops up.
    113              *
    114              * This differs from the "method exit" break in that it stops
    115              * with the PC at the next instruction in the returned-to
    116              * function, rather than the end of the returning function.
    117              */
    118             frameDepth = dvmComputeVagueFrameDepth(self, fp);
    119             if (frameDepth < pCtrl->frameDepth) {
    120                 doStop = true;
    121                 msg = "method pop";
    122             }
    123         }
    124 
    125         if (doStop) {
    126             LOGV("#####S %s\n", msg);
    127             eventFlags |= DBG_SINGLE_STEP;
    128         }
    129     }
    130 
    131     /*
    132      * Check to see if this is a "return" instruction.  JDWP says we should
    133      * send the event *after* the code has been executed, but it also says
    134      * the location we provide is the last instruction.  Since the "return"
    135      * instruction has no interesting side effects, we should be safe.
    136      * (We can't just move this down to the returnFromMethod label because
    137      * we potentially need to combine it with other events.)
    138      *
    139      * We're also not supposed to generate a method exit event if the method
    140      * terminates "with a thrown exception".
    141      */
    142     u2 inst = INST_INST(FETCH(0));
    143     if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE ||
    144         inst == OP_RETURN_OBJECT)
    145     {
    146         eventFlags |= DBG_METHOD_EXIT;
    147     }
    148 
    149     /*
    150      * If there's something interesting going on, see if it matches one
    151      * of the debugger filters.
    152      */
    153     if (eventFlags != 0) {
    154         Object* thisPtr = dvmGetThisPtr(method, fp);
    155         if (thisPtr != NULL && !dvmIsValidObject(thisPtr)) {
    156             /*
    157              * TODO: remove this check if we're confident that the "this"
    158              * pointer is where it should be -- slows us down, especially
    159              * during single-step.
    160              */
    161             char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
    162             LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr,
    163                 method->clazz->descriptor, method->name, desc);
    164             free(desc);
    165             dvmAbort();
    166         }
    167         dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
    168             eventFlags);
    169     }
    170 }
    171 
    172 /*
    173  * Perform some operations at the "top" of the interpreter loop.
    174  * This stuff is required to support debugging and profiling.
    175  *
    176  * Using" __attribute__((noinline))" seems to do more harm than good.  This
    177  * is best when inlined due to the large number of parameters, most of
    178  * which are local vars in the main interp loop.
    179  */
    180 static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self,
    181     const Method* method, bool* pIsMethodEntry)
    182 {
    183     /* check to see if we've run off end of method */
    184     assert(pc >= method->insns && pc <
    185             method->insns + dvmGetMethodInsnsSize(method));
    186 
    187 #if 0
    188     /*
    189      * When we hit a specific method, enable verbose instruction logging.
    190      * Sometimes it's helpful to use the debugger attach as a trigger too.
    191      */
    192     if (*pIsMethodEntry) {
    193         static const char* cd = "Landroid/test/Arithmetic;";
    194         static const char* mn = "shiftTest2";
    195         static const char* sg = "()V";
    196 
    197         if (/*gDvm.debuggerActive &&*/
    198             strcmp(method->clazz->descriptor, cd) == 0 &&
    199             strcmp(method->name, mn) == 0 &&
    200             strcmp(method->shorty, sg) == 0)
    201         {
    202             LOGW("Reached %s.%s, enabling verbose mode\n",
    203                 method->clazz->descriptor, method->name);
    204             android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
    205             dumpRegs(method, fp, true);
    206         }
    207 
    208         if (!gDvm.debuggerActive)
    209             *pIsMethodEntry = false;
    210     }
    211 #endif
    212 
    213     /*
    214      * If the debugger is attached, check for events.  If the profiler is
    215      * enabled, update that too.
    216      *
    217      * This code is executed for every instruction we interpret, so for
    218      * performance we use a couple of #ifdef blocks instead of runtime tests.
    219      */
    220     bool isEntry = *pIsMethodEntry;
    221     if (isEntry) {
    222         *pIsMethodEntry = false;
    223         TRACE_METHOD_ENTER(self, method);
    224     }
    225     if (gDvm.debuggerActive) {
    226         updateDebugger(method, pc, fp, isEntry, self);
    227     }
    228     if (gDvm.instructionCountEnableCount != 0) {
    229         /*
    230          * Count up the #of executed instructions.  This isn't synchronized
    231          * for thread-safety; if we need that we should make this
    232          * thread-local and merge counts into the global area when threads
    233          * exit (perhaps suspending all other threads GC-style and pulling
    234          * the data out of them).
    235          */
    236         int inst = *pc & 0xff;
    237         gDvm.executedInstrCounts[inst]++;
    238     }
    239 }
    240