0N/A/*
2362N/A * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include "util.h"
0N/A#include "stepControl.h"
0N/A#include "eventHandler.h"
0N/A#include "eventHelper.h"
0N/A#include "threadControl.h"
0N/A#include "SDE.h"
0N/A
0N/Astatic jrawMonitorID stepLock;
0N/A
0N/Astatic jint
0N/AgetFrameCount(jthread thread)
0N/A{
0N/A jint count = 0;
0N/A jvmtiError error;
0N/A
0N/A error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameCount)
0N/A (gdata->jvmti, thread, &count);
0N/A if (error != JVMTI_ERROR_NONE) {
0N/A EXIT_ERROR(error, "getting frame count");
0N/A }
0N/A return count;
0N/A}
0N/A
0N/A/*
0N/A * Most enabling/disabling of JVMTI events happens implicitly through
0N/A * the inserting and freeing of handlers for those events. Stepping is
0N/A * different because requested steps are usually not identical to JVMTI steps.
0N/A * They usually require multiple events step, and otherwise, before they
0N/A * complete. While a step request is pending, we may need to temporarily
0N/A * disable and re-enable stepping, but we can't just remove the handlers
0N/A * because that would break the application's ability to remove the
0N/A * events. So, for step events only, we directly enable and disable stepping.
0N/A * This is safe because there can only ever be one pending step request
0N/A * per thread.
0N/A */
0N/Astatic void
0N/AenableStepping(jthread thread)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A LOG_STEP(("enableStepping: thread=%p", thread));
0N/A
0N/A error = threadControl_setEventMode(JVMTI_ENABLE, EI_SINGLE_STEP,
0N/A thread);
0N/A if (error != JVMTI_ERROR_NONE) {
0N/A EXIT_ERROR(error, "enabling single step");
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/AdisableStepping(jthread thread)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A LOG_STEP(("disableStepping: thread=%p", thread));
0N/A
0N/A error = threadControl_setEventMode(JVMTI_DISABLE, EI_SINGLE_STEP,
0N/A thread);
0N/A if (error != JVMTI_ERROR_NONE) {
0N/A EXIT_ERROR(error, "disabling single step");
0N/A }
0N/A}
0N/A
0N/Astatic jvmtiError
0N/AgetFrameLocation(jthread thread,
0N/A jclass *pclazz, jmethodID *pmethod, jlocation *plocation)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A *pclazz = NULL;
0N/A *pmethod = NULL;
0N/A *plocation = -1;
0N/A
0N/A error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
0N/A (gdata->jvmti, thread, 0, pmethod, plocation);
0N/A if (error == JVMTI_ERROR_NONE && *pmethod!=NULL ) {
0N/A /* This also serves to verify that the methodID is valid */
0N/A error = methodClass(*pmethod, pclazz);
0N/A }
0N/A return error;
0N/A}
0N/A
0N/Astatic void
0N/AgetLineNumberTable(jmethodID method, jint *pcount,
0N/A jvmtiLineNumberEntry **ptable)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A *pcount = 0;
0N/A *ptable = NULL;
0N/A
0N/A /* If the method is native or obsolete, don't even ask for the line table */
0N/A if ( isMethodObsolete(method) || isMethodNative(method)) {
0N/A return;
0N/A }
0N/A
0N/A error = JVMTI_FUNC_PTR(gdata->jvmti,GetLineNumberTable)
0N/A (gdata->jvmti, method, pcount, ptable);
0N/A if (error != JVMTI_ERROR_NONE) {
0N/A *pcount = 0;
0N/A }
0N/A}
0N/A
0N/Astatic jint
0N/AfindLineNumber(jthread thread, jlocation location,
0N/A jvmtiLineNumberEntry *lines, jint count)
0N/A{
0N/A jint line = -1;
0N/A
0N/A if (location != -1) {
0N/A if (count > 0) {
0N/A jint i;
0N/A /* any preface before first line is assigned to first line */
0N/A for (i=1; i<count; i++) {
0N/A if (location < lines[i].start_location) {
0N/A break;
0N/A }
0N/A }
0N/A line = lines[i-1].line_number;
0N/A }
0N/A }
0N/A return line;
0N/A}
0N/A
0N/Astatic jboolean
0N/AhasLineNumbers(jmethodID method)
0N/A{
0N/A jint count;
0N/A jvmtiLineNumberEntry *table;
0N/A
0N/A getLineNumberTable(method, &count, &table);
0N/A if ( count == 0 ) {
0N/A return JNI_FALSE;
0N/A } else {
0N/A jvmtiDeallocate(table);
0N/A }
0N/A return JNI_TRUE;
0N/A}
0N/A
0N/Astatic jvmtiError
0N/AinitState(JNIEnv *env, jthread thread, StepRequest *step)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A /*
0N/A * Initial values that may be changed below
0N/A */
0N/A step->fromLine = -1;
0N/A step->fromNative = JNI_FALSE;
0N/A step->frameExited = JNI_FALSE;
0N/A step->fromStackDepth = getFrameCount(thread);
0N/A
0N/A if (step->fromStackDepth <= 0) {
0N/A /*
0N/A * If there are no stack frames, treat the step as though
0N/A * from a native frame. This is most likely to occur at the
0N/A * beginning of a debug session, right after the VM_INIT event,
0N/A * so we need to do something intelligent.
0N/A */
0N/A step->fromNative = JNI_TRUE;
0N/A return JVMTI_ERROR_NONE;
0N/A }
0N/A
0N/A /*
0N/A * Try to get a notification on frame pop. If we're in an opaque frame
0N/A * we won't be able to, but we can use other methods to detect that
0N/A * a native frame has exited.
0N/A *
0N/A * TO DO: explain the need for this notification.
0N/A */
0N/A error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
0N/A (gdata->jvmti, thread, 0);
0N/A if (error == JVMTI_ERROR_OPAQUE_FRAME) {
0N/A step->fromNative = JNI_TRUE;
0N/A error = JVMTI_ERROR_NONE;
0N/A /* continue without error */
0N/A } else if (error == JVMTI_ERROR_DUPLICATE) {
0N/A error = JVMTI_ERROR_NONE;
0N/A /* Already being notified, continue without error */
0N/A } else if (error != JVMTI_ERROR_NONE) {
0N/A return error;
0N/A }
0N/A
0N/A LOG_STEP(("initState(): frame=%d", step->fromStackDepth));
0N/A
0N/A /*
0N/A * Note: we can't undo the frame pop notify, so
0N/A * we'll just have to let the handler ignore it if
0N/A * there are any errors below.
0N/A */
0N/A
0N/A if (step->granularity == JDWP_STEP_SIZE(LINE) ) {
0N/A
0N/A LOG_STEP(("initState(): Begin line step"));
0N/A
0N/A WITH_LOCAL_REFS(env, 1) {
0N/A
0N/A jclass clazz;
0N/A jmethodID method;
0N/A jlocation location;
0N/A
0N/A error = getFrameLocation(thread, &clazz, &method, &location);
0N/A if (error == JVMTI_ERROR_NONE) {
0N/A /* Clear out previous line table only if we changed methods */
0N/A if ( method != step->method ) {
0N/A step->lineEntryCount = 0;
0N/A if (step->lineEntries != NULL) {
0N/A jvmtiDeallocate(step->lineEntries);
0N/A step->lineEntries = NULL;
0N/A }
0N/A step->method = method;
0N/A getLineNumberTable(step->method,
0N/A &step->lineEntryCount, &step->lineEntries);
0N/A if (step->lineEntryCount > 0) {
0N/A convertLineNumberTable(env, clazz,
0N/A &step->lineEntryCount, &step->lineEntries);
0N/A }
0N/A }
0N/A step->fromLine = findLineNumber(thread, location,
0N/A step->lineEntries, step->lineEntryCount);
0N/A }
0N/A
0N/A } END_WITH_LOCAL_REFS(env);
0N/A
0N/A }
0N/A
0N/A return error;
0N/A}
0N/A
0N/A/*
0N/A * TO DO: The step handlers (handleFrameChange and handleStep can
0N/A * be broken down and made simpler now that we can install and de-install event
0N/A * handlers.
0N/A */
0N/Astatic void
0N/AhandleFramePopEvent(JNIEnv *env, EventInfo *evinfo,
0N/A HandlerNode *node,
0N/A struct bag *eventBag)
0N/A{
0N/A StepRequest *step;
0N/A jthread thread = evinfo->thread;
0N/A
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A if (step == NULL) {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
0N/A }
0N/A
0N/A if (step->pending) {
0N/A /*
0N/A * Note: current depth is reported as *before* the pending frame
0N/A * pop.
0N/A */
0N/A jint currentDepth;
0N/A jint fromDepth;
0N/A jint afterPopDepth;
0N/A
0N/A currentDepth = getFrameCount(thread);
0N/A fromDepth = step->fromStackDepth;
0N/A afterPopDepth = currentDepth-1;
0N/A
0N/A LOG_STEP(("handleFramePopEvent: BEGIN fromDepth=%d, currentDepth=%d",
0N/A fromDepth, currentDepth));
0N/A
0N/A /*
0N/A * If we are exiting the original stepping frame, record that
0N/A * fact here. Once the next step event comes in, we can safely
0N/A * stop stepping there.
0N/A */
0N/A if (fromDepth > afterPopDepth ) {
0N/A step->frameExited = JNI_TRUE;
0N/A }
0N/A
0N/A if (step->depth == JDWP_STEP_DEPTH(OVER)) {
0N/A /*
0N/A * Either
0N/A * 1) the original stepping frame is about to be popped
0N/A * [fromDepth == currentDepth]. Re-enable stepping to
0N/A * reach a point where we can stop.
0N/A * 2) a method called from the stepping frame has returned
0N/A * (during which we had stepping disabled)
0N/A * [fromDepth == currentDepth - 1]. Re-enable stepping
0N/A * so that we can continue instructions steps in the
0N/A * original stepping frame.
0N/A * 3) a method further down the call chain has notified
0N/A * of a frame pop [fromDepth < currentDepth - 1]. This
0N/A * *might* represent case (2) above if the stepping frame
0N/A * was calling a native method which in turn called a
0N/A * java method. If so, we must enable stepping to
0N/A * ensure that we get control back after the intervening
0N/A * native frame is popped (you can't get frame pop
0N/A * notifications on native frames). If the native caller
0N/A * calls another Java method before returning,
0N/A * stepping will be diabled again and another frame pop
0N/A * will be awaited.
0N/A *
0N/A * If it turns out that this is not case (2) with native
0N/A * methods, then the enabled stepping is benign and
0N/A * will be disabled again on the next step event.
0N/A *
0N/A * Note that the condition not covered above,
0N/A * [fromDepth > currentDepth] shouldn't happen since it means
0N/A * that too many frames have been popped. For robustness,
0N/A * we enable stepping in that case too, so that the errant
0N/A * step-over can be stopped.
0N/A *
0N/A */
0N/A LOG_STEP(("handleFramePopEvent: starting singlestep, depth==OVER"));
0N/A enableStepping(thread);
0N/A } else if (step->depth == JDWP_STEP_DEPTH(OUT) &&
0N/A fromDepth > afterPopDepth) {
0N/A /*
0N/A * The original stepping frame is about to be popped. Step
0N/A * until we reach the next safe place to stop.
0N/A */
0N/A LOG_STEP(("handleFramePopEvent: starting singlestep, depth==OUT && fromDepth > afterPopDepth (%d>%d)",fromDepth, afterPopDepth));
0N/A enableStepping(thread);
0N/A } else if (step->methodEnterHandlerNode != NULL &&
0N/A fromDepth >= afterPopDepth) {
0N/A /*
0N/A * We installed a method entry event handler as part of a
0N/A * step into operation. We've popped back to the original
0N/A * stepping frame without finding a place to stop.
0N/A * Resume stepping in the original frame.
0N/A */
0N/A LOG_STEP(("handleFramePopEvent: starting singlestep, have methodEnter handler && depth==OUT && fromDepth >= afterPopDepth (%d>%d)",fromDepth, afterPopDepth));
0N/A enableStepping(thread);
0N/A (void)eventHandler_free(step->methodEnterHandlerNode);
0N/A step->methodEnterHandlerNode = NULL;
0N/A }
0N/A LOG_STEP(("handleFramePopEvent: finished"));
0N/A }
0N/A
0N/A stepControl_unlock();
0N/A}
0N/A
0N/Astatic void
0N/AhandleExceptionCatchEvent(JNIEnv *env, EventInfo *evinfo,
0N/A HandlerNode *node,
0N/A struct bag *eventBag)
0N/A{
0N/A StepRequest *step;
0N/A jthread thread = evinfo->thread;
0N/A
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A if (step == NULL) {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
0N/A }
0N/A
0N/A if (step->pending) {
0N/A /*
0N/A * Determine where we are on the call stack relative to where
0N/A * we started.
0N/A */
0N/A jint currentDepth = getFrameCount(thread);
0N/A jint fromDepth = step->fromStackDepth;
0N/A
0N/A LOG_STEP(("handleExceptionCatchEvent: fromDepth=%d, currentDepth=%d",
0N/A fromDepth, currentDepth));
0N/A
0N/A /*
0N/A * If we are exiting the original stepping frame, record that
0N/A * fact here. Once the next step event comes in, we can safely
0N/A * stop stepping there.
0N/A */
0N/A if (fromDepth > currentDepth) {
0N/A step->frameExited = JNI_TRUE;
0N/A }
0N/A
0N/A if (step->depth == JDWP_STEP_DEPTH(OVER) &&
0N/A fromDepth >= currentDepth) {
0N/A /*
0N/A * Either the original stepping frame is done,
0N/A * or a called method has returned (during which we had stepping
0N/A * disabled). In either case we must resume stepping.
0N/A */
0N/A enableStepping(thread);
0N/A } else if (step->depth == JDWP_STEP_DEPTH(OUT) &&
0N/A fromDepth > currentDepth) {
0N/A /*
0N/A * The original stepping frame is done. Step
0N/A * until we reach the next safe place to stop.
0N/A */
0N/A enableStepping(thread);
0N/A } else if (step->methodEnterHandlerNode != NULL &&
0N/A fromDepth >= currentDepth) {
0N/A /*
0N/A * We installed a method entry event handler as part of a
0N/A * step into operation. We've popped back to the original
0N/A * stepping frame or higher without finding a place to stop.
0N/A * Resume stepping in the original frame.
0N/A */
0N/A enableStepping(thread);
0N/A (void)eventHandler_free(step->methodEnterHandlerNode);
0N/A step->methodEnterHandlerNode = NULL;
0N/A }
0N/A }
0N/A
0N/A stepControl_unlock();
0N/A}
0N/A
0N/Astatic void
0N/AhandleMethodEnterEvent(JNIEnv *env, EventInfo *evinfo,
0N/A HandlerNode *node,
0N/A struct bag *eventBag)
0N/A{
0N/A StepRequest *step;
0N/A jthread thread;
0N/A
0N/A thread = evinfo->thread;
0N/A
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A if (step == NULL) {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
0N/A }
0N/A
0N/A if (step->pending) {
0N/A jclass clazz;
0N/A jmethodID method;
0N/A char *classname;
0N/A
0N/A LOG_STEP(("handleMethodEnterEvent: thread=%p", thread));
0N/A
0N/A clazz = evinfo->clazz;
0N/A method = evinfo->method;
0N/A classname = getClassname(clazz);
0N/A
0N/A /*
0N/A * This handler is relevant only to step into
0N/A */
0N/A JDI_ASSERT(step->depth == JDWP_STEP_DEPTH(INTO));
0N/A
0N/A if ( (!eventFilter_predictFiltering(step->stepHandlerNode,
0N/A clazz, classname))
0N/A && ( step->granularity != JDWP_STEP_SIZE(LINE)
0N/A || hasLineNumbers(method) ) ) {
0N/A /*
0N/A * We've found a suitable method in which to stop. Step
0N/A * until we reach the next safe location to complete the step->,
0N/A * and we can get rid of the method entry handler.
0N/A */
0N/A enableStepping(thread);
0N/A if ( step->methodEnterHandlerNode != NULL ) {
0N/A (void)eventHandler_free(step->methodEnterHandlerNode);
0N/A step->methodEnterHandlerNode = NULL;
0N/A }
0N/A }
0N/A jvmtiDeallocate(classname);
0N/A classname = NULL;
0N/A }
0N/A
0N/A stepControl_unlock();
0N/A}
0N/A
0N/Astatic void
0N/AcompleteStep(JNIEnv *env, jthread thread, StepRequest *step)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A /*
0N/A * We've completed a step; reset state for the next one, if any
0N/A */
0N/A
0N/A LOG_STEP(("completeStep: thread=%p", thread));
0N/A
0N/A if (step->methodEnterHandlerNode != NULL) {
0N/A (void)eventHandler_free(step->methodEnterHandlerNode);
0N/A step->methodEnterHandlerNode = NULL;
0N/A }
0N/A
0N/A error = initState(env, thread, step);
0N/A if (error != JVMTI_ERROR_NONE) {
0N/A /*
0N/A * None of the initState errors should happen after one step
0N/A * has successfully completed.
0N/A */
0N/A EXIT_ERROR(error, "initializing step state");
0N/A }
0N/A}
0N/A
0N/Ajboolean
0N/AstepControl_handleStep(JNIEnv *env, jthread thread,
0N/A jclass clazz, jmethodID method)
0N/A{
0N/A jboolean completed = JNI_FALSE;
0N/A StepRequest *step;
0N/A jint currentDepth;
0N/A jint fromDepth;
0N/A jvmtiError error;
0N/A char *classname;
0N/A
0N/A classname = NULL;
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A if (step == NULL) {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
0N/A }
0N/A
0N/A /*
0N/A * If no step is currently pending, ignore the event
0N/A */
0N/A if (!step->pending) {
0N/A goto done;
0N/A }
0N/A
0N/A LOG_STEP(("stepControl_handleStep: thread=%p", thread));
0N/A
0N/A /*
0N/A * We never filter step into instruction. It's always over on the
0N/A * first step event.
0N/A */
0N/A if (step->depth == JDWP_STEP_DEPTH(INTO) &&
0N/A step->granularity == JDWP_STEP_SIZE(MIN)) {
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, into min"));
0N/A goto done;
0N/A }
0N/A
0N/A /*
0N/A * If we have left the method in which
0N/A * stepping started, the step is always complete.
0N/A */
0N/A if (step->frameExited) {
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, frame exited"));
0N/A goto done;
0N/A }
0N/A
0N/A /*
0N/A * Determine where we are on the call stack relative to where
0N/A * we started.
0N/A */
0N/A currentDepth = getFrameCount(thread);
0N/A fromDepth = step->fromStackDepth;
0N/A
0N/A if (fromDepth > currentDepth) {
0N/A /*
0N/A * We have returned from the caller. There are cases where
0N/A * we don't get frame pop notifications
0N/A * (e.g. stepping from opaque frames), and that's when
0N/A * this code will be reached. Complete the step->
0N/A */
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, fromDepth>currentDepth(%d>%d)", fromDepth, currentDepth));
0N/A } else if (fromDepth < currentDepth) {
0N/A /* We have dropped into a called method. */
0N/A if ( step->depth == JDWP_STEP_DEPTH(INTO)
0N/A && (!eventFilter_predictFiltering(step->stepHandlerNode, clazz,
0N/A (classname = getClassname(clazz))))
0N/A && hasLineNumbers(method) ) {
0N/A
0N/A /* Stepped into a method with lines, so we're done */
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, fromDepth<currentDepth(%d<%d) and into method with lines", fromDepth, currentDepth));
0N/A } else {
0N/A /*
0N/A * We need to continue, but don't want the overhead of step
0N/A * events from this method. So, we disable stepping and
0N/A * enable a frame pop. If we're stepping into, we also
0N/A * enable method enter events because a called frame may be
0N/A * where we want to stop.
0N/A */
0N/A disableStepping(thread);
0N/A
0N/A if (step->depth == JDWP_STEP_DEPTH(INTO)) {
0N/A step->methodEnterHandlerNode =
0N/A eventHandler_createInternalThreadOnly(
0N/A EI_METHOD_ENTRY,
0N/A handleMethodEnterEvent, thread);
0N/A if (step->methodEnterHandlerNode == NULL) {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,
0N/A "installing event method enter handler");
0N/A }
0N/A }
0N/A
0N/A error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
0N/A (gdata->jvmti, thread, 0);
0N/A if (error == JVMTI_ERROR_DUPLICATE) {
0N/A error = JVMTI_ERROR_NONE;
0N/A } else if (error != JVMTI_ERROR_NONE) {
0N/A EXIT_ERROR(error, "setting up notify frame pop");
0N/A }
0N/A }
0N/A jvmtiDeallocate(classname);
0N/A classname = NULL;
0N/A } else {
0N/A /*
0N/A * We are at the same stack depth where stepping started.
0N/A * Instruction steps are complete at this point. For line
0N/A * steps we must check to see whether we've moved to a
0N/A * different line.
0N/A */
0N/A if (step->granularity == JDWP_STEP_SIZE(MIN)) {
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and min", fromDepth));
0N/A } else {
0N/A if (step->fromLine != -1) {
0N/A jint line = -1;
0N/A jlocation location;
0N/A jmethodID method;
0N/A WITH_LOCAL_REFS(env, 1) {
0N/A jclass clazz;
0N/A error = getFrameLocation(thread,
0N/A &clazz, &method, &location);
0N/A if ( isMethodObsolete(method)) {
0N/A method = NULL;
0N/A location = -1;
0N/A }
0N/A if (error != JVMTI_ERROR_NONE || location == -1) {
0N/A EXIT_ERROR(error, "getting frame location");
0N/A }
0N/A if ( method == step->method ) {
0N/A LOG_STEP(("stepControl_handleStep: checking line location"));
0N/A log_debugee_location("stepControl_handleStep: checking line loc",
0N/A thread, method, location);
0N/A line = findLineNumber(thread, location,
0N/A step->lineEntries, step->lineEntryCount);
0N/A }
0N/A if (line != step->fromLine) {
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and different line", fromDepth));
0N/A }
0N/A } END_WITH_LOCAL_REFS(env);
0N/A } else {
0N/A /*
0N/A * This is a rare case. We have stepped from a location
0N/A * inside a native method to a location within a Java
0N/A * method at the same stack depth. This means that
0N/A * the original native method returned to another
0N/A * native method which, in turn, invoked a Java method.
0N/A *
0N/A * Since the original frame was native, we were unable
0N/A * to ask for a frame pop event, and, thus, could not
0N/A * set the step->frameExited flag when the original
0N/A * method was done. Instead we end up here
0N/A * and act just as though the frameExited flag was set
0N/A * and complete the step immediately.
0N/A */
0N/A completed = JNI_TRUE;
0N/A LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and no line", fromDepth));
0N/A }
0N/A }
0N/A LOG_STEP(("stepControl_handleStep: finished"));
0N/A }
0N/Adone:
0N/A if (completed) {
0N/A completeStep(env, thread, step);
0N/A }
0N/A stepControl_unlock();
0N/A return completed;
0N/A}
0N/A
0N/A
0N/Avoid
0N/AstepControl_initialize(void)
0N/A{
0N/A stepLock = debugMonitorCreate("JDWP Step Handler Lock");
0N/A}
0N/A
0N/Avoid
0N/AstepControl_reset(void)
0N/A{
0N/A}
0N/A
0N/A/*
0N/A * Reset step control request stack depth and line number.
0N/A */
0N/Avoid
0N/AstepControl_resetRequest(jthread thread)
0N/A{
0N/A
0N/A StepRequest *step;
0N/A jvmtiError error;
0N/A
0N/A LOG_STEP(("stepControl_resetRequest: thread=%p", thread));
0N/A
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A
0N/A if (step != NULL) {
0N/A JNIEnv *env;
0N/A env = getEnv();
0N/A error = initState(env, thread, step);
0N/A if (error != JVMTI_ERROR_NONE) {
0N/A EXIT_ERROR(error, "initializing step state");
0N/A }
0N/A } else {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
0N/A }
0N/A
0N/A stepControl_unlock();
0N/A}
0N/A
0N/Astatic void
0N/AinitEvents(jthread thread, StepRequest *step)
0N/A{
0N/A /* Need to install frame pop handler and exception catch handler when
0N/A * single-stepping is enabled (i.e. step-into or step-over/step-out
0N/A * when fromStackDepth > 0).
0N/A */
0N/A if (step->depth == JDWP_STEP_DEPTH(INTO) || step->fromStackDepth > 0) {
0N/A /*
0N/A * TO DO: These might be able to applied more selectively to
0N/A * boost performance.
0N/A */
0N/A step->catchHandlerNode = eventHandler_createInternalThreadOnly(
0N/A EI_EXCEPTION_CATCH,
0N/A handleExceptionCatchEvent,
0N/A thread);
0N/A step->framePopHandlerNode = eventHandler_createInternalThreadOnly(
0N/A EI_FRAME_POP,
0N/A handleFramePopEvent,
0N/A thread);
0N/A
0N/A if (step->catchHandlerNode == NULL ||
0N/A step->framePopHandlerNode == NULL) {
0N/A EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,
0N/A "installing step event handlers");
0N/A }
0N/A
0N/A }
0N/A /*
0N/A * Initially enable stepping:
0N/A * 1) For step into, always
0N/A * 2) For step over, unless right after the VM_INIT.
0N/A * Enable stepping for STEP_MIN or STEP_LINE with or without line numbers.
0N/A * If the class is redefined then non EMCP methods may not have line
0N/A * number info. So enable line stepping for non line number so that it
0N/A * behaves like STEP_MIN/STEP_OVER.
0N/A * 3) For step out, only if stepping from native, except right after VM_INIT
0N/A *
0N/A * (right after VM_INIT, a step->over or out is identical to running
0N/A * forever)
0N/A */
0N/A switch (step->depth) {
0N/A case JDWP_STEP_DEPTH(INTO):
0N/A enableStepping(thread);
0N/A break;
0N/A case JDWP_STEP_DEPTH(OVER):
0N/A if (step->fromStackDepth > 0 && !step->fromNative ) {
0N/A enableStepping(thread);
0N/A }
0N/A break;
0N/A case JDWP_STEP_DEPTH(OUT):
0N/A if (step->fromNative &&
0N/A (step->fromStackDepth > 0)) {
0N/A enableStepping(thread);
0N/A }
0N/A break;
0N/A default:
0N/A JDI_ASSERT(JNI_FALSE);
0N/A }
0N/A}
0N/A
0N/AjvmtiError
0N/AstepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth,
0N/A HandlerNode *node)
0N/A{
0N/A StepRequest *step;
0N/A jvmtiError error;
0N/A jvmtiError error2;
0N/A
0N/A LOG_STEP(("stepControl_beginStep: thread=%p,size=%d,depth=%d",
0N/A thread, size, depth));
0N/A
0N/A eventHandler_lock(); /* for proper lock order */
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A if (step == NULL) {
0N/A error = AGENT_ERROR_INVALID_THREAD;
0N/A /* Normally not getting a StepRequest struct pointer is a fatal error
0N/A * but on a beginStep, we just return an error code.
0N/A */
0N/A } else {
0N/A /*
0N/A * In case the thread isn't already suspended, do it again.
0N/A */
0N/A error = threadControl_suspendThread(thread, JNI_FALSE);
0N/A if (error == JVMTI_ERROR_NONE) {
0N/A /*
0N/A * Overwrite any currently executing step.
0N/A */
0N/A step->granularity = size;
0N/A step->depth = depth;
0N/A step->catchHandlerNode = NULL;
0N/A step->framePopHandlerNode = NULL;
0N/A step->methodEnterHandlerNode = NULL;
0N/A step->stepHandlerNode = node;
0N/A error = initState(env, thread, step);
0N/A if (error == JVMTI_ERROR_NONE) {
0N/A initEvents(thread, step);
0N/A }
0N/A /* false means it is not okay to unblock the commandLoop thread */
0N/A error2 = threadControl_resumeThread(thread, JNI_FALSE);
0N/A if (error2 != JVMTI_ERROR_NONE && error == JVMTI_ERROR_NONE) {
0N/A error = error2;
0N/A }
0N/A
0N/A /*
0N/A * If everything went ok, indicate a step is pending.
0N/A */
0N/A if (error == JVMTI_ERROR_NONE) {
0N/A step->pending = JNI_TRUE;
0N/A }
0N/A } else {
0N/A EXIT_ERROR(error, "stepControl_beginStep: cannot suspend thread");
0N/A }
0N/A }
0N/A
0N/A stepControl_unlock();
0N/A eventHandler_unlock();
0N/A
0N/A return error;
0N/A}
0N/A
0N/A
0N/Astatic void
0N/AclearStep(jthread thread, StepRequest *step)
0N/A{
0N/A if (step->pending) {
0N/A
0N/A disableStepping(thread);
0N/A if ( step->catchHandlerNode != NULL ) {
0N/A (void)eventHandler_free(step->catchHandlerNode);
0N/A step->catchHandlerNode = NULL;
0N/A }
0N/A if ( step->framePopHandlerNode!= NULL ) {
0N/A (void)eventHandler_free(step->framePopHandlerNode);
0N/A step->framePopHandlerNode = NULL;
0N/A }
0N/A if ( step->methodEnterHandlerNode != NULL ) {
0N/A (void)eventHandler_free(step->methodEnterHandlerNode);
0N/A step->methodEnterHandlerNode = NULL;
0N/A }
0N/A step->pending = JNI_FALSE;
0N/A
0N/A /*
0N/A * Warning: Do not clear step->method, step->lineEntryCount,
0N/A * or step->lineEntries here, they will likely
0N/A * be needed on the next step.
0N/A */
0N/A
0N/A }
0N/A}
0N/A
0N/AjvmtiError
0N/AstepControl_endStep(jthread thread)
0N/A{
0N/A StepRequest *step;
0N/A jvmtiError error;
0N/A
0N/A LOG_STEP(("stepControl_endStep: thread=%p", thread));
0N/A
0N/A eventHandler_lock(); /* for proper lock order */
0N/A stepControl_lock();
0N/A
0N/A step = threadControl_getStepRequest(thread);
0N/A if (step != NULL) {
0N/A clearStep(thread, step);
0N/A error = JVMTI_ERROR_NONE;
0N/A } else {
0N/A /* If the stepRequest can't be gotten, then this thread no longer
0N/A * exists, just return, don't die here, this is normal at
0N/A * termination time. Return JVMTI_ERROR_NONE so the thread Ref
0N/A * can be tossed.
0N/A */
0N/A error = JVMTI_ERROR_NONE;
0N/A }
0N/A
0N/A stepControl_unlock();
0N/A eventHandler_unlock();
0N/A
0N/A return error;
0N/A}
0N/A
0N/Avoid
0N/AstepControl_clearRequest(jthread thread, StepRequest *step)
0N/A{
0N/A LOG_STEP(("stepControl_clearRequest: thread=%p", thread));
0N/A clearStep(thread, step);
0N/A}
0N/A
0N/Avoid
0N/AstepControl_lock(void)
0N/A{
0N/A debugMonitorEnter(stepLock);
0N/A}
0N/A
0N/Avoid
0N/AstepControl_unlock(void)
0N/A{
0N/A debugMonitorExit(stepLock);
0N/A}