diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java index 92f9ef7ec951..e2d3d1fb8d51 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java @@ -25,6 +25,7 @@ import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.debugger.memory.component.MemoryViewDebugProcessData; import com.intellij.debugger.memory.ui.ClassesFilteredView; import com.intellij.debugger.settings.DebuggerSettings; +import com.intellij.debugger.settings.ThreadsViewSettings; import com.intellij.debugger.ui.AlternativeSourceNotificationProvider; import com.intellij.debugger.ui.DebuggerContentInfo; import com.intellij.debugger.ui.breakpoints.Breakpoint; @@ -95,6 +96,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider; +import java.util.concurrent.CompletableFuture; + public class JavaDebugProcess extends XDebugProcess { private final DebuggerSession myJavaSession; private final JavaDebuggerEditorsProvider myEditorsProvider; @@ -291,7 +294,13 @@ public class JavaDebugProcess extends XDebugProcess { } @Override - public void computeRunningExecutionStacks(XSuspendContext.XExecutionStackContainer container) { + public void computeRunningExecutionStacks(XSuspendContext.XExecutionStackGroupContainer container, @Nullable XSuspendContext suspendContext) { + // TODO: Provide a platform-level way to define threads view settings, IDEA-384653 + var showThreadGroups = ThreadsViewSettings.getInstance().SHOW_THREAD_GROUPS; + if (!showThreadGroups && suspendContext != null) { + suspendContext.computeExecutionStacks(container); + return; + } var debugProcess = getDebuggerSession().getProcess(); var context = debugProcess.getDebuggerContext(); var managerThread = context.getManagerThread(); @@ -303,12 +312,11 @@ public class JavaDebugProcess extends XDebugProcess { @Override protected void action() { try { - var currentThread = context.getThreadProxy(); - var allThreads = VirtualMachineProxyImpl.getCurrent().allThreads(); - var executionStacks = ContainerUtil.map( - allThreads, (thread) -> (XExecutionStack) new JavaExecutionStack(thread, debugProcess, thread.equals(currentThread)) - ); - container.addExecutionStack(executionStacks, true); + if (showThreadGroups) { + addExecutionStackGroups(container, debugProcess); + } else { + addRunningExecutionStacks(container, debugProcess); + } } catch (Throwable e) { container.errorOccurred(XDebuggerBundle.message("debugger.threads.not.available") + ": " + e.getMessage()); @@ -322,6 +330,30 @@ public class JavaDebugProcess extends XDebugProcess { }); } + private static void addExecutionStackGroups(XSuspendContext.XExecutionStackGroupContainer container, DebugProcessImpl debugProcess) { + var vm = VirtualMachineProxyImpl.getCurrent(); + var currentThread = debugProcess.getDebuggerContext().getThreadProxy(); + var executionStackGroups = ContainerUtil.map( + vm.topLevelThreadGroups(), + t -> JavaThreadGroup.buildJavaThreadGroup(t, debugProcess, currentThread) + ); + CompletableFuture.allOf(executionStackGroups.toArray(CompletableFuture[]::new)) + .thenAccept(unused -> { + if (container.isObsolete()) return; + var groups = ContainerUtil.map(executionStackGroups, CompletableFuture::join); + container.addExecutionStackGroups(groups, true); + }) + .exceptionally(DebuggerUtilsAsync::logError); + } + + private static void addRunningExecutionStacks(XSuspendContext.XExecutionStackGroupContainer container, DebugProcessImpl debugProcess) { + var allThreads = VirtualMachineProxyImpl.getCurrent().allThreads(); + var executionStacks = ContainerUtil.map( + allThreads, (thread) -> (XExecutionStack) new JavaExecutionStack(thread, debugProcess, false) + ); + container.addExecutionStack(executionStacks, true); + } + @Override public @NotNull XDebuggerEditorsProvider getEditorsProvider() { return myEditorsProvider; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaThreadGroup.kt b/java/debugger/impl/src/com/intellij/debugger/engine/JavaThreadGroup.kt new file mode 100644 index 000000000000..7a23aff42ab5 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaThreadGroup.kt @@ -0,0 +1,37 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.debugger.engine + +import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl +import com.intellij.icons.AllIcons +import com.intellij.xdebugger.frame.XExecutionStackGroup +import org.jetbrains.annotations.ApiStatus +import java.util.concurrent.CompletableFuture +import javax.swing.Icon + +private class JavaThreadGroup( + name: String, + override val groups: List, + override val stacks: List +) : XExecutionStackGroup(name) { + override val icon: Icon + get() = AllIcons.Debugger.ThreadGroup + + companion object { + // Here we load all the groups at once. For Java groups it's not a problem, there are few, + // but if virtual thread groups are introduced to XThreadsView, execution stacks should be loaded lazily. + @ApiStatus.Internal + @JvmStatic + fun buildJavaThreadGroup(group: ThreadGroupReferenceProxyImpl, debugProcess: DebugProcessImpl, currentThread: ThreadReferenceProxyImpl?): CompletableFuture { + val name = group.name() + val subGroups = group.threadGroups().map { buildJavaThreadGroup(it, debugProcess, currentThread) } + val stacks = group.threads().map { thread -> JavaExecutionStack.create(thread, debugProcess, thread == currentThread) } + return CompletableFuture.allOf(*(subGroups + stacks).toTypedArray()) + .thenApply { + val subGroups = subGroups.mapNotNull { it.join() } + val stacks = stacks.mapNotNull { it.join() } + JavaThreadGroup(name, subGroups, stacks) + } + } + } +} \ No newline at end of file diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java index f08d81876515..f59bf7a5231b 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java @@ -12,10 +12,7 @@ import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XBreakpointHandler; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; -import com.intellij.xdebugger.frame.XDropFrameHandler; -import com.intellij.xdebugger.frame.XStackFrame; -import com.intellij.xdebugger.frame.XSuspendContext; -import com.intellij.xdebugger.frame.XValueMarkerProvider; +import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.mixedMode.XMixedModeDebugProcessExtension; import com.intellij.xdebugger.stepping.XSmartStepIntoHandler; import com.intellij.xdebugger.ui.XDebugTabLayouter; @@ -306,7 +303,7 @@ public abstract class XDebugProcess { * Provide execution stacks corresponding to all the live threads in the debug process to the {@code container}. */ @ApiStatus.Internal - public void computeRunningExecutionStacks(XSuspendContext.XExecutionStackContainer container) { + public void computeRunningExecutionStacks(XSuspendContext.XExecutionStackGroupContainer container, @Nullable XSuspendContext suspendContext) { container.addExecutionStack(List.of(), true); } diff --git a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebugSessionApi.kt b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebugSessionApi.kt index dfd937e244da..27183605cc0f 100644 --- a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebugSessionApi.kt +++ b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXDebugSessionApi.kt @@ -28,6 +28,7 @@ import com.intellij.xdebugger.XSourcePosition import com.intellij.xdebugger.evaluation.EvaluationMode import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider import com.intellij.xdebugger.frame.XExecutionStack +import com.intellij.xdebugger.frame.XExecutionStackGroup import com.intellij.xdebugger.frame.XStackFrame import com.intellij.xdebugger.frame.XSuspendContext import com.intellij.xdebugger.impl.XDebugSessionImpl @@ -56,6 +57,7 @@ import kotlinx.coroutines.withContext import org.jetbrains.concurrency.Promise import org.jetbrains.concurrency.await import org.jetbrains.concurrency.rejectedPromise +import kotlin.collections.map internal class BackendXDebugSessionApi : XDebugSessionApi { override suspend fun createDocument(frontendDocumentId: FrontendDocumentId, sessionId: XDebugSessionId, expression: XExpressionDto, sourcePosition: XSourcePositionDto?, evaluationMode: EvaluationMode): XExpressionDocumentDto? { @@ -212,11 +214,12 @@ internal class BackendXDebugSessionApi : XDebugSessionApi { } } - override suspend fun computeRunningExecutionStacks(sessionId: XDebugSessionId): Flow { + override suspend fun computeRunningExecutionStacks(sessionId: XDebugSessionId, suspendContextId: XSuspendContextId?): Flow { val session = sessionId.findValue() ?: return emptyFlow() + val suspendContextModel = suspendContextId?.findValue() val scope = session.coroutineScope.childScopeCancelledOnSessionEvents("RunningExecutionStacksScope", session) - return createExecutionStacksEventFlow(session, scope) { container -> - session.debugProcess.computeRunningExecutionStacks(container) + return createExecutionStackGroupEventFlow(session, scope) { container -> + session.debugProcess.computeRunningExecutionStacks(container, suspendContextModel?.suspendContext) } } @@ -247,14 +250,14 @@ internal class BackendXDebugSessionApi : XDebugSessionApi { val stacks = executionStacks.map { stack -> stack.toRpc(scope, session) } - trySend(XExecutionStacksEvent.NewExecutionStacks(stacks, last)) + trySend(NewExecutionStacksEvent(stacks, last)) if (last) { this@channelFlow.close() } } override fun errorOccurred(errorMessage: @NlsContexts.DialogMessage String) { - trySend(XExecutionStacksEvent.ErrorOccurred(errorMessage)) + trySend(ErrorOccurredEvent(errorMessage)) } } computeExecutionStacks(container) @@ -265,6 +268,54 @@ internal class BackendXDebugSessionApi : XDebugSessionApi { }.buffer(Channel.UNLIMITED) } + private fun createExecutionStackGroupEventFlow( + session: XDebugSessionImpl, + scope: CoroutineScope, + computeExecutionStacks: (XSuspendContext.XExecutionStackGroupContainer) -> Unit + ) : Flow { + return channelFlow { + attachAsChildTo(scope) + val container = object : XSuspendContext.XExecutionStackGroupContainer { + @Volatile + var obsolete = false + + override fun isObsolete(): Boolean { + return obsolete + } + + override fun addExecutionStack(executionStacks: List, last: Boolean) { + val stacks = executionStacks.map { stack -> + stack.toRpc(scope, session) + } + trySend(NewExecutionStacksEvent(stacks, last)) + if (last) { + this@channelFlow.close() + } + } + + override fun addExecutionStackGroups(executionStackGroups: List, last: Boolean) { + val groups = executionStackGroups.map { group -> + group.toRpc(scope, session) + } + trySend(NewExecutionStackGroupsEvent(groups, last)) + if (last) { + this@channelFlow.close() + } + } + + override fun errorOccurred(errorMessage: @NlsContexts.DialogMessage String) { + trySend(ErrorOccurredEvent(errorMessage)) + } + } + computeExecutionStacks(container) + + awaitClose { + container.obsolete = true + } + }.buffer(Channel.UNLIMITED) + } + + // TODO use a util function from the shared module private fun CoroutineScope.childScopeCancelledOnSessionEvents(name: String, session: XDebugSessionImpl): CoroutineScope = childScope(name).also { childScope -> val listener = object : XDebugSessionListener { @@ -374,6 +425,15 @@ internal fun XExecutionStack.toRpc(coroutineScope: CoroutineScope, session: XDeb ) } +internal fun XExecutionStackGroup.toRpc(coroutineScope: CoroutineScope, session: XDebugSessionImpl): XExecutionStackGroupDto { + return XExecutionStackGroupDto( + groups.map { it.toRpc(coroutineScope, session) }, + stacks.map { it.toRpc(coroutineScope, session) }, + this.name, + this.icon?.rpcId(), + ) +} + private fun XStackFrame.captionInfo(): XStackFrameCaptionInfo { return if (this is XStackFrameWithSeparatorAbove) { XStackFrameCaptionInfo(hasSeparatorAbove(), captionAboveOf) diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerSession.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerSession.kt index 402c1177b842..69eaf08eb777 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerSession.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/FrontendXDebuggerSession.kt @@ -16,12 +16,14 @@ import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.project.Project import com.intellij.platform.debugger.impl.frontend.evaluate.quick.FrontendXValue import com.intellij.platform.debugger.impl.frontend.frame.* +import com.intellij.platform.debugger.impl.frontend.storage.FrontendXStackFramesStorage import com.intellij.platform.debugger.impl.frontend.storage.getOrCreateStackFrame import com.intellij.platform.debugger.impl.rpc.* import com.intellij.platform.debugger.impl.shared.proxy.XBreakpointProxy import com.intellij.platform.debugger.impl.shared.proxy.XDebugSessionProxy import com.intellij.platform.debugger.impl.shared.proxy.XSmartStepIntoHandlerEntry import com.intellij.platform.debugger.impl.shared.proxy.XStackFramesListColorsCache +import com.intellij.platform.debugger.impl.shared.childScopeCancelledOnSessionEvents import com.intellij.platform.execution.impl.frontend.createFrontendProcessHandler import com.intellij.platform.execution.impl.frontend.executionEnvironment import com.intellij.platform.util.coroutines.childScope @@ -37,6 +39,7 @@ import com.intellij.xdebugger.frame.XDropFrameHandler import com.intellij.xdebugger.frame.XExecutionStack import com.intellij.xdebugger.frame.XStackFrame import com.intellij.xdebugger.frame.XSuspendContext +import com.intellij.xdebugger.frame.XSuspendContext.XExecutionStackContainer import com.intellij.xdebugger.impl.XSourceKind import com.intellij.xdebugger.impl.frame.XValueMarkers import com.intellij.xdebugger.impl.inline.DebuggerInlayListener @@ -446,15 +449,23 @@ class FrontendXDebuggerSession( return currentContext.isStepping } - override fun computeExecutionStacks(container: XSuspendContext.XExecutionStackContainer) { + override fun computeExecutionStacks(container: XExecutionStackContainer) { getCurrentSuspendContext()?.computeExecutionStacks(container) } - override fun computeRunningExecutionStacks(container: XSuspendContext.XExecutionStackContainer) { - coroutineScope.launch { + override fun computeRunningExecutionStacks(container: XSuspendContext.XExecutionStackGroupContainer) { + val suspendContext = getCurrentSuspendContext() + val scope = suspendContext?.lifetimeScope ?: coroutineScope.childScopeForRunningExecutionStack() + scope.launch { XDebugSessionApi.getInstance() - .computeRunningExecutionStacks(id) - .collectExecutionStackEvents(project, coroutineScope, container) + .computeRunningExecutionStacks(id, suspendContext?.id) + .collectExecutionStackGroupEvents(project, scope, container) + } + } + + private fun CoroutineScope.childScopeForRunningExecutionStack(): CoroutineScope { + return coroutineScope.childScopeCancelledOnSessionEvents("FrontendRunningExecutionStacksScope", this@FrontendXDebuggerSession).also { + coroutineContext.plus(FrontendXStackFramesStorage()) } } @@ -609,3 +620,28 @@ private fun CoroutineScope.syncWithLocalFlow(sourceFlow: Flow, localFlowS } } +private suspend fun Flow.collectExecutionStackGroupEvents( + project: Project, + coroutineScope: CoroutineScope, + container: XSuspendContext.XExecutionStackGroupContainer +) { + collect { executionStackEvent -> + when (executionStackEvent) { + is ErrorOccurredEvent -> { + container.errorOccurred(executionStackEvent.errorMessage) + } + is NewExecutionStacksEvent -> { + val feStacks = executionStackEvent.stacks.map { FrontendXExecutionStack(it, project, coroutineScope) } + container.addExecutionStack(feStacks, executionStackEvent.last) + } + is NewExecutionStackGroupsEvent -> { + val feStackGroups = executionStackEvent.groups.map { + FrontendXExecutionStackGroup(it, project, coroutineScope) + } + container.addExecutionStackGroups(feStackGroups, executionStackEvent.last) + } + } + } +} + + diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXSuspendContext.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXSuspendContext.kt index 49d8724d922d..317bac079233 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXSuspendContext.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXSuspendContext.kt @@ -4,6 +4,8 @@ package com.intellij.platform.debugger.impl.frontend.frame import com.intellij.openapi.project.Project import com.intellij.platform.debugger.impl.frontend.storage.FrontendXStackFramesStorage import com.intellij.platform.debugger.impl.frontend.storage.getOrCreateStackFrame +import com.intellij.platform.debugger.impl.rpc.ErrorOccurredEvent +import com.intellij.platform.debugger.impl.rpc.NewExecutionStacksEvent import com.intellij.platform.debugger.impl.rpc.XDebugSessionApi import com.intellij.platform.debugger.impl.rpc.XExecutionStacksEvent import com.intellij.platform.debugger.impl.rpc.XStackFrameDto @@ -54,17 +56,17 @@ internal class FrontendXSuspendContext( } } -internal suspend fun Flow.collectExecutionStackEvents( +private suspend fun Flow.collectExecutionStackEvents( project: Project, coroutineScope: CoroutineScope, container: XSuspendContext.XExecutionStackContainer ) { collect { executionStackEvent -> when (executionStackEvent) { - is XExecutionStacksEvent.ErrorOccurred -> { + is ErrorOccurredEvent -> { container.errorOccurred(executionStackEvent.errorMessage) } - is XExecutionStacksEvent.NewExecutionStacks -> { + is NewExecutionStacksEvent -> { // TODO[IJPL-177087]: here we are binding FrontendXExecutionStack to the suspend context scope, // which is the safest-narrowest scope in our possession. // However, maybe it's possible to set up, for example, a scope that ends when another stack is selected from a combobox. diff --git a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebugSessionApi.kt b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebugSessionApi.kt index 70b97fb6b012..bd534cebaadb 100644 --- a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebugSessionApi.kt +++ b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XDebugSessionApi.kt @@ -66,7 +66,7 @@ interface XDebugSessionApi : RemoteApi { suspend fun computeExecutionStacks(suspendContextId: XSuspendContextId): Flow - suspend fun computeRunningExecutionStacks(sessionId: XDebugSessionId): Flow + suspend fun computeRunningExecutionStacks(sessionId: XDebugSessionId, suspendContextId: XSuspendContextId?): Flow suspend fun muteBreakpoints(sessionDataId: XDebugSessionDataId, muted: Boolean) diff --git a/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/coroutineUtils.kt b/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/coroutineUtils.kt index adfb318e1766..8cce8cc50601 100644 --- a/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/coroutineUtils.kt +++ b/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/coroutineUtils.kt @@ -1,6 +1,12 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.platform.debugger.impl.shared +import com.intellij.platform.debugger.impl.shared.proxy.XDebugSessionProxy +import com.intellij.platform.util.coroutines.childScope +import com.intellij.util.asDisposable +import com.intellij.xdebugger.XDebugSessionListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -20,4 +26,17 @@ fun mapFlow(f: Flow, mapper: (T) -> R): Flow = f.map(mapper) @ApiStatus.Internal fun createMutableSharedFlow(replay: Int, extraBufferCapacity: Int): MutableSharedFlow { return MutableSharedFlow(replay, extraBufferCapacity) -} \ No newline at end of file +} + +@ApiStatus.Internal +fun CoroutineScope.childScopeCancelledOnSessionEvents(name: String, session: XDebugSessionProxy): CoroutineScope = + childScope(name).also { childScope -> + val listener = object : XDebugSessionListener { + override fun sessionPaused() { childScope.cancel() } + + override fun sessionResumed() { childScope.cancel() } + + override fun sessionStopped() { childScope.cancel() } + } + session.addSessionListener(listener, childScope.asDisposable()) + } \ No newline at end of file diff --git a/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/proxy/XDebugSessionProxy.kt b/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/proxy/XDebugSessionProxy.kt index 95f36d7db410..905c2682f8ac 100644 --- a/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/proxy/XDebugSessionProxy.kt +++ b/platform/xdebugger-impl/shared/src/com/intellij/platform/debugger/impl/shared/proxy/XDebugSessionProxy.kt @@ -89,16 +89,22 @@ interface XDebugSessionProxy { fun isSteppingSuspendContext(): Boolean /** - * Computes execution stacks corresponding to all the live threads in the debug process and adds them to the provided container + * Computes execution stacks corresponding to all the live threads in the debug process and adds them to the provided [container] * if suspendContext is available. */ fun computeExecutionStacks(container: XSuspendContext.XExecutionStackContainer) /** - * Computes execution stacks corresponding to all the live threads in the debug process and adds them to the provided container. - * Uses [com.intellij.xdebugger.XDebugProcess.computeRunningExecutionStacks] on the backend and doesn't require suspendContext. + * Computes execution stacks corresponding to all the live threads in the debug process and adds them to the provided [container]. + * + * - Uses [com.intellij.xdebugger.XDebugProcess.computeRunningExecutionStacks] to retrieve execution stacks. + * - Can provide execution stacks even if there is no active suspend context (i.e., when the process is running). + * - Supports execution stacks grouping. The provided [XSuspendContext.XExecutionStackGroupContainer] can accept + * [com.intellij.xdebugger.frame.XExecutionStackGroup] objects, allowing for a hierarchical display of execution stacks + * (e.g., grouped by thread group) in the UI. */ - fun computeRunningExecutionStacks(container: XSuspendContext.XExecutionStackContainer) + fun computeRunningExecutionStacks(container: XSuspendContext.XExecutionStackGroupContainer) + fun createTabLayouter(): XDebugTabLayouter fun addSessionListener(listener: XDebugSessionListener) fun addSessionListener(listener: XDebugSessionListener, disposable: Disposable) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/proxy/MonolithSessionProxy.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/proxy/MonolithSessionProxy.kt index 8c8ebd3d97ca..72f09f5c636b 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/proxy/MonolithSessionProxy.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/proxy/MonolithSessionProxy.kt @@ -181,8 +181,8 @@ internal class MonolithSessionProxy(val session: XDebugSession) : XDebugSessionP session.suspendContext?.computeExecutionStacks(container) } - override fun computeRunningExecutionStacks(container: XSuspendContext.XExecutionStackContainer) { - session.debugProcess.computeRunningExecutionStacks(container) + override fun computeRunningExecutionStacks(container: XSuspendContext.XExecutionStackGroupContainer) { + session.debugProcess.computeRunningExecutionStacks(container, session.suspendContext) } override fun createTabLayouter(): XDebugTabLayouter = session.debugProcess.createTabLayouter()