1 | /*
|
---|
2 | * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
|
---|
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
---|
4 | *
|
---|
5 | * This code is free software; you can redistribute it and/or modify it
|
---|
6 | * under the terms of the GNU General Public License version 2 only, as
|
---|
7 | * published by the Free Software Foundation. Oracle designates this
|
---|
8 | * particular file as subject to the "Classpath" exception as provided
|
---|
9 | * by Oracle in the LICENSE file that accompanied this code.
|
---|
10 | *
|
---|
11 | * This code is distributed in the hope that it will be useful, but WITHOUT
|
---|
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
14 | * version 2 for more details (a copy is included in the LICENSE file that
|
---|
15 | * accompanied this code).
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License version
|
---|
18 | * 2 along with this work; if not, write to the Free Software Foundation,
|
---|
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
---|
20 | *
|
---|
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
---|
22 | * or visit www.oracle.com if you need additional information or have any
|
---|
23 | * questions.
|
---|
24 | */
|
---|
25 |
|
---|
26 | #include "util.h"
|
---|
27 | #include "stepControl.h"
|
---|
28 | #include "eventHandler.h"
|
---|
29 | #include "eventHelper.h"
|
---|
30 | #include "threadControl.h"
|
---|
31 | #include "SDE.h"
|
---|
32 |
|
---|
33 | static jrawMonitorID stepLock;
|
---|
34 |
|
---|
35 | static jint
|
---|
36 | getFrameCount(jthread thread)
|
---|
37 | {
|
---|
38 | jint count = 0;
|
---|
39 | jvmtiError error;
|
---|
40 |
|
---|
41 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameCount)
|
---|
42 | (gdata->jvmti, thread, &count);
|
---|
43 | if (error != JVMTI_ERROR_NONE) {
|
---|
44 | EXIT_ERROR(error, "getting frame count");
|
---|
45 | }
|
---|
46 | return count;
|
---|
47 | }
|
---|
48 |
|
---|
49 | /*
|
---|
50 | * Most enabling/disabling of JVMTI events happens implicitly through
|
---|
51 | * the inserting and freeing of handlers for those events. Stepping is
|
---|
52 | * different because requested steps are usually not identical to JVMTI steps.
|
---|
53 | * They usually require multiple events step, and otherwise, before they
|
---|
54 | * complete. While a step request is pending, we may need to temporarily
|
---|
55 | * disable and re-enable stepping, but we can't just remove the handlers
|
---|
56 | * because that would break the application's ability to remove the
|
---|
57 | * events. So, for step events only, we directly enable and disable stepping.
|
---|
58 | * This is safe because there can only ever be one pending step request
|
---|
59 | * per thread.
|
---|
60 | */
|
---|
61 | static void
|
---|
62 | enableStepping(jthread thread)
|
---|
63 | {
|
---|
64 | jvmtiError error;
|
---|
65 |
|
---|
66 | LOG_STEP(("enableStepping: thread=%p", thread));
|
---|
67 |
|
---|
68 | error = threadControl_setEventMode(JVMTI_ENABLE, EI_SINGLE_STEP,
|
---|
69 | thread);
|
---|
70 | if (error != JVMTI_ERROR_NONE) {
|
---|
71 | EXIT_ERROR(error, "enabling single step");
|
---|
72 | }
|
---|
73 | }
|
---|
74 |
|
---|
75 | static void
|
---|
76 | disableStepping(jthread thread)
|
---|
77 | {
|
---|
78 | jvmtiError error;
|
---|
79 |
|
---|
80 | LOG_STEP(("disableStepping: thread=%p", thread));
|
---|
81 |
|
---|
82 | error = threadControl_setEventMode(JVMTI_DISABLE, EI_SINGLE_STEP,
|
---|
83 | thread);
|
---|
84 | if (error != JVMTI_ERROR_NONE) {
|
---|
85 | EXIT_ERROR(error, "disabling single step");
|
---|
86 | }
|
---|
87 | }
|
---|
88 |
|
---|
89 | static jvmtiError
|
---|
90 | getFrameLocation(jthread thread,
|
---|
91 | jclass *pclazz, jmethodID *pmethod, jlocation *plocation)
|
---|
92 | {
|
---|
93 | jvmtiError error;
|
---|
94 |
|
---|
95 | *pclazz = NULL;
|
---|
96 | *pmethod = NULL;
|
---|
97 | *plocation = -1;
|
---|
98 |
|
---|
99 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
|
---|
100 | (gdata->jvmti, thread, 0, pmethod, plocation);
|
---|
101 | if (error == JVMTI_ERROR_NONE && *pmethod!=NULL ) {
|
---|
102 | /* This also serves to verify that the methodID is valid */
|
---|
103 | error = methodClass(*pmethod, pclazz);
|
---|
104 | }
|
---|
105 | return error;
|
---|
106 | }
|
---|
107 |
|
---|
108 | static void
|
---|
109 | getLineNumberTable(jmethodID method, jint *pcount,
|
---|
110 | jvmtiLineNumberEntry **ptable)
|
---|
111 | {
|
---|
112 | jvmtiError error;
|
---|
113 |
|
---|
114 | *pcount = 0;
|
---|
115 | *ptable = NULL;
|
---|
116 |
|
---|
117 | /* If the method is native or obsolete, don't even ask for the line table */
|
---|
118 | if ( isMethodObsolete(method) || isMethodNative(method)) {
|
---|
119 | return;
|
---|
120 | }
|
---|
121 |
|
---|
122 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetLineNumberTable)
|
---|
123 | (gdata->jvmti, method, pcount, ptable);
|
---|
124 | if (error != JVMTI_ERROR_NONE) {
|
---|
125 | *pcount = 0;
|
---|
126 | }
|
---|
127 | }
|
---|
128 |
|
---|
129 | static jint
|
---|
130 | findLineNumber(jthread thread, jlocation location,
|
---|
131 | jvmtiLineNumberEntry *lines, jint count)
|
---|
132 | {
|
---|
133 | jint line = -1;
|
---|
134 |
|
---|
135 | if (location != -1) {
|
---|
136 | if (count > 0) {
|
---|
137 | jint i;
|
---|
138 | /* any preface before first line is assigned to first line */
|
---|
139 | for (i=1; i<count; i++) {
|
---|
140 | if (location < lines[i].start_location) {
|
---|
141 | break;
|
---|
142 | }
|
---|
143 | }
|
---|
144 | line = lines[i-1].line_number;
|
---|
145 | }
|
---|
146 | }
|
---|
147 | return line;
|
---|
148 | }
|
---|
149 |
|
---|
150 | static jboolean
|
---|
151 | hasLineNumbers(jmethodID method)
|
---|
152 | {
|
---|
153 | jint count;
|
---|
154 | jvmtiLineNumberEntry *table;
|
---|
155 |
|
---|
156 | getLineNumberTable(method, &count, &table);
|
---|
157 | if ( count == 0 ) {
|
---|
158 | return JNI_FALSE;
|
---|
159 | } else {
|
---|
160 | jvmtiDeallocate(table);
|
---|
161 | }
|
---|
162 | return JNI_TRUE;
|
---|
163 | }
|
---|
164 |
|
---|
165 | static jvmtiError
|
---|
166 | initState(JNIEnv *env, jthread thread, StepRequest *step)
|
---|
167 | {
|
---|
168 | jvmtiError error;
|
---|
169 |
|
---|
170 | /*
|
---|
171 | * Initial values that may be changed below
|
---|
172 | */
|
---|
173 | step->fromLine = -1;
|
---|
174 | step->fromNative = JNI_FALSE;
|
---|
175 | step->frameExited = JNI_FALSE;
|
---|
176 | step->fromStackDepth = getFrameCount(thread);
|
---|
177 |
|
---|
178 | if (step->fromStackDepth <= 0) {
|
---|
179 | /*
|
---|
180 | * If there are no stack frames, treat the step as though
|
---|
181 | * from a native frame. This is most likely to occur at the
|
---|
182 | * beginning of a debug session, right after the VM_INIT event,
|
---|
183 | * so we need to do something intelligent.
|
---|
184 | */
|
---|
185 | step->fromNative = JNI_TRUE;
|
---|
186 | return JVMTI_ERROR_NONE;
|
---|
187 | }
|
---|
188 |
|
---|
189 | /*
|
---|
190 | * Try to get a notification on frame pop. If we're in an opaque frame
|
---|
191 | * we won't be able to, but we can use other methods to detect that
|
---|
192 | * a native frame has exited.
|
---|
193 | *
|
---|
194 | * TO DO: explain the need for this notification.
|
---|
195 | */
|
---|
196 | error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
|
---|
197 | (gdata->jvmti, thread, 0);
|
---|
198 | if (error == JVMTI_ERROR_OPAQUE_FRAME) {
|
---|
199 | step->fromNative = JNI_TRUE;
|
---|
200 | error = JVMTI_ERROR_NONE;
|
---|
201 | /* continue without error */
|
---|
202 | } else if (error == JVMTI_ERROR_DUPLICATE) {
|
---|
203 | error = JVMTI_ERROR_NONE;
|
---|
204 | /* Already being notified, continue without error */
|
---|
205 | } else if (error != JVMTI_ERROR_NONE) {
|
---|
206 | return error;
|
---|
207 | }
|
---|
208 |
|
---|
209 | LOG_STEP(("initState(): frame=%d", step->fromStackDepth));
|
---|
210 |
|
---|
211 | /*
|
---|
212 | * Note: we can't undo the frame pop notify, so
|
---|
213 | * we'll just have to let the handler ignore it if
|
---|
214 | * there are any errors below.
|
---|
215 | */
|
---|
216 |
|
---|
217 | if (step->granularity == JDWP_STEP_SIZE(LINE) ) {
|
---|
218 |
|
---|
219 | LOG_STEP(("initState(): Begin line step"));
|
---|
220 |
|
---|
221 | WITH_LOCAL_REFS(env, 1) {
|
---|
222 |
|
---|
223 | jclass clazz;
|
---|
224 | jmethodID method;
|
---|
225 | jlocation location;
|
---|
226 |
|
---|
227 | error = getFrameLocation(thread, &clazz, &method, &location);
|
---|
228 | if (error == JVMTI_ERROR_NONE) {
|
---|
229 | /* Clear out previous line table only if we changed methods */
|
---|
230 | if ( method != step->method ) {
|
---|
231 | step->lineEntryCount = 0;
|
---|
232 | if (step->lineEntries != NULL) {
|
---|
233 | jvmtiDeallocate(step->lineEntries);
|
---|
234 | step->lineEntries = NULL;
|
---|
235 | }
|
---|
236 | step->method = method;
|
---|
237 | getLineNumberTable(step->method,
|
---|
238 | &step->lineEntryCount, &step->lineEntries);
|
---|
239 | if (step->lineEntryCount > 0) {
|
---|
240 | convertLineNumberTable(env, clazz,
|
---|
241 | &step->lineEntryCount, &step->lineEntries);
|
---|
242 | }
|
---|
243 | }
|
---|
244 | step->fromLine = findLineNumber(thread, location,
|
---|
245 | step->lineEntries, step->lineEntryCount);
|
---|
246 | }
|
---|
247 |
|
---|
248 | } END_WITH_LOCAL_REFS(env);
|
---|
249 |
|
---|
250 | }
|
---|
251 |
|
---|
252 | return error;
|
---|
253 | }
|
---|
254 |
|
---|
255 | /*
|
---|
256 | * TO DO: The step handlers (handleFrameChange and handleStep can
|
---|
257 | * be broken down and made simpler now that we can install and de-install event
|
---|
258 | * handlers.
|
---|
259 | */
|
---|
260 | static void
|
---|
261 | handleFramePopEvent(JNIEnv *env, EventInfo *evinfo,
|
---|
262 | HandlerNode *node,
|
---|
263 | struct bag *eventBag)
|
---|
264 | {
|
---|
265 | StepRequest *step;
|
---|
266 | jthread thread = evinfo->thread;
|
---|
267 |
|
---|
268 | stepControl_lock();
|
---|
269 |
|
---|
270 | step = threadControl_getStepRequest(thread);
|
---|
271 | if (step == NULL) {
|
---|
272 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
|
---|
273 | }
|
---|
274 |
|
---|
275 | if (step->pending) {
|
---|
276 | /*
|
---|
277 | * Note: current depth is reported as *before* the pending frame
|
---|
278 | * pop.
|
---|
279 | */
|
---|
280 | jint currentDepth;
|
---|
281 | jint fromDepth;
|
---|
282 | jint afterPopDepth;
|
---|
283 |
|
---|
284 | currentDepth = getFrameCount(thread);
|
---|
285 | fromDepth = step->fromStackDepth;
|
---|
286 | afterPopDepth = currentDepth-1;
|
---|
287 |
|
---|
288 | LOG_STEP(("handleFramePopEvent: BEGIN fromDepth=%d, currentDepth=%d",
|
---|
289 | fromDepth, currentDepth));
|
---|
290 |
|
---|
291 | /*
|
---|
292 | * If we are exiting the original stepping frame, record that
|
---|
293 | * fact here. Once the next step event comes in, we can safely
|
---|
294 | * stop stepping there.
|
---|
295 | */
|
---|
296 | if (fromDepth > afterPopDepth ) {
|
---|
297 | step->frameExited = JNI_TRUE;
|
---|
298 | }
|
---|
299 |
|
---|
300 | if (step->depth == JDWP_STEP_DEPTH(OVER)) {
|
---|
301 | /*
|
---|
302 | * Either
|
---|
303 | * 1) the original stepping frame is about to be popped
|
---|
304 | * [fromDepth == currentDepth]. Re-enable stepping to
|
---|
305 | * reach a point where we can stop.
|
---|
306 | * 2) a method called from the stepping frame has returned
|
---|
307 | * (during which we had stepping disabled)
|
---|
308 | * [fromDepth == currentDepth - 1]. Re-enable stepping
|
---|
309 | * so that we can continue instructions steps in the
|
---|
310 | * original stepping frame.
|
---|
311 | * 3) a method further down the call chain has notified
|
---|
312 | * of a frame pop [fromDepth < currentDepth - 1]. This
|
---|
313 | * *might* represent case (2) above if the stepping frame
|
---|
314 | * was calling a native method which in turn called a
|
---|
315 | * java method. If so, we must enable stepping to
|
---|
316 | * ensure that we get control back after the intervening
|
---|
317 | * native frame is popped (you can't get frame pop
|
---|
318 | * notifications on native frames). If the native caller
|
---|
319 | * calls another Java method before returning,
|
---|
320 | * stepping will be diabled again and another frame pop
|
---|
321 | * will be awaited.
|
---|
322 | *
|
---|
323 | * If it turns out that this is not case (2) with native
|
---|
324 | * methods, then the enabled stepping is benign and
|
---|
325 | * will be disabled again on the next step event.
|
---|
326 | *
|
---|
327 | * Note that the condition not covered above,
|
---|
328 | * [fromDepth > currentDepth] shouldn't happen since it means
|
---|
329 | * that too many frames have been popped. For robustness,
|
---|
330 | * we enable stepping in that case too, so that the errant
|
---|
331 | * step-over can be stopped.
|
---|
332 | *
|
---|
333 | */
|
---|
334 | LOG_STEP(("handleFramePopEvent: starting singlestep, depth==OVER"));
|
---|
335 | enableStepping(thread);
|
---|
336 | } else if (step->depth == JDWP_STEP_DEPTH(OUT) &&
|
---|
337 | fromDepth > afterPopDepth) {
|
---|
338 | /*
|
---|
339 | * The original stepping frame is about to be popped. Step
|
---|
340 | * until we reach the next safe place to stop.
|
---|
341 | */
|
---|
342 | LOG_STEP(("handleFramePopEvent: starting singlestep, depth==OUT && fromDepth > afterPopDepth (%d>%d)",fromDepth, afterPopDepth));
|
---|
343 | enableStepping(thread);
|
---|
344 | } else if (step->methodEnterHandlerNode != NULL &&
|
---|
345 | fromDepth >= afterPopDepth) {
|
---|
346 | /*
|
---|
347 | * We installed a method entry event handler as part of a
|
---|
348 | * step into operation. We've popped back to the original
|
---|
349 | * stepping frame without finding a place to stop.
|
---|
350 | * Resume stepping in the original frame.
|
---|
351 | */
|
---|
352 | LOG_STEP(("handleFramePopEvent: starting singlestep, have methodEnter handler && depth==OUT && fromDepth >= afterPopDepth (%d>%d)",fromDepth, afterPopDepth));
|
---|
353 | enableStepping(thread);
|
---|
354 | (void)eventHandler_free(step->methodEnterHandlerNode);
|
---|
355 | step->methodEnterHandlerNode = NULL;
|
---|
356 | }
|
---|
357 | LOG_STEP(("handleFramePopEvent: finished"));
|
---|
358 | }
|
---|
359 |
|
---|
360 | stepControl_unlock();
|
---|
361 | }
|
---|
362 |
|
---|
363 | static void
|
---|
364 | handleExceptionCatchEvent(JNIEnv *env, EventInfo *evinfo,
|
---|
365 | HandlerNode *node,
|
---|
366 | struct bag *eventBag)
|
---|
367 | {
|
---|
368 | StepRequest *step;
|
---|
369 | jthread thread = evinfo->thread;
|
---|
370 |
|
---|
371 | stepControl_lock();
|
---|
372 |
|
---|
373 | step = threadControl_getStepRequest(thread);
|
---|
374 | if (step == NULL) {
|
---|
375 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
|
---|
376 | }
|
---|
377 |
|
---|
378 | if (step->pending) {
|
---|
379 | /*
|
---|
380 | * Determine where we are on the call stack relative to where
|
---|
381 | * we started.
|
---|
382 | */
|
---|
383 | jint currentDepth = getFrameCount(thread);
|
---|
384 | jint fromDepth = step->fromStackDepth;
|
---|
385 |
|
---|
386 | LOG_STEP(("handleExceptionCatchEvent: fromDepth=%d, currentDepth=%d",
|
---|
387 | fromDepth, currentDepth));
|
---|
388 |
|
---|
389 | /*
|
---|
390 | * If we are exiting the original stepping frame, record that
|
---|
391 | * fact here. Once the next step event comes in, we can safely
|
---|
392 | * stop stepping there.
|
---|
393 | */
|
---|
394 | if (fromDepth > currentDepth) {
|
---|
395 | step->frameExited = JNI_TRUE;
|
---|
396 | }
|
---|
397 |
|
---|
398 | if (step->depth == JDWP_STEP_DEPTH(OVER) &&
|
---|
399 | fromDepth >= currentDepth) {
|
---|
400 | /*
|
---|
401 | * Either the original stepping frame is done,
|
---|
402 | * or a called method has returned (during which we had stepping
|
---|
403 | * disabled). In either case we must resume stepping.
|
---|
404 | */
|
---|
405 | enableStepping(thread);
|
---|
406 | } else if (step->depth == JDWP_STEP_DEPTH(OUT) &&
|
---|
407 | fromDepth > currentDepth) {
|
---|
408 | /*
|
---|
409 | * The original stepping frame is done. Step
|
---|
410 | * until we reach the next safe place to stop.
|
---|
411 | */
|
---|
412 | enableStepping(thread);
|
---|
413 | } else if (step->methodEnterHandlerNode != NULL &&
|
---|
414 | fromDepth >= currentDepth) {
|
---|
415 | /*
|
---|
416 | * We installed a method entry event handler as part of a
|
---|
417 | * step into operation. We've popped back to the original
|
---|
418 | * stepping frame or higher without finding a place to stop.
|
---|
419 | * Resume stepping in the original frame.
|
---|
420 | */
|
---|
421 | enableStepping(thread);
|
---|
422 | (void)eventHandler_free(step->methodEnterHandlerNode);
|
---|
423 | step->methodEnterHandlerNode = NULL;
|
---|
424 | }
|
---|
425 | }
|
---|
426 |
|
---|
427 | stepControl_unlock();
|
---|
428 | }
|
---|
429 |
|
---|
430 | static void
|
---|
431 | handleMethodEnterEvent(JNIEnv *env, EventInfo *evinfo,
|
---|
432 | HandlerNode *node,
|
---|
433 | struct bag *eventBag)
|
---|
434 | {
|
---|
435 | StepRequest *step;
|
---|
436 | jthread thread;
|
---|
437 |
|
---|
438 | thread = evinfo->thread;
|
---|
439 |
|
---|
440 | stepControl_lock();
|
---|
441 |
|
---|
442 | step = threadControl_getStepRequest(thread);
|
---|
443 | if (step == NULL) {
|
---|
444 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
|
---|
445 | }
|
---|
446 |
|
---|
447 | if (step->pending) {
|
---|
448 | jclass clazz;
|
---|
449 | jmethodID method;
|
---|
450 | char *classname;
|
---|
451 |
|
---|
452 | LOG_STEP(("handleMethodEnterEvent: thread=%p", thread));
|
---|
453 |
|
---|
454 | clazz = evinfo->clazz;
|
---|
455 | method = evinfo->method;
|
---|
456 | classname = getClassname(clazz);
|
---|
457 |
|
---|
458 | /*
|
---|
459 | * This handler is relevant only to step into
|
---|
460 | */
|
---|
461 | JDI_ASSERT(step->depth == JDWP_STEP_DEPTH(INTO));
|
---|
462 |
|
---|
463 | if ( (!eventFilter_predictFiltering(step->stepHandlerNode,
|
---|
464 | clazz, classname))
|
---|
465 | && ( step->granularity != JDWP_STEP_SIZE(LINE)
|
---|
466 | || hasLineNumbers(method) ) ) {
|
---|
467 | /*
|
---|
468 | * We've found a suitable method in which to stop. Step
|
---|
469 | * until we reach the next safe location to complete the step->,
|
---|
470 | * and we can get rid of the method entry handler.
|
---|
471 | */
|
---|
472 | enableStepping(thread);
|
---|
473 | if ( step->methodEnterHandlerNode != NULL ) {
|
---|
474 | (void)eventHandler_free(step->methodEnterHandlerNode);
|
---|
475 | step->methodEnterHandlerNode = NULL;
|
---|
476 | }
|
---|
477 | }
|
---|
478 | jvmtiDeallocate(classname);
|
---|
479 | classname = NULL;
|
---|
480 | }
|
---|
481 |
|
---|
482 | stepControl_unlock();
|
---|
483 | }
|
---|
484 |
|
---|
485 | static void
|
---|
486 | completeStep(JNIEnv *env, jthread thread, StepRequest *step)
|
---|
487 | {
|
---|
488 | jvmtiError error;
|
---|
489 |
|
---|
490 | /*
|
---|
491 | * We've completed a step; reset state for the next one, if any
|
---|
492 | */
|
---|
493 |
|
---|
494 | LOG_STEP(("completeStep: thread=%p", thread));
|
---|
495 |
|
---|
496 | if (step->methodEnterHandlerNode != NULL) {
|
---|
497 | (void)eventHandler_free(step->methodEnterHandlerNode);
|
---|
498 | step->methodEnterHandlerNode = NULL;
|
---|
499 | }
|
---|
500 |
|
---|
501 | error = initState(env, thread, step);
|
---|
502 | if (error != JVMTI_ERROR_NONE) {
|
---|
503 | /*
|
---|
504 | * None of the initState errors should happen after one step
|
---|
505 | * has successfully completed.
|
---|
506 | */
|
---|
507 | EXIT_ERROR(error, "initializing step state");
|
---|
508 | }
|
---|
509 | }
|
---|
510 |
|
---|
511 | jboolean
|
---|
512 | stepControl_handleStep(JNIEnv *env, jthread thread,
|
---|
513 | jclass clazz, jmethodID method)
|
---|
514 | {
|
---|
515 | jboolean completed = JNI_FALSE;
|
---|
516 | StepRequest *step;
|
---|
517 | jint currentDepth;
|
---|
518 | jint fromDepth;
|
---|
519 | jvmtiError error;
|
---|
520 | char *classname;
|
---|
521 |
|
---|
522 | classname = NULL;
|
---|
523 | stepControl_lock();
|
---|
524 |
|
---|
525 | step = threadControl_getStepRequest(thread);
|
---|
526 | if (step == NULL) {
|
---|
527 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
|
---|
528 | }
|
---|
529 |
|
---|
530 | /*
|
---|
531 | * If no step is currently pending, ignore the event
|
---|
532 | */
|
---|
533 | if (!step->pending) {
|
---|
534 | goto done;
|
---|
535 | }
|
---|
536 |
|
---|
537 | LOG_STEP(("stepControl_handleStep: thread=%p", thread));
|
---|
538 |
|
---|
539 | /*
|
---|
540 | * We never filter step into instruction. It's always over on the
|
---|
541 | * first step event.
|
---|
542 | */
|
---|
543 | if (step->depth == JDWP_STEP_DEPTH(INTO) &&
|
---|
544 | step->granularity == JDWP_STEP_SIZE(MIN)) {
|
---|
545 | completed = JNI_TRUE;
|
---|
546 | LOG_STEP(("stepControl_handleStep: completed, into min"));
|
---|
547 | goto done;
|
---|
548 | }
|
---|
549 |
|
---|
550 | /*
|
---|
551 | * If we have left the method in which
|
---|
552 | * stepping started, the step is always complete.
|
---|
553 | */
|
---|
554 | if (step->frameExited) {
|
---|
555 | completed = JNI_TRUE;
|
---|
556 | LOG_STEP(("stepControl_handleStep: completed, frame exited"));
|
---|
557 | goto done;
|
---|
558 | }
|
---|
559 |
|
---|
560 | /*
|
---|
561 | * Determine where we are on the call stack relative to where
|
---|
562 | * we started.
|
---|
563 | */
|
---|
564 | currentDepth = getFrameCount(thread);
|
---|
565 | fromDepth = step->fromStackDepth;
|
---|
566 |
|
---|
567 | if (fromDepth > currentDepth) {
|
---|
568 | /*
|
---|
569 | * We have returned from the caller. There are cases where
|
---|
570 | * we don't get frame pop notifications
|
---|
571 | * (e.g. stepping from opaque frames), and that's when
|
---|
572 | * this code will be reached. Complete the step->
|
---|
573 | */
|
---|
574 | completed = JNI_TRUE;
|
---|
575 | LOG_STEP(("stepControl_handleStep: completed, fromDepth>currentDepth(%d>%d)", fromDepth, currentDepth));
|
---|
576 | } else if (fromDepth < currentDepth) {
|
---|
577 | /* We have dropped into a called method. */
|
---|
578 | if ( step->depth == JDWP_STEP_DEPTH(INTO)
|
---|
579 | && (!eventFilter_predictFiltering(step->stepHandlerNode, clazz,
|
---|
580 | (classname = getClassname(clazz))))
|
---|
581 | && hasLineNumbers(method) ) {
|
---|
582 |
|
---|
583 | /* Stepped into a method with lines, so we're done */
|
---|
584 | completed = JNI_TRUE;
|
---|
585 | LOG_STEP(("stepControl_handleStep: completed, fromDepth<currentDepth(%d<%d) and into method with lines", fromDepth, currentDepth));
|
---|
586 | } else {
|
---|
587 | /*
|
---|
588 | * We need to continue, but don't want the overhead of step
|
---|
589 | * events from this method. So, we disable stepping and
|
---|
590 | * enable a frame pop. If we're stepping into, we also
|
---|
591 | * enable method enter events because a called frame may be
|
---|
592 | * where we want to stop.
|
---|
593 | */
|
---|
594 | disableStepping(thread);
|
---|
595 |
|
---|
596 | if (step->depth == JDWP_STEP_DEPTH(INTO)) {
|
---|
597 | step->methodEnterHandlerNode =
|
---|
598 | eventHandler_createInternalThreadOnly(
|
---|
599 | EI_METHOD_ENTRY,
|
---|
600 | handleMethodEnterEvent, thread);
|
---|
601 | if (step->methodEnterHandlerNode == NULL) {
|
---|
602 | EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,
|
---|
603 | "installing event method enter handler");
|
---|
604 | }
|
---|
605 | }
|
---|
606 |
|
---|
607 | error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
|
---|
608 | (gdata->jvmti, thread, 0);
|
---|
609 | if (error == JVMTI_ERROR_DUPLICATE) {
|
---|
610 | error = JVMTI_ERROR_NONE;
|
---|
611 | } else if (error != JVMTI_ERROR_NONE) {
|
---|
612 | EXIT_ERROR(error, "setting up notify frame pop");
|
---|
613 | }
|
---|
614 | }
|
---|
615 | jvmtiDeallocate(classname);
|
---|
616 | classname = NULL;
|
---|
617 | } else {
|
---|
618 | /*
|
---|
619 | * We are at the same stack depth where stepping started.
|
---|
620 | * Instruction steps are complete at this point. For line
|
---|
621 | * steps we must check to see whether we've moved to a
|
---|
622 | * different line.
|
---|
623 | */
|
---|
624 | if (step->granularity == JDWP_STEP_SIZE(MIN)) {
|
---|
625 | completed = JNI_TRUE;
|
---|
626 | LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and min", fromDepth));
|
---|
627 | } else {
|
---|
628 | if (step->fromLine != -1) {
|
---|
629 | jint line = -1;
|
---|
630 | jlocation location;
|
---|
631 | jmethodID method;
|
---|
632 | WITH_LOCAL_REFS(env, 1) {
|
---|
633 | jclass clazz;
|
---|
634 | error = getFrameLocation(thread,
|
---|
635 | &clazz, &method, &location);
|
---|
636 | if ( isMethodObsolete(method)) {
|
---|
637 | method = NULL;
|
---|
638 | location = -1;
|
---|
639 | }
|
---|
640 | if (error != JVMTI_ERROR_NONE || location == -1) {
|
---|
641 | EXIT_ERROR(error, "getting frame location");
|
---|
642 | }
|
---|
643 | if ( method == step->method ) {
|
---|
644 | LOG_STEP(("stepControl_handleStep: checking line location"));
|
---|
645 | log_debugee_location("stepControl_handleStep: checking line loc",
|
---|
646 | thread, method, location);
|
---|
647 | line = findLineNumber(thread, location,
|
---|
648 | step->lineEntries, step->lineEntryCount);
|
---|
649 | }
|
---|
650 | if (line != step->fromLine) {
|
---|
651 | completed = JNI_TRUE;
|
---|
652 | LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and different line", fromDepth));
|
---|
653 | }
|
---|
654 | } END_WITH_LOCAL_REFS(env);
|
---|
655 | } else {
|
---|
656 | /*
|
---|
657 | * This is a rare case. We have stepped from a location
|
---|
658 | * inside a native method to a location within a Java
|
---|
659 | * method at the same stack depth. This means that
|
---|
660 | * the original native method returned to another
|
---|
661 | * native method which, in turn, invoked a Java method.
|
---|
662 | *
|
---|
663 | * Since the original frame was native, we were unable
|
---|
664 | * to ask for a frame pop event, and, thus, could not
|
---|
665 | * set the step->frameExited flag when the original
|
---|
666 | * method was done. Instead we end up here
|
---|
667 | * and act just as though the frameExited flag was set
|
---|
668 | * and complete the step immediately.
|
---|
669 | */
|
---|
670 | completed = JNI_TRUE;
|
---|
671 | LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and no line", fromDepth));
|
---|
672 | }
|
---|
673 | }
|
---|
674 | LOG_STEP(("stepControl_handleStep: finished"));
|
---|
675 | }
|
---|
676 | done:
|
---|
677 | if (completed) {
|
---|
678 | completeStep(env, thread, step);
|
---|
679 | }
|
---|
680 | stepControl_unlock();
|
---|
681 | return completed;
|
---|
682 | }
|
---|
683 |
|
---|
684 |
|
---|
685 | void
|
---|
686 | stepControl_initialize(void)
|
---|
687 | {
|
---|
688 | stepLock = debugMonitorCreate("JDWP Step Handler Lock");
|
---|
689 | }
|
---|
690 |
|
---|
691 | void
|
---|
692 | stepControl_reset(void)
|
---|
693 | {
|
---|
694 | }
|
---|
695 |
|
---|
696 | /*
|
---|
697 | * Reset step control request stack depth and line number.
|
---|
698 | */
|
---|
699 | void
|
---|
700 | stepControl_resetRequest(jthread thread)
|
---|
701 | {
|
---|
702 |
|
---|
703 | StepRequest *step;
|
---|
704 | jvmtiError error;
|
---|
705 |
|
---|
706 | LOG_STEP(("stepControl_resetRequest: thread=%p", thread));
|
---|
707 |
|
---|
708 | stepControl_lock();
|
---|
709 |
|
---|
710 | step = threadControl_getStepRequest(thread);
|
---|
711 |
|
---|
712 | if (step != NULL) {
|
---|
713 | JNIEnv *env;
|
---|
714 | env = getEnv();
|
---|
715 | error = initState(env, thread, step);
|
---|
716 | if (error != JVMTI_ERROR_NONE) {
|
---|
717 | EXIT_ERROR(error, "initializing step state");
|
---|
718 | }
|
---|
719 | } else {
|
---|
720 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
|
---|
721 | }
|
---|
722 |
|
---|
723 | stepControl_unlock();
|
---|
724 | }
|
---|
725 |
|
---|
726 | static void
|
---|
727 | initEvents(jthread thread, StepRequest *step)
|
---|
728 | {
|
---|
729 | /* Need to install frame pop handler and exception catch handler when
|
---|
730 | * single-stepping is enabled (i.e. step-into or step-over/step-out
|
---|
731 | * when fromStackDepth > 0).
|
---|
732 | */
|
---|
733 | if (step->depth == JDWP_STEP_DEPTH(INTO) || step->fromStackDepth > 0) {
|
---|
734 | /*
|
---|
735 | * TO DO: These might be able to applied more selectively to
|
---|
736 | * boost performance.
|
---|
737 | */
|
---|
738 | step->catchHandlerNode = eventHandler_createInternalThreadOnly(
|
---|
739 | EI_EXCEPTION_CATCH,
|
---|
740 | handleExceptionCatchEvent,
|
---|
741 | thread);
|
---|
742 | step->framePopHandlerNode = eventHandler_createInternalThreadOnly(
|
---|
743 | EI_FRAME_POP,
|
---|
744 | handleFramePopEvent,
|
---|
745 | thread);
|
---|
746 |
|
---|
747 | if (step->catchHandlerNode == NULL ||
|
---|
748 | step->framePopHandlerNode == NULL) {
|
---|
749 | EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,
|
---|
750 | "installing step event handlers");
|
---|
751 | }
|
---|
752 |
|
---|
753 | }
|
---|
754 | /*
|
---|
755 | * Initially enable stepping:
|
---|
756 | * 1) For step into, always
|
---|
757 | * 2) For step over, unless right after the VM_INIT.
|
---|
758 | * Enable stepping for STEP_MIN or STEP_LINE with or without line numbers.
|
---|
759 | * If the class is redefined then non EMCP methods may not have line
|
---|
760 | * number info. So enable line stepping for non line number so that it
|
---|
761 | * behaves like STEP_MIN/STEP_OVER.
|
---|
762 | * 3) For step out, only if stepping from native, except right after VM_INIT
|
---|
763 | *
|
---|
764 | * (right after VM_INIT, a step->over or out is identical to running
|
---|
765 | * forever)
|
---|
766 | */
|
---|
767 | switch (step->depth) {
|
---|
768 | case JDWP_STEP_DEPTH(INTO):
|
---|
769 | enableStepping(thread);
|
---|
770 | break;
|
---|
771 | case JDWP_STEP_DEPTH(OVER):
|
---|
772 | if (step->fromStackDepth > 0 && !step->fromNative ) {
|
---|
773 | enableStepping(thread);
|
---|
774 | }
|
---|
775 | break;
|
---|
776 | case JDWP_STEP_DEPTH(OUT):
|
---|
777 | if (step->fromNative &&
|
---|
778 | (step->fromStackDepth > 0)) {
|
---|
779 | enableStepping(thread);
|
---|
780 | }
|
---|
781 | break;
|
---|
782 | default:
|
---|
783 | JDI_ASSERT(JNI_FALSE);
|
---|
784 | }
|
---|
785 | }
|
---|
786 |
|
---|
787 | jvmtiError
|
---|
788 | stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth,
|
---|
789 | HandlerNode *node)
|
---|
790 | {
|
---|
791 | StepRequest *step;
|
---|
792 | jvmtiError error;
|
---|
793 | jvmtiError error2;
|
---|
794 |
|
---|
795 | LOG_STEP(("stepControl_beginStep: thread=%p,size=%d,depth=%d",
|
---|
796 | thread, size, depth));
|
---|
797 |
|
---|
798 | eventHandler_lock(); /* for proper lock order */
|
---|
799 | stepControl_lock();
|
---|
800 |
|
---|
801 | step = threadControl_getStepRequest(thread);
|
---|
802 | if (step == NULL) {
|
---|
803 | error = AGENT_ERROR_INVALID_THREAD;
|
---|
804 | /* Normally not getting a StepRequest struct pointer is a fatal error
|
---|
805 | * but on a beginStep, we just return an error code.
|
---|
806 | */
|
---|
807 | } else {
|
---|
808 | /*
|
---|
809 | * In case the thread isn't already suspended, do it again.
|
---|
810 | */
|
---|
811 | error = threadControl_suspendThread(thread, JNI_FALSE);
|
---|
812 | if (error == JVMTI_ERROR_NONE) {
|
---|
813 | /*
|
---|
814 | * Overwrite any currently executing step.
|
---|
815 | */
|
---|
816 | step->granularity = size;
|
---|
817 | step->depth = depth;
|
---|
818 | step->catchHandlerNode = NULL;
|
---|
819 | step->framePopHandlerNode = NULL;
|
---|
820 | step->methodEnterHandlerNode = NULL;
|
---|
821 | step->stepHandlerNode = node;
|
---|
822 | error = initState(env, thread, step);
|
---|
823 | if (error == JVMTI_ERROR_NONE) {
|
---|
824 | initEvents(thread, step);
|
---|
825 | }
|
---|
826 | /* false means it is not okay to unblock the commandLoop thread */
|
---|
827 | error2 = threadControl_resumeThread(thread, JNI_FALSE);
|
---|
828 | if (error2 != JVMTI_ERROR_NONE && error == JVMTI_ERROR_NONE) {
|
---|
829 | error = error2;
|
---|
830 | }
|
---|
831 |
|
---|
832 | /*
|
---|
833 | * If everything went ok, indicate a step is pending.
|
---|
834 | */
|
---|
835 | if (error == JVMTI_ERROR_NONE) {
|
---|
836 | step->pending = JNI_TRUE;
|
---|
837 | }
|
---|
838 | } else {
|
---|
839 | EXIT_ERROR(error, "stepControl_beginStep: cannot suspend thread");
|
---|
840 | }
|
---|
841 | }
|
---|
842 |
|
---|
843 | stepControl_unlock();
|
---|
844 | eventHandler_unlock();
|
---|
845 |
|
---|
846 | return error;
|
---|
847 | }
|
---|
848 |
|
---|
849 |
|
---|
850 | static void
|
---|
851 | clearStep(jthread thread, StepRequest *step)
|
---|
852 | {
|
---|
853 | if (step->pending) {
|
---|
854 |
|
---|
855 | disableStepping(thread);
|
---|
856 | if ( step->catchHandlerNode != NULL ) {
|
---|
857 | (void)eventHandler_free(step->catchHandlerNode);
|
---|
858 | step->catchHandlerNode = NULL;
|
---|
859 | }
|
---|
860 | if ( step->framePopHandlerNode!= NULL ) {
|
---|
861 | (void)eventHandler_free(step->framePopHandlerNode);
|
---|
862 | step->framePopHandlerNode = NULL;
|
---|
863 | }
|
---|
864 | if ( step->methodEnterHandlerNode != NULL ) {
|
---|
865 | (void)eventHandler_free(step->methodEnterHandlerNode);
|
---|
866 | step->methodEnterHandlerNode = NULL;
|
---|
867 | }
|
---|
868 | step->pending = JNI_FALSE;
|
---|
869 |
|
---|
870 | /*
|
---|
871 | * Warning: Do not clear step->method, step->lineEntryCount,
|
---|
872 | * or step->lineEntries here, they will likely
|
---|
873 | * be needed on the next step.
|
---|
874 | */
|
---|
875 |
|
---|
876 | }
|
---|
877 | }
|
---|
878 |
|
---|
879 | jvmtiError
|
---|
880 | stepControl_endStep(jthread thread)
|
---|
881 | {
|
---|
882 | StepRequest *step;
|
---|
883 | jvmtiError error;
|
---|
884 |
|
---|
885 | LOG_STEP(("stepControl_endStep: thread=%p", thread));
|
---|
886 |
|
---|
887 | eventHandler_lock(); /* for proper lock order */
|
---|
888 | stepControl_lock();
|
---|
889 |
|
---|
890 | step = threadControl_getStepRequest(thread);
|
---|
891 | if (step != NULL) {
|
---|
892 | clearStep(thread, step);
|
---|
893 | error = JVMTI_ERROR_NONE;
|
---|
894 | } else {
|
---|
895 | /* If the stepRequest can't be gotten, then this thread no longer
|
---|
896 | * exists, just return, don't die here, this is normal at
|
---|
897 | * termination time. Return JVMTI_ERROR_NONE so the thread Ref
|
---|
898 | * can be tossed.
|
---|
899 | */
|
---|
900 | error = JVMTI_ERROR_NONE;
|
---|
901 | }
|
---|
902 |
|
---|
903 | stepControl_unlock();
|
---|
904 | eventHandler_unlock();
|
---|
905 |
|
---|
906 | return error;
|
---|
907 | }
|
---|
908 |
|
---|
909 | void
|
---|
910 | stepControl_clearRequest(jthread thread, StepRequest *step)
|
---|
911 | {
|
---|
912 | LOG_STEP(("stepControl_clearRequest: thread=%p", thread));
|
---|
913 | clearStep(thread, step);
|
---|
914 | }
|
---|
915 |
|
---|
916 | void
|
---|
917 | stepControl_lock(void)
|
---|
918 | {
|
---|
919 | debugMonitorEnter(stepLock);
|
---|
920 | }
|
---|
921 |
|
---|
922 | void
|
---|
923 | stepControl_unlock(void)
|
---|
924 | {
|
---|
925 | debugMonitorExit(stepLock);
|
---|
926 | }
|
---|