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