Skip to content

Commit

Permalink
debugging, attach to process
Browse files Browse the repository at this point in the history
  • Loading branch information
daimor committed Aug 6, 2019
1 parent 22f6d11 commit c0849e6
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 80 deletions.
18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,26 @@
"description": "Absolute path to the program."
}
}
},
"attach": {
"required": [],
"properties": {
"processId": {
"type": ["number", "string"],
"description": "ID of process to attach to.",
"default": "${command:PickProcess}"
},
"system": {
"type": "boolean",
"description": "Enable to attach to system process.",
"default": false
}
}
}
},
"variables": {
"PickProcess": "vscode-objectscript.pickProcess"
},
"initialConfigurations": [
{
"type": "objectscript",
Expand Down
7 changes: 7 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,11 @@ export class AtelierAPI {
includes,
});
}
// v1+
public getJobs(system: boolean) {
const params = {
system,
};
return this.request(1, "GET", `%SYS/jobs`, null, params);
}
}
2 changes: 1 addition & 1 deletion src/debug/debugConfProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class ObjectScriptConfigurationProvider implements DebugConfigurationProv
}
}

if (!config.program) {
if (config.request === "launch" && !config.program) {
return vscode.window.showInformationMessage("Cannot find a program to debug").then(_ => {
return undefined; // abort launch
});
Expand Down
86 changes: 77 additions & 9 deletions src/debug/debugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
program: string;
/** Automatically stop target after launch. If not specified, target does not stop. */
stopOnEntry?: boolean;
/** enable logging the Debug Adapter Protocol */
trace?: boolean;
}

interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments {
/** The process id to attach to. */
processId: string;
/** Automatically stop target after connect. If not specified, target does not stop. */
stopOnEntry?: boolean;
}

/** converts a local path from VS Code to a server-side XDebug file URI with respect to source root settings */
Expand Down Expand Up @@ -95,10 +100,12 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
args: DebugProtocol.InitializeRequestArguments
): Promise<void> {
// build and return the capabilities of this debug adapter:
response.body = response.body || {
supportsConfigurationDoneRequest: false,
response.body = {
...response.body,
supportsConfigurationDoneRequest: true,
supportsEvaluateForHovers: true,
supportsSetVariable: false, // TODO:
supportsSetVariable: false, // TODO
supportsConditionalBreakpoints: false, // TODO
supportsStepBack: false,
};

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

await this._connection.waitForInitPacket();

await this._connection.sendFeatureSetCommand("max_data", 8192);
await this._connection.sendFeatureSetCommand("max_children", 32);
await this._connection.sendFeatureSetCommand("max_depth", 2);
await this._connection.sendFeatureSetCommand("notify_ok", 1);

this.sendResponse(response);

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

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

this._debugTargetSet.notify();

// const xdebugResponse = await this._connection.sendStepIntoCommand();
this.sendResponse(response);
}

protected async attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): Promise<void> {
const debugTarget = `PID:${args.processId}`;
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
this._debugTargetSet.notify();

this.sendResponse(response);
}

protected async pauseRequest(
response: DebugProtocol.PauseResponse,
args: DebugProtocol.PauseArguments
): Promise<void> {
const xdebugResponse = await this._connection.sendBreakCommand();
await this._checkStatus(xdebugResponse);

this.sendResponse(response);
}

protected async configurationDoneRequest(
response: DebugProtocol.ConfigurationDoneResponse,
args: DebugProtocol.ConfigurationDoneArguments
): Promise<void> {
const xdebugResponse = await this._connection.sendRunCommand();
await this._checkStatus(xdebugResponse);

this.sendResponse(response);
}

protected async disconnectRequest(
response: DebugProtocol.DisconnectResponse,
args: DebugProtocol.DisconnectArguments
): Promise<void> {
if (this._connection) {
const stopSupported = (await this._connection.sendFeatureGetCommand("stop")).supported;
if (stopSupported) {
const xdebugResponse = await this._connection.sendStopCommand();
await this._checkStatus(xdebugResponse);
}

const detachSupported = (await this._connection.sendFeatureGetCommand("detach")).supported;
if (detachSupported) {
const xdebugResponse = await this._connection.sendDetachCommand();
await this._checkStatus(xdebugResponse);
}
}

this.sendResponse(response);
}

