Skip to content

Commit c0849e6

Browse files
committed
debugging, attach to process
1 parent 22f6d11 commit c0849e6

10 files changed

+217
-80
lines changed

Diff for: package.json

+18
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,26 @@
559559
"description": "Absolute path to the program."
560560
}
561561
}
562+
},
563+
"attach": {
564+
"required": [],
565+
"properties": {
566+
"processId": {
567+
"type": ["number", "string"],
568+
"description": "ID of process to attach to.",
569+
"default": "${command:PickProcess}"
570+
},
571+
"system": {
572+
"type": "boolean",
573+
"description": "Enable to attach to system process.",
574+
"default": false
575+
}
576+
}
562577
}
563578
},
579+
"variables": {
580+
"PickProcess": "vscode-objectscript.pickProcess"
581+
},
564582
"initialConfigurations": [
565583
{
566584
"type": "objectscript",

Diff for: src/api/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -302,4 +302,11 @@ export class AtelierAPI {
302302
includes,
303303
});
304304
}
305+
// v1+
306+
public getJobs(system: boolean) {
307+
const params = {
308+
system,
309+
};
310+
return this.request(1, "GET", `%SYS/jobs`, null, params);
311+
}
305312
}

Diff for: src/debug/debugConfProvider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class ObjectScriptConfigurationProvider implements DebugConfigurationProv
3030
}
3131
}
3232

33-
if (!config.program) {
33+
if (config.request === "launch" && !config.program) {
3434
return vscode.window.showInformationMessage("Cannot find a program to debug").then(_ => {
3535
return undefined; // abort launch
3636
});

Diff for: src/debug/debugSession.ts

+77-9
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
2828
program: string;
2929
/** Automatically stop target after launch. If not specified, target does not stop. */
3030
stopOnEntry?: boolean;
31-
/** enable logging the Debug Adapter Protocol */
32-
trace?: boolean;
31+
}
32+
33+
interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments {
34+
/** The process id to attach to. */
35+
processId: string;
36+
/** Automatically stop target after connect. If not specified, target does not stop. */
37+
stopOnEntry?: boolean;
3338
}
3439

3540
/** converts a local path from VS Code to a server-side XDebug file URI with respect to source root settings */
@@ -95,10 +100,12 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
95100
args: DebugProtocol.InitializeRequestArguments
96101
): Promise<void> {
97102
// build and return the capabilities of this debug adapter:
98-
response.body = response.body || {
99-
supportsConfigurationDoneRequest: false,
103+
response.body = {
104+
...response.body,
105+
supportsConfigurationDoneRequest: true,
100106
supportsEvaluateForHovers: true,
101-
supportsSetVariable: false, // TODO:
107+
supportsSetVariable: false, // TODO
108+
supportsConditionalBreakpoints: false, // TODO
102109
supportsStepBack: false,
103110
};
104111

@@ -120,6 +127,11 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
120127

121128
await this._connection.waitForInitPacket();
122129

130+
await this._connection.sendFeatureSetCommand("max_data", 8192);
131+
await this._connection.sendFeatureSetCommand("max_children", 32);
132+
await this._connection.sendFeatureSetCommand("max_depth", 2);
133+
await this._connection.sendFeatureSetCommand("notify_ok", 1);
134+
123135
this.sendResponse(response);
124136

125137
this.sendEvent(new InitializedEvent());
@@ -130,24 +142,76 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
130142

131143
const debugTarget = `${this._namespace}:${args.program}`;
132144
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
133-
await this._connection.sendFeatureSetCommand("max_data", 1000);
134145

135146
this._debugTargetSet.notify();
136147

137-
// const xdebugResponse = await this._connection.sendStepIntoCommand();
148+
this.sendResponse(response);
149+
}
150+
151+
protected async attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): Promise<void> {
152+
const debugTarget = `PID:${args.processId}`;
153+
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
154+
this._debugTargetSet.notify();
155+
156+
this.sendResponse(response);
157+
}
158+
159+
protected async pauseRequest(
160+
response: DebugProtocol.PauseResponse,
161+
args: DebugProtocol.PauseArguments
162+
): Promise<void> {
163+
const xdebugResponse = await this._connection.sendBreakCommand();
164+
await this._checkStatus(xdebugResponse);
165+
166+
this.sendResponse(response);
167+
}
168+
169+
protected async configurationDoneRequest(
170+
response: DebugProtocol.ConfigurationDoneResponse,
171+
args: DebugProtocol.ConfigurationDoneArguments
172+
): Promise<void> {
138173
const xdebugResponse = await this._connection.sendRunCommand();
139174
await this._checkStatus(xdebugResponse);
140175

141176
this.sendResponse(response);
142177
}
143178

