Skip to content

Commit d2240c0

Browse files
committed
Add Periodic Refresh Timer
Signed-off-by: Jens Reinecke <[email protected]>
1 parent e4cd97b commit d2240c0

File tree

4 files changed

+100
-9
lines changed

4 files changed

+100
-9
lines changed

src/debug-session/gdbtarget-debug-session.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,22 @@ import * as vscode from 'vscode';
1818
import { DebugProtocol } from '@vscode/debugprotocol';
1919
import { logger } from '../logger';
2020
import { CbuildRunReader } from '../cbuild-run';
21+
import { PeriodicRefreshTimer } from './periodic-refresh-timer';
2122

2223
/**
2324
* GDBTargetDebugSession - Wrapper class to provide session state/details
2425
*/
2526
export class GDBTargetDebugSession {
27+
public readonly refreshTimer: PeriodicRefreshTimer<GDBTargetDebugSession>;
2628
private _cbuildRun: CbuildRunReader|undefined;
2729
private _cbuildRunParsePromise: Promise<void>|undefined;
2830

29-
constructor(public session: vscode.DebugSession) {}
31+
constructor(public session: vscode.DebugSession) {
32+
this.refreshTimer = new PeriodicRefreshTimer(this);
33+
if (this.session.configuration.type === 'gdbtarget') {
34+
this.refreshTimer.enabled = this.session.configuration['auxiliaryGdb'] === true;
35+
}
36+
}
3037

3138
public async getCbuildRun(): Promise<CbuildRunReader|undefined> {
3239
if (!this._cbuildRun) {
@@ -89,7 +96,7 @@ export class GDBTargetDebugSession {
8996
}
9097

9198
public async readMemoryU32(address: number): Promise<number|undefined> {
92-
const data = await this.readMemory(address, 4);
99+
const data = await this.readMemory(address, 8 /* 4 */); // Temporary workaround for GDB servers with extra caching of 4 byte reads
93100
if (!data) {
94101
return undefined;
95102
}

src/debug-session/gdbtarget-debug-tracker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ export class GDBTargetDebugTracker {
120120
switch (event.event) {
121121
case 'continued':
122122
this._onContinued.fire({ session: gdbTargetSession, event } as ContinuedEvent);
123+
gdbTargetSession?.refreshTimer.start();
123124
break;
124125
case 'stopped':
126+
gdbTargetSession?.refreshTimer.stop();
125127
this._onStopped.fire({ session: gdbTargetSession, event } as StoppedEvent);
126128
break;
127129
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright 2025 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as vscode from 'vscode';
18+
19+
const DEFAULT_REFRESH_INTERVAL = 500;
20+
21+
export class PeriodicRefreshTimer<T> {
22+
private _enabled: boolean = false;
23+
private _timer: NodeJS.Timeout|undefined;
24+
25+
private readonly _onRefresh: vscode.EventEmitter<T> = new vscode.EventEmitter<T>();
26+
public readonly onRefresh: vscode.Event<T> = this._onRefresh.event;
27+
28+
public get enabled(): boolean {
29+
return this._enabled;
30+
}
31+
32+
public set enabled(value: boolean) {
33+
this._enabled = value;
34+
if (!value) {
35+
this.stop();
36+
}
37+
}
38+
39+
constructor(public session: T, public interval: number = DEFAULT_REFRESH_INTERVAL) {
40+
}
41+
42+
public start(): void {
43+
this.stop();
44+
if (!this._enabled) {
45+
return;
46+
}
47+
const doPeriodicRefresh = () => {
48+
this._onRefresh.fire(this.session);
49+
this._timer = setTimeout(doPeriodicRefresh, this.interval);
50+
};
51+
this._timer = setTimeout(doPeriodicRefresh, this.interval);
52+
}
53+
54+
public stop(): void {
55+
if (this._timer) {
56+
clearTimeout(this._timer);
57+
this._timer = undefined;
58+
}
59+
}
60+
}

src/features/cpu-states/cpu-states.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export class CpuStates {
7777
hasStates: undefined
7878
};
7979
this.sessionCpuStates.set(session.session.id, states);
80+
session.refreshTimer.onRefresh(async (refreshSession) => this.handlePeriodicRefresh(refreshSession));
8081
}
8182

8283
protected handleOnWillStopSession(session: GDBTargetDebugSession): void {
@@ -109,23 +110,41 @@ export class CpuStates {
109110
cpuStates.isRunning = true;
110111
}
111112

113+
private async shouldUpdate(session: GDBTargetDebugSession, cpuStates: SessionCpuStates): Promise<boolean> {
114+
if (cpuStates.hasStates === undefined) {
115+
// Retry if early read after launch/attach response failed (e.g. if
116+
// target was running).
117+
cpuStates.hasStates = await this.supportsCpuStates(session);
118+
}
119+
return cpuStates.hasStates;
120+
}
121+
112122
protected async handleStoppedEvent(event: StoppedEvent): Promise<void> {
113123
const cpuStates = this.sessionCpuStates.get(event.session.session.id);
114124
if (!cpuStates) {
115125
return;
116126
}
117127
cpuStates.isRunning = false;
118-
if (cpuStates.hasStates === undefined) {
119-
// Retry if early read after launch/attach response failed (e.g. if
120-
// target was running).
121-
cpuStates.hasStates = await this.supportsCpuStates(event.session);
122-
}
123-
if (!cpuStates.hasStates) {
128+
const doUpdate = await this.shouldUpdate(event.session, cpuStates);
129+
if (!doUpdate) {
124130
return;
125131
}
126132
return this.updateCpuStates(event.session, event.event.body.threadId, event.event.body.reason);
127133
}
128134

135+
protected async handlePeriodicRefresh(session: GDBTargetDebugSession): Promise<void> {
136+
const cpuStates = this.sessionCpuStates.get(session.session.id);
137+
if (!cpuStates) {
138+
return;
139+
}
140+
const doUpdate = await this.shouldUpdate(session, cpuStates);
141+
if (!doUpdate) {
142+
return;
143+
}
144+
await this.updateCpuStates(session);
145+
this._onRefresh.fire(0);
146+
}
147+
129148
protected handleStackTrace(stackTrace: SessionStackTrace): void {
130149
const states = this.activeCpuStates;
131150
if (!states) {
@@ -186,7 +205,10 @@ export class CpuStates {
186205
// Caution with types...
187206
states.lastCycles = newCycles;
188207
states.states += BigInt(cycleAdd);
189-
states.statesHistory.updateHistory(states.states, threadId, reason);
208+
if (reason) {
209+
// Stopped events always have a reason, only update history for them
210+
states.statesHistory.updateHistory(states.states, threadId, reason);
211+
}
190212
}
191213

192214
protected async getFrequency(): Promise<number|undefined> {

0 commit comments

Comments
 (0)