@@ -51,8 +51,9 @@ const TWO_CRLF = '\r\n\r\n';
5151// start method is called.
5252export class ProxyDebugAdapter implements vscode . DebugAdapter {
5353 private messageEmitter = new vscode . EventEmitter < vscode . DebugProtocolMessage > ( ) ;
54- // connection to server.
55- private conn ?: stream . Duplex ;
54+ // connection from/to server (= dlv dap)
55+ private readable ?: stream . Readable ;
56+ private writable ?: stream . Writable ;
5657
5758 constructor ( ) {
5859 this . onDidSendMessage = this . messageEmitter . event ;
@@ -63,13 +64,6 @@ export class ProxyDebugAdapter implements vscode.DebugAdapter {
6364 // listen on onDidSendMessage to receive messages.
6465 onDidSendMessage : vscode . Event < vscode . DebugProtocolMessage > ;
6566 async handleMessage ( message : vscode . DebugProtocolMessage ) : Promise < void > {
66- // TODO(hyangah): dlv dap often terminates before us
67- // receiving the disconnect response, which causes
68- // vscode to hang forever. Either generate a disconnect
69- // respond after timeout so vscode completes the normal
70- // debug session teardown including the call to this
71- // thin adapter's dispose() or change dlv dap not to
72- // kill itself until the client connection is closed.
7367 await this . sendMessageToServer ( message ) ;
7468 }
7569
@@ -79,46 +73,45 @@ export class ProxyDebugAdapter implements vscode.DebugAdapter {
7973 }
8074 protected sendMessageToServer ( message : vscode . DebugProtocolMessage ) : void {
8175 const json = JSON . stringify ( message ) ?? '' ;
82- if ( this . conn ) {
83- this . conn . write ( `Content-Length: ${ Buffer . byteLength ( json , 'utf8' ) } ${ TWO_CRLF } ${ json } ` , 'utf8' , ( err ) => {
84- if ( err ) {
85- console . log ( `error sending message: ${ err } ` ) ;
86- this . sendMessageToClient ( new TerminatedEvent ( ) ) ;
76+ if ( this . writable ) {
77+ this . writable . write (
78+ `Content-Length: ${ Buffer . byteLength ( json , 'utf8' ) } ${ TWO_CRLF } ${ json } ` ,
79+ 'utf8' ,
80+ ( err ) => {
81+ if ( err ) {
82+ console . log ( `error sending message: ${ err } ` ) ;
83+ this . sendMessageToClient ( new TerminatedEvent ( ) ) ;
84+ }
8785 }
88- } ) ;
86+ ) ;
8987 } else {
9088 console . log ( `stream is closed; dropping ${ json } ` ) ;
9189 }
9290 }
9391
94- public async start ( server : stream . Duplex ) {
95- if ( this . conn ) {
92+ public async start ( readable : stream . Readable , writable : stream . Writable ) {
93+ if ( this . readable || this . writable ) {
9694 throw new Error ( 'start was called more than once' ) ;
9795 }
98- this . conn = server ;
99- this . conn . on ( 'data' , ( data : Buffer ) => {
96+ this . readable = readable ;
97+ this . writable = writable ;
98+ this . readable . on ( 'data' , ( data : Buffer ) => {
10099 this . handleDataFromServer ( data ) ;
101100 } ) ;
102- this . conn . once ( 'close' , ( ) => {
103- console . log ( 'stream closed' ) ;
104- this . conn . destroy ( ) ;
105- this . conn = undefined ;
101+ this . readable . once ( 'close' , ( ) => {
102+ this . readable = undefined ;
106103 } ) ;
107- this . conn . on ( 'error' , ( err ) => {
108- console . log ( `stream error: ${ err } ` ) ;
104+ this . readable . on ( 'error' , ( err ) => {
109105 if ( err ) {
106+ console . log ( `stream error: ${ err } ` ) ;
110107 this . sendMessageToClient ( new OutputEvent ( `socket to network closed: ${ err } ` , 'console' ) ) ;
111108 }
112109 this . sendMessageToClient ( new TerminatedEvent ( ) ) ;
113110 } ) ;
114111 }
115112
116113 async dispose ( ) {
117- if ( this . conn ) {
118- // TODO(hyangah): not sure if sleep is necessary.
119- await sleep ( 500 ) ;
120- this . conn ?. destroy ( ) ;
121- }
114+ this . writable ?. end ( ) ; // no more write.
122115 }
123116
124117 private rawData = Buffer . alloc ( 0 ) ;
@@ -163,10 +156,6 @@ export class ProxyDebugAdapter implements vscode.DebugAdapter {
163156 }
164157}
165158
166- function sleep ( ms : number ) {
167- return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
168- }
169-
170159// DelveDAPOutputAdapter is a ProxyDebugAdapter that proxies between
171160// VSCode and a dlv dap process spawned and managed by this adapter.
172161// It turns the process's stdout/stderrr into OutputEvent.
@@ -189,12 +178,27 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
189178 }
190179
191180 async dispose ( ) {
192- console . log ( `DelveDAPOutputAdapter.dispose ${ this . dlvDapServer ?. pid } ` ) ;
193- super . dispose ( ) ;
194- if ( this . connected ) {
195- killProcessTree ( this . dlvDapServer ) ;
196- this . connected = undefined ;
181+ await super . dispose ( ) ;
182+
183+ if ( this . connected === undefined ) {
184+ return ;
197185 }
186+ this . connected = undefined ;
187+ const dlvDapServer = this . dlvDapServer ;
188+ if ( dlvDapServer . exitCode !== null ) {
189+ return ;
190+ }
191+ await new Promise < void > ( ( resolve ) => {
192+ const exitTimeoutToken = setTimeout ( ( ) => {
193+ console . log ( `killing dlv dap process(${ dlvDapServer . pid } ) after 1sec` ) ;
194+ killProcessTree ( dlvDapServer ) ;
195+ resolve ( ) ;
196+ } , 1_000 ) ;
197+ dlvDapServer . on ( 'exit' , ( ) => {
198+ clearTimeout ( exitTimeoutToken ) ;
199+ resolve ( ) ;
200+ } ) ;
201+ } ) ;
198202 }
199203
200204 private async startAndConnectToServer ( ) {
@@ -212,16 +216,14 @@ export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
212216 } ) ;
213217 timer = setTimeout ( ( ) => {
214218 reject ( 'connection timeout' ) ;
215- console . log ( 'failed to connect within 1s' ) ;
216219 s ?. destroy ( ) ;
217- killProcessTree ( dlvDapServer ) ;
218220 } , 1000 ) ;
219221 } ) ;
220222
221223 this . dlvDapServer = dlvDapServer ;
222224 this . port = port ;
223225 this . socket = socket ;
224- this . start ( this . socket ) ;
226+ this . start ( this . socket , this . socket ) ;
225227 }
226228
227229 stdoutEvent ( output : string , data ?: any ) {
@@ -330,6 +332,12 @@ async function spawnDlvDapServerProcess(
330332
331333 p . stdout . on ( 'data' , ( chunk ) => {
332334 if ( ! started ) {
335+ // TODO(hyangah): when --log-dest is specified, the following message
336+ // will be written to the log dest file, not stdout/stderr.
337+ // Either disable --log-dest, or take advantage of it, i.e.,
338+ // always pass a file descriptor to --log-dest, watch the file
339+ // descriptor to process the log output, and also swap os.Stdout/os.Stderr
340+ // in dlv dap for launch requests to generate proper OutputEvents.
333341 if ( chunk . toString ( ) . startsWith ( 'DAP server listening at:' ) ) {
334342 stopWaitingForServerToStart ( ) ;
335343 } else {
@@ -347,13 +355,12 @@ async function spawnDlvDapServerProcess(
347355 logErr ( chunk . toString ( ) ) ;
348356 } ) ;
349357 p . on ( 'close' , ( code ) => {
358+ // TODO: should we watch 'exit' instead?
350359 if ( ! started ) {
351360 stopWaitingForServerToStart ( `dlv dap closed with code: '${ code } ' signal: ${ p . killed } ` ) ;
352361 }
353362 if ( code ) {
354363 logErr ( `Process exiting with code: ${ code } signal: ${ p . killed } ` ) ;
355- } else {
356- log ( `Process exited normally: ${ p . killed } ` ) ;
357364 }
358365 } ) ;
359366 p . on ( 'error' , ( err ) => {
0 commit comments