diff --git a/vscode/src/main.ts b/vscode/src/main.ts index 9ec019bcf9d1..fcd919f257c9 100644 --- a/vscode/src/main.ts +++ b/vscode/src/main.ts @@ -104,6 +104,7 @@ import { VSCodeSecretStorage, secretStorage } from './services/SecretStorageProv import { registerSidebarCommands } from './services/SidebarCommands' import { CodyStatusBar } from './services/StatusBar' import { createOrUpdateTelemetryRecorderProvider } from './services/telemetry-v2' +import { ThreadOccupancyMonitor } from './services/thread-occupancy-monitor' import { enableVerboseDebugMode, exportOutputLog, @@ -123,6 +124,11 @@ export async function start( ): Promise { const disposables: vscode.Disposable[] = [] + const threadMonitor = new ThreadOccupancyMonitor() + threadMonitor.start() + + disposables.push(new vscode.Disposable(() => threadMonitor.stop())) + //TODO: Add override flag const isExtensionModeDevOrTest = context.extensionMode === vscode.ExtensionMode.Development || diff --git a/vscode/src/services/thread-occupancy-monitor.ts b/vscode/src/services/thread-occupancy-monitor.ts new file mode 100644 index 000000000000..2d1edc5a41b1 --- /dev/null +++ b/vscode/src/services/thread-occupancy-monitor.ts @@ -0,0 +1,62 @@ +import { telemetryRecorder } from '@sourcegraph/cody-shared' + +export class ThreadOccupancyMonitor { + private lastMeasurement: number = performance.now() + private measurements: number[] = [] + private measurementInterval: NodeJS.Timeout | null = null + + // Sample interval in ms (e.g. every 1 minute) + private readonly SAMPLE_INTERVAL = 60_000 + // How many samples to keep for rolling average + private readonly SAMPLE_WINDOW = 6 + + start(): void { + if (this.measurementInterval) { + return + } + + this.measurementInterval = setInterval(() => { + this.measure() + }, this.SAMPLE_INTERVAL) + } + + stop(): void { + if (this.measurementInterval) { + clearInterval(this.measurementInterval) + this.measurementInterval = null + } + } + + private measure(): void { + const now = performance.now() + const elapsed = now - this.lastMeasurement + this.lastMeasurement = now + + // Get CPU usage for the main thread + const cpuUsage = process.cpuUsage() + const userCPUMs = cpuUsage.user / 1000 // Convert to ms + const systemCPUMs = cpuUsage.system / 1000 + + // Calculate occupancy as percentage + const occupancy = ((userCPUMs + systemCPUMs) / elapsed) * 100 + + // Keep rolling window of measurements + this.measurements.push(occupancy) + if (this.measurements.length > this.SAMPLE_WINDOW) { + this.measurements.shift() + } + + this.emitTelemetry(occupancy) + } + + private emitTelemetry(occupancy: number): void { + telemetryRecorder.recordEvent('cody.performance', 'threadOccupancy', { + metadata: { + occupancyPercent: Math.round(occupancy), + rollingAveragePercent: Math.round( + this.measurements.reduce((a, b) => a + b, 0) / this.measurements.length + ), + }, + }) + } +}