179+
protected async disconnectRequest(
180+
response: DebugProtocol.DisconnectResponse,
181+
args: DebugProtocol.DisconnectArguments
182+
): Promise<void> {
183+
if (this._connection) {
184+
const stopSupported = (await this._connection.sendFeatureGetCommand("stop")).supported;
185+
if (stopSupported) {
186+
const xdebugResponse = await this._connection.sendStopCommand();
187+
await this._checkStatus(xdebugResponse);
188+
}
189+
190+
const detachSupported = (await this._connection.sendFeatureGetCommand("detach")).supported;
191+
if (detachSupported) {
192+
const xdebugResponse = await this._connection.sendDetachCommand();
193+
await this._checkStatus(xdebugResponse);
194+
}
195+
}
196+
197+
this.sendResponse(response);
198+
}
199+
144200
protected async setBreakPointsRequest(
145201
response: DebugProtocol.SetBreakpointsResponse,
146202
args: DebugProtocol.SetBreakpointsArguments
147203
): Promise<void> {
204+
await this._debugTargetSet.wait(1000);
205+
148206
const filePath = args.source.path;
149207
const fileUri = await convertClientPathToDebugger(args.source.path, this._namespace);
150208

209+
// const currentList = (await this._connection.sendBreakpointListCommand()).breakpoints.filter(breakpoint => {
210+
// if (breakpoint instanceof xdebug.LineBreakpoint) {
211+
// return breakpoint.fileUri === fileUri;
212+
// }
213+
// });
214+
151215
let xdebugBreakpoints: (xdebug.ConditionalBreakpoint | xdebug.ClassLineBreakpoint | xdebug.LineBreakpoint)[] = [];
152216
xdebugBreakpoints = await Promise.all(
153217
args.breakpoints.map(async breakpoint => {
@@ -167,7 +231,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
167231
}
168232
});
169233
} else {
170-
return new xdebug.LineBreakpoint(fileUri, line - 1);
234+
return new xdebug.LineBreakpoint(fileUri, line);
171235
}
172236
})
173237
);
@@ -374,14 +438,16 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
374438
response: DebugProtocol.ContinueResponse,
375439
args: DebugProtocol.ContinueArguments
376440
): Promise<void> {
441+
this.sendResponse(response);
442+
377443
const xdebugResponse = await this._connection.sendRunCommand();
378444
this._checkStatus(xdebugResponse);
379-
this.sendResponse(response);
380445
}
381446

382447
protected async nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): Promise<void> {
383448
const xdebugResponse = await this._connection.sendStepOverCommand();
384449
this._checkStatus(xdebugResponse);
450+
385451
this.sendResponse(response);
386452
}
387453

@@ -391,6 +457,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
391457
): Promise<void> {
392458
const xdebugResponse = await this._connection.sendStepIntoCommand();
393459
this._checkStatus(xdebugResponse);
460+
394461
this.sendResponse(response);
395462
}
396463