protected async setBreakPointsRequest(
response: DebugProtocol.SetBreakpointsResponse,
args: DebugProtocol.SetBreakpointsArguments
): Promise<void> {
await this._debugTargetSet.wait(1000);

const filePath = args.source.path;
const fileUri = await convertClientPathToDebugger(args.source.path, this._namespace);

// const currentList = (await this._connection.sendBreakpointListCommand()).breakpoints.filter(breakpoint => {
// if (breakpoint instanceof xdebug.LineBreakpoint) {
// return breakpoint.fileUri === fileUri;
// }
// });

let xdebugBreakpoints: (xdebug.ConditionalBreakpoint | xdebug.ClassLineBreakpoint | xdebug.LineBreakpoint)[] = [];
xdebugBreakpoints = await Promise.all(
args.breakpoints.map(async breakpoint => {
Expand All @@ -167,7 +231,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
}
});
} else {
return new xdebug.LineBreakpoint(fileUri, line - 1);
return new xdebug.LineBreakpoint(fileUri, line);
}
})
);
Expand Down Expand Up @@ -374,14 +438,16 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
response: DebugProtocol.ContinueResponse,
args: DebugProtocol.ContinueArguments
): Promise<void> {
this.sendResponse(response);

const xdebugResponse = await this._connection.sendRunCommand();
this._checkStatus(xdebugResponse);
this.sendResponse(response);
}

protected async nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): Promise<void> {
const xdebugResponse = await this._connection.sendStepOverCommand();
this._checkStatus(xdebugResponse);

this.sendResponse(response);
}

Expand All @@ -391,6 +457,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
): Promise<void> {
const xdebugResponse = await this._connection.sendStepIntoCommand();
this._checkStatus(xdebugResponse);

this.sendResponse(response);
}

Expand All @@ -400,6 +467,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
): Promise<void> {
const xdebugResponse = await this._connection.sendStepOutCommand();
this._checkStatus(xdebugResponse);

this.sendResponse(response);
}

Expand Down
86 changes: 45 additions & 41 deletions src/debug/xdebugConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class StatusResponse extends Response {
}
}

export type BreakpointType = "line" | "call" | "return" | "exception" | "conditional" | "watch";
export type BreakpointType = "line" | "return" | "conditional" | "watch";
export type BreakpointState = "enabled" | "disabled";

/** Abstract base class for all breakpoints */
Expand All @@ -130,9 +130,6 @@ export abstract class Breakpoint {
case "conditional":
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new ConditionalBreakpoint(breakpointNode, connection);
case "call":
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return new CallBreakpoint(breakpointNode, connection);
default:
throw new Error(`Invalid type ${breakpointNode.getAttribute("type")}`);
}
Expand All @@ -159,6 +156,7 @@ export abstract class Breakpoint {
this.state = breakpointNode.getAttribute("state") as BreakpointState;
} else {
this.type = rest[0];
this.state = "enabled";
}
}
/** Removes the breakpoint by sending a breakpoint_remove command */
Expand Down Expand Up @@ -214,39 +212,13 @@ export class ClassLineBreakpoint extends LineBreakpoint {
}
}

/** class for call breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
export class CallBreakpoint extends Breakpoint {
/** the function to break on */
public fn: string;
/** optional expression that must evaluate to true */
public expression: string;
/** constructs a call breakpoint from an XML node */
public constructor(breakpointNode: Element, connection: Connection);
/** contructs a call breakpoint for passing to sendSetBreakpointCommand */
public constructor(fn: string, expression?: string);
public constructor(...rest) {
if (typeof rest[0] === "object") {
const breakpointNode: Element = rest[0];
const connection: Connection = rest[1];
super(breakpointNode, connection);
this.fn = breakpointNode.getAttribute("function");
this.expression = breakpointNode.getAttribute("expression"); // Base64 encoded?
} else {
// construct from arguments
super("call");
this.fn = rest[0];
this.expression = rest[1];
}
}
}

/** class for conditional breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
export class ConditionalBreakpoint extends Breakpoint {
/** File URI */
public fileUri: string;
/** Line (optional) */
public line: number;
/** The PHP expression under which to break on */
/** The expression under which to break on */
public expression: string;
/** Constructs a breakpoint object from an XML node from a XDebug response */
public constructor(breakpointNode: Element, connection: Connection);
Expand Down Expand Up @@ -543,6 +515,18 @@ export class FeatureSetResponse extends Response {
}
}

