Skip to content

Commit 205dfaa

Browse files
committed
Make data breakpoints sort of work
Works with CodeLLDB mostly for named things. Expressions don't seem to be supported by anyone. Fixed some state issues with the connections being retained across restarts in watches. ugh. so messy. Added access type question.
1 parent 30dafd8 commit 205dfaa

File tree

8 files changed

+112
-50
lines changed

8 files changed

+112
-50
lines changed

python3/vimspector/breakpoints.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -495,23 +495,21 @@ def _ClearServerBreakpointData( self, conn: DebugAdapterConnection ):
495495
if not bp[ 'server_bp' ]:
496496
del bp[ 'server_bp' ]
497497

498-
499498
# Clear all instruction breakpoints because they aren't truly portable
500499
# across sessions.
501-
#
502-
# TODO: It might be possible to re-resolve the address stored in the
503-
# breakpoint, though this would only work in a limited way (as load
504-
# addresses will frequently not be the same across runs)
505-
506500

507-
def ShouldKeep( bp ):
501+
def ShouldKeepInsBP( bp ):
508502
if not bp[ 'is_instruction_breakpoint' ]:
509503
return True
510504
if 'address' in bp and bp[ 'session_id' ] != conn.GetSessionId():
511505
return True
512506
return False
513507

514-
breakpoints[ : ] = [ bp for bp in breakpoints if ShouldKeep( bp ) ]
508+
breakpoints[ : ] = [ bp for bp in breakpoints if ShouldKeepInsBP( bp ) ]
509+
510+
# Erase any data breakpoints for this connection too
511+
self._data_breakponts[ : ] = [ bp for bp in self._data_breakponts
512+
if bp[ 'conn' ] != conn.GetSessionId() ]
515513

516514

517515
def _CopyServerLineBreakpointProperties( self,
@@ -1001,12 +999,11 @@ def response_handler( conn, msg, bp_idxs = [] ):
1001999
'supportsDataBreakpoints' ]:
10021000
connection: DebugAdapterConnection
10031001
for connection in self._connections:
1004-
bp_idxs = []
10051002
breakpoints = []
10061003
for bp in self._data_breakponts:
10071004
if bp[ 'state' ] != 'ENABLED':
10081005
continue
1009-
if bp[ 'conn' ] == connection.GetSessionId():
1006+
if bp[ 'conn' ] != connection.GetSessionId():
10101007
continue
10111008
if not bp[ 'info' ].get( 'dataId' ):
10121009
continue
@@ -1015,18 +1012,16 @@ def response_handler( conn, msg, bp_idxs = [] ):
10151012
data_bp.update( bp[ 'options' ] )
10161013
data_bp[ 'dataId' ] = bp[ 'info' ][ 'dataId' ]
10171014
breakpoints.append( data_bp )
1018-
bp_idxs.append( [ len( breakpoints ), bp ] )
10191015

10201016
if breakpoints:
10211017
self._awaiting_bp_responses += 1
10221018
connection.DoRequest(
1023-
lambda msg, conn=connection, idxs=bp_idxs: response_handler(
1024-
conn,
1025-
msg,
1026-
idxs ),
1019+
lambda msg, conn=connection: response_handler( conn, msg ),
10271020
{
10281021
'command': 'setDataBreakpoints',
1029-
'arguments': breakpoints,
1022+
'arguments': {
1023+
'breakpoints': breakpoints,
1024+
},
10301025
},
10311026
failure_handler = response_received
10321027
)

python3/vimspector/debug_session.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -998,19 +998,29 @@ def OnDisassemblyWindowScrolled( self, win_id ):
998998
self._disassemblyView.OnWindowScrolled( win_id )
999999

10001000

1001-
@CurrentSession()
1001+
@ParentOnly()
10021002
@IfConnected()
10031003
def AddDataBreakpoint( self, opts, buf = None, line_num = None ):
1004-
def add_bp( breakpoint_info ):
1004+
# Use the parent session, because the _connection_ comes from the
1005+
# variable/watch result that is actually chosen
1006+
1007+
def add_bp( conn, breakpoint_info ):
10051008
if breakpoint_info[ 'dataId' ] is None:
10061009
utils.UserMessage(
1007-
"Can't set data breakpoint here: { breakpoint_info[ 'description' ] }"
1010+
f"Can't set data breakpoint here: {breakpoint_info[ 'description' ]}"
10081011
)
10091012
return
10101013

1011-
# TODO: Ask the user about the possible DataBreakpointAccessType's and add
1012-
# that in to opts here
1013-
self._breakpoints.AddDataBreakpoint( breakpoint_info[ 'dataId' ], opts )
1014+
access_types = breakpoint_info.get( 'accessTypes' )
1015+
if access_types and 'accessType' not in opts:
1016+
access_type = utils.SelectFromList( 'What type of access?',
1017+
access_types )
1018+
if access_type is not None:
1019+
opts[ 'accessType' ] = access_type
1020+
1021+
self._breakpoints.AddDataBreakpoint( conn,
1022+
breakpoint_info,
1023+
opts )
10141024