@@ -400,6 +467,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
400467
): Promise<void> {
401468
const xdebugResponse = await this._connection.sendStepOutCommand();
402469
this._checkStatus(xdebugResponse);
470+
403471
this.sendResponse(response);
404472
}
405473

Diff for: src/debug/xdebugConnection.ts

+45-41
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class StatusResponse extends Response {
116116
}
117117
}
118118

119-
export type BreakpointType = "line" | "call" | "return" | "exception" | "conditional" | "watch";
119+
export type BreakpointType = "line" | "return" | "conditional" | "watch";
120120
export type BreakpointState = "enabled" | "disabled";
121121

122122
/** Abstract base class for all breakpoints */
@@ -130,9 +130,6 @@ export abstract class Breakpoint {
130130
case "conditional":
131131
// eslint-disable-next-line @typescript-eslint/no-use-before-define
132132
return new ConditionalBreakpoint(breakpointNode, connection);
133-
case "call":
134-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
135-
return new CallBreakpoint(breakpointNode, connection);
136133
default:
137134
throw new Error(`Invalid type ${breakpointNode.getAttribute("type")}`);
138135
}
@@ -159,6 +156,7 @@ export abstract class Breakpoint {
159156
this.state = breakpointNode.getAttribute("state") as BreakpointState;
160157
} else {
161158
this.type = rest[0];
159+
this.state = "enabled";
162160
}
163161
}
164162
/** Removes the breakpoint by sending a breakpoint_remove command */
@@ -214,39 +212,13 @@ export class ClassLineBreakpoint extends LineBreakpoint {
214212
}
215213
}
216214

217-
/** class for call breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
218-
export class CallBreakpoint extends Breakpoint {
219-
/** the function to break on */
220-
public fn: string;
221-
/** optional expression that must evaluate to true */
222-
public expression: string;
223-
/** constructs a call breakpoint from an XML node */
224-
public constructor(breakpointNode: Element, connection: Connection);
225-
/** contructs a call breakpoint for passing to sendSetBreakpointCommand */
226-
public constructor(fn: string, expression?: string);
227-
public constructor(...rest) {
228-
if (typeof rest[0] === "object") {
229-
const breakpointNode: Element = rest[0];
230-
const connection: Connection = rest[1];
231-
super(breakpointNode, connection);
232-
this.fn = breakpointNode.getAttribute("function");
233-
this.expression = breakpointNode.getAttribute("expression"); // Base64 encoded?
234-
} else {
235-
// construct from arguments
236-
super("call");
237-
this.fn = rest[0];
238-
this.expression = rest[1];
239-
}
240-
}
241-
}
242-
243215
/** class for conditional breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
244216
export class ConditionalBreakpoint extends Breakpoint {
245217
/** File URI */
246218
public fileUri: string;
247219
/** Line (optional) */
248220
public line: number;
249-
/** The PHP expression under which to break on */
221+
/** The expression under which to break on */
250222
public expression: string;
251223
/** Constructs a breakpoint object from an XML node from a XDebug response */
252224
public constructor(breakpointNode: Element, connection: Connection);
@@ -543,6 +515,18 @@ export class FeatureSetResponse extends Response {
543515
}
544516
}
545517

