alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (stepControl.c)

This example Java source code file (stepControl.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

eventinfo, exit_error, handlernode, jdi_assert, jdwp_step_depth, jdwp_step_size, jni_false, jni_true, jvmti_error_duplicate, jvmti_error_none, jvmti_func_ptr, log_step, null, steprequest

The stepControl.c Java example source code

/*
 * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "util.h"
#include "stepControl.h"
#include "eventHandler.h"
#include "eventHelper.h"
#include "threadControl.h"
#include "SDE.h"

static jrawMonitorID stepLock;

static jint
getFrameCount(jthread thread)
{
    jint count = 0;
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameCount)
                    (gdata->jvmti, thread, &count);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "getting frame count");
    }
    return count;
}

/*
 * Most enabling/disabling of JVMTI events happens implicitly through
 * the inserting and freeing of handlers for those events. Stepping is
 * different because requested steps are usually not identical to JVMTI steps.
 * They usually require multiple events step, and otherwise, before they
 * complete. While a step request is pending, we may need to temporarily
 * disable and re-enable stepping, but we can't just remove the handlers
 * because that would break the application's ability to remove the
 * events. So, for step events only, we directly enable and disable stepping.
 * This is safe because there can only ever be one pending step request
 * per thread.
 */
static void
enableStepping(jthread thread)
{
    jvmtiError error;

    LOG_STEP(("enableStepping: thread=%p", thread));

    error = threadControl_setEventMode(JVMTI_ENABLE, EI_SINGLE_STEP,
                                            thread);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "enabling single step");
    }
}

static void
disableStepping(jthread thread)
{
    jvmtiError error;

    LOG_STEP(("disableStepping: thread=%p", thread));

    error = threadControl_setEventMode(JVMTI_DISABLE, EI_SINGLE_STEP,
                                            thread);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "disabling single step");
    }
}

static jvmtiError
getFrameLocation(jthread thread,
        jclass *pclazz, jmethodID *pmethod, jlocation *plocation)
{
    jvmtiError error;

    *pclazz = NULL;
    *pmethod = NULL;
    *plocation = -1;

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
            (gdata->jvmti, thread, 0, pmethod, plocation);
    if (error == JVMTI_ERROR_NONE && *pmethod!=NULL ) {
        /* This also serves to verify that the methodID is valid */
        error = methodClass(*pmethod, pclazz);
    }
    return error;
}

static void
getLineNumberTable(jmethodID method, jint *pcount,
                jvmtiLineNumberEntry **ptable)
{
    jvmtiError error;

    *pcount = 0;
    *ptable = NULL;

    /* If the method is native or obsolete, don't even ask for the line table */
    if ( isMethodObsolete(method) || isMethodNative(method)) {
        return;
    }

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetLineNumberTable)
                (gdata->jvmti, method, pcount, ptable);
    if (error != JVMTI_ERROR_NONE) {
        *pcount = 0;
    }
}

static jint
findLineNumber(jthread thread, jlocation location,
               jvmtiLineNumberEntry *lines, jint count)
{
    jint line = -1;

    if (location != -1) {
        if (count > 0) {
            jint i;
            /* any preface before first line is assigned to first line */
            for (i=1; i<count; i++) {
                if (location < lines[i].start_location) {
                    break;
                }
            }
            line = lines[i-1].line_number;
        }
    }
    return line;
}

static jboolean
hasLineNumbers(jmethodID method)
{
    jint count;
    jvmtiLineNumberEntry *table;

    getLineNumberTable(method, &count, &table);
    if ( count == 0 ) {
        return JNI_FALSE;
    } else {
        jvmtiDeallocate(table);
    }
    return JNI_TRUE;
}

