< prev index next >

src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c

Print this page

        

*** 1,7 **** /* ! * 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 --- 1,7 ---- /* ! * Copyright (c) 1998, 2019, 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
*** 30,53 **** #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 --- 30,39 ----
*** 84,93 **** --- 70,87 ---- if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "disabling single step"); } } + void stepControl_enableStepping(jthread thread) { + enableStepping(thread); + } + + void stepControl_disableStepping(jthread thread) { + disableStepping(thread); + } + static jvmtiError getFrameLocation(jthread thread, jclass *pclazz, jmethodID *pmethod, jlocation *plocation) { jvmtiError error;
*** 171,181 **** * 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 --- 165,175 ---- * Initial values that may be changed below */ step->fromLine = -1; step->fromNative = JNI_FALSE; step->frameExited = JNI_FALSE; ! step->fromStackDepth = getThreadFrameCount(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
*** 279,289 **** */ 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)); --- 273,283 ---- */ jint currentDepth; jint fromDepth; jint afterPopDepth; ! currentDepth = getThreadFrameCount(thread); fromDepth = step->fromStackDepth; afterPopDepth = currentDepth-1; LOG_STEP(("handleFramePopEvent: BEGIN fromDepth=%d, currentDepth=%d", fromDepth, currentDepth));
*** 339,357 **** * 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")); --- 333,360 ---- * 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) { ! /* We installed a method entry event handler as part of a step into operation. */ ! JDI_ASSERT(step->depth == JDWP_STEP_DEPTH(INTO)); ! if (fromDepth >= afterPopDepth) { ! /* ! * 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==INTO && fromDepth >= afterPopDepth (%d>=%d)", fromDepth, afterPopDepth)); ! } else { ! /* ! * The only way this should happen is if FramePop events were enabled to support a ! * CONTINUATION_RUN event while doing a STEP_INTO. See stepControl_handleContinuationRun(). ! * Resume stepping in the current frame. If it is not the correct frame to resume stepping ! * in, then handleStep() will disable single stepping and setup another FramePop request. ! */ ! LOG_STEP(("handleFramePopEvent: starting singlestep, have methodEnter handler && depth==INTO && fromDepth < afterPopDepth (%d<%d)", fromDepth, afterPopDepth)); ! } enableStepping(thread); (void)eventHandler_free(step->methodEnterHandlerNode); step->methodEnterHandlerNode = NULL; } LOG_STEP(("handleFramePopEvent: finished"));
*** 378,388 **** 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)); --- 381,391 ---- if (step->pending) { /* * Determine where we are on the call stack relative to where * we started. */ ! jint currentDepth = getThreadFrameCount(thread); jint fromDepth = step->fromStackDepth; LOG_STEP(("handleExceptionCatchEvent: fromDepth=%d, currentDepth=%d", fromDepth, currentDepth));
*** 425,486 **** } 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) --- 428,533 ---- } stepControl_unlock(); } + /* + * Help deal with a METHOD_ENTRY event (or a simulated one). Returns JNI_TRUE if + * single stepping gets enabled. + */ + static jboolean + methodEnterHelper(JNIEnv *env, jthread thread, StepRequest *step, jclass clazz, jmethodID method) + { + jboolean steppingEnabled = JNI_FALSE; + if (step->pending) { + char *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 resume stepping. + * We can also get rid of the method entry handler now. + */ + enableStepping(thread); + steppingEnabled = JNI_TRUE; + if ( step->methodEnterHandlerNode != NULL ) { + (void)eventHandler_free(step->methodEnterHandlerNode); + step->methodEnterHandlerNode = NULL; + } + } + jvmtiDeallocate(classname); + classname = NULL; + } + return steppingEnabled; + } + static void handleMethodEnterEvent(JNIEnv *env, EventInfo *evinfo, HandlerNode *node, struct bag *eventBag) { StepRequest *step; ! jthread thread = evinfo->thread; stepControl_lock(); ! LOG_STEP(("handleMethodEnterEvent: thread=%p", thread)); step = threadControl_getStepRequest(thread); if (step == NULL) { EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting step request"); } + methodEnterHelper(env, thread, step, evinfo->clazz, evinfo->method); + stepControl_unlock(); + } ! void ! stepControl_handleContinuationRun(JNIEnv *env, jthread thread, StepRequest *step) ! { ! stepControl_lock(); ! if (step->methodEnterHandlerNode != NULL) { ! /* ! * Looks like we were doing a single step INTO with filtering enabled, so a MethodEntry ! * handler was installed to detect when we enter an unfiltered method. It's possible ! * that the continuation method we are resuming execution in is unfiltered, but there ! * will be no MethodEntry event for it. So we fake one here so the single stepping ! * state is udpated appropriately. ! */ ! jboolean steppingEnabled; ! jclass clazz; jmethodID method; ! jlocation location; ! jvmtiError error; ! ! JDI_ASSERT(step->depth == JDWP_STEP_DEPTH(INTO)); /* ! * We leverage the existing MethodEntry handler support for this, but it needs to know ! * the location of the MethodEntry, so we get this from the current frame. */ ! error = getFrameLocation(thread, &clazz, &method, &location); ! if (error != JVMTI_ERROR_NONE) { ! EXIT_ERROR(error, "getting frame location"); ! } ! steppingEnabled = methodEnterHelper(env, thread, step, clazz, method); ! if (!steppingEnabled) { /* ! * It looks like the continuation is resuming in a filtered method. We need to setup ! * a FramePop request on the current frame, so when it exits we can check if the ! * method we return to is filtered, and enable single stepping if not. */ ! 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"); } } } stepControl_unlock(); } static void completeStep(JNIEnv *env, jthread thread, StepRequest *step)
*** 507,517 **** 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; --- 554,564 ---- EXIT_ERROR(error, "initializing step state"); } } jboolean ! stepControl_handleStep(JNIEnv *env, jthread thread, jthread fiber, jboolean matchesFiber, jclass clazz, jmethodID method) { jboolean completed = JNI_FALSE; StepRequest *step; jint currentDepth;
*** 534,543 **** --- 581,593 ---- goto done; } LOG_STEP(("stepControl_handleStep: thread=%p", thread)); + /* Make sure the StepRequest is in agreement as to whether or not we are stepping in a fiber. */ + JDI_ASSERT(step->is_fiber == matchesFiber); + /* * We never filter step into instruction. It's always over on the * first step event. */ if (step->depth == JDWP_STEP_DEPTH(INTO) &&
*** 559,569 **** /* * 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 --- 609,619 ---- /* * Determine where we are on the call stack relative to where * we started. */ ! currentDepth = getThreadFrameCount(thread); fromDepth = step->fromStackDepth; if (fromDepth > currentDepth) { /* * We have returned from the caller. There are cases where
*** 595,610 **** 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; --- 645,661 ---- if (step->depth == JDWP_STEP_DEPTH(INTO)) { step->methodEnterHandlerNode = eventHandler_createInternalThreadOnly( EI_METHOD_ENTRY, ! handleMethodEnterEvent, matchesFiber ? fiber : thread); if (step->methodEnterHandlerNode == NULL) { EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE, "installing event method enter handler"); } } + LOG_STEP(("stepControl_handleStep: NotifyFramePop, (fromDepth currentDepth)(%d %d) ", fromDepth, currentDepth)); error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop) (gdata->jvmti, thread, 0); if (error == JVMTI_ERROR_DUPLICATE) { error = JVMTI_ERROR_NONE;
*** 722,732 **** 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). */ --- 773,783 ---- stepControl_unlock(); } static void ! initEvents(jthread thread, jthread filter_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). */
*** 736,750 **** * 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"); --- 787,802 ---- * boost performance. */ step->catchHandlerNode = eventHandler_createInternalThreadOnly( EI_EXCEPTION_CATCH, handleExceptionCatchEvent, ! filter_thread); ! JDI_ASSERT(step->framePopHandlerNode == NULL); step->framePopHandlerNode = eventHandler_createInternalThreadOnly( EI_FRAME_POP, handleFramePopEvent, ! filter_thread); if (step->catchHandlerNode == NULL || step->framePopHandlerNode == NULL) { EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE, "installing step event handlers");
*** 783,805 **** 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. --- 835,890 ---- JDI_ASSERT(JNI_FALSE); } } jvmtiError ! stepControl_beginStep(JNIEnv *env, jthread filter_thread, jint size, jint depth, HandlerNode *node) { StepRequest *step; jvmtiError error; jvmtiError error2; + jthread thread; + jboolean is_fiber; + + /* filter_thread could be a fiber. Get the carrier thread it is mounted on. */ + is_fiber = isFiber(filter_thread); + if (is_fiber) { + thread = getFiberThread(filter_thread); + if (thread == NULL) { + /* fiber fixme: It is possible for the StepRequest to have been made on an unmounted + * fiber, and currently we don't support this. + */ + LOG_STEP(("stepControl_beginStep: thread is an unmounted fiber(%p)", filter_thread)); + return JVMTI_ERROR_INVALID_THREAD; + } + } else { + thread = filter_thread; + } ! LOG_STEP(("stepControl_beginStep: filter_thread=%p,thread=%p,size=%d,depth=%d", ! filter_thread, thread, size, depth)); eventHandler_lock(); /* for proper lock order */ stepControl_lock(); + /* fiber fixme: we should consider getting the StepRequest from the fiber instead of the thread. + * That way we don't need to copy back and forth in threadControl_mountFiber and + * threadControl_unmountFiber. It would require some additional changes in the step event + * support to always pass the fiber to threadControl_getStepRequest(). We also need to + * deal with node->instructionStepMode, referenceing the fiber copy when appropriate. Note + * this will get tricky if you try to single step in both the fiber and thread. With the + * current impl, if you single step in the fiber first, hit a breakpoint while stepping over + * a method call, and then switch to the carrier thread and start to single step there, + * that will clear out the fiber single stepping. If we get the StepRequest + * from the fiber instead, it won't automatically clear out the fiber single stepping + * when you start single stepping in the carrier thread, but it won't work as expected either + * because JVMTI single stepping on the carrier thread will be disabled once the single + * step is complete. We'd need to detect that we were single stepping on the fiber and + * keep JVMTI single stepping enabled, or we need to clear the single stepping state of + * the fiber. + */ 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.
*** 813,829 **** /* * 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; --- 898,915 ---- /* * Overwrite any currently executing step. */ step->granularity = size; step->depth = depth; + step->is_fiber = is_fiber; step->catchHandlerNode = NULL; step->framePopHandlerNode = NULL; step->methodEnterHandlerNode = NULL; step->stepHandlerNode = node; error = initState(env, thread, step); if (error == JVMTI_ERROR_NONE) { ! initEvents(thread, filter_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;
*** 885,894 **** --- 971,987 ---- LOG_STEP(("stepControl_endStep: thread=%p", thread)); eventHandler_lock(); /* for proper lock order */ stepControl_lock(); + if (isFiber(thread)) { + jthread carrier_thread = getFiberThread(thread); + /* During termination the fiber might not be mounted, so a NULL carrier thead is ok in that case. */ + if (carrier_thread != NULL) { + thread = carrier_thread; + } + } step = threadControl_getStepRequest(thread); if (step != NULL) { clearStep(thread, step); error = JVMTI_ERROR_NONE; } else {
< prev index next >