10151025
self._variablesView.GetDataBreakpointInfo( add_bp, buf, line_num )
10161026

python3/vimspector/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
'delete': [ '<Del>' ],
9292
'set_value': [ '<C-CR>', '<leader><CR>' ],
9393
'read_memory': [ '<leader>m' ],
94+
'add_data_breakpoint': [ '<F9>' ],
9495
},
9596
'stack_trace': {
9697
'expand_or_jump': [ '<CR>', '<2-LeftMouse>' ],

python3/vimspector/variables.py

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from functools import partial
2020
import typing
2121

22-
from vimspector import utils, settings
22+
from vimspector import utils, settings, session_manager
2323
from vimspector.debug_adapter_connection import DebugAdapterConnection
2424

2525

@@ -67,7 +67,7 @@ def FrameID( self ):
6767
assert False
6868

6969
@abc.abstractmethod
70-
def EvaluateName( self ):
70+
def Name( self ):
7171
assert False
7272

7373
@abc.abstractmethod
@@ -78,6 +78,9 @@ def MemoryReference( self ):
7878
def HoverText( self ):
7979
return ""
8080

81+
def Update( self, connection ):
82+
self.connection = connection
83+
8184

8285
class Scope( Expandable ):
8386
"""Holds an expandable scope (a DAP scope dict), with expand/collapse state"""
@@ -94,10 +97,11 @@ def MemoryReference( self ):
9497
def FrameID( self ):
9598
return None
9699

97-
def EvaluateName( self ):
100+
def Name( self ):
98101
return self.scope[ 'name' ]
99102

100-
def Update( self, scope ):
103+
def Update( self, connection, scope ):
104+
super().Update( connection )
101105
self.scope = scope
102106

103107
def HoverText( self ):
@@ -125,10 +129,11 @@ def MemoryReference( self ):
125129
def FrameID( self ):
126130
return self.watch.expression.get( 'frameId' )
127131

128-
def EvaluateName( self ):
132+
def Name( self ):
129133
return self.watch.expression.get( 'expression' )
130134

131-
def Update( self, result ):
135+
def Update( self, connection, result ):
136+
super().Update( connection )
132137
self.changed = False
133138
if self.result[ 'result' ] != result[ 'result' ]:
134139
self.changed = True
@@ -145,8 +150,8 @@ def HoverText( self ):
145150

146151

147152
class WatchFailure( WatchResult ):
148-
def __init__( self, connection: DebugAdapterConnection, reason ):
149-
super().__init__( connection, { 'result': reason } )
153+
def __init__( self, connection: DebugAdapterConnection, watch, reason ):
154+
super().__init__( connection, watch, { 'result': reason } )
150155
self.changed = True
151156

152157

@@ -170,10 +175,11 @@ def MemoryReference( self ):
170175
def FrameID( self ):
171176
return self.container.FrameID()
172177

173-
def EvaluateName( self ):
174-
return self.variable.get( 'evaluateName', self.variable[ 'name' ] )
178+
def Name( self ):
179+
return self.variable[ 'name' ]
175180

176-
def Update( self, variable ):
181+
def Update( self, connection, variable ):
182+
super().Update( connection )
177183
self.changed = False
178184
if self.variable[ 'value' ] != variable[ 'value' ]:
179185
self.changed = True
@@ -201,6 +207,11 @@ def __init__( self, connection: DebugAdapterConnection, expression: dict ):
201207
self.result = None
202208

203209
def SetCurrentFrame( self, connection, frame ):
210+
if connection is None:
211+
self.connection = None
212+
self.result.connection = None
213+
return
214+
204215
if self.connection is None:
205216
self.connection = connection
206217
elif self.connection != connection:
@@ -253,6 +264,9 @@ def AddExpandMappings( mappings = None ):
253264
for mapping in utils.GetVimList( mappings, 'read_memory' ):
254265
vim.command( f'nnoremap <silent> <buffer> { mapping } '
255266
':<C-u>call vimspector#ReadMemory()<CR>' )
267+
for mapping in utils.GetVimList( mappings, 'add_data_breakpoint' ):
268+
vim.command( f'nnoremap <silent> <buffer> { mapping } '
269+
':<C-u>call vimspector#AddDataBreakpoint()<CR>' )
256270

257271

258272

@@ -348,7 +362,7 @@ def ConnectionClosed( self, connection ):
348362
]
349363
for w in self._watches:
350364
if w.connection == connection:
351-
w.connection = None
365+
w.SetCurrentFrame( None, None )
352366

353367

354368
def Reset( self ):
@@ -388,7 +402,7 @@ def scopes_consumer( message ):
388402
if not found:
389403
scope = Scope( connection, scope_body )
390404
else:
391-
scope.Update( scope_body )
405+
scope.Update( connection, scope_body )
392406

393407
new_scopes.append( scope )
394408

@@ -459,9 +473,9 @@ def handler( message ):
459473

