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 */ 27static 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 */ 180static 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