diff --git a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventHandler.java b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventHandler.java index bc9459759fca2..e4f9ecdec35d1 100644 --- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventHandler.java +++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, 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 @@ -101,13 +101,19 @@ private boolean handleEvent(Event event) { notifier.receivedEvent(event); /* - * See if the event thread is a vthread that we need to start tracking. + * See if the event thread is a vthread that we need to start tracking. Note + * we don't track the thread if it is not going to be suspended because it + * might terminate before we even register the ThreadDeathRequest below, + * which will result in it never being unregistered. */ ThreadReference eventThread = null; - if (event instanceof ClassPrepareEvent evt) { - eventThread = evt.thread(); - } else if (event instanceof LocatableEvent evt) { - eventThread = evt.thread(); + EventRequest req = event.request(); + if (req != null && req.suspendPolicy() != EventRequest.SUSPEND_NONE) { + if (event instanceof ClassPrepareEvent evt) { + eventThread = evt.thread(); + } else if (event instanceof LocatableEvent evt) { + eventThread = evt.thread(); + } } if (eventThread != null) { // This might be a vthread we haven't seen before, so add it to the list. diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c b/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c index 2bed6b6bb281b..1035898f02d68 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c @@ -989,6 +989,8 @@ parseOptions(char *options) gdata->includeVThreads = JNI_FALSE; gdata->rememberVThreadsWhenDisconnected = JNI_FALSE; + gdata->virtualThreadStartEventsPermanentlyEnabled = JNI_FALSE; + gdata->jvmti_data_dump = JNI_FALSE; /* Options being NULL will end up being an error. */ diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventFilter.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventFilter.c index 3ba875e88cdd3..b13c7d8300f97 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventFilter.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventFilter.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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 @@ -1207,6 +1207,15 @@ clearWatchpoint(HandlerNode *node) /** * Determine the thread this node is filtered on. * NULL if not thread filtered. + * + * FIXME - This API does not take into account if there is more than one filter thread. + * As a result the event will end up getting enabled on the thread, but when an event + * comes in, it will (properly) fail the eventFilterRestricted_passesFilter() check, + * and thus (properly) not pass the event on to the debugger. The downside of all this + * is that it was not actually necessary to enable the event on the "request" thread. + * However, if we return NULL if there is more than one filter thread, then the result + * is enabling the event on all threads, and we don't want that either. This API needs + * a way to communicate that there was more than one filter thread, so don't enable the event. */ static jthread requestThread(HandlerNode *node) @@ -1243,6 +1252,39 @@ matchThread(JNIEnv *env, HandlerNode *node, void *arg) return isSameObject(env, reqThread, goalThread); } +// Returns true if this handler has the PlatformThreadsOnly filter enabled. +static jboolean +hasPlatformThreadsOnlyFilter(HandlerNode *node) +{ + int i; + Filter *filter = FILTERS_ARRAY(node); + + for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) { + switch (filter->modifier) { + case JDWP_REQUEST_MODIFIER(PlatformThreadsOnly): + return JNI_TRUE; + default: + continue; + } + } + return JNI_FALSE; +} + +// Used to determine if no handler of the given type have the PlatformThreadsOnly filter. +static jboolean +matchHasNoPlatformThreadsOnlyFilter(JNIEnv *env, HandlerNode *node, void *arg) +{ + jthread goalThread = (jthread)arg; + jthread reqThread = requestThread(node); // the filter thread + if (hasPlatformThreadsOnlyFilter(node)) { + return JNI_FALSE; + } else { + // If this handler does not have a PlatformThreadsOnly filter, then we + // only return true if the threads also match, or are both NULL. + return isSameObject(env, reqThread, goalThread); + } +} + /** * Do any enabling of events (including setting breakpoints etc) * needed to get the events requested by this handler node. @@ -1251,8 +1293,9 @@ static jvmtiError enableEvents(HandlerNode *node) { jvmtiError error = JVMTI_ERROR_NONE; + EventIndex ei = NODE_EI(node); - switch (NODE_EI(node)) { + switch (ei) { /* The stepping code directly enables/disables stepping as * necessary */ @@ -1261,14 +1304,54 @@ enableEvents(HandlerNode *node) * (hardwired in the event hook), so we don't change the * notification mode here. */ - case EI_THREAD_START: - case EI_THREAD_END: case EI_VM_INIT: case EI_VM_DEATH: case EI_CLASS_UNLOAD: + return JVMTI_ERROR_NONE; + + case EI_THREAD_END: + case EI_THREAD_START: + /* JVMTI_EVENT_THREAD_START/END are always enabled. However, we need to + * conditionally enable JVMTI_EVENT_VIRTUAL_THREAD_START/END based on + * whether or not there is any handler that does not use the + * PlatformThreadsOnly filter. Note we don't have a separate JDWP + * event type for virtual theads. They use THREAD_START/END, but + * JVMTI does have different event types for them. + */ + if (gdata->includeVThreads) { + // JVMTI_EVENT_VIRTUAL_THREAD_START/END are already always enabled. + return JVMTI_ERROR_NONE; + } + if (ei == EI_THREAD_START && gdata->virtualThreadStartEventsPermanentlyEnabled) { + // JVMTI_EVENT_VIRTUAL_THREAD_START is already permanently enabled. + return JVMTI_ERROR_NONE; + } + if (hasPlatformThreadsOnlyFilter(node)) { + // This request has the filter so would not end up triggering + // enabling the VIRTUAL events. + return JVMTI_ERROR_NONE; + } + + // This request does not have the filter, so enable VIRTUAL events. It's possible + // that the events are already enabled, but rather than trying to determine that + // first, it's a lot easier to just blindly enable them. There's no harm if they + // were already enabled. + if (ei == EI_THREAD_START) { + error = threadControl_setEventMode(JVMTI_ENABLE, EI_VIRTUAL_THREAD_START, NULL); + } else { + jthread thread = requestThread(node); + error = threadControl_setEventMode(JVMTI_ENABLE, EI_VIRTUAL_THREAD_END, thread); + } + if (error != JVMTI_ERROR_NONE && error != JVMTI_ERROR_THREAD_NOT_ALIVE) { + EXIT_ERROR(error, "enabling VIRTUAL_THREAD_START/END"); + } + return error; + case EI_VIRTUAL_THREAD_START: case EI_VIRTUAL_THREAD_END: - return error; + // These are mapped to EI_THREAD_START/END so we should never see a handler for them. + JDI_ASSERT(JNI_FALSE); + return JVMTI_ERROR_NONE; case EI_FIELD_ACCESS: case EI_FIELD_MODIFICATION: @@ -1291,10 +1374,8 @@ enableEvents(HandlerNode *node) * thread (or all threads (thread == NULL)) then enable * these events on this thread. */ - if (!eventHandlerRestricted_iterator( - NODE_EI(node), matchThread, thread)) { - error = threadControl_setEventMode(JVMTI_ENABLE, - NODE_EI(node), thread); + if (!eventHandlerRestricted_iterator(ei, matchThread, thread)) { + error = threadControl_setEventMode(JVMTI_ENABLE, ei, thread); } } return error; @@ -1310,9 +1391,9 @@ disableEvents(HandlerNode *node) jvmtiError error = JVMTI_ERROR_NONE; jvmtiError error2 = JVMTI_ERROR_NONE; jthread thread; + EventIndex ei = NODE_EI(node); - - switch (NODE_EI(node)) { + switch (ei) { /* The stepping code directly enables/disables stepping as * necessary */ @@ -1321,14 +1402,60 @@ disableEvents(HandlerNode *node) * (hardwired in the event hook), so we don't change the * notification mode here. */ - case EI_THREAD_START: - case EI_THREAD_END: case EI_VM_INIT: case EI_VM_DEATH: case EI_CLASS_UNLOAD: + return JVMTI_ERROR_NONE; + + case EI_THREAD_START: + case EI_THREAD_END: + // See comments above in enableEvents() for special handling of virtual thread events. + if (gdata->includeVThreads) { + // JVMTI_EVENT_VIRTUAL_THREAD_START/END are already enabled and stay enabled. + return JVMTI_ERROR_NONE; + } + if (ei == EI_THREAD_START && gdata->virtualThreadStartEventsPermanentlyEnabled) { + // JVMTI_EVENT_VIRTUAL_THREAD_START is already permanently enabled. + return JVMTI_ERROR_NONE; + } + if (hasPlatformThreadsOnlyFilter(node)) { + // This request has the filter, so removing it would not end up + // triggering disabling the virtual thread events. + return JVMTI_ERROR_NONE; + } + + jthread thread = requestThread(node); + // Unlike when enabling events, we can't just blindly disable them here. + // If, other than this handler, there are one or more handlers that require + // events to be enabled, then we need to keep them enabled, so we need to + // check all the other handlers. + // + // One thing important to note when we call eventHandlerRestricted_iterator() + // below is that "node" has already been removed from the event handler list, + // so it won't show up during the iteration. + if (!eventHandlerRestricted_iterator(ei, matchHasNoPlatformThreadsOnlyFilter, thread)) { + // This request doesn't have the filter, but all the other existing + // requests do, so we should disable the event because none of the + // remaining requests rely on it. + if (ei == EI_THREAD_START) { + error = threadControl_setEventMode(JVMTI_DISABLE, EI_VIRTUAL_THREAD_START, NULL); + } else { + error = threadControl_setEventMode(JVMTI_DISABLE, EI_VIRTUAL_THREAD_END, thread); + } + //tty_message("DISABLE:(%d) DISABLED - all nodes have filters", ei); + } else { + //tty_message("DISABLE:(%d) NO ACTION - at least one node with the filter", ei); + } + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error, "disabling VIRTUAL_THREAD_START/END"); + } + return error; + case EI_VIRTUAL_THREAD_START: case EI_VIRTUAL_THREAD_END: - return error; + // These are mapped to EI_THREAD_START/END so we should never see a handler for them. + JDI_ASSERT(JNI_FALSE); + return JVMTI_ERROR_NONE; case EI_FIELD_ACCESS: case EI_FIELD_MODIFICATION: @@ -1351,11 +1478,10 @@ disableEvents(HandlerNode *node) * * Disable even if the above caused an error */ - if (!eventHandlerRestricted_iterator(NODE_EI(node), matchThread, thread)) { - error2 = threadControl_setEventMode(JVMTI_DISABLE, - NODE_EI(node), thread); + if (!eventHandlerRestricted_iterator(ei, matchThread, thread)) { + error2 = threadControl_setEventMode(JVMTI_DISABLE, ei, thread); } - return error != JVMTI_ERROR_NONE? error : error2; + return error != JVMTI_ERROR_NONE ? error : error2; } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c index 3968b1de6b646..0121173a34651 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, 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 @@ -1640,29 +1640,6 @@ eventHandler_initialize(jbyte sessionID) void eventHandler_onConnect() { - debugMonitorEnter(handlerLock); - - /* - * Enable vthread START and END events if they are not already always enabled. - * They are always enabled if we are remembering vthreads when no debugger is - * connected. Otherwise they are only enabled when connected because they can - * be very noisy and hurt performance a lot. - */ - if (gdata->vthreadsSupported && !gdata->rememberVThreadsWhenDisconnected) { - jvmtiError error; - error = threadControl_setEventMode(JVMTI_ENABLE, - EI_VIRTUAL_THREAD_START, NULL); - if (error != JVMTI_ERROR_NONE) { - EXIT_ERROR(error,"Can't enable vthread start events"); - } - error = threadControl_setEventMode(JVMTI_ENABLE, - EI_VIRTUAL_THREAD_END, NULL); - if (error != JVMTI_ERROR_NONE) { - EXIT_ERROR(error,"Can't enable vthread end events"); - } - } - - debugMonitorExit(handlerLock); } static jvmtiError @@ -1708,6 +1685,19 @@ eventHandler_reset(jbyte sessionID) } } + /* We also want to disable VIRTUAL_THREAD_START events if they were enabled due to + * a deferred event request. + */ + if (gdata->virtualThreadStartEventsPermanentlyEnabled) { + jvmtiError error; + error = threadControl_setEventMode(JVMTI_DISABLE, + EI_VIRTUAL_THREAD_START, NULL); + if (adjust_jvmti_error(error) != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't disable vthread start events"); + } + gdata->virtualThreadStartEventsPermanentlyEnabled = JNI_FALSE; + } + /* Reset the event helper thread, purging all queued and * in-process commands. */ @@ -1931,6 +1921,8 @@ eventHandler_dumpHandlers(EventIndex ei, jboolean dumpPermanent) void eventHandler_dumpHandler(HandlerNode *node) { - tty_message("Handler for %s(%d)\n", eventIndex2EventName(node->ei), node->ei); + tty_message("handlerID(%d) for %s(%d) suspendPolicy(%d) permanent(%d)", + node->handlerID, eventIndex2EventName(node->ei), node->ei, + node->suspendPolicy, node->permanent); eventFilter_dumpHandlerFilters(node); } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c index cf330d74d2956..ea6f39eadd883 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c @@ -894,6 +894,13 @@ clearStep(jthread thread, StepRequest *step) * be needed on the next step. */ + jint state = getThreadState(thread); + if (state & JVMTI_THREAD_STATE_TERMINATED) { + // If this thread has terminated, there is no need to do any of the + // below, and doing so would produce errors. + return; + } + jvmtiError error; jboolean needsSuspending; // true if we needed to suspend this thread @@ -902,7 +909,6 @@ clearStep(jthread thread, StepRequest *step) if (isSameObject(getEnv(), threadControl_currentThread(), thread)) { needsSuspending = JNI_FALSE; } else { - jint state = getThreadState(thread); needsSuspending = ((state & JVMTI_THREAD_STATE_SUSPENDED) == 0); } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c index 491182a583feb..b7e64ead5ac87 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c @@ -94,6 +94,9 @@ typedef struct ThreadList { ThreadNode *first; } ThreadList; +static void dumpThread(ThreadNode *node); +static void dumpThreadList(ThreadList *list); + /* * popFrameEventLock is used to notify that the event has been received */ @@ -157,11 +160,20 @@ setThreadLocalStorage(jthread thread, ThreadNode *node) error = JVMTI_FUNC_PTR(gdata->jvmti,SetThreadLocalStorage) (gdata->jvmti, thread, (void*)node); - if ( error == JVMTI_ERROR_THREAD_NOT_ALIVE && node == NULL) { - /* Just return. This can happen when clearing the TLS. */ - return; - } else if ( error != JVMTI_ERROR_NONE ) { - /* The jthread object must be valid, so this must be a fatal error */ + if (error == JVMTI_ERROR_THREAD_NOT_ALIVE) { + if (node == NULL) { + // Just return. This can happen when clearing the TLS. + return; + } + if (isVThread(thread)) { + // Just return. This can happen with a vthread that is running and we + // had to create a ThreadNode for it. By the time we get here, it may + // have already terminated. + return; + } + } + if (error != JVMTI_ERROR_NONE) { + // The jthread object must be valid, so this must be a fatal error. EXIT_ERROR(error, "cannot set thread local storage"); } } @@ -251,9 +263,10 @@ findThread(ThreadList *list, jthread thread) * Otherwise the thread should not be on the runningThreads. */ if ( !gdata->jvmtiCallBacksCleared ) { - /* The thread better not be on either list if the TLS lookup failed. */ + // The thread better not be on the runningThreads list if the TLS lookup failed. + // It might be on the runningVThreads list because VIRTUAL_THREAD_END events + // might not be enabled, so we don't check runningVThreads. JDI_ASSERT(!nonTlsSearch(getEnv(), &runningThreads, thread)); - JDI_ASSERT(!nonTlsSearch(getEnv(), &runningVThreads, thread)); } else { /* * Search the runningThreads and runningVThreads lists. The TLS lookup may have @@ -320,7 +333,7 @@ findRunningThread(jthread thread) // Unlike platform threads, we don't always have a ThreadNode for all vthreads. // They can be freed if not holding on to any relevant state info. It's also // possible that the vthread was created before the debugger attached. Also - // in the future we won't be enabling VIRTUAL_THREAD_START events in some + // we won't be enabling VIRTUAL_THREAD_START events in some // cases, which means we won't be creating a ThreadNode when the vthread is // created. If for any of the above reasons the ThreadNode lookup failed, // we'll create one for the vthread now, but only if really needed. @@ -556,6 +569,26 @@ freeUnusedVThreadNode(JNIEnv *env, ThreadNode* node) return; } + /* + * We are about to do a bunch of checks to see if the ThreadNode is holding + * on to any important thread state information. If it is, then we can't + * free it. However, sometimes this state information is still present even + * after the thread has termintated. In this case the state information + * is no longer relevant and we can still free the ThreadNode, so the first + * thing we do is check if the thread is termintated, and bypass all the + * other checks if it is. + */ + jint vthread_state = 0; + jvmtiError error = threadState(node->thread, &vthread_state); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error, "getting vthread state"); + } + if ((vthread_state & JVMTI_THREAD_STATE_TERMINATED) != 0) { + removeNode(node); + clearThread(env, node); + return; + } + /* * node->suspendCount requires special handling to see if it triggers having * to keep the node around. It's possible for it to be 0 yet we still need to @@ -657,6 +690,18 @@ enumerateOverThreadList(JNIEnv *env, ThreadList *list, return error; } + +/* + * DeferredEventMode: Deferred event mode support + * + * JDWP allows filtering of events by thread. This is allowed after the thread has been + * created, even if it has not been started yet. However, JVMTI does not allow enabling + * events for a specific thread before the thread has started. If an EventRequest is made + * with thread filtering enabled, and this is done before the thread is running, we must + * defer the enabling of the JVMTI event until the THREAD_START event is received. The + * code below supports this deferred event enabling. + */ + static void insertEventMode(DeferredEventModeList *list, DeferredEventMode *eventMode) { @@ -697,6 +742,32 @@ addDeferredEventMode(JNIEnv *env, jvmtiEventMode mode, EventIndex ei, jthread th eventMode->ei = ei; eventMode->next = NULL; insertEventMode(&deferredEventModes, eventMode); + + /* + * Deferred event mode handling relies on getting a THREAD_START event or + * VIRTUAL_THREAD_START event to trigger the enabling of the event. THREAD_START + * is always enabled, but VIRTUAL_THREAD_START may not be. So that means if + * there is a deferred event mode request for a virtual thread, we must make + * sure VIRTUAL_THREAD_START events are enabled. + * + * Since it is not common to enable events on a thread that has not yet been + * started, we choose to just permanently enable VIRTUAL_THREAD_START events + * when needed here. A more elegant solution would also disable them when + * the deferred event mode is dequeued and processed, but that comes with + * complications. We'd not only need to make sure that there are no other + * deferred event modes queue up for virtual threads, but we'd also need to + * make sure that there are no event handlers (event requests) for THREAD_START + * events that do not have the PlatformThreadsFilter enabled. + */ + if (!gdata->includeVThreads && !gdata->virtualThreadStartEventsPermanentlyEnabled && isVThread(thread)) { + jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventNotificationMode) + (gdata->jvmti, JVMTI_ENABLE, JVMTI_EVENT_VIRTUAL_THREAD_START, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error, "cannot enable JVMTI_EVENT_VIRTUAL_THREAD_START"); + } + gdata->virtualThreadStartEventsPermanentlyEnabled = JNI_TRUE; + } + return JVMTI_ERROR_NONE; } @@ -1240,10 +1311,28 @@ commonResumeList(JNIEnv *env) (gdata->jvmti, reqCnt, reqList, results); for (i = 0; i < reqCnt; i++) { ThreadNode *node; + jthread thread = reqList[i]; - node = findRunningThread(reqList[i]); + node = findRunningThread(thread); if (node == NULL) { - EXIT_ERROR(AGENT_ERROR_INVALID_THREAD,"missing entry in running thread table"); + node = nonTlsSearch(getEnv(), &runningVThreads, thread); + if (node != NULL) { + // This means the vthread has terminated already. This can only happen + // with vthreads since VIRTUAL_THREAD_END events might not be enable to + // trigger removal of the ThreadNode when the thread exits. Just assert + // that the thread is in the TERMINATED state. The ThreadNode will + // eventually be removed by freeUnusedVThreadNodes(). + if (gdata->assertOn) { + jint vthread_state = 0; + jvmtiError error = threadState(thread, &vthread_state); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error, "getting vthread state"); + } + JDI_ASSERT((vthread_state & JVMTI_THREAD_STATE_TERMINATED) != 0); + } + } else { + EXIT_ERROR(AGENT_ERROR_INVALID_THREAD,"missing entry in running thread table"); + } } LOG_MISC(("thread=%p resumed as part of list", node->thread)); @@ -1548,8 +1637,7 @@ threadControl_suspendAll(void) jint count; if (gdata->vthreadsSupported) { - // Now is a good time to garbage collect vthread nodes. We want to do it before - // any suspendAll because it will prevent the suspended nodes from being freed. + // Now is a good time to garbage collect vthread nodes. if (!gdata->includeVThreads) { freeUnusedVThreadNodes(env); } @@ -2716,9 +2804,6 @@ threadControl_allVThreads(jint *numVThreads) /***** APIs for debugging the debug agent *****/ -static void dumpThreadList(ThreadList *list); -static void dumpThread(ThreadNode *node); - void threadControl_dumpAllThreads() { @@ -2806,6 +2891,14 @@ dumpThread(ThreadNode *node) { tty_message("\tcurrentInvoke.pending: %d", node->currentInvoke.pending); tty_message("\tcurrentInvoke.started: %d", node->currentInvoke.started); tty_message("\tcurrentInvoke.available: %d", node->currentInvoke.available); + tty_message("\tcurrentStep.pending: %d", node->currentStep.pending); + tty_message("\tinstructionStepMode: %d", node->instructionStepMode); + tty_message("\tcurrent_ei: %d", node->current_ei); + tty_message("\tcleInfo.ei: %d", node->cleInfo.ei); + tty_message("\tpopFrameEvent: %d", node->popFrameEvent); + tty_message("\tpopFrameProceed: %d", node->popFrameProceed); + tty_message("\tpopFrameThread: %d", node->popFrameThread); + tty_message("\tpendingStop: 0x%x", node->pendingStop); tty_message("\tobjID: %d", commonRef_refToID(getEnv(), node->thread)); #endif } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.h b/src/jdk.jdwp.agent/share/native/libjdwp/util.h index a48c8ba2c095e..e0d7a9bcc7b91 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.h @@ -87,6 +87,7 @@ typedef struct { jboolean vthreadsSupported; /* If true, debugging support for vthreads is enabled. */ jboolean includeVThreads; /* If true, VM.AllThreads includes vthreads. */ jboolean rememberVThreadsWhenDisconnected; + jboolean virtualThreadStartEventsPermanentlyEnabled; jboolean doerrorexit; jboolean modifiedUtf8; jboolean quiet;