460474
watch = self._variable_eval
461475
if watch.result is None or watch.result.connection != connection:
462-
watch.result = WatchResult( connection, message[ 'body' ] )
476+
watch.result = WatchResult( connection, watch, message[ 'body' ] )
463477
else:
464-
watch.result.Update( message[ 'body' ] )
478+
watch.result.Update( connection, message[ 'body' ] )
465479

466480
popup_win_id = utils.CreateTooltip( [], is_hover )
467481
# record the global eval window id
@@ -566,10 +580,10 @@ def EvaluateWatches( self,
566580

567581
def _UpdateWatchExpression( self, watch: Watch, message: dict ):
568582
if watch.result is not None:
569-
watch.result.Update( message[ 'body' ] )
583+
watch.result.Update( watch.connection, message[ 'body' ] )
570584
else:
571585
watch.result = WatchResult( watch.connection,
572-
watch[ 'frameId' ],
586+
watch,
573587
message[ 'body' ] )
574588

575589
if ( watch.result.IsExpandable() and
@@ -590,7 +604,7 @@ def _WatchExpressionFailed( self, reason: str, watch: Watch ):
590604
# We already have a result for this watch. Wut ?
591605
return
592606

593-
watch.result = WatchFailure( watch.connection, reason )
607+
watch.result = WatchFailure( watch.connection, watch, reason )
594608
self._DrawWatches()
595609

596610
def _GetVariable( self, buf = None, line_num = None ):
@@ -682,7 +696,7 @@ def handler( message ):
682696
},
683697
} )
684698

685-
variable.Update( new_variable )
699+
variable.Update( variable.connection, new_variable )
686700
view.draw()
687701

688702
def failure_handler( reason, message ):
@@ -714,15 +728,17 @@ def GetDataBreakpointInfo( self,
714728
variable: Expandable
715729
view: View
716730

717-
if not self._server_capabilities.get( 'supportsDataBreakpoints' ):
718-
return None
719-
720731
variable, view = self._GetVariable( buf, line_num )
721732
if variable is None:
722733
return None
723734

735+
if not session_manager.Get().GetSession(
736+
variable.connection.GetSessionId() )._server_capabilities.get(
737+
'supportsDataBreakpoints' ):
738+
return None
739+
724740
arguments = {
725-
'name': variable.EvaluateName()
741+
'name': variable.Name()
726742
}
727743
frameId = variable.FrameID()
728744
if frameId:
@@ -732,7 +748,8 @@ def GetDataBreakpointInfo( self,
732748
arguments[ 'variablesReference' ] = (
733749
variable.container.VariablesReference() )
734750

735-
variable.connection.DoRequest( lambda msg: then( msg[ 'body' ] ), {
751+
variable.connection.DoRequest( lambda msg: then( variable.connection,
752+
msg[ 'body' ] ), {
736753
'command': 'dataBreakpointInfo',
737754
'arguments': arguments,
738755
} )
@@ -886,7 +903,7 @@ def _ConsumeVariables( self, draw, parent, message ):
886903
if not found:
887904
variable = Variable( parent.connection, parent, variable_body )
888905
else:
889-
variable.Update( variable_body )
906+
variable.Update( parent.connection, variable_body )
890907

891908
new_variables.append( variable )
892909

support/test/cpp/simple_c_program/.vimspector.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"adapter": "vscode-cpptools",
5757
"variables": {
5858
"BUILDME": {
59-
"shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${workspaceRoot}/test_c.cpp"
59+
"shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${file}"
6060
},
6161
"arch": {
6262
"shell": "uname -m"
@@ -78,7 +78,7 @@
7878
"adapter": "vscode-cpptools",
7979
"variables": {
8080
"BUILDME": {
81-
"shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${workspaceRoot}/test_c.cpp"
81+
"shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${file}"
8282
}
8383
},
8484
"configuration": {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
struct Test
2+
{
3+
int x;
4+
int y;
5+
};
6+
7+
int main( int argc , char ** argv )
8+
{
9+
Test x[] = {
10+
{ 1, 2 },
11+
{ 3, 4 },
12+
{ 5, 6 },
13+
};
14+
15+
Test y = { 7, 8 };
16+
17+
x[0].x += argc;
18+
argv[ 0 ][ 0 ] = 'x' ;
19+
20+
y.x += **argv;
21+
y.y += argc * **argv;
22+
23+
return argc;
24+
}

support/test/rust/vimspector_test/.vimspector.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"adapter": "CodeLLDB",
66
"configuration": {
77
"request": "launch",
8-
"program": "${workspaceRoot}/target/debug/vimspector_test"
8+
"program": "${workspaceRoot}/target/debug/vimspector_test",
9+
"expressions": "native"
910
}
1011
}
1112
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1+
struct Point {
2+
x: i32,
3+
y: i32,
4+
}
5+
16
fn main() {
27
let s = "World!";
38
println!("Hello, {}!", s);
9+
10+
let mut p = Point{ x: 1, y: 11 };
11+
12+
p.x = 11;
13+
p.y = 11;
14+
p.x += 11;
15+
p.y += 11;
16+
17+
println!("Point: {}, {}", p.x, p.y);
418
}

0 commit comments

Comments
 (0)