11import * as fs from "fs" ;
2+ import * as os from "os" ;
23import * as path from "path" ;
4+ import { URL } from "url" ;
35import { DebugSession , Event , InitializedEvent , OutputEvent , Scope , Source , StackFrame , StoppedEvent , TerminatedEvent } from "vscode-debugadapter" ;
46import { DebugProtocol } from "vscode-debugprotocol" ;
57import { getLogHeader } from "../extension/utils/log" ;
@@ -12,6 +14,7 @@ import { PackageMap } from "../shared/pub/package_map";
1214import { errorString , flatMap , notUndefined , throttle , uniq , uriToFilePath } from "../shared/utils" ;
1315import { sortBy } from "../shared/utils/array" ;
1416import { applyColor , grey , grey2 } from "../shared/utils/colors" ;
17+ import { getRandomInt } from "../shared/utils/fs" ;
1518import { 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" ;
1619import { DebugAdapterLogger } from "./logging" ;
1720import { 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 ) {
0 commit comments