From 3cb27850fd16c1fb91408d3928fee63f197472a6 Mon Sep 17 00:00:00 2001 From: Maksim Zuev Date: Wed, 28 Jan 2026 14:20:00 +0100 Subject: [PATCH] [debugger] IDEA-385233 Do not show notification GitOrigin-RevId: 4c6fbc0269369fcede26bc8944816cff2a495b81 --- .../impl/resources/META-INF/java-debugger.xml | 2 - .../engine/AsyncStackTracesOverheadUtils.kt | 109 +++--------------- .../debugger/engine/AsyncStacksUtils.java | 15 +-- .../debugger/statistics/DebuggerStatistics.kt | 26 +---- .../messages/JavaDebuggerBundle.properties | 7 -- 5 files changed, 18 insertions(+), 141 deletions(-) diff --git a/java/debugger/impl/resources/META-INF/java-debugger.xml b/java/debugger/impl/resources/META-INF/java-debugger.xml index c6321d4d6f4b..53be02677186 100644 --- a/java/debugger/impl/resources/META-INF/java-debugger.xml +++ b/java/debugger/impl/resources/META-INF/java-debugger.xml @@ -169,8 +169,6 @@ - diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStackTracesOverheadUtils.kt b/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStackTracesOverheadUtils.kt index ee25a48d434a..1a1e86b29d39 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStackTracesOverheadUtils.kt +++ b/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStackTracesOverheadUtils.kt @@ -1,109 +1,28 @@ // 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.JavaDebuggerBundle -import com.intellij.debugger.impl.DebuggerUtilsEx import com.intellij.debugger.statistics.DebuggerStatistics -import com.intellij.notification.Notification -import com.intellij.notification.NotificationType -import com.intellij.notification.NotificationsManager -import com.intellij.openapi.application.EDT -import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.util.Key -import com.intellij.xdebugger.XDebugSessionListener -import com.intellij.xdebugger.impl.XDebugSessionImpl -import com.sun.jdi.ObjectReference -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds -private class LastSessionPauseListener : XDebugSessionListener { - private val isPaused = MutableStateFlow(false) - private val startNs = System.nanoTime() +private val sessionStartTimestampKey = Key.create("debuggerSessionStartTimestamp") - override fun sessionPaused() { - isPaused.value = true - } - - override fun sessionResumed() { - isPaused.value = false - } - - @OptIn(FlowPreview::class) - suspend fun awaitNoPauseFor(duration: Duration) { - isPaused.debounce(duration).first { !it } - } - - fun passedSinceSessionStartMs(): Long { - return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs) - } -} - -private val lastSessionPauseListenerKey = Key.create("lastSessionPauseListenerKey") - -internal fun initializeLastSessionPauseListener(process: DebugProcessImpl) { - val xDebugSession = process.session.xDebugSession ?: return - val listener = LastSessionPauseListener() - process.putUserData(lastSessionPauseListenerKey, listener) - xDebugSession.addSessionListener(listener) +internal fun initializeOverheadListener(process: DebugProcessImpl) { + val startNs = System.nanoTime() + process.putUserData(sessionStartTimestampKey, startNs) } @OptIn(FlowPreview::class) -internal fun showOverheadNotification(process: DebugProcessImpl, overhead: ObjectReference) { - val xDebugSession = process.session.xDebugSession as? XDebugSessionImpl ?: return - val cs = xDebugSession.coroutineScope - val managerThread = DebuggerManagerThreadImpl.getCurrentThread() - cs.launch(Dispatchers.EDT) { - val project = xDebugSession.project - val listener = process.getUserData(lastSessionPauseListenerKey) - val passedSinceSessionStartMs = listener?.passedSinceSessionStartMs() ?: -1 - DebuggerStatistics.logAgentOverheadDetected(project, passedSinceSessionStartMs) - if (listener != null) { - // we don't want to show notification if user is actively debugging - listener.awaitNoPauseFor(3.seconds) - } - - if (process.isDetached) return@launch - val title = JavaDebuggerBundle.message("async.stack.traces.overhead.title") - val content = JavaDebuggerBundle.message("async.stack.traces.overhead.description") - val notification = Notification("AsyncStackTraces", title, content, NotificationType.WARNING) - notification.addAction(DumbAwareAction.create(JavaDebuggerBundle.message("async.stack.traces.overhead.throttle.button")) { - notification.expire() - DebuggerStatistics.logAgentOverheadNotificationThrottlingEnabled(project) - executeOnDMT(managerThread) { - val field = DebuggerUtils.findField(overhead.referenceType(), "throttleWhenOverhead") - if (field != null) { - overhead.setValue(field, overhead.virtualMachine().mirrorOf(true)) - } - } - }) - notification.addAction(DumbAwareAction.create(JavaDebuggerBundle.message("async.stack.traces.overhead.disable.button")) { - notification.expire() - DebuggerStatistics.logAgentOverheadNotificationAgentDisabled(project) - executeOnDMT(managerThread) { - DebuggerUtilsEx.setStaticBooleanField(process, AsyncStacksUtils.CAPTURE_STORAGE_CLASS_NAME, "ENABLED", false) - } - }) - notification.addAction(DumbAwareAction.create(JavaDebuggerBundle.message("async.stack.traces.overhead.ignore.button")) { - notification.expire() - DebuggerStatistics.logAgentOverheadNotificationDismissed(project) - }) - val processListener = object : DebugProcessListener { - override fun processDetached(process: DebugProcess, closedByUser: Boolean) { - notification.expire() - } - } - process.addDebugProcessListener(processListener) - notification.whenExpired { - process.removeDebugProcessListener(processListener) - } - NotificationsManager.getNotificationsManager().showNotification(notification, process.project) - DebuggerStatistics.logAgentOverheadNotificationShown(project) +internal fun onOverheadDetected(process: DebugProcessImpl) { + val project = process.project + val sessionStartNs = process.getUserData(sessionStartTimestampKey) + val sessionLengthMs = if (sessionStartNs != null) { + val durationNs = System.nanoTime() - sessionStartNs + TimeUnit.NANOSECONDS.toMillis(durationNs) } + else { + -1 + } + DebuggerStatistics.logAgentOverheadDetected(project, sessionLengthMs) } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStacksUtils.java b/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStacksUtils.java index a05381f0606f..94af1ffc7549 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStacksUtils.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/AsyncStacksUtils.java @@ -1,6 +1,7 @@ // Copyright 2000-2026 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.SourcePosition; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; @@ -517,24 +518,14 @@ public final class AsyncStacksUtils { } private static void initializeOverheadDetector(DebugProcessImpl process) { - AsyncStackTracesOverheadUtilsKt.initializeLastSessionPauseListener(process); + AsyncStackTracesOverheadUtilsKt.initializeOverheadListener(process); String className = "com.intellij.rt.debugger.agent.OverheadDetector"; String methodName = "overheadDetected"; var breakpoint = new SyntheticMethodBreakpoint(className, methodName, null, process.getProject()) { @Override public boolean processLocatableEvent(@NotNull SuspendContextCommandImpl action, LocatableEvent event) { - if (event == null) return false; - try { - List args = DebuggerUtilsEx.getArgumentValues(event.thread().frame(0)); - Value overheadValue = ContainerUtil.getFirstItem(args); - if (overheadValue instanceof ObjectReference overhead) { - AsyncStackTracesOverheadUtilsKt.showOverheadNotification(process, overhead); - } - } - catch (IncompatibleThreadStateException e) { - LOG.error(e); - } + AsyncStackTracesOverheadUtilsKt.onOverheadDetected(process); return false; } }; diff --git a/java/debugger/impl/src/com/intellij/debugger/statistics/DebuggerStatistics.kt b/java/debugger/impl/src/com/intellij/debugger/statistics/DebuggerStatistics.kt index 21f0168dfafe..93a0167d7497 100644 --- a/java/debugger/impl/src/com/intellij/debugger/statistics/DebuggerStatistics.kt +++ b/java/debugger/impl/src/com/intellij/debugger/statistics/DebuggerStatistics.kt @@ -21,7 +21,7 @@ import org.jetbrains.annotations.ApiStatus object DebuggerStatistics : CounterUsagesCollector() { override fun getGroup(): EventLogGroup = GROUP - private val GROUP = EventLogGroup("java.debugger", 15) + private val GROUP = EventLogGroup("java.debugger", 16) // fields @@ -76,14 +76,6 @@ object DebuggerStatistics : CounterUsagesCollector() { private val agentOverheadDetected = GROUP.registerEvent("debugger.agent.overhead.detected", EventFields.Long("passed_since_session_start_ms"), "Detected noticeable overhead of the debugger agent due to async stack traces collection") - private val agentOverheadNotificationShown = GROUP.registerEvent("debugger.agent.overhead.notification.shown", - "Debugger agent overhead notification was shown (happens after overhead detected in 3 seconds)") - private val agentOverheadNotificationDismissed = GROUP.registerEvent("debugger.agent.overhead.notification.dismissed", - "Debugger agent overhead notification was dismissed") - private val agentOverheadNotificationAgentDisabled = GROUP.registerEvent("debugger.agent.overhead.notification.agent.disabled", - "Debugger agent was disabled on overhead detected") - private val agentOverheadNotificationThrottlingEnabled = GROUP.registerEvent("debugger.agent.overhead.notification.throttling.enabled", - "Throttling of debugger agent was enabled on overhead detected") @JvmStatic fun logProcessStatistics(debugProcess: DebugProcess) { @@ -193,22 +185,6 @@ object DebuggerStatistics : CounterUsagesCollector() { agentOverheadDetected.log(project, passedSinceSessionStartMs) } - fun logAgentOverheadNotificationShown(project: Project) { - agentOverheadNotificationShown.log(project) - } - - fun logAgentOverheadNotificationDismissed(project: Project) { - agentOverheadNotificationDismissed.log(project) - } - - fun logAgentOverheadNotificationAgentDisabled(project: Project) { - agentOverheadNotificationAgentDisabled.log(project) - } - - fun logAgentOverheadNotificationThrottlingEnabled(project: Project) { - agentOverheadNotificationThrottlingEnabled.log(project) - } - private val Breakpoint<*>.type: String? get() = xBreakpoint?.type?.id } diff --git a/java/debugger/openapi/resources/messages/JavaDebuggerBundle.properties b/java/debugger/openapi/resources/messages/JavaDebuggerBundle.properties index 940a4497ec0c..3ca800d981d2 100644 --- a/java/debugger/openapi/resources/messages/JavaDebuggerBundle.properties +++ b/java/debugger/openapi/resources/messages/JavaDebuggerBundle.properties @@ -558,10 +558,3 @@ debugger.variables.not.available.in.async=Variables are not available for async exception=Exception ref=Ref thread.dump.during.previous.dump.evaluation.warning=Evaluation of the previous dump is still in progress. Java platform thread dump is taken. - -async.stack.traces.notification.group=Async Stack Traces -async.stack.traces.overhead.title=Async stack traces overhead detected -async.stack.traces.overhead.description=Collecting async stack traces may significantly impact your application's performance -async.stack.traces.overhead.throttle.button=Throttle -async.stack.traces.overhead.ignore.button=Ignore -async.stack.traces.overhead.disable.button=Disable