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