static jvmtiError
initState(JNIEnv *env, jthread thread, StepRequest *step)
{
    jvmtiError error;

    /*
     * Initial values that may be changed below
     */
    step->fromLine = -1;
    step->fromNative = JNI_FALSE;
    step->frameExited = JNI_FALSE;
    step->fromStackDepth = getFrameCount(thread);

    if (step->fromStackDepth <= 0) {
        /*
         * If there are no stack frames, treat the step as though
         * from a native frame. This is most likely to occur at the
         * beginning of a debug session, right after the VM_INIT event,
         * so we need to do something intelligent.
         */
        step->fromNative = JNI_TRUE;
        return JVMTI_ERROR_NONE;
    }

    /*
     * Try to get a notification on frame pop. If we're in an opaque frame
     * we won't be able to, but we can use other methods to detect that
     * a native frame has exited.
     *
     * TO DO: explain the need for this notification.
     */
    error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
                (gdata->jvmti, thread, 0);
    if (error == JVMTI_ERROR_OPAQUE_FRAME) {
        step->fromNative = JNI_TRUE;
        error = JVMTI_ERROR_NONE;
        /* continue without error */
    } else if (error == JVMTI_ERROR_DUPLICATE) {
        error = JVMTI_ERROR_NONE;
        /* Already being notified, continue without error */
    } else if (error != JVMTI_ERROR_NONE) {
        return error;
    }

    LOG_STEP(("initState(): frame=%d", step->fromStackDepth));

    /*
     * Note: we can't undo the frame pop notify, so
     * we'll just have to let the handler ignore it if
     * there are any errors below.
     */

    if (step->granularity == JDWP_STEP_SIZE(LINE) ) {

        LOG_STEP(("initState(): Begin line step"));

        WITH_LOCAL_REFS(env, 1) {

            jclass clazz;
            jmethodID method;
            jlocation location;

            error = getFrameLocation(thread, &clazz, &method, &location);
            if (error == JVMTI_ERROR_NONE) {
                /* Clear out previous line table only if we changed methods */
                if ( method != step->method ) {
                    step->lineEntryCount = 0;
                    if (step->lineEntries != NULL) {
                        jvmtiDeallocate(step->lineEntries);
                        step->lineEntries = NULL;
                    }
                    step->method = method;
                    getLineNumberTable(step->method,
                                 &step->lineEntryCount, &step->lineEntries);
                    if (step->lineEntryCount > 0) {
                        convertLineNumberTable(env, clazz,
                                &step->lineEntryCount, &step->lineEntries);
                    }
                }
                step->fromLine = findLineNumber(thread, location,
                                     step->lineEntries, step->lineEntryCount);
            }

        } END_WITH_LOCAL_REFS(env);

    }

    return error;
}

/*
 * TO DO: The step handlers (handleFrameChange and handleStep can
 * be broken down and made simpler now that we can install and de-install event
 * handlers.
 */
static void
handleFramePopEvent(JNIEnv *env, EventInfo *evinfo,
                    HandlerNode *node,
                    struct bag *eventBag)
{
    StepRequest *step;
    jthread thread = evinfo->thread;

    stepControl_lock();

    step = threadControl_getStepRequest(thread);
    if (step == NULL) {
        EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
    }

    if (step->pending) {
        /*
         * Note: current depth is reported as *before* the pending frame
         * pop.
         */
        jint currentDepth;
        jint fromDepth;
        jint afterPopDepth;

        currentDepth = getFrameCount(thread);
        fromDepth = step->fromStackDepth;
        afterPopDepth = currentDepth-1;

        LOG_STEP(("handleFramePopEvent: BEGIN fromDepth=%d, currentDepth=%d",
                        fromDepth, currentDepth));

        /*
         * If we are exiting the original stepping frame, record that
         * fact here. Once the next step event comes in, we can safely
         * stop stepping there.
         */
        if (fromDepth > afterPopDepth ) {
            step->frameExited = JNI_TRUE;
        }

        if (step->depth == JDWP_STEP_DEPTH(OVER)) {
            /*
             * Either
             * 1) the original stepping frame is about to be popped
             *    [fromDepth == currentDepth]. Re-enable stepping to
             *    reach a point where we can stop.
             * 2) a method called from the stepping frame has returned
             *    (during which we had stepping disabled)
             *    [fromDepth == currentDepth - 1]. Re-enable stepping
             *    so that we can continue instructions steps in the
             *    original stepping frame.
             * 3) a method further down the call chain has notified
             *    of a frame pop [fromDepth < currentDepth - 1]. This
             *    *might* represent case (2) above if the stepping frame
             *    was calling a native method which in turn called a
             *    java method. If so, we must enable stepping to
             *    ensure that we get control back after the intervening
             *    native frame is popped (you can't get frame pop
             *    notifications on native frames). If the native caller
             *    calls another Java method before returning,
             *    stepping will be diabled again and another frame pop
             *    will be awaited.
             *
             *    If it turns out that this is not case (2) with native
             *    methods, then the enabled stepping is benign and
             *    will be disabled again on the next step event.
             *
             * Note that the condition not covered above,
             * [fromDepth > currentDepth] shouldn't happen since it means
             * that too many frames have been popped. For robustness,
             * we enable stepping in that case too, so that the errant
             * step-over can be stopped.
             *
             */
            LOG_STEP(("handleFramePopEvent: starting singlestep, depth==OVER"));
            enableStepping(thread);
        } else if (step->depth == JDWP_STEP_DEPTH(OUT) &&
                   fromDepth > afterPopDepth) {
            /*
             * The original stepping frame is about to be popped. Step
             * until we reach the next safe place to stop.
             */
            LOG_STEP(("handleFramePopEvent: starting singlestep, depth==OUT && fromDepth > afterPopDepth (%d>%d)",fromDepth, afterPopDepth));
            enableStepping(thread);
        } else if (step->methodEnterHandlerNode != NULL &&
                   fromDepth >= afterPopDepth) {
            /*
             * We installed a method entry event handler as part of a
             * step into operation. We've popped back to the original
             * stepping frame without finding a place to stop.
             * Resume stepping in the original frame.
             */
            LOG_STEP(("handleFramePopEvent: starting singlestep, have methodEnter handler && depth==OUT && fromDepth >= afterPopDepth (%d>%d)",fromDepth, afterPopDepth));
            enableStepping(thread);
            (void)eventHandler_free(step->methodEnterHandlerNode);
            step->methodEnterHandlerNode = NULL;
        }
        LOG_STEP(("handleFramePopEvent: finished"));
    }

