Skip to content

Commit 9575b08

Browse files
committed
Use write-service-info to get Observatory URI instead of relying on stdout
Fixes #2148 and will help towards #2230.
1 parent 111b803 commit 9575b08

4 files changed

Lines changed: 73 additions & 0 deletions

File tree

‎src/debug/dart_debug_impl.ts‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as fs from "fs";
2+
import * as os from "os";
23
import * as path from "path";
4+
import { URL } from "url";
35
import { DebugSession, Event, InitializedEvent, OutputEvent, Scope, Source, StackFrame, StoppedEvent, TerminatedEvent } from "vscode-debugadapter";
46
import { DebugProtocol } from "vscode-debugprotocol";
57
import { getLogHeader } from "../extension/utils/log";
@@ -12,6 +14,7 @@ import { PackageMap } from "../shared/pub/package_map";
1214
import { errorString, flatMap, notUndefined, throttle, uniq, uriToFilePath } from "../shared/utils";
1315
import { sortBy } from "../shared/utils/array";
1416
import { applyColor, grey, grey2 } from "../shared/utils/colors";
17+
import { getRandomInt } from "../shared/utils/fs";
1518
import { DebuggerResult, ObservatoryConnection, SourceReportKind, Version, VM, VMClass, VMClassRef, VMErrorRef, VMEvent, VMFrame, VMInstance, VMInstanceRef, VMIsolate, VMIsolateRef, VMLibrary, VMMapEntry, VMObj, VMScript, VMScriptRef, VMSentinel, VMSourceReport, VMStack, VMTypeRef } from "./dart_debug_protocol";
1619
import { DebugAdapterLogger } from "./logging";
1720
import { ThreadInfo, ThreadManager } from "./threads";
@@ -65,6 +68,9 @@ export class DartDebugSession extends DebugSession {
6568
public useFlutterStructuredErrors = false;
6669
public evaluateGettersInDebugViews = false;
6770
protected previewToStringInDebugViews = false;
71+
protected useWriteServiceInfo = false;
72+
protected vmServiceInfoFile?: string;
73+
private serviceInfoPollTimer?: NodeJS.Timer;
6874
public debuggerHandlesPathsEverywhereForBreakpoints = false;
6975
protected threadManager: ThreadManager;
7076
public packageMap?: PackageMap;
@@ -142,6 +148,7 @@ export class DartDebugSession extends DebugSession {
142148
this.useFlutterStructuredErrors = args.useFlutterStructuredErrors;
143149
this.evaluateGettersInDebugViews = args.evaluateGettersInDebugViews;
144150
this.previewToStringInDebugViews = args.previewToStringInDebugViews;
151+
this.useWriteServiceInfo = args.useWriteServiceInfo;
145152
this.sendOutputAsCustomEvent = args.console === "terminal";
146153
this.allowMessageBuffering = args.console !== "terminal";
147154
this.debuggerHandlesPathsEverywhereForBreakpoints = args.debuggerHandlesPathsEverywhereForBreakpoints;
@@ -151,6 +158,12 @@ export class DartDebugSession extends DebugSession {
151158
this.sendResponse(response);
152159

153160
this.childProcess = this.spawnProcess(args);
161+
162+
// Start watching for the service info file.
163+
if (this.vmServiceInfoFile) {
164+
this.startServiceFilePolling();
165+
}
166+
154167
const process = this.childProcess;
155168
this.processExited = false;
156169
this.processExit = new Promise((resolve) => process.on("exit", resolve));
@@ -173,6 +186,7 @@ export class DartDebugSession extends DebugSession {
173186
this.logToUser(`${error}\n`, "stderr");
174187
});
175188
process.on("exit", async (code, signal) => {
189+
this.stopServiceFilePolling();
176190
this.processExited = true;
177191
this.log(`Process exited (${signal ? `${signal}`.toLowerCase() : code})`);
178192
if (!code && !signal)
@@ -243,6 +257,12 @@ export class DartDebugSession extends DebugSession {
243257
appArgs.push(`--enable-vm-service=${args.vmServicePort}`);
244258
appArgs.push("--pause_isolates_on_start=true");
245259
}
260+
if (this.useWriteServiceInfo) {
261+
this.parseObservatoryUriFromStdOut = false;
262+
this.vmServiceInfoFile = path.join(os.tmpdir(), `dart-vm-service-${getRandomInt(0x1000, 0x10000).toString(16)}.json`);
263+
appArgs.push(`--write-service-info=${this.vmServiceInfoFile}`);
264+
appArgs.push("-DSILENT_OBSERVATORY=true");
265+
}
246266
if (args.enableAsserts !== false) { // undefined = on
247267
appArgs.push("--enable-asserts");
248268
}
@@ -293,6 +313,53 @@ export class DartDebugSession extends DebugSession {
293313
this.sendEvent(new Event("dart.log", { message, severity, category: LogCategory.Observatory } as LogMessage));
294314
}
295315

316+
private startServiceFilePolling() {
317+
// Ensure we stop if we were already running, to avoid leaving timers running
318+
// if this is somehow called twice.
319+
this.stopServiceFilePolling();
320+
this.serviceInfoPollTimer = setInterval(() => this.tryReadServiceFile(), 50);
321+
}
322+
323+
private stopServiceFilePolling() {
324+
if (this.serviceInfoPollTimer)
325+
clearInterval(this.serviceInfoPollTimer);
326+
if (this.vmServiceInfoFile && fs.existsSync(this.vmServiceInfoFile)) {
327+
try {
328+
fs.unlinkSync(this.vmServiceInfoFile);
329+
this.vmServiceInfoFile = undefined;
330+
} catch (e) {
331+
// Don't complain if we failed - the file may have been cleaned up
332+
// in the meantime.
333+
}
334+
}
335+
}
336+
337+
private tryReadServiceFile() {
338+
if (!this.vmServiceInfoFile || !fs.existsSync(this.vmServiceInfoFile))
339+
return;
340+
341+
try {
342+
const serviceInfoJson = fs.readFileSync(this.vmServiceInfoFile, "utf8");
343+
const serviceInfo: { uri: string } = JSON.parse(serviceInfoJson);
344+
345+
const url = new URL(serviceInfo.uri);
346+
url.protocol = "ws";
347+
// Ensure we no trailing /
348+
if (url.pathname.endsWith("/"))
349+
url.pathname = url.pathname.substr(0, url.pathname.length - 1);
350+
// Ensure we always end with /ws
351+
if (!url.pathname.endsWith("/ws"))
352+
url.pathname = `${url.pathname}/ws`;
353+
354+
this.stopServiceFilePolling();
355+
this.logToUser(`${this.vmServiceInfoFile}\n`);
356+
this.logToUser(url.toString());
357+
this.initDebugger(url.toString());
358+
} catch (e) {
359+
this.logger.error(e, LogCategory.Observatory);
360+
}
361+
}
362+
296363
protected async initDebugger(uri: string): Promise<void> {
297364
// Send the uri back to the editor so it can be used to launch browsers etc.
298365
let browserFriendlyUri: string;
@@ -457,6 +524,7 @@ export class DartDebugSession extends DebugSession {
457524
const signal = force ? "SIGKILL" : "SIGINT";
458525
const request = force ? "DISC" : "TERM";
459526
this.log(`${request}: Requested to terminate with ${signal}...`);
527+
this.stopServiceFilePolling();
460528
if (this.shouldKillProcessOnTerminate && this.childProcess && !this.processExited) {
461529
this.log(`${request}: Terminating processes...`);
462530
for (const pid of this.additionalPidsToTerminate) {

‎src/debug/utils.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface DartLaunchRequestArguments extends DebugProtocol.LaunchRequestA
3636
debuggerHandlesPathsEverywhereForBreakpoints: boolean;
3737
evaluateGettersInDebugViews: boolean;
3838
previewToStringInDebugViews: boolean;
39+
useWriteServiceInfo: boolean;
3940
env: any;
4041
program: string;
4142
args: string[];

‎src/extension/providers/debug_config_provider.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,9 @@ export class DebugConfigProvider implements DebugConfigurationProvider {
473473
: this.dartCapabilities.handlesPathsEverywhereForBreakpoints;
474474
debugConfig.evaluateGettersInDebugViews = debugConfig.evaluateGettersInDebugViews || conf.evaluateGettersInDebugViews;
475475
debugConfig.previewToStringInDebugViews = debugConfig.previewToStringInDebugViews || config.previewToStringInDebugViews;
476+
debugConfig.useWriteServiceInfo = debugConfig.useWriteServiceInfo !== undefined && debugConfig.useWriteServiceInfo !== null
477+
? debugConfig.useWriteServiceInfo
478+
: this.dartCapabilities.supportsWriteServiceInfo;
476479
if (isFlutter && this.sdks.flutter) {
477480
debugConfig.args = conf.flutterAdditionalArgs.concat(debugConfig.args);
478481
debugConfig.forceFlutterVerboseMode = isLogging || isCI;

‎src/extension/sdk/capabilities.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export class DartCapabilities {
1616
get hasDocumentationInCompletions() { return !versionIsAtLeast(this.version, "2.5.1"); }
1717
get handlesPathsEverywhereForBreakpoints() { return versionIsAtLeast(this.version, "2.2.1-edge"); }
1818
get supportsDisableServiceTokens() { return versionIsAtLeast(this.version, "2.2.1-dev.4.2"); }
19+
get supportsWriteServiceInfo() { return versionIsAtLeast(this.version, "2.7.1"); }
1920
// TODO: Update this (along with Flutter) when supported.
2021
get webSupportsEvaluation() { return false; }
2122
get webSupportsDebugging() { return false; }

0 commit comments

Comments
 (0)