• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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