    stepControl_unlock();
}

static void
handleExceptionCatchEvent(JNIEnv *env, EventInfo *evinfo,
                          HandlerNode *node,
                          struct bag *eventBag)
{
    StepRequest *step;
    jthread thread = evinfo->thread;

    stepControl_lock();

    step = threadControl_getStepRequest(thread);
    if (step == NULL) {
        EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
    }

    if (step->pending) {
        /*
         *  Determine where we are on the call stack relative to where
         *  we started.
         */
        jint currentDepth = getFrameCount(thread);
        jint fromDepth = step->fromStackDepth;

        LOG_STEP(("handleExceptionCatchEvent: fromDepth=%d, currentDepth=%d",
                        fromDepth, currentDepth));

        /*
         * If we are exiting the original stepping frame, record that
         * fact here. Once the next step event comes in, we can safely
         * stop stepping there.
         */
        if (fromDepth > currentDepth) {
            step->frameExited = JNI_TRUE;
        }

        if (step->depth == JDWP_STEP_DEPTH(OVER) &&
            fromDepth >= currentDepth) {
            /*
             * Either the original stepping frame is done,
             * or a called method has returned (during which we had stepping
             * disabled). In either case we must resume stepping.
             */
            enableStepping(thread);
        } else if (step->depth == JDWP_STEP_DEPTH(OUT) &&
                   fromDepth > currentDepth) {
            /*
             * The original stepping frame is done. Step
             * until we reach the next safe place to stop.
             */
            enableStepping(thread);
        } else if (step->methodEnterHandlerNode != NULL &&
                   fromDepth >= currentDepth) {
            /*
             * We installed a method entry event handler as part of a
             * step into operation. We've popped back to the original
             * stepping frame or higher without finding a place to stop.
             * Resume stepping in the original frame.
             */
            enableStepping(thread);
            (void)eventHandler_free(step->methodEnterHandlerNode);
            step->methodEnterHandlerNode = NULL;
        }
    }

    stepControl_unlock();
}

static void
handleMethodEnterEvent(JNIEnv *env, EventInfo *evinfo,
                       HandlerNode *node,
                       struct bag *eventBag)
{
    StepRequest *step;
    jthread thread;

    thread = evinfo->thread;

    stepControl_lock();

    step = threadControl_getStepRequest(thread);
    if (step == NULL) {
        EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
    }

    if (step->pending) {
        jclass    clazz;
        jmethodID method;
        char     *classname;

        LOG_STEP(("handleMethodEnterEvent: thread=%p", thread));

        clazz     = evinfo->clazz;
        method    = evinfo->method;
        classname = getClassname(clazz);

        /*
         * This handler is relevant only to step into
         */
        JDI_ASSERT(step->depth == JDWP_STEP_DEPTH(INTO));

        if (    (!eventFilter_predictFiltering(step->stepHandlerNode,
                                               clazz, classname))
             && (   step->granularity != JDWP_STEP_SIZE(LINE)
                 || hasLineNumbers(method) ) ) {
            /*
             * We've found a suitable method in which to stop. Step
             * until we reach the next safe location to complete the step->,
             * and we can get rid of the method entry handler.
             */
            enableStepping(thread);
            if ( step->methodEnterHandlerNode != NULL ) {
                (void)eventHandler_free(step->methodEnterHandlerNode);
                step->methodEnterHandlerNode = NULL;
            }
        }
        jvmtiDeallocate(classname);
        classname = NULL;
    }

