• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
updateDebugger(const Method * method,const u2 * pc,const u4 * fp,bool methodEntry,Thread * self)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  */
checkDebugAndProf(const u2 * pc,const u4 * fp,Thread * self,const Method * method,bool * pIsMethodEntry)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