export class FeatureGetResponse extends Response {
/** the feature that was get */
public feature: string;
/** supported flag for the feature */
public supported: boolean;
public constructor(document: XMLDocument, connection: Connection) {
super(document, connection);
this.feature = document.documentElement.getAttribute("feature");
this.supported = document.documentElement.getAttribute("supported") === "1";
}
}

/** A command inside the queue */
interface Command {
/** The name of the command, like breakpoint_list */
Expand All @@ -555,7 +539,7 @@ interface Command {
resolveFn: (response: XMLDocument) => any;
/** callback that gets called if an error happened while parsing the response */
rejectFn: (error?: Error) => any;
/** whether command results in PHP code being executed or not */
/** whether command results in code being executed or not */
isExecuteCommand: boolean;
}

Expand All @@ -564,7 +548,7 @@ interface Command {
*/
export class Connection extends DbgpConnection {
/**
* Whether a command was started that executes PHP, which means the connection will be blocked from
* Whether a command was started that executes code, which means the connection will be blocked from
* running any additional commands until the execution gets to the next stopping point or exits.
*/
public get isPendingExecuteCommand(): boolean {
Expand Down Expand Up @@ -682,8 +666,8 @@ export class Connection extends DbgpConnection {
* - notify_ok
* or any command.
*/
public async sendFeatureGetCommand(feature: string): Promise<XMLDocument> {
return await this._enqueueCommand("feature_get", `-n ${feature}`);
public async sendFeatureGetCommand(feature: string): Promise<FeatureGetResponse> {
return new FeatureGetResponse(await this._enqueueCommand("feature_get", `-n ${feature}`), this);
}

/**
Expand Down Expand Up @@ -712,8 +696,8 @@ export class Connection extends DbgpConnection {
public async sendBreakpointSetCommand(breakpoint: Breakpoint): Promise<BreakpointSetResponse> {
let args = `-t ${breakpoint.type}`;
let data: string | undefined;
args += ` -s ${breakpoint.state}`;
if (breakpoint instanceof LineBreakpoint) {
args += ` -s enabled`;
args += ` -f ${breakpoint.fileUri}`;
if (breakpoint instanceof ClassLineBreakpoint) {
args += ` -m ${breakpoint.method} -n ${breakpoint.methodOffset}`;
Expand All @@ -726,9 +710,6 @@ export class Connection extends DbgpConnection {
args += ` -n ${breakpoint.line}`;
}
data = breakpoint.expression;
} else if (breakpoint instanceof CallBreakpoint) {
args += ` -m ${breakpoint.fn}`;
data = breakpoint.expression;
}
return new BreakpointSetResponse(await this._enqueueCommand("breakpoint_set", args, data), this);
}
Expand Down Expand Up @@ -767,7 +748,17 @@ export class Connection extends DbgpConnection {

/** sends a stop command */
public async sendStopCommand(): Promise<StatusResponse> {
return new StatusResponse(await this._enqueueCommand("stop"), this);
return new StatusResponse(await this._immediateCommand("stop"), this);
}

/** sends an detach command */
public async sendDetachCommand(): Promise<StatusResponse> {
return new StatusResponse(await this._immediateCommand("detach"), this);
}

/** sends an break command */
public async sendBreakCommand(): Promise<StatusResponse> {
return new StatusResponse(await this._immediateCommand("break"), this);
}

// ------------------------------ stack ----------------------------------------
Expand Down Expand Up @@ -835,8 +826,21 @@ export class Connection extends DbgpConnection {
});
}

private _immediateCommand(name: string, args?: string, data?: string): Promise<XMLDocument> {
return new Promise((resolveFn, rejectFn): void => {
this._executeCommand({
name,
args,
data,
resolveFn,
rejectFn,
isExecuteCommand: false,
});
});
}

/**
* Pushes a new execute command (one that results in executing PHP code)
* Pushes a new execute command (one that results in executing code)
* to the queue that will be executed after all the previous
* commands have finished and we received a response.
* If the queue is empty AND there are no pending transactions
Expand Down
Loading

0 comments on commit c0849e6

Please sign in to comment.