Skip to content

Commit

Permalink
debug: adopt external changes (#190602)
Browse files Browse the repository at this point in the history
For #183449

Also adds the leak detector to debug tests and fixes some usages of it (#190503)
  • Loading branch information
connor4312 authored Aug 16, 2023
1 parent 3f8a55b commit 719a6a6
Show file tree
Hide file tree
Showing 22 changed files with 344 additions and 186 deletions.
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/debug/browser/debugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class DebugService implements IDebugService {
this.disposables.add(this.adapterManager);
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager);
this.disposables.add(this.configurationManager);
this.debugStorage = this.instantiationService.createInstance(DebugStorage);
this.debugStorage = this.disposables.add(this.instantiationService.createInstance(DebugStorage));

this.chosenEnvironments = this.debugStorage.loadChosenEnvironments();

Expand Down
52 changes: 27 additions & 25 deletions src/vs/workbench/contrib/debug/browser/debugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';

export class DebugSession implements IDebugSession {
export class DebugSession implements IDebugSession, IDisposable {
parentSession: IDebugSession | undefined;

private _subId: string | undefined;
Expand All @@ -52,7 +52,7 @@ export class DebugSession implements IDebugSession {
private threads = new Map<number, Thread>();
private threadIds: number[] = [];
private cancellationMap = new Map<number, CancellationTokenSource[]>();
private rawListeners: IDisposable[] = [];
private rawListeners = new DisposableStore();
private fetchThreadsScheduler: RunOnceScheduler | undefined;
private passFocusScheduler: RunOnceScheduler;
private lastContinuedThreadId: number | undefined;
Expand Down Expand Up @@ -103,14 +103,11 @@ export class DebugSession implements IDebugSession {
this.repl = (this.parentSession as DebugSession).repl;
}

const toDispose = new DisposableStore();
const toDispose = this.rawListeners.add(new DisposableStore());
const replListener = toDispose.add(new MutableDisposable());
replListener.value = this.repl.onDidChangeElements(() => this._onDidChangeREPLElements.fire());
if (lifecycleService) {
toDispose.add(lifecycleService.onWillShutdown(() => {
this.shutdown();
dispose(toDispose);
}));
toDispose.add(lifecycleService.onWillShutdown(() => this.shutdown()));
}

const compoundRoot = this._options.compoundRoot;
Expand Down Expand Up @@ -950,7 +947,7 @@ export class DebugSession implements IDebugSession {
return;
}

this.rawListeners.push(this.raw.onDidInitialize(async () => {
this.rawListeners.add(this.raw.onDidInitialize(async () => {
aria.status(localize('debuggingStarted', "Debugging started."));
const sendConfigurationDone = async () => {
if (this.raw && this.raw.capabilities.supportsConfigurationDoneRequest) {
Expand All @@ -976,7 +973,7 @@ export class DebugSession implements IDebugSession {
}));

const statusQueue = new Queue<void>();
this.rawListeners.push(this.raw.onDidStop(async event => {
this.rawListeners.add(this.raw.onDidStop(async event => {
statusQueue.queue(async () => {
this.passFocusScheduler.cancel();
this.stoppedDetails.push(event.body);
Expand Down Expand Up @@ -1026,15 +1023,15 @@ export class DebugSession implements IDebugSession {
});
}));

this.rawListeners.push(this.raw.onDidThread(event => {
this.rawListeners.add(this.raw.onDidThread(event => {
statusQueue.queue(async () => {
if (event.body.reason === 'started') {
// debounce to reduce threadsRequest frequency and improve performance
if (!this.fetchThreadsScheduler) {
this.fetchThreadsScheduler = new RunOnceScheduler(() => {
this.fetchThreads();
}, 100);
this.rawListeners.push(this.fetchThreadsScheduler);
this.rawListeners.add(this.fetchThreadsScheduler);
}
if (!this.fetchThreadsScheduler.isScheduled()) {
this.fetchThreadsScheduler.schedule();
Expand All @@ -1052,7 +1049,7 @@ export class DebugSession implements IDebugSession {
});
}));

this.rawListeners.push(this.raw.onDidTerminateDebugee(async event => {
this.rawListeners.add(this.raw.onDidTerminateDebugee(async event => {
aria.status(localize('debuggingStopped', "Debugging stopped."));
if (event.body && event.body.restart) {
await this.debugService.restartSession(this, event.body.restart);
Expand All @@ -1061,14 +1058,14 @@ export class DebugSession implements IDebugSession {
}
}));

this.rawListeners.push(this.raw.onDidContinued(event => {
this.rawListeners.add(this.raw.onDidContinued(event => {
statusQueue.queue(async () => {
const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
if (typeof threadId === 'number') {
this.stoppedDetails = this.stoppedDetails.filter(sd => sd.threadId !== threadId);
const tokens = this.cancellationMap.get(threadId);
this.cancellationMap.delete(threadId);
tokens?.forEach(t => t.cancel());
tokens?.forEach(t => t.dispose(true));
} else {
this.stoppedDetails = [];
this.cancelAllRequests();
Expand All @@ -1082,7 +1079,7 @@ export class DebugSession implements IDebugSession {
}));

const outputQueue = new Queue<void>();
this.rawListeners.push(this.raw.onDidOutput(async event => {
this.rawListeners.add(this.raw.onDidOutput(async event => {
const outputSeverity = event.body.category === 'stderr' ? Severity.Error : event.body.category === 'console' ? Severity.Warning : Severity.Info;

// When a variables event is received, execute immediately to obtain the variables value #126967
Expand Down Expand Up @@ -1161,7 +1158,7 @@ export class DebugSession implements IDebugSession {
});
}));

this.rawListeners.push(this.raw.onDidBreakpoint(event => {
this.rawListeners.add(this.raw.onDidBreakpoint(event => {
const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
const breakpoint = this.model.getBreakpoints().find(bp => bp.getIdFromAdapter(this.getId()) === id);
const functionBreakpoint = this.model.getFunctionBreakpoints().find(bp => bp.getIdFromAdapter(this.getId()) === id);
Expand Down Expand Up @@ -1216,30 +1213,30 @@ export class DebugSession implements IDebugSession {
}
}));

this.rawListeners.push(this.raw.onDidLoadedSource(event => {
this.rawListeners.add(this.raw.onDidLoadedSource(event => {
this._onDidLoadedSource.fire({
reason: event.body.reason,
source: this.getSource(event.body.source)
});
}));

this.rawListeners.push(this.raw.onDidCustomEvent(event => {
this.rawListeners.add(this.raw.onDidCustomEvent(event => {
this._onDidCustomEvent.fire(event);
}));

this.rawListeners.push(this.raw.onDidProgressStart(event => {
this.rawListeners.add(this.raw.onDidProgressStart(event => {
this._onDidProgressStart.fire(event);
}));
this.rawListeners.push(this.raw.onDidProgressUpdate(event => {
this.rawListeners.add(this.raw.onDidProgressUpdate(event => {
this._onDidProgressUpdate.fire(event);
}));
this.rawListeners.push(this.raw.onDidProgressEnd(event => {
this.rawListeners.add(this.raw.onDidProgressEnd(event => {
this._onDidProgressEnd.fire(event);
}));
this.rawListeners.push(this.raw.onDidInvalidateMemory(event => {
this.rawListeners.add(this.raw.onDidInvalidateMemory(event => {
this._onDidInvalidMemory.fire(event);
}));
this.rawListeners.push(this.raw.onDidInvalidated(async event => {
this.rawListeners.add(this.raw.onDidInvalidated(async event => {
if (!(event.body.areas && event.body.areas.length === 1 && (event.body.areas[0] === 'variables' || event.body.areas[0] === 'watch'))) {
// If invalidated event only requires to update variables or watch, do that, otherwise refatch threads https://github.com/microsoft/vscode/issues/106745
this.cancelAllRequests();
Expand All @@ -1253,7 +1250,7 @@ export class DebugSession implements IDebugSession {
}
}));

this.rawListeners.push(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event)));
this.rawListeners.add(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event)));
}

private onDidExitAdapter(event?: AdapterEndEvent): void {
Expand All @@ -1280,6 +1277,11 @@ export class DebugSession implements IDebugSession {
this._onDidChangeState.fire();
}

public dispose() {
this.cancelAllRequests();
dispose(this.rawListeners);
}

//---- sources

getSourceForUri(uri: URI): Source | undefined {
Expand Down Expand Up @@ -1325,7 +1327,7 @@ export class DebugSession implements IDebugSession {
}

private cancelAllRequests(): void {
this.cancellationMap.forEach(tokens => tokens.forEach(t => t.cancel()));
this.cancellationMap.forEach(tokens => tokens.forEach(t => t.dispose(true)));
this.cancellationMap.clear();
}

Expand Down
39 changes: 25 additions & 14 deletions src/vs/workbench/contrib/debug/common/debugModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassem
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ILogService } from 'vs/platform/log/common/log';
import { autorun } from 'vs/base/common/observable';

interface IDebugProtocolVariableWithContext extends DebugProtocol.Variable {
__vscodeVariableMenuContext?: string;
Expand Down Expand Up @@ -1173,19 +1174,19 @@ export class ThreadAndSessionIds implements ITreeElement {
}
}

export class DebugModel implements IDebugModel {
export class DebugModel extends Disposable implements IDebugModel {

private sessions: IDebugSession[];
private schedulers = new Map<string, { scheduler: RunOnceScheduler; completeDeferred: DeferredPromise<void> }>();
private breakpointsActivated = true;
private readonly _onDidChangeBreakpoints = new Emitter<IBreakpointsChangeEvent | undefined>();
private readonly _onDidChangeCallStack = new Emitter<void>();
private readonly _onDidChangeWatchExpressions = new Emitter<IExpression | undefined>();
private breakpoints: Breakpoint[];
private functionBreakpoints: FunctionBreakpoint[];
private exceptionBreakpoints: ExceptionBreakpoint[];
private dataBreakpoints: DataBreakpoint[];
private watchExpressions: Expression[];
private readonly _onDidChangeBreakpoints = this._register(new Emitter<IBreakpointsChangeEvent | undefined>());
private readonly _onDidChangeCallStack = this._register(new Emitter<void>());
private readonly _onDidChangeWatchExpressions = this._register(new Emitter<IExpression | undefined>());
private breakpoints!: Breakpoint[];
private functionBreakpoints!: FunctionBreakpoint[];
private exceptionBreakpoints!: ExceptionBreakpoint[];
private dataBreakpoints!: DataBreakpoint[];
private watchExpressions!: Expression[];
private instructionBreakpoints: InstructionBreakpoint[];

constructor(
Expand All @@ -1194,11 +1195,21 @@ export class DebugModel implements IDebugModel {
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@ILogService private readonly logService: ILogService
) {
this.breakpoints = debugStorage.loadBreakpoints();
this.functionBreakpoints = debugStorage.loadFunctionBreakpoints();
this.exceptionBreakpoints = debugStorage.loadExceptionBreakpoints();
this.dataBreakpoints = debugStorage.loadDataBreakpoints();
this.watchExpressions = debugStorage.loadWatchExpressions();
super();

this._register(autorun(reader => {
this.breakpoints = debugStorage.breakpoints.read(reader);
this.functionBreakpoints = debugStorage.functionBreakpoints.read(reader);
this.exceptionBreakpoints = debugStorage.exceptionBreakpoints.read(reader);
this.dataBreakpoints = debugStorage.dataBreakpoints.read(reader);
this._onDidChangeBreakpoints.fire(undefined);
}));

this._register(autorun(reader => {
this.watchExpressions = debugStorage.watchExpressions.read(reader);
this._onDidChangeWatchExpressions.fire(undefined);
}));

this.instructionBreakpoints = [];
this.sessions = [];
}
Expand Down
41 changes: 34 additions & 7 deletions src/vs/workbench/contrib/debug/common/debugStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { IEvaluate, IExpression, IDebugModel } from 'vs/workbench/contrib/debug/
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { ILogService } from 'vs/platform/log/common/log';
import { observableValue } from 'vs/base/common/observable';
import { Disposable } from 'vs/base/common/lifecycle';

const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
Expand All @@ -19,13 +21,38 @@ const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
const DEBUG_CHOSEN_ENVIRONMENTS_KEY = 'debug.chosenenvironment';
const DEBUG_UX_STATE_KEY = 'debug.uxstate';

export class DebugStorage {
export class DebugStorage extends Disposable {
public readonly breakpoints = observableValue('debugBreakpoints', this.loadBreakpoints());
public readonly functionBreakpoints = observableValue('debugFunctionBreakpoints', this.loadFunctionBreakpoints());
public readonly exceptionBreakpoints = observableValue('debugExceptionBreakpoints', this.loadExceptionBreakpoints());
public readonly dataBreakpoints = observableValue('debugDataBreakpoints', this.loadDataBreakpoints());
public readonly watchExpressions = observableValue('debugWatchExpressions', this.loadWatchExpressions());

constructor(
@IStorageService private readonly storageService: IStorageService,
@ITextFileService private readonly textFileService: ITextFileService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@ILogService private readonly logService: ILogService
) { }
) {
super();

this._register(storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, this._store)(e => {
if (e.external) {
switch (e.key) {
case DEBUG_BREAKPOINTS_KEY:
return this.breakpoints.set(this.loadBreakpoints(), undefined);
case DEBUG_FUNCTION_BREAKPOINTS_KEY:
return this.functionBreakpoints.set(this.loadFunctionBreakpoints(), undefined);
case DEBUG_EXCEPTION_BREAKPOINTS_KEY:
return this.exceptionBreakpoints.set(this.loadExceptionBreakpoints(), undefined);
case DEBUG_DATA_BREAKPOINTS_KEY:
return this.dataBreakpoints.set(this.loadDataBreakpoints(), undefined);
case DEBUG_WATCH_EXPRESSIONS_KEY:
return this.watchExpressions.set(this.loadWatchExpressions(), undefined);
}
}
}));
}

loadDebugUxState(): 'simple' | 'default' {
return this.storageService.get(DEBUG_UX_STATE_KEY, StorageScope.WORKSPACE, 'default') as 'simple' | 'default';
Expand All @@ -35,7 +62,7 @@ export class DebugStorage {
this.storageService.store(DEBUG_UX_STATE_KEY, value, StorageScope.WORKSPACE, StorageTarget.MACHINE);
}

loadBreakpoints(): Breakpoint[] {
private loadBreakpoints(): Breakpoint[] {
let result: Breakpoint[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
Expand All @@ -46,7 +73,7 @@ export class DebugStorage {
return result || [];
}

loadFunctionBreakpoints(): FunctionBreakpoint[] {
private loadFunctionBreakpoints(): FunctionBreakpoint[] {
let result: FunctionBreakpoint[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
Expand All @@ -57,7 +84,7 @@ export class DebugStorage {
return result || [];
}

loadExceptionBreakpoints(): ExceptionBreakpoint[] {
private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
let result: ExceptionBreakpoint[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
Expand All @@ -68,7 +95,7 @@ export class DebugStorage {
return result || [];
}

loadDataBreakpoints(): DataBreakpoint[] {
private loadDataBreakpoints(): DataBreakpoint[] {
let result: DataBreakpoint[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: any) => {
Expand All @@ -79,7 +106,7 @@ export class DebugStorage {
return result || [];
}

loadWatchExpressions(): Expression[] {
private loadWatchExpressions(): Expression[] {
let result: Expression[] | undefined;
try {
result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string; id: string }) => {
Expand Down
Loading

0 comments on commit 719a6a6

Please sign in to comment.