@@ -1355,9 +1355,10 @@ func (s *Session) setBreakpoints(prefix string, totalBps int, metadataFunc func(
1355
1355
} else {
1356
1356
got .Cond = want .condition
1357
1357
got .HitCond = want .hitCondition
1358
- got .Tracepoint = want .logMessage != ""
1359
- got .UserData = want .logMessage
1360
- err = s .debugger .AmendBreakpoint (got )
1358
+ err = setLogMessage (got , want .logMessage )
1359
+ if err == nil {
1360
+ err = s .debugger .AmendBreakpoint (got )
1361
+ }
1361
1362
}
1362
1363
createdBps [want .name ] = struct {}{}
1363
1364
s .updateBreakpointsResponse (breakpoints , i , err , got )
@@ -1380,19 +1381,20 @@ func (s *Session) setBreakpoints(prefix string, totalBps int, metadataFunc func(
1380
1381
if _ , ok := createdBps [want .name ]; ok {
1381
1382
err = fmt .Errorf ("breakpoint already exists" )
1382
1383
} else {
1383
- // Create new breakpoints.
1384
- got , err = s .debugger .CreateBreakpoint (
1385
- & api.Breakpoint {
1386
- Name : want .name ,
1387
- File : wantLoc .file ,
1388
- Line : wantLoc .line ,
1389
- Addr : wantLoc .addr ,
1390
- Addrs : wantLoc .addrs ,
1391
- Cond : want .condition ,
1392
- HitCond : want .hitCondition ,
1393
- Tracepoint : want .logMessage != "" ,
1394
- UserData : want .logMessage ,
1395
- })
1384
+ bp := & api.Breakpoint {
1385
+ Name : want .name ,
1386
+ File : wantLoc .file ,
1387
+ Line : wantLoc .line ,
1388
+ Addr : wantLoc .addr ,
1389
+ Addrs : wantLoc .addrs ,
1390
+ Cond : want .condition ,
1391
+ HitCond : want .hitCondition ,
1392
+ }
1393
+ err = setLogMessage (bp , want .logMessage )
1394
+ if err == nil {
1395
+ // Create new breakpoints.
1396
+ got , err = s .debugger .CreateBreakpoint (bp )
1397
+ }
1396
1398
}
1397
1399
}
1398
1400
createdBps [want .name ] = struct {}{}
@@ -1401,6 +1403,18 @@ func (s *Session) setBreakpoints(prefix string, totalBps int, metadataFunc func(
1401
1403
return breakpoints
1402
1404
}
1403
1405
1406
+ func setLogMessage (bp * api.Breakpoint , msg string ) error {
1407
+ tracepoint , userdata , err := parseLogPoint (msg )
1408
+ if err != nil {
1409
+ return err
1410
+ }
1411
+ bp .Tracepoint = tracepoint
1412
+ if userdata != nil {
1413
+ bp .UserData = * userdata
1414
+ }
1415
+ return nil
1416
+ }
1417
+
1404
1418
func (s * Session ) updateBreakpointsResponse (breakpoints []dap.Breakpoint , i int , err error , got * api.Breakpoint ) {
1405
1419
breakpoints [i ].Verified = (err == nil )
1406
1420
if err != nil {
@@ -3601,8 +3615,8 @@ func (s *Session) logBreakpointMessage(bp *api.Breakpoint, goid int) bool {
3601
3615
if ! bp .Tracepoint {
3602
3616
return false
3603
3617
}
3604
- // TODO(suzmue): allow evaluate expressions within log points.
3605
- if msg , ok := bp . UserData .( string ); ok {
3618
+ if lMsg , ok := bp . UserData .( logMessage ); ok {
3619
+ msg := lMsg . evaluate ( s , goid )
3606
3620
s .send (& dap.OutputEvent {
3607
3621
Event : * newEvent ("output" ),
3608
3622
Body : dap.OutputEventBody {
@@ -3618,6 +3632,19 @@ func (s *Session) logBreakpointMessage(bp *api.Breakpoint, goid int) bool {
3618
3632
return true
3619
3633
}
3620
3634
3635
+ func (msg * logMessage ) evaluate (s * Session , goid int ) string {
3636
+ evaluated := make ([]interface {}, len (msg .args ))
3637
+ for i := range msg .args {
3638
+ exprVar , err := s .debugger .EvalVariableInScope (goid , 0 , 0 , msg .args [i ], DefaultLoadConfig )
3639
+ if err != nil {
3640
+ evaluated [i ] = fmt .Sprintf ("{eval err: %e}" , err )
3641
+ continue
3642
+ }
3643
+ evaluated [i ] = s .convertVariableToString (exprVar )
3644
+ }
3645
+ return fmt .Sprintf (msg .format , evaluated ... )
3646
+ }
3647
+
3621
3648
func (s * Session ) toClientPath (path string ) string {
3622
3649
if len (s .args .substitutePathServerToClient ) == 0 {
3623
3650
return path
@@ -3639,3 +3666,64 @@ func (s *Session) toServerPath(path string) string {
3639
3666
}
3640
3667
return serverPath
3641
3668
}
3669
+
3670
+ type logMessage struct {
3671
+ format string
3672
+ args []string
3673
+ }
3674
+
3675
+ // parseLogPoint parses a log message according to the DAP spec:
3676
+ // "Expressions within {} are interpolated."
3677
+ func parseLogPoint (msg string ) (bool , * logMessage , error ) {
3678
+ // Note: All braces *must* come in pairs, even those within an
3679
+ // expression to be interpolated.
3680
+ // TODO(suzmue): support individual braces in string values in
3681
+ // eval expressions.
3682
+ var args []string
3683
+
3684
+ var isArg bool
3685
+ var formatSlice , argSlice []rune
3686
+ braceCount := 0
3687
+ for _ , r := range msg {
3688
+ if isArg {
3689
+ switch r {
3690
+ case '}' :
3691
+ if braceCount -- ; braceCount == 0 {
3692
+ argStr := strings .TrimSpace (string (argSlice ))
3693
+ if len (argStr ) == 0 {
3694
+ return false , nil , fmt .Errorf ("empty evaluation string" )
3695
+ }
3696
+ args = append (args , argStr )
3697
+ formatSlice = append (formatSlice , '%' , 's' )
3698
+ isArg = false
3699
+ continue
3700
+ }
3701
+ case '{' :
3702
+ braceCount += 1
3703
+ }
3704
+ argSlice = append (argSlice , r )
3705
+ continue
3706
+ }
3707
+
3708
+ switch r {
3709
+ case '}' :
3710
+ return false , nil , fmt .Errorf ("invalid log point format, unexpected '}'" )
3711
+ case '{' :
3712
+ if braceCount ++ ; braceCount == 1 {
3713
+ isArg , argSlice = true , []rune {}
3714
+ continue
3715
+ }
3716
+ }
3717
+ formatSlice = append (formatSlice , r )
3718
+ }
3719
+ if isArg {
3720
+ return false , nil , fmt .Errorf ("invalid log point format" )
3721
+ }
3722
+ if len (formatSlice ) == 0 {
3723
+ return false , nil , nil
3724
+ }
3725
+ return true , & logMessage {
3726
+ format : string (formatSlice ),
3727
+ args : args ,
3728
+ }, nil
3729
+ }
0 commit comments