518+
export class FeatureGetResponse extends Response {
519+
/** the feature that was get */
520+
public feature: string;
521+
/** supported flag for the feature */
522+
public supported: boolean;
523+
public constructor(document: XMLDocument, connection: Connection) {
524+
super(document, connection);
525+
this.feature = document.documentElement.getAttribute("feature");
526+
this.supported = document.documentElement.getAttribute("supported") === "1";
527+
}
528+
}
529+
546530
/** A command inside the queue */
547531
interface Command {
548532
/** The name of the command, like breakpoint_list */
@@ -555,7 +539,7 @@ interface Command {
555539
resolveFn: (response: XMLDocument) => any;
556540
/** callback that gets called if an error happened while parsing the response */
557541
rejectFn: (error?: Error) => any;
558-
/** whether command results in PHP code being executed or not */
542+
/** whether command results in code being executed or not */
559543
isExecuteCommand: boolean;
560544
}
561545

@@ -564,7 +548,7 @@ interface Command {
564548
*/
565549
export class Connection extends DbgpConnection {
566550
/**
567-
* Whether a command was started that executes PHP, which means the connection will be blocked from
551+
* Whether a command was started that executes code, which means the connection will be blocked from
568552
* running any additional commands until the execution gets to the next stopping point or exits.
569553
*/
570554
public get isPendingExecuteCommand(): boolean {
@@ -682,8 +666,8 @@ export class Connection extends DbgpConnection {
682666
* - notify_ok
683667
* or any command.
684668
*/
685-
public async sendFeatureGetCommand(feature: string): Promise<XMLDocument> {
686-
return await this._enqueueCommand("feature_get", `-n ${feature}`);
669+
public async sendFeatureGetCommand(feature: string): Promise<FeatureGetResponse> {
670+
return new FeatureGetResponse(await this._enqueueCommand("feature_get", `-n ${feature}`), this);
687671
}
688672

689673
/**
@@ -712,8 +696,8 @@ export class Connection extends DbgpConnection {
712696
public async sendBreakpointSetCommand(breakpoint: Breakpoint): Promise<BreakpointSetResponse> {
713697
let args = `-t ${breakpoint.type}`;
714698
let data: string | undefined;
699+
args += ` -s ${breakpoint.state}`;
715700
if (breakpoint instanceof LineBreakpoint) {
716-
args += ` -s enabled`;
717701
args += ` -f ${breakpoint.fileUri}`;
718702
if (breakpoint instanceof ClassLineBreakpoint) {
719703
args += ` -m ${breakpoint.method} -n ${breakpoint.methodOffset}`;
@@ -726,9 +710,6 @@ export class Connection extends DbgpConnection {
726710
args += ` -n ${breakpoint.line}`;
727711
}
728712
data = breakpoint.expression;
729-
} else if (breakpoint instanceof CallBreakpoint) {
730-
args += ` -m ${breakpoint.fn}`;
731-
data = breakpoint.expression;
732713
}
733714
return new BreakpointSetResponse(await this._enqueueCommand("breakpoint_set", args, data), this);
734715
}
@@ -767,7 +748,17 @@ export class Connection extends DbgpConnection {
767748

768749
/** sends a stop command */
769750
public async sendStopCommand(): Promise<StatusResponse> {
770-
return new StatusResponse(await this._enqueueCommand("stop"), this);
751+
return new StatusResponse(await this._immediateCommand("stop"), this);
752+
}
753+
754+
/** sends an detach command */
755+
public async sendDetachCommand(): Promise<StatusResponse> {
756+
return new StatusResponse(await this._immediateCommand("detach"), this);
757+
}
758+
759+
/** sends an break command */
760+
public async sendBreakCommand(): Promise<StatusResponse> {
761+
return new StatusResponse(await this._immediateCommand("break"), this);
771762
}
772763

773764
// ------------------------------ stack ----------------------------------------
@@ -835,8 +826,21 @@ export class Connection extends DbgpConnection {
835826
});
836827
}
837828

829+
private _immediateCommand(name: string, args?: string, data?: string): Promise<XMLDocument> {
830+
return new Promise((resolveFn, rejectFn): void => {
831+
this._executeCommand({
832+
name,
833+
args,
834+
data,
835+
resolveFn,
836+
rejectFn,
837+
isExecuteCommand: false,
838+
});
839+
});
840+
}
841+
838842
/**
839-
* Pushes a new execute command (one that results in executing PHP code)
843+
* Pushes a new execute command (one that results in executing code)
840844
* to the queue that will be executed after all the previous
841845
* commands have finished and we received a response.
842846
* If the queue is empty AND there are no pending transactions

0 commit comments

Comments
 (0)