Skip to content

Commit 74698b3

Browse files
committed
[DebugAdapter] Separate log debugging from the DebugAdapter
1 parent 1f8f2cb commit 74698b3

4 files changed

Lines changed: 182 additions & 161 deletions

File tree

editors/code/src/debugAdapter.ts

Lines changed: 41 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
/**
22
* debugAdapter.ts implements the Debug Adapter protocol and integrates it with the log2src
33
* "debugger".
4-
*
5-
* Care should be given to make sure that this module is independent from VS Code so that it
6-
* could potentially be used in other IDE.
74
*/
85

96
import {
@@ -18,6 +15,7 @@ import * as vscode from 'vscode';
1815
import * as path from 'path';
1916

2017
import { outputChannel } from './extension';
18+
import { LogDebugger } from './logDebugger';
2119

2220
interface CallSite {
2321
name: string,
@@ -43,7 +41,7 @@ interface SourceRef {
4341
name: string,
4442
}
4543

46-
interface ILaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
44+
export interface ILaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
4745
// the source to debug, currently a single file
4846
source: string;
4947
// the log files to use for "debugging"
@@ -81,18 +79,16 @@ export class DebugSession extends LoggingDebugSession {
8179

8280
private static _threadID = 1;
8381
private _binaryPath: string;
84-
private _breakPoints = new Map<string, DebugProtocol.Breakpoint[]>();
85-
private _variableHandles = new Handles<'locals'>();
86-
private _line = 1;
82+
private readonly _variableHandles = new Handles<'locals'>();
8783
private _launchArgs: ILaunchRequestArguments = { source: "", log: "", log_format: "" };
88-
private _logLines = Number.MAX_SAFE_INTEGER;
89-
private _highlightDecoration: vscode.TextEditorDecorationType;
84+
private readonly _highlightDecoration: vscode.TextEditorDecorationType;
9085
private _mapping?: LogMapping = undefined;
86+
private readonly _logDebugger: LogDebugger;
9187

9288
/**
9389
* Create a new debug adapter to use with a debug session.
9490
*/
95-
public constructor() {
91+
public constructor(logDebugger: LogDebugger) {
9692
super("log2src-dap.txt");
9793

9894
this._binaryPath = PLATFORM_TO_BINARY.get(`${process.platform}-${process.arch}`)!;
@@ -103,6 +99,7 @@ export class DebugSession extends LoggingDebugSession {
10399
);
104100
}
105101

102+
this._logDebugger = logDebugger;
106103
this.setDebuggerLinesStartAt1(true);
107104
this.setDebuggerColumnsStartAt1(true);
108105

@@ -136,19 +133,10 @@ export class DebugSession extends LoggingDebugSession {
136133
protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments) {
137134
console.log(`setBreakPointsRequest ${JSON.stringify(args)}`);
138135

139-
const bpPath = args.source.path as string;
140-
// TODO handle lines?
136+
const source = args.source.path as string;
141137
const bps = args.breakpoints || [];
142-
this._breakPoints.set(bpPath, new Array<DebugProtocol.Breakpoint>());
143-
bps.forEach((sourceBp) => {
144-
if (this._line === 1) {
145-
this._line = sourceBp.line;
146-
}
147-
let bps = this._breakPoints.get(bpPath) || [];
148-
const verified = sourceBp.line > 0 && sourceBp.line < this._logLines;
149-
bps.push({ line: sourceBp.line, verified: verified });
150-
});
151-
const breakpoints = this._breakPoints.get(bpPath) || [];
138+
const breakpoints = this._logDebugger.setBreakPoint(source, bps);
139+
152140
response.body = {
153141
breakpoints: breakpoints
154142
};
@@ -168,18 +156,16 @@ export class DebugSession extends LoggingDebugSession {
168156
outputChannel.appendLine(`launchRequest ${JSON.stringify(args)}`);
169157

170158
// make sure to 'Stop' the buffered logging if 'trace' is not set
171-
logger.setup(args.trace ? Logger.LogLevel.Verbose : Logger.LogLevel.Verbose, false);
159+
logger.setup(args.trace ? Logger.LogLevel.Verbose : Logger.LogLevel.Error, false);
172160

173161
this._launchArgs = args;
174162
this.openLogAndFocus();
175-
var execFile = require('child_process').execFileSync;
163+
const execFile = require('child_process').execFileSync;
176164
let stdout = execFile('wc', ['-l', this._launchArgs.log]);
177-
this._logLines = +stdout.toString().trim().split(" ")[0] || Number.MAX_VALUE;
165+
const logLines = +stdout.toString().trim().split(" ")[0] || Number.MAX_VALUE
166+
this._logDebugger.setToLog(this._launchArgs.log, logLines);
178167

179-
// TODO do we need this?
180-
// wait 1 second until configuration has finished (and configurationDoneRequest has been called)
181-
// await this._configurationDone.wait(1000);
182-
if (this._breakPoints.size === 0) {
168+
if (!this._logDebugger.hasBreakpoints()) {
183169
this.sendEvent(new StoppedEvent('entry', DebugSession._threadID));
184170
}
185171
this.sendResponse(response);
@@ -190,15 +176,22 @@ export class DebugSession extends LoggingDebugSession {
190176
if (editors.length >= 1) {
191177
this.focusEditor(editors[0]);
192178
} else {
193-
vscode.workspace
194-
.openTextDocument(this._launchArgs.log)
179+
Promise.resolve(vscode.workspace.openTextDocument(this._launchArgs.log))
195180
.then(doc => {
196181
return vscode.window.showTextDocument(doc, {
197182
viewColumn: vscode.ViewColumn.Beside,
198183
preserveFocus: false
199184
});
200185
})
201-
.then(editor => this.focusEditor(editor));
186+
.then(editor => {
187+
this.focusEditor(editor);
188+
return editor;
189+
})
190+
.catch(error => {
191+
const message = `Failed to open log file: ${error.message}`;
192+
outputChannel.appendLine(message);
193+
console.error(message);
194+
});
202195
}
203196
}
204197

@@ -217,55 +210,29 @@ export class DebugSession extends LoggingDebugSession {
217210
protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
218211
console.log(`continueRequest ${JSON.stringify(args)}`);
219212

220-
const next = this.findNextLineToStop();
221-
this._line = next;
213+
this._logDebugger.gotoNextBreakpoint();
222214
this.sendEvent(new StoppedEvent('breakpoint', DebugSession._threadID));
223215
this.sendResponse(response);
224216
}
225217

226218
protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments): void {
227219
console.log(`reverseContinueRequest ${JSON.stringify(args)}`);
228220

229-
const next = this.findNextLineToStop(true);
230-
this._line = next;
221+
this._logDebugger.gotoNextBreakpoint(true);
231222
this.sendEvent(new StoppedEvent('breakpoint', DebugSession._threadID));
232223
this.sendResponse(response);
233224
}
234225

235-
private findNextLineToStop(reverse = false): number {
236-
const bps = this._breakPoints.get(this._launchArgs.log) || [];
237-
let bp;
238-
if (reverse) {
239-
bp = bps.findLast((bp) => {
240-
return reverse ?
241-
(bp.line !== undefined && this._line > bp.line) :
242-
(bp.line !== undefined && this._line < bp.line);
243-
});
244-
} else {
245-
bp = bps.find((bp) => {
246-
return reverse ?
247-
(bp.line !== undefined && this._line > bp.line) :
248-
(bp.line !== undefined && this._line < bp.line);
249-
});
250-
}
251-
252-
if (bp !== undefined && bp.line !== undefined) {
253-
return bp.line;
254-
} else {
255-
return reverse ? 1 : this._logLines;
256-
}
257-
}
258-
259226
protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): void {
260-
console.log(`nextRequest ${JSON.stringify(args)} line=${this._line}`);
261-
this._line = Math.min(this._logLines, this._line + 1);
227+
console.log(`nextRequest ${JSON.stringify(args)} line=${this._logDebugger.linenum()}`);
228+
this._logDebugger.stepForward()
262229
this.sendEvent(new StoppedEvent('step', DebugSession._threadID));
263230
this.sendResponse(response);
264231
}
265232

266233
protected stepBackRequest(response: DebugProtocol.StepBackResponse, args: DebugProtocol.StepBackArguments): void {
267-
console.log(`stepBackRequest ${JSON.stringify(args)} line=${this._line}`);
268-
this._line = Math.max(1, this._line - 1);
234+
console.log(`stepBackRequest ${JSON.stringify(args)} line=${this._logDebugger.linenum()}`);
235+
this._logDebugger.stepBackward();
269236
this.sendEvent(new StoppedEvent('step', DebugSession._threadID));
270237
this.sendResponse(response);
271238
}
@@ -275,7 +242,7 @@ export class DebugSession extends LoggingDebugSession {
275242

276243
const log2srcPath = path.resolve(__dirname, this._binaryPath);
277244
const execFile = require('child_process').execFileSync;
278-
const start = this._line - 1;
245+
const start = this._logDebugger.linenum() - 1;
279246

280247
const editors = this.findEditors();
281248
if (editors.length > 0) {
@@ -292,7 +259,7 @@ export class DebugSession extends LoggingDebugSession {
292259
}
293260
outputChannel.appendLine(`args ${l2sArgs.join(" ")}`);
294261
let stdout = execFile(log2srcPath, l2sArgs);
295-
this._mapping = JSON.parse(stdout);
262+
this._mapping = JSON.parse(stdout.toString('utf8'));
296263
outputChannel.appendLine(`mapped ${JSON.stringify(this._mapping)}`);
297264

298265
let index = 0;
@@ -309,11 +276,12 @@ export class DebugSession extends LoggingDebugSession {
309276
}
310277

311278
private findEditors(): vscode.TextEditor[] {
312-
return vscode.window.visibleTextEditors.filter((editor) => editor.document.fileName === this._launchArgs.log);
279+
const target = path.resolve(this._launchArgs.log);
280+
return vscode.window.visibleTextEditors.filter((editor) => editor.document.fileName === target);
313281
}
314282

315283
private focusEditor(editor: vscode.TextEditor) {
316-
const start = this._line - 1;
284+
const start = this._logDebugger.linenum() - 1;
317285
let range = new vscode.Range(
318286
new vscode.Position(start, 0),
319287
new vscode.Position(start, Number.MAX_VALUE)
@@ -367,11 +335,11 @@ export class DebugSession extends LoggingDebugSession {
367335
const v = this._variableHandles.get(args.variablesReference);
368336
if (v === 'locals' && this._mapping !== undefined) {
369337
for (let pair of this._mapping.variables) {
370-
vs.push({
371-
name: pair.expr,
372-
value: pair.value,
373-
variablesReference: 0
374-
});
338+
vs.push({
339+
name: pair.expr,
340+
value: pair.value,
341+
variablesReference: 0
342+
});
375343
}
376344
}
377345

editors/code/src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import * as vscode from 'vscode';
1616
import { ProviderResult } from 'vscode';
1717
import { BinaryNotFoundError, DebugSession } from './debugAdapter';
18+
import { LogDebugger } from './logDebugger';
1819

1920
const runMode: 'external' | 'server' | 'namedPipeServer' | 'inline' = 'inline';
2021
const outputChannel = vscode.window.createOutputChannel("Log2Src");
@@ -47,7 +48,7 @@ class InlineDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory
4748

4849
createDebugAdapterDescriptor(_session: vscode.DebugSession): ProviderResult<vscode.DebugAdapterDescriptor> {
4950
try {
50-
return new vscode.DebugAdapterInlineImplementation(new DebugSession());
51+
return new vscode.DebugAdapterInlineImplementation(new DebugSession(new LogDebugger()));
5152
} catch (error) {
5253
if (error instanceof BinaryNotFoundError) {
5354
vscode.window.showErrorMessage(`Log2Src Error: ${error.message}`);

editors/code/src/logDebugger.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* logDebugger.ts handles tracking the state of the "log driven debugger".
3+
*
4+
*/
5+
6+
import { DebugProtocol } from '@vscode/debugprotocol';
7+
import * as path from 'path';
8+
9+
10+
export class LogDebugger {
11+
12+
private _breakPoints = new Map<string, DebugProtocol.Breakpoint[]>();
13+
private _log: string | undefined = undefined;
14+
private _line = 1;
15+
private _logLines = Number.MAX_SAFE_INTEGER;
16+
17+
public constructor() {
18+
}
19+
20+
setToLog(log: string, logLines: number): void {
21+
this._log = path.resolve(log);
22+
this._logLines = logLines;
23+
}
24+
25+
setBreakPoint(source: string, breakpoints: DebugProtocol.SourceBreakpoint[]): DebugProtocol.Breakpoint[] {
26+
const bps = new Array<DebugProtocol.Breakpoint>();
27+
const sourcePath = path.resolve(source);
28+
this._breakPoints.set(sourcePath, bps);
29+
breakpoints.forEach((breakpoint) => {
30+
if (this._line === 1) {
31+
this._line = breakpoint.line;
32+
}
33+
const verified = breakpoint.line > 0 && breakpoint.line < this._logLines;
34+
bps.push({ line: breakpoint.line, verified: verified });
35+
});
36+
return bps;
37+
}
38+
39+
hasBreakpoints(): boolean {
40+
const bps = (this._log && this._breakPoints.get(this._log)) || [];
41+
return bps.length !== 0;
42+
}
43+
44+
linenum(): number {
45+
return this._line;
46+
}
47+
48+
stepForward(): void {
49+
this._line = Math.min(this._logLines, this._line + 1);
50+
}
51+
52+
stepBackward(): void {
53+
this._line = Math.max(1, this._line - 1);
54+
}
55+
56+
gotoNextBreakpoint(reverse = false): void {
57+
this._line = this.findNextLineToStop(reverse);
58+
}
59+
60+
private findNextLineToStop(reverse = false): number {
61+
const bps = (this._log && this._breakPoints.get(this._log)) || [];
62+
let bp;
63+
if (reverse) {
64+
bp = bps.findLast((bp) => bp.line !== undefined && this._line > bp.line);
65+
} else {
66+
bp = bps.find((bp) => bp.line !== undefined && this._line < bp.line);
67+
}
68+
69+
if (bp !== undefined && bp.line !== undefined) {
70+
return bp.line;
71+
} else {
72+
return reverse ? 1 : this._logLines;
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)