mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
[debugger] IDEA-366895 Debugger: collect performance statistics on the command execution time
(cherry picked from commit 1de07f7dba0a674838012e1ba228c3356febf93e) GitOrigin-RevId: b809e4cd32925e534ed853b497dc5ff6aa5f49e5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6565a5c303
commit
ba1db90b28
@@ -201,7 +201,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
|||||||
|
|
||||||
private DebuggerManagerThreadImpl createManagerThread() {
|
private DebuggerManagerThreadImpl createManagerThread() {
|
||||||
CoroutineScope projectScope = ((XDebuggerManagerImpl)XDebuggerManager.getInstance(project)).getCoroutineScope();
|
CoroutineScope projectScope = ((XDebuggerManagerImpl)XDebuggerManager.getInstance(project)).getCoroutineScope();
|
||||||
return new DebuggerManagerThreadImpl(disposable, projectScope);
|
return new DebuggerManagerThreadImpl(disposable, projectScope, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reloadRenderers() {
|
private void reloadRenderers() {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.intellij.debugger.engine.managerThread.DebuggerCommand
|
|||||||
import com.intellij.debugger.engine.managerThread.DebuggerManagerThread
|
import com.intellij.debugger.engine.managerThread.DebuggerManagerThread
|
||||||
import com.intellij.debugger.engine.managerThread.SuspendContextCommand
|
import com.intellij.debugger.engine.managerThread.SuspendContextCommand
|
||||||
import com.intellij.debugger.impl.*
|
import com.intellij.debugger.impl.*
|
||||||
|
import com.intellij.debugger.statistics.StatisticsStorage
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.components.ComponentManagerEx
|
import com.intellij.openapi.components.ComponentManagerEx
|
||||||
@@ -27,17 +28,23 @@ import kotlinx.coroutines.*
|
|||||||
import org.jetbrains.annotations.ApiStatus
|
import org.jetbrains.annotations.ApiStatus
|
||||||
import org.jetbrains.annotations.Nls
|
import org.jetbrains.annotations.Nls
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.system.measureNanoTime
|
||||||
|
|
||||||
class DebuggerManagerThreadImpl(parent: Disposable, private val parentScope: CoroutineScope) :
|
class DebuggerManagerThreadImpl @ApiStatus.Internal @JvmOverloads constructor(
|
||||||
InvokeAndWaitThread<DebuggerCommandImpl?>(), DebuggerManagerThread, Disposable {
|
parent: Disposable,
|
||||||
|
private val parentScope: CoroutineScope,
|
||||||
|
debugProcess: DebugProcess? = null,
|
||||||
|
) : InvokeAndWaitThread<DebuggerCommandImpl?>(), DebuggerManagerThread, Disposable {
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var myDisposed = false
|
private var myDisposed = false
|
||||||
|
|
||||||
private val myDebuggerThreadDispatcher = DebuggerThreadDispatcher(this)
|
private val myDebuggerThreadDispatcher = DebuggerThreadDispatcher(this)
|
||||||
|
private val myDebugProcess = WeakReference(debugProcess)
|
||||||
val unfinishedCommands = ConcurrentCollectionFactory.createConcurrentSet<DebuggerCommandImpl>()
|
val unfinishedCommands = ConcurrentCollectionFactory.createConcurrentSet<DebuggerCommandImpl>()
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
@@ -163,7 +170,13 @@ class DebuggerManagerThreadImpl(parent: Disposable, private val parentScope: Cor
|
|||||||
managerCommand.notifyCancelled()
|
managerCommand.notifyCancelled()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
managerCommand.invokeCommand(myDebuggerThreadDispatcher, coroutineScope)
|
val commandTimeNs = measureNanoTime {
|
||||||
|
managerCommand.invokeCommand(myDebuggerThreadDispatcher, coroutineScope)
|
||||||
|
}
|
||||||
|
myDebugProcess.get()?.let { debugProcess ->
|
||||||
|
val commandTimeMs = TimeUnit.NANOSECONDS.toMillis(commandTimeNs)
|
||||||
|
StatisticsStorage.addCommandTime(debugProcess, commandTimeMs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e: VMDisconnectedException) {
|
catch (e: VMDisconnectedException) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.intellij.debugger.actions.JvmSmartStepIntoHandler
|
|||||||
import com.intellij.debugger.engine.DebugProcess
|
import com.intellij.debugger.engine.DebugProcess
|
||||||
import com.intellij.debugger.engine.DebugProcessEvents
|
import com.intellij.debugger.engine.DebugProcessEvents
|
||||||
import com.intellij.debugger.engine.SteppingAction
|
import com.intellij.debugger.engine.SteppingAction
|
||||||
|
import com.intellij.debugger.impl.DebuggerUtilsImpl
|
||||||
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
||||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||||
@@ -16,7 +17,7 @@ import org.jetbrains.annotations.ApiStatus
|
|||||||
object DebuggerStatistics : CounterUsagesCollector() {
|
object DebuggerStatistics : CounterUsagesCollector() {
|
||||||
override fun getGroup(): EventLogGroup = GROUP
|
override fun getGroup(): EventLogGroup = GROUP
|
||||||
|
|
||||||
private val GROUP = EventLogGroup("java.debugger", 9)
|
private val GROUP = EventLogGroup("java.debugger", 10)
|
||||||
|
|
||||||
// fields
|
// fields
|
||||||
|
|
||||||
@@ -51,6 +52,10 @@ object DebuggerStatistics : CounterUsagesCollector() {
|
|||||||
|
|
||||||
private val breakpointSkipped = GROUP.registerEvent("breakpoint.skipped", EventFields.Enum<DebugProcessEvents.SkippedBreakpointReason>("reason"))
|
private val breakpointSkipped = GROUP.registerEvent("breakpoint.skipped", EventFields.Enum<DebugProcessEvents.SkippedBreakpointReason>("reason"))
|
||||||
|
|
||||||
|
/** Reports execution time of debugger commands in buckets, updated at the end of a debugger session. */
|
||||||
|
private val timeBucketCount = GROUP.registerEvent("debugger.command.time.bucket.updated", EventFields.Int("bucket_upper_limit_ms"), EventFields.Count, EventFields.Boolean("is_remote"))
|
||||||
|
internal val bucketUpperLimits = (generateSequence(1L) { it * 2 }.takeWhile { it <= 2048 } + Long.MAX_VALUE).toList().toLongArray()
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun logProcessStatistics(debugProcess: DebugProcess) {
|
fun logProcessStatistics(debugProcess: DebugProcess) {
|
||||||
val collectedStats = StatisticsStorage.collectAndClearData(debugProcess)
|
val collectedStats = StatisticsStorage.collectAndClearData(debugProcess)
|
||||||
@@ -61,6 +66,13 @@ object DebuggerStatistics : CounterUsagesCollector() {
|
|||||||
is SteppingStatistic -> logSteppingOverhead(debugProcess.project, key, timeMs)
|
is SteppingStatistic -> logSteppingOverhead(debugProcess.project, key, timeMs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isRemote = DebuggerUtilsImpl.isRemote(debugProcess)
|
||||||
|
val bucketCounts = StatisticsStorage.collectCommandsPerformance(debugProcess)
|
||||||
|
val intUpperLimits = bucketUpperLimits.map { if (it == Long.MAX_VALUE) Int.MAX_VALUE else it.toInt() }.toIntArray()
|
||||||
|
for ((upperLimitMs, count) in intUpperLimits.zip(bucketCounts)) {
|
||||||
|
timeBucketCount.log(debugProcess.project, upperLimitMs, count, isRemote)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package com.intellij.debugger.statistics
|
|||||||
import com.intellij.debugger.engine.DebugProcess
|
import com.intellij.debugger.engine.DebugProcess
|
||||||
import com.intellij.debugger.engine.SteppingAction
|
import com.intellij.debugger.engine.SteppingAction
|
||||||
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
||||||
|
import com.intellij.openapi.diagnostic.fileLogger
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ private val KEY: Key<StatisticsStorage> = Key.create("DEBUGGER_STATISTICS_STORAG
|
|||||||
|
|
||||||
class StatisticsStorage {
|
class StatisticsStorage {
|
||||||
private val data = ConcurrentHashMap<StatisticElement, Long>()
|
private val data = ConcurrentHashMap<StatisticElement, Long>()
|
||||||
|
private val timeBucketCounts = IntArray(DebuggerStatistics.bucketUpperLimits.size)
|
||||||
|
|
||||||
private fun append(key: StatisticElement, timeMs: Long) = data.merge(key, timeMs, Long::plus)
|
private fun append(key: StatisticElement, timeMs: Long) = data.merge(key, timeMs, Long::plus)
|
||||||
private fun remove(key: StatisticElement) = data.remove(key)
|
private fun remove(key: StatisticElement) = data.remove(key)
|
||||||
@@ -63,6 +65,25 @@ class StatisticsStorage {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSteppingStatisticOrNull(token: Any?): SteppingStatistic? = token as? SteppingStatistic
|
fun getSteppingStatisticOrNull(token: Any?): SteppingStatistic? = token as? SteppingStatistic
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun addCommandTime(debugProcess: DebugProcess, timeMs: Long) {
|
||||||
|
val storage = getStorage(debugProcess)
|
||||||
|
val bucketIndex = DebuggerStatistics.bucketUpperLimits.indexOfFirst { timeMs <= it }
|
||||||
|
if (bucketIndex < 0) {
|
||||||
|
fileLogger().error("Unexpected command time $timeMs, found no bucket for it: ${DebuggerStatistics.bucketUpperLimits}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
storage.timeBucketCounts[bucketIndex]++
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
internal fun collectCommandsPerformance(debugProcess: DebugProcess): IntArray {
|
||||||
|
val storage = getStorage(debugProcess)
|
||||||
|
return storage.timeBucketCounts.copyOf().also {
|
||||||
|
storage.timeBucketCounts.fill(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user