1- import { Breakpoint , IBackend , Stack , Variable , VariableObject , MIError } from '../backend' ;
1+ import { Breakpoint , DataBreakpoint , IBackend , Stack , Variable , VariableObject , MIError } from '../backend' ;
22import * as ChildProcess from 'child_process' ;
33import { EventEmitter } from 'events' ;
44import { parseMI , MINode } from '../mi_parse' ;
@@ -28,8 +28,7 @@ function couldBeOutput(line: string) {
2828const trace = false ;
2929
3030export class MI2 extends EventEmitter implements IBackend {
31- public printCalls : boolean ;
32- public debugOutput : boolean ;
31+ public debugOutput : boolean | 'raw' | 'raw-only' ;
3332 public procEnv : any ;
3433 protected currentToken : number = 1 ;
3534 protected handlers : { [ index : number ] : ( info : MINode ) => any } = { } ;
@@ -140,7 +139,12 @@ export class MI2 extends EventEmitter implements IBackend {
140139 else {
141140 const parsed = parseMI ( line ) ;
142141 if ( this . debugOutput ) {
143- this . log ( 'log' , 'GDB -> App: ' + JSON . stringify ( parsed ) ) ;
142+ if ( ( this . debugOutput === 'raw-only' ) || ( this . debugOutput === 'raw' ) ) {
143+ this . log ( 'log' , '-> ' + line ) ;
144+ }
145+ if ( this . debugOutput !== 'raw-only' ) {
146+ this . log ( 'log' , 'GDB -> App: ' + JSON . stringify ( parsed ) ) ;
147+ }
144148 }
145149 let handled = false ;
146150 if ( parsed . token !== undefined && parsed . resultRecords ) {
@@ -174,6 +178,13 @@ export class MI2 extends EventEmitter implements IBackend {
174178 if ( reason === 'breakpoint-hit' ) {
175179 this . emit ( 'breakpoint' , parsed ) ;
176180 }
181+ else if ( reason && ( reason as string ) . includes ( 'watchpoint-trigger' ) ) {
182+ this . emit ( 'watchpoint' , parsed ) ;
183+ }
184+ else if ( reason && ( reason as string ) . includes ( 'watchpoint-scope' ) ) {
185+ // When a local variable goes out of scope
186+ this . emit ( 'watchpoint-scope' , parsed ) ;
187+ }
177188 else if ( reason === 'end-stepping-range' ) {
178189 this . emit ( 'step-end' , parsed ) ;
179190 }
@@ -244,7 +255,7 @@ export class MI2 extends EventEmitter implements IBackend {
244255 }
245256
246257 private tryKill ( ) {
247- if ( ! this . exited ) {
258+ if ( ! this . exited && this . process ) {
248259 const proc = this . process ;
249260 try {
250261 ServerConsoleLog ( 'GDB kill()' ) ;
@@ -274,10 +285,31 @@ export class MI2 extends EventEmitter implements IBackend {
274285 if ( trace ) {
275286 this . log ( 'stderr' , 'detach' ) ;
276287 }
277- this . sendCommand ( 'target-detach' ) ; // This may not be successful, go ahead and stop gdb as well. Sometimes hangs!
278- setTimeout ( ( ) => {
288+ let to = setTimeout ( ( ) => {
289+ if ( to ) {
290+ ServerConsoleLog ( 'target-detach hung: target probably running, thats okay, continue to stop()' ) ;
291+ to = null ;
292+ this . stop ( ) ;
293+ }
294+ } , 10 ) ;
295+
296+ // Following can hang if no response, or fail because the target is still running. Yes,
297+ // we sometimes detach when target is still running. This also causes unhandled rejection
298+ // warning/error from Node, so handle rejections.
299+ this . sendCommand ( 'target-detach' ) . then ( ( ) => {
300+ if ( to ) {
301+ clearTimeout ( to ) ;
302+ to = null ;
303+ }
279304 this . stop ( ) ;
280- } , 5 ) ;
305+ } , ( e ) => {
306+ if ( to ) {
307+ clearTimeout ( to ) ;
308+ to = null ;
309+ }
310+ ServerConsoleLog ( 'target-detach failed: target probably running, thats okay, continue to stop()' ) ;
311+ this . stop ( ) ;
312+ } ) ;
281313 }
282314
283315 public interrupt ( arg : string = '' ) : Thenable < boolean > {
@@ -473,6 +505,57 @@ export class MI2 extends EventEmitter implements IBackend {
473505 } ) ;
474506 }
475507
508+ public addDataBreakPoint ( breakpoint : DataBreakpoint ) : Promise < DataBreakpoint > {
509+ if ( trace ) {
510+ this . log ( 'stderr' , 'addBreakPoint' ) ;
511+ }
512+ return new Promise ( ( resolve , reject ) => {
513+ let location = '' ;
514+ if ( breakpoint . countCondition ) {
515+ if ( breakpoint . countCondition [ 0 ] === '>' ) {
516+ location += '-i ' + numRegex . exec ( breakpoint . countCondition . substr ( 1 ) ) [ 0 ] + ' ' ;
517+ }
518+ else {
519+ const match = numRegex . exec ( breakpoint . countCondition ) [ 0 ] ;
520+ if ( match . length !== breakpoint . countCondition . length ) {
521+ // tslint:disable-next-line:max-line-length
522+ this . log ( 'stderr' , 'Unsupported break count expression: \'' + breakpoint . countCondition + '\'. Only supports \'X\' for breaking once after X times or \'>X\' for ignoring the first X breaks' ) ;
523+ location += '-t ' ;
524+ }
525+ else if ( parseInt ( match ) !== 0 ) {
526+ location += '-t -i ' + parseInt ( match ) + ' ' ;
527+ }
528+ }
529+ }
530+
531+ location += breakpoint . exp ;
532+ const aType = breakpoint . accessType === 'read' ? '-r' : ( breakpoint . accessType === 'readWrite' ? '-a' : '' ) ;
533+ this . sendCommand ( `break-watch ${ aType } ${ location } ` ) . then ( ( result ) => {
534+ if ( result . resultRecords . resultClass === 'done' ) {
535+ const bkptNum = parseInt ( result . result ( 'bkpt.number' ) ) ;
536+ const line = result . result ( 'bkpt.line' ) ;
537+ breakpoint . number = bkptNum ;
538+
539+ if ( breakpoint . condition ) {
540+ this . setBreakPointCondition ( bkptNum , breakpoint . condition ) . then ( ( result ) => {
541+ if ( result . resultRecords . resultClass === 'done' ) {
542+ resolve ( breakpoint ) ;
543+ } else {
544+ reject ( new MIError ( result . result ( 'msg' ) || 'Internal error' , 'Setting breakpoint condition' ) ) ;
545+ }
546+ } , reject ) ;
547+ }
548+ else {
549+ resolve ( breakpoint ) ;
550+ }
551+ }
552+ else {
553+ reject ( new MIError ( result . result ( 'msg' ) || 'Internal error' , `Setting breakpoint at ${ location } ` ) ) ;
554+ }
555+ } , reject ) ;
556+ } ) ;
557+ }
558+
476559 public getFrame ( thread : number , frame : number ) : Thenable < Stack > {
477560 return new Promise ( ( resolve , reject ) => {
478561 const command = `stack-info-frame --thread ${ thread } --frame ${ frame } ` ;
@@ -600,7 +683,7 @@ export class MI2 extends EventEmitter implements IBackend {
600683 x : 'hexadecimal'
601684 } ;
602685
603- public async varCreate ( expression : string , name : string = '-' , scope : string = '@' ) : Promise < VariableObject > {
686+ public async varCreate ( parent : number , expression : string , name : string = '-' , scope : string = '@' ) : Promise < VariableObject > {
604687 if ( trace ) {
605688 this . log ( 'stderr' , 'varCreate' ) ;
606689 }
@@ -623,7 +706,7 @@ export class MI2 extends EventEmitter implements IBackend {
623706 if ( overrideVal ) {
624707 result = result . map ( ( r : string [ ] ) => r [ 0 ] === 'value' ? [ 'value' , overrideVal ] : r ) ;
625708 }
626- return new VariableObject ( result ) ;
709+ return new VariableObject ( parent , result ) ;
627710 }
628711
629712 public async varEvalExpression ( name : string ) : Promise < MINode > {
@@ -633,7 +716,7 @@ export class MI2 extends EventEmitter implements IBackend {
633716 return this . sendCommand ( `var-evaluate-expression ${ name } ` ) ;
634717 }
635718
636- public async varListChildren ( name : string , flattenAnonymous : boolean ) : Promise < VariableObject [ ] > {
719+ public async varListChildren ( parent : number , name : string , flattenAnonymous : boolean ) : Promise < VariableObject [ ] > {
637720 if ( trace ) {
638721 this . log ( 'stderr' , 'varListChildren' ) ;
639722 }
@@ -642,9 +725,9 @@ export class MI2 extends EventEmitter implements IBackend {
642725 const children = res . result ( 'children' ) || [ ] ;
643726 const omg : VariableObject [ ] = [ ] ;
644727 for ( const item of children ) {
645- const child = new VariableObject ( item [ 1 ] ) ;
728+ const child = new VariableObject ( parent , item [ 1 ] ) ;
646729 if ( flattenAnonymous && child . exp . startsWith ( '<anonymous ' ) ) {
647- omg . push ( ... await this . varListChildren ( child . name , flattenAnonymous ) ) ;
730+ omg . push ( ... await this . varListChildren ( parent , child . name , flattenAnonymous ) ) ;
648731 } else {
649732 omg . push ( child ) ;
650733 }
@@ -692,7 +775,7 @@ export class MI2 extends EventEmitter implements IBackend {
692775 }
693776
694777 public sendRaw ( raw : string ) {
695- if ( this . printCalls || trace ) {
778+ if ( this . debugOutput || trace ) {
696779 this . log ( 'log' , raw ) ;
697780 }
698781 if ( raw . includes ( 'undefined' ) ) {
0 commit comments