    stepControl_unlock();
}

static void
completeStep(JNIEnv *env, jthread thread, StepRequest *step)
{
    jvmtiError error;

    /*
     * We've completed a step; reset state for the next one, if any
     */

    LOG_STEP(("completeStep: thread=%p", thread));

    if (step->methodEnterHandlerNode != NULL) {
        (void)eventHandler_free(step->methodEnterHandlerNode);
        step->methodEnterHandlerNode = NULL;
    }

    error = initState(env, thread, step);
    if (error != JVMTI_ERROR_NONE) {
        /*
         * None of the initState errors should happen after one step
         * has successfully completed.
         */
        EXIT_ERROR(error, "initializing step state");
    }
}

jboolean
stepControl_handleStep(JNIEnv *env, jthread thread,
                       jclass clazz, jmethodID method)
{
    jboolean completed = JNI_FALSE;
    StepRequest *step;
    jint currentDepth;
    jint fromDepth;
    jvmtiError error;
    char *classname;

    classname = NULL;
    stepControl_lock();

    step = threadControl_getStepRequest(thread);
    if (step == NULL) {
        EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
    }

    /*
     * If no step is currently pending, ignore the event
     */
    if (!step->pending) {
        goto done;
    }

    LOG_STEP(("stepControl_handleStep: thread=%p", thread));

    /*
     * We never filter step into instruction. It's always over on the
     * first step event.
     */
    if (step->depth == JDWP_STEP_DEPTH(INTO) &&
        step->granularity == JDWP_STEP_SIZE(MIN)) {
        completed = JNI_TRUE;
        LOG_STEP(("stepControl_handleStep: completed, into min"));
        goto done;
    }

    /*
     * If we have left the method in which
     * stepping started, the step is always complete.
     */
    if (step->frameExited) {
        completed = JNI_TRUE;
        LOG_STEP(("stepControl_handleStep: completed, frame exited"));
        goto done;
    }

    /*
     *  Determine where we are on the call stack relative to where
     *  we started.
     */
    currentDepth = getFrameCount(thread);
    fromDepth = step->fromStackDepth;

    if (fromDepth > currentDepth) {
        /*
         * We have returned from the caller. There are cases where
         * we don't get frame pop notifications
         * (e.g. stepping from opaque frames), and that's when
         * this code will be reached. Complete the step->
         */
        completed = JNI_TRUE;
        LOG_STEP(("stepControl_handleStep: completed, fromDepth>currentDepth(%d>%d)", fromDepth, currentDepth));
    } else if (fromDepth < currentDepth) {
        /* We have dropped into a called method. */
        if (   step->depth == JDWP_STEP_DEPTH(INTO)
            && (!eventFilter_predictFiltering(step->stepHandlerNode, clazz,
                                          (classname = getClassname(clazz))))
            && hasLineNumbers(method) ) {

            /* Stepped into a method with lines, so we're done */
            completed = JNI_TRUE;
            LOG_STEP(("stepControl_handleStep: completed, fromDepth<currentDepth(%d<%d) and into method with lines", fromDepth, currentDepth));
        } else {
            /*
             * We need to continue, but don't want the overhead of step
             * events from this method. So, we disable stepping and
             * enable a frame pop. If we're stepping into, we also
             * enable method enter events because a called frame may be
             * where we want to stop.
             */
            disableStepping(thread);

            if (step->depth == JDWP_STEP_DEPTH(INTO)) {
                step->methodEnterHandlerNode =
                    eventHandler_createInternalThreadOnly(
                                       EI_METHOD_ENTRY,
                                       handleMethodEnterEvent, thread);
                if (step->methodEnterHandlerNode == NULL) {
                    EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,
                                "installing event method enter handler");
                }
            }

            error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
                        (gdata->jvmti, thread, 0);
            if (error == JVMTI_ERROR_DUPLICATE) {
                error = JVMTI_ERROR_NONE;
            } else if (error != JVMTI_ERROR_NONE) {
                EXIT_ERROR(error, "setting up notify frame pop");
            }
        }
        jvmtiDeallocate(classname);
        classname = NULL;
    } else {
        /*
         * We are at the same stack depth where stepping started.
         * Instruction steps are complete at this point. For line
         * steps we must check to see whether we've moved to a
         * different line.
         */
        if (step->granularity == JDWP_STEP_SIZE(MIN)) {
            completed = JNI_TRUE;
            LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and min", fromDepth));
        } else {
            if (step->fromLine != -1) {
                jint line = -1;
                jlocation location;
                jmethodID method;
                WITH_LOCAL_REFS(env, 1) {
                    jclass clazz;
                    error = getFrameLocation(thread,
                                        &clazz, &method, &location);
                    if ( isMethodObsolete(method)) {
                        method = NULL;
                        location = -1;
                    }
                    if (error != JVMTI_ERROR_NONE || location == -1) {
                        EXIT_ERROR(error, "getting frame location");
                    }
                    if ( method == step->method ) {
                        LOG_STEP(("stepControl_handleStep: checking line location"));
                        log_debugee_location("stepControl_handleStep: checking line loc",
                                thread, method, location);
                        line = findLineNumber(thread, location,
                                      step->lineEntries, step->lineEntryCount);
                    }
                    if (line != step->fromLine) {
                        completed = JNI_TRUE;
                        LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and different line", fromDepth));
                    }
                } END_WITH_LOCAL_REFS(env);
            } else {
                /*
                 * This is a rare case. We have stepped from a location
                 * inside a native method to a location within a Java
                 * method at the same stack depth. This means that
                 * the original native method returned to another
                 * native method which, in turn, invoked a Java method.
                 *
                 * Since the original frame was  native, we were unable
                 * to ask for a frame pop event, and, thus, could not
                 * set the step->frameExited flag when the original
                 * method was done. Instead we end up here
                 * and act just as though the frameExited flag was set
                 * and complete the step immediately.
                 */
                completed = JNI_TRUE;
                LOG_STEP(("stepControl_handleStep: completed, fromDepth==currentDepth(%d) and no line", fromDepth));
            }
        }
        LOG_STEP(("stepControl_handleStep: finished"));
    }
