mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 06:47:40 +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() {
|
||||
CoroutineScope projectScope = ((XDebuggerManagerImpl)XDebuggerManager.getInstance(project)).getCoroutineScope();
|
||||
return new DebuggerManagerThreadImpl(disposable, projectScope);
|
||||
return new DebuggerManagerThreadImpl(disposable, projectScope, this);
|
||||
}
|
||||
|
||||
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.SuspendContextCommand
|
||||
import com.intellij.debugger.impl.*
|
||||
import com.intellij.debugger.statistics.StatisticsStorage
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ComponentManagerEx
|
||||
@@ -27,17 +28,23 @@ import kotlinx.coroutines.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.system.measureNanoTime
|
||||
|
||||
class DebuggerManagerThreadImpl(parent: Disposable, private val parentScope: CoroutineScope) :
|
||||
InvokeAndWaitThread<DebuggerCommandImpl?>(), DebuggerManagerThread, Disposable {
|
||||
class DebuggerManagerThreadImpl @ApiStatus.Internal @JvmOverloads constructor(
|
||||
parent: Disposable,
|
||||
private val parentScope: CoroutineScope,
|
||||
debugProcess: DebugProcess? = null,
|
||||
) : InvokeAndWaitThread<DebuggerCommandImpl?>(), DebuggerManagerThread, Disposable {
|
||||
|
||||
@Volatile
|
||||
private var myDisposed = false
|
||||
|
||||
private val myDebuggerThreadDispatcher = DebuggerThreadDispatcher(this)
|
||||
private val myDebugProcess = WeakReference(debugProcess)
|
||||
val unfinishedCommands = ConcurrentCollectionFactory.createConcurrentSet<DebuggerCommandImpl>()
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -163,7 +170,13 @@ class DebuggerManagerThreadImpl(parent: Disposable, private val parentScope: Cor
|
||||
managerCommand.notifyCancelled()
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.debugger.actions.JvmSmartStepIntoHandler
|
||||
import com.intellij.debugger.engine.DebugProcess
|
||||
import com.intellij.debugger.engine.DebugProcessEvents
|
||||
import com.intellij.debugger.engine.SteppingAction
|
||||
import com.intellij.debugger.impl.DebuggerUtilsImpl
|
||||
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
@@ -16,7 +17,7 @@ import org.jetbrains.annotations.ApiStatus
|
||||
object DebuggerStatistics : CounterUsagesCollector() {
|
||||
override fun getGroup(): EventLogGroup = GROUP
|
||||
|
||||
private val GROUP = EventLogGroup("java.debugger", 9)
|
||||
private val GROUP = EventLogGroup("java.debugger", 10)
|
||||
|
||||
// fields
|
||||
|
||||
@@ -51,6 +52,10 @@ object DebuggerStatistics : CounterUsagesCollector() {
|
||||
|
||||
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
|
||||
fun logProcessStatistics(debugProcess: DebugProcess) {
|
||||
val collectedStats = StatisticsStorage.collectAndClearData(debugProcess)
|
||||
@@ -61,6 +66,13 @@ object DebuggerStatistics : CounterUsagesCollector() {
|
||||
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
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.debugger.statistics
|
||||
import com.intellij.debugger.engine.DebugProcess
|
||||
import com.intellij.debugger.engine.SteppingAction
|
||||
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.openapi.util.Key
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@@ -11,6 +12,7 @@ private val KEY: Key<StatisticsStorage> = Key.create("DEBUGGER_STATISTICS_STORAG
|
||||
|
||||
class StatisticsStorage {
|
||||
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 remove(key: StatisticElement) = data.remove(key)
|
||||
@@ -63,6 +65,25 @@ class StatisticsStorage {
|
||||
|
||||
@JvmStatic
|
||||
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