Skip to content

Commit 0a69868

Browse files
committed
Data breakpoints
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. Java debug adapter returns broken breakpoints info response
1 parent ecb4669 commit 0a69868

File tree

13 files changed

+294
-26
lines changed

13 files changed

+294
-26
lines changed

autoload/vimspector.vim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,14 @@ function! vimspector#ShowDisassembly( ... ) abort
439439
py3 _vimspector_session.ShowDisassembly()
440440
endfunction
441441

442+
function! vimspector#AddDataBreakpoint( ... ) abort
443+
if !s:Enabled()
444+
return
445+
endif
446+
" TODO: how to set options?
447+
py3 _vimspector_session.AddDataBreakpoint( {} )
448+
endfunction
449+
442450
function! vimspector#DeleteWatch() abort
443451
if !s:Enabled()
444452
return

python3/vimspector/breakpoints.py

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ def __init__( self,
219219
self._func_breakpoints = []
220220
self._exception_breakpoints = None
221221
self._configured_breakpoints = {}
222+
self._data_breakponts = []
222223

223224
self._server_capabilities = {}
224225

@@ -523,23 +524,21 @@ def _ClearServerBreakpointData( self, conn: DebugAdapterConnection ):
523524
if not bp[ 'server_bp' ]:
524525
del bp[ 'server_bp' ]
525526

526-
527527
# Clear all instruction breakpoints because they aren't truly portable
528528
# across sessions.
529-
#
530-
# TODO: It might be possible to re-resolve the address stored in the
531-
# breakpoint, though this would only work in a limited way (as load
532-
# addresses will frequently not be the same across runs)
533529

534-
535-
def ShouldKeep( bp ):
530+
def ShouldKeepInsBP( bp ):
536531
if not bp[ 'is_instruction_breakpoint' ]:
537532
return True
538533
if 'address' in bp and bp[ 'session_id' ] != conn.GetSessionId():
539534
return True
540535
return False
541536

542-
breakpoints[ : ] = [ bp for bp in breakpoints if ShouldKeep( bp ) ]
537+
breakpoints[ : ] = [ bp for bp in breakpoints if ShouldKeepInsBP( bp ) ]
538+
539+
# Erase any data breakpoints for this connection too
540+
self._data_breakponts[ : ] = [ bp for bp in self._data_breakponts
541+
if bp[ 'conn' ] != conn.GetSessionId() ]
543542

544543

545544
def _CopyServerLineBreakpointProperties( self,
@@ -807,7 +806,19 @@ def AddFunctionBreakpoint( self, function, options ):
807806
# 'condition': ...,
808807
# 'hitCondition': ...,
809808
} )
809+
self.UpdateUI()
810+
810811

812+
def AddDataBreakpoint( self,
813+
conn: DebugAdapterConnection,
814+
info,
815+
options ):
816+
self._data_breakponts.append( {
817+
'state': 'ENABLED',
818+
'conn': conn.GetSessionId(),
819+
'info': info,
820+
'options': options
821+
} )
811822
self.UpdateUI()
812823

813824

@@ -1014,6 +1025,37 @@ def response_handler( conn, msg, bp_idxs = [] ):
10141025
failure_handler = response_received
10151026
)
10161027

1028+
if self._data_breakponts and self._server_capabilities[
1029+
'supportsDataBreakpoints' ]:
1030+
connection: DebugAdapterConnection
1031+
for connection in self._connections:
1032+
breakpoints = []
1033+
for bp in self._data_breakponts:
1034+
if bp[ 'state' ] != 'ENABLED':
1035+
continue
1036+
if bp[ 'conn' ] != connection.GetSessionId():
1037+
continue
1038+
if not bp[ 'info' ].get( 'dataId' ):
1039+
continue
1040+
1041+
data_bp = {}
1042+
data_bp.update( bp[ 'options' ] )
1043+
data_bp[ 'dataId' ] = bp[ 'info' ][ 'dataId' ]
1044+
breakpoints.append( data_bp )
1045+
1046+
if breakpoints:
1047+
self._awaiting_bp_responses += 1
1048+
connection.DoRequest(
1049+
lambda msg, conn=connection: response_handler( conn, msg ),
1050+
{
1051+
'command': 'setDataBreakpoints',
1052+
'arguments': {
1053+
'breakpoints': breakpoints,
1054+
},
1055+
},
1056+
failure_handler = response_received
1057+
)
1058+
10171059
if self._exception_breakpoints:
10181060
for connection in self._connections:
10191061
self._awaiting_bp_responses += 1
@@ -1112,6 +1154,11 @@ def Save( self ):
11121154
if bps:
11131155
line[ file_name ] = bps
11141156

1157+
# TODO: Some way to persis data breakpoints? Currently they require
1158+
# variablesReference, which is clearly not something that can be persisted
1159+
#
1160+
# That said, the spec now seems to support data bps on expressions, though i
1161+
# can't see any servers which support that.
11151162
return {
11161163
'line': line,
11171164
'function': self._func_breakpoints,
@@ -1183,6 +1230,11 @@ def _HideBreakpoints( self ):
11831230
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
11841231
del bp[ 'sign_id' ]
11851232

1233+
# TODO could/should we show a sign in the variables view when there's a data
1234+
# brakpoint on the variable? Not sure how best to actually do that, but
1235+
# maybe the variable view can pass that info when calling AddDataBreakpoint,
1236+
# such as the variablesReference/name
1237+
11861238

11871239
def _SignToLine( self, file_name, bp ):
11881240
if bp[ 'is_instruction_breakpoint' ]:

python3/vimspector/debug_session.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,36 @@ def OnDisassemblyWindowScrolled( self, win_id ):
10161016
self._disassemblyView.OnWindowScrolled( win_id )
10171017

10181018

1019-
@CurrentSession()
1019+
@ParentOnly()
1020+
def AddDataBreakpoint( self, opts, buf = None, line_num = None ):
1021+
# Use the parent session, because the _connection_ comes from the
1022+
# variable/watch result that is actually chosen
1023+
1024+
def add_bp( conn, msg ):
1025+
breakpoint_info = msg.get( 'body' )
1026+
if not breakpoint_info:
1027+
utils.UserMessage( "Can't set data breakpoint here" )
1028+
return
1029+
1030+
if breakpoint_info[ 'dataId' ] is None:
1031+
utils.UserMessage(
1032+
f"Can't set data breakpoint here: {breakpoint_info[ 'description' ]}"
1033+
)
1034+
return
1035+
1036+
access_types = breakpoint_info.get( 'accessTypes' )
1037+
if access_types and 'accessType' not in opts:
1038+
access_type = utils.SelectFromList( 'What type of access?',
1039+
access_types )
1040+
if access_type is not None:
1041+
opts[ 'accessType' ] = access_type
1042+
1043+
self._breakpoints.AddDataBreakpoint( conn,
1044+
breakpoint_info,
1045+
opts )
1046+
1047+
self._variablesView.GetDataBreakpointInfo( add_bp, buf, line_num )
1048+
10201049
@IfConnected()
10211050
def AddWatch( self, expression ):
10221051
self._variablesView.AddWatch( self._connection,

python3/vimspector/settings.py

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

0 commit comments

Comments
 (0)