done:
    if (completed) {
        completeStep(env, thread, step);
    }
    stepControl_unlock();
    return completed;
}


void
stepControl_initialize(void)
{
    stepLock = debugMonitorCreate("JDWP Step Handler Lock");
}

void
stepControl_reset(void)
{
}

/*
 * Reset step control request stack depth and line number.
 */
void
stepControl_resetRequest(jthread thread)
{

    StepRequest *step;
    jvmtiError error;

    LOG_STEP(("stepControl_resetRequest: thread=%p", thread));

    stepControl_lock();

    step = threadControl_getStepRequest(thread);

    if (step != NULL) {
        JNIEnv *env;
        env = getEnv();
        error = initState(env, thread, step);
        if (error != JVMTI_ERROR_NONE) {
            EXIT_ERROR(error, "initializing step state");
        }
    } else {
        EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request");
    }

    stepControl_unlock();
}

static void
initEvents(jthread thread, StepRequest *step)
{
    /* Need to install frame pop handler and exception catch handler when
     * single-stepping is enabled (i.e. step-into or step-over/step-out
     * when fromStackDepth > 0).
     */
    if (step->depth == JDWP_STEP_DEPTH(INTO) || step->fromStackDepth > 0) {
        /*
         * TO DO: These might be able to applied more selectively to
         * boost performance.
         */
        step->catchHandlerNode = eventHandler_createInternalThreadOnly(
                                     EI_EXCEPTION_CATCH,
                                     handleExceptionCatchEvent,
                                     thread);
        step->framePopHandlerNode = eventHandler_createInternalThreadOnly(
                                        EI_FRAME_POP,
                                        handleFramePopEvent,
                                        thread);

        if (step->catchHandlerNode == NULL ||
            step->framePopHandlerNode == NULL) {
            EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,
                        "installing step event handlers");
        }

    }
    /*
     * Initially enable stepping:
     * 1) For step into, always
     * 2) For step over, unless right after the VM_INIT.
     *    Enable stepping for STEP_MIN or STEP_LINE with or without line numbers.
     *    If the class is redefined then non EMCP methods may not have line
     *    number info. So enable line stepping for non line number so that it
     *    behaves like STEP_MIN/STEP_OVER.
     * 3) For step out, only if stepping from native, except right after VM_INIT
     *
     * (right after VM_INIT, a step->over or out is identical to running
     * forever)
     */
    switch (step->depth) {
        case JDWP_STEP_DEPTH(INTO):
            enableStepping(thread);
            break;
        case JDWP_STEP_DEPTH(OVER):
            if (step->fromStackDepth > 0 && !step->fromNative ) {
              enableStepping(thread);
            }
            break;
        case JDWP_STEP_DEPTH(OUT):
            if (step->fromNative &&
                (step->fromStackDepth > 0)) {
                enableStepping(thread);
            }
            break;
        default:
            JDI_ASSERT(JNI_FALSE);
    }
}

jvmtiError
stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth,
                      HandlerNode *node)
{
    StepRequest *step;
    jvmtiError error;
    jvmtiError error2;

    LOG_STEP(("stepControl_beginStep: thread=%p,size=%d,depth=%d",
                        thread, size, depth));

    eventHandler_lock(); /* for proper lock order */
    stepControl_lock();

    step = threadControl_getStepRequest(thread);
    if (step == NULL) {
        error = AGENT_ERROR_INVALID_THREAD;
        /* Normally not getting a StepRequest struct pointer is a fatal error
         *   but on a beginStep, we just return an error code.
         */
    } else {
        /*
         * In case the thread isn't already suspended, do it again.
         */
        error = threadControl_suspendThread(thread, JNI_FALSE);
        if (error == JVMTI_ERROR_NONE) {
            /*
             * Overwrite any currently executing step.
             */
            step->granularity = size;
            step->depth = depth;
            step->catchHandlerNode = NULL;
            step->framePopHandlerNode = NULL;
            step->methodEnterHandlerNode = NULL;
            step->stepHandlerNode = node;
            error = initState(env, thread, step);
            if (error == JVMTI_ERROR_NONE) {
                initEvents(thread, step);
            }
            /* false means it is not okay to unblock the commandLoop thread */
            error2 = threadControl_resumeThread(thread, JNI_FALSE);
            if (error2 != JVMTI_ERROR_NONE && error == JVMTI_ERROR_NONE) {
                error = error2;
            }

            /*
             * If everything went ok, indicate a step is pending.
             */
            if (error == JVMTI_ERROR_NONE) {
                step->pending = JNI_TRUE;
            }
        } else {
            EXIT_ERROR(error, "stepControl_beginStep: cannot suspend thread");
        }
    }

    stepControl_unlock();
    eventHandler_unlock();

    return error;
}


static void
clearStep(jthread thread, StepRequest *step)
{
    if (step->pending) {

        disableStepping(thread);
        if ( step->catchHandlerNode != NULL ) {
            (void)eventHandler_free(step->catchHandlerNode);
            step->catchHandlerNode = NULL;
        }
        if ( step->framePopHandlerNode!= NULL ) {
            (void)eventHandler_free(step->framePopHandlerNode);
            step->framePopHandlerNode = NULL;
        }
        if ( step->methodEnterHandlerNode != NULL ) {
            (void)eventHandler_free(step->methodEnterHandlerNode);
            step->methodEnterHandlerNode = NULL;
        }
        step->pending = JNI_FALSE;

        /*
         * Warning: Do not clear step->method, step->lineEntryCount,
         *          or step->lineEntries here, they will likely
         *          be needed on the next step.
         */

    }
}

jvmtiError
stepControl_endStep(jthread thread)
{
    StepRequest *step;
    jvmtiError error;

    LOG_STEP(("stepControl_endStep: thread=%p", thread));

    eventHandler_lock(); /* for proper lock order */
    stepControl_lock();

    step = threadControl_getStepRequest(thread);
    if (step != NULL) {
        clearStep(thread, step);
        error = JVMTI_ERROR_NONE;
    } else {
        /* If the stepRequest can't be gotten, then this thread no longer
         *   exists, just return, don't die here, this is normal at
         *   termination time. Return JVMTI_ERROR_NONE so the thread Ref
         *   can be tossed.
         */
         error = JVMTI_ERROR_NONE;
    }

    stepControl_unlock();
    eventHandler_unlock();

    return error;
}

void
stepControl_clearRequest(jthread thread, StepRequest *step)
{
    LOG_STEP(("stepControl_clearRequest: thread=%p", thread));
    clearStep(thread, step);
}

void
stepControl_lock(void)
{
    debugMonitorEnter(stepLock);
}

void
stepControl_unlock(void)
{
    debugMonitorExit(stepLock);
}

Other Java examples (source code examples)

Here is a short list of links related to this Java stepControl.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.