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