diff --git a/README.md b/README.md index a1d7ef002..dc875bcee 100644 --- a/README.md +++ b/README.md @@ -474,7 +474,7 @@ Example: ```json { "adapters": { - "lldb-vscode": { + "lldb-dap": { "variables": { "LLVM": { "shell": "brew --prefix llvm" @@ -485,7 +485,7 @@ Example: "pidSelect": "ask" }, "command": [ - "${LLVM}/bin/lldb-vscode" + "lldb-dap" ], "env": { "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" @@ -1057,6 +1057,28 @@ then answer `Y` to that (for example). You can configure your choices in the `.vimspector.json`. See [the configuration guide][vimspector-ref-exception] for details on that. +### Data breakpoints + +Data breakpoints are not supported by all debug adapters. They are breakpoints +which trigger when some memory is read or written. They can be created: + +- For a given variable in the variables window (`` on variable) +- For a given child variable in the watches or variables windows + (`` on child variable) +- For an arbitrary expression which evaluates to an address (`` in watch + window, not on any variable) + +When specifying an expression, you can also specify a size. + +In general, if you hit `` (or whatever mapping you have) in the Variables or +Watch window, you'll be adding a Data Breakpoint. If the context looks like a +variable, then Vimspector will ask the debug adapter to create a data breakpoint +on that variable expression. Otherwise, you'll be asked to enter an expression, +or an address and a size, depending on the capabilities of the debugger. + +NOTE: Not all debug adapters support data breakpoints, and the ability to +actually create them often depends on the hardware of the target. + ### API Summary ***NOTE:*** Previously, ToggleBreakpoint would cycle between 3 states: @@ -1082,6 +1104,7 @@ deletes a breakpoint. If you wish to 'disable' breakpoints, use the * `call vimspector#ListBreakpoints()` - toggle breakpoints window * `call vimspector#BreakpointsAsQuickFix()` - return the current set of breakpoints in vim quickfix format +* `call vimspector#AddDataBreakpoint()` - add a data breakpoint Examples: @@ -1209,6 +1232,7 @@ autocmd SessionLoadPost * silent! VimspectorLoadSession * View the type of the variable via mouse hover. * When changing the stack frame the locals window updates. * While paused, hover to see values. +* Create a data breakpoint with ``. ![locals window](https://puremourning.github.io/vimspector-web/img/vimspector-locals-window.png) @@ -1259,6 +1283,7 @@ to add a new watch expression. * Set the value of the variable with `` (control + ``) or `` (if `modifyOtherKeys` doesn't work for you) * Delete with ``. +* Create a data breakpoint with ``. ![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png) @@ -1571,14 +1596,16 @@ Currently tested with the following debug adapters. ## C, C++, Rust, etc. +* Recommended: [CodeLLDB](#rust) * [vscode-cpptools](https://github.com/Microsoft/vscode-cpptools) -* On macOS, I *strongly* recommend using [CodeLLDB](#rust) instead for C and C++ +* [lldb-dap](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.lldb-dap) +* I *strongly* recommend using [CodeLLDB](#rust) over cpptools for almost all projects. It's really excellent, has fewer dependencies and doesn't open console apps in another Terminal window. -Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-vscode`. -For `lldb-vscode` replace the name of the adapter with `lldb-vscode`: +Example `.vimspector.json` (works with both `vscode-cpptools` and `lldb-dap`. +For `lldb-dap` replace the name of the adapter with `lldb-dap`: * vscode-cpptools Linux/MacOS: @@ -1634,6 +1661,31 @@ licensing. } ``` +* `lldb-dap` + +```json + + "lldb-dap": { + "adapter": { + "command": [ + // TODO: Replace this with the path to your installation of lldb + "/opt/homebrew/Cellar/llvm/bin/lldb-dap" + ], + "name": "lldb" + }, + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${fileBasenameNoExtension}", + "args": [ + "*${args}" + ], + "stopOnEntry": true, + "runInTerminal": true, + "cwd": "${workspaceRoot}" + } + } +``` + ### Data visualization / pretty printing Depending on the backend you need to enable pretty printing of complex types @@ -1681,22 +1733,22 @@ an example of getting Vimspector to remotely launch and attach. * CodeLLDB (MacOS) -CodeLLDB is superior to vscode-cpptools in a number of ways on macOS at least. +CodeLLDB is superior to vscode-cpptools in a number of ways. See [Rust](#rust). -* lldb-vscode (MacOS) +* lldb-dap (MacOS) -An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: +An alternative is to to use `lldb-dap`, which comes with llvm. Here's how: * Install llvm (e.g. with HomeBrew: `brew install llvm`) * Create a file named - `/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-vscode.json`: + `/path/to/vimspector/gadgets/macos/.gadgets.d/lldb-dap.json`: ```json { "adapters": { - "lldb-vscode": { + "lldb-dap": { "variables": { "LLVM": { "shell": "brew --prefix llvm" @@ -1707,7 +1759,7 @@ An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: "pidSelect": "ask" }, "command": [ - "${LLVM}/bin/lldb-vscode" + "${LLVM}/bin/lldb-dap" ], "env": { "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" @@ -1721,7 +1773,7 @@ An alternative is to to use `lldb-vscode`, which comes with llvm. Here's how: ## Rust Rust is supported with any gdb/lldb-based debugger. So it works fine with -`vscode-cpptools` and `lldb-vscode` above. However, support for rust is best in +`vscode-cpptools` and `lldb-dap` above. However, support for rust is best in [`CodeLLDB`](https://github.com/vadimcn/vscode-lldb#features). * `./install_gadget.py --enable-rust` or `:VimspectorInstall CodeLLDB` @@ -1735,7 +1787,8 @@ Rust is supported with any gdb/lldb-based debugger. So it works fine with "filetypes": [ "rust" ], "configuration": { "request": "launch", - "program": "${workspaceRoot}/target/debug/vimspector_test" + "program": "${workspaceRoot}/target/debug/vimspector_test", + "sourceLanguages": [ "rust" ] } }, "attach": { @@ -1744,7 +1797,8 @@ Rust is supported with any gdb/lldb-based debugger. So it works fine with "configuration": { "request": "attach", "program": "${workspaceRoot}/${fileBasenameNoExtension}", - "PID": "${PID}" + "PID": "${PID}", + "sourceLanguages": [ "rust" ] } } } diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 4785399e7..1e4ecc22d 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -439,6 +439,14 @@ function! vimspector#ShowDisassembly( ... ) abort py3 _vimspector_session.ShowDisassembly() endfunction +function! vimspector#AddDataBreakpoint( ... ) abort + if !s:Enabled() + return + endif + " TODO: how to set options? + py3 _vimspector_session.AddDataBreakpoint( {} ) +endfunction + function! vimspector#DeleteWatch() abort if !s:Enabled() return diff --git a/python3/vimspector/breakpoints.py b/python3/vimspector/breakpoints.py index 2aab632eb..09dc70119 100644 --- a/python3/vimspector/breakpoints.py +++ b/python3/vimspector/breakpoints.py @@ -181,6 +181,9 @@ def ToggleBreakpointView( self, breakpoint_list ): else: self._UpdateView( breakpoint_list ) + def ShowBreakpointsView( self, breakpoint_list ): + self._UpdateView( breakpoint_list ) + def RefreshBreakpoints( self, breakpoint_list ): self._UpdateView( breakpoint_list, show=False ) @@ -219,6 +222,7 @@ def __init__( self, self._func_breakpoints = [] self._exception_breakpoints = None self._configured_breakpoints = {} + self._data_breakponts = [] self._server_capabilities = {} @@ -289,13 +293,22 @@ def ConnectionClosed( self, connection: DebugAdapterConnection ): def ToggleBreakpointsView( self ): self._breakpoints_view.ToggleBreakpointView( self.BreakpointsAsQuickFix() ) + def ShowBreakpointsView( self ): + self._breakpoints_view.ShowBreakpointsView( self.BreakpointsAsQuickFix() ) + def ToggleBreakpointViewBreakpoint( self ): bp = self._breakpoints_view.GetBreakpointForLine() if not bp: return + + # FIXME: what about instruction breakpoints if bp.get( 'type' ) == 'F': + # FIXME: We don't really handle 'DISABLED' state for function breakpoints, + # so they are just deleted self.ClearFunctionBreakpoint( bp.get( 'filename' ) ) + elif bp.get( 'type' ) == 'D': + self.ToggleDataBreakpoint( bp[ 'session_id' ], bp[ 'data_id' ] ) else: # This should find the breakpoint by the "current" line in lnum. If not, # pass an empty options just in case we end up in "ADD" codepath. @@ -315,6 +328,11 @@ def ToggleAllBreakpointsViewBreakpoint( self ): enabled += 1 else: disabled += 1 + for dbp in self._line_breakpoints: + if bp[ 'state' ] == 'ENABLED': + enabled += 1 + else: + disabled += 1 if enabled > disabled: new_state = 'DISABLED' @@ -324,9 +342,13 @@ def ToggleAllBreakpointsViewBreakpoint( self ): for filename, bps in self._line_breakpoints.items(): for bp in bps: bp[ 'state' ] = new_state + for dbp in self._data_breakponts: + dbp[ 'state' ] = new_state # FIXME: We don't really handle 'DISABLED' state for function breakpoints, # so they are not touched + # FIXME: Same for exception breakpoints + # FIXME: Same for instruction breakpoints self.UpdateUI() def JumpToBreakpointViewBreakpoint( self ): @@ -344,6 +366,9 @@ def EditBreakpointOptionsViewBreakpoint( self ): if not vbp: return + if vbp.get( 'type' ) != 'L': + return + # Try to find the actual breakpoint bp, index = self._FindLineBreakpoint( vbp.get( 'filename' ), vbp.get( 'lnum' ) ) @@ -385,8 +410,11 @@ def ClearBreakpointViewBreakpoint( self ): if not bp: return + # FIXME: what about instruction breakpoints if bp.get( 'type' ) == 'F': self.ClearFunctionBreakpoint( bp.get( 'filename' ) ) + elif bp.get( 'type' ) == 'D': + self.ClearDataBreakpoint( bp[ 'session_id' ], bp[ 'data_id' ] ) else: self.ClearLineBreakpoint( bp.get( 'filename' ), bp.get( 'lnum' ) ) @@ -395,6 +423,7 @@ def BreakpointsAsQuickFix( self ): qf = [] for file_name, breakpoints in self._line_breakpoints.items(): for bp in breakpoints: + msg = [] self._SignToLine( file_name, bp ) line = bp[ 'line' ] @@ -405,8 +434,11 @@ def BreakpointsAsQuickFix( self ): if server_bp[ 'verified' ]: line = server_bp.get( 'line', line ) state = 'VERIFIED' + msg = [ server_bp.get( 'message' ) ] valid = 1 break + elif 'message' in server_bp: + msg.append( server_bp[ 'message' ] ) else: state = bp[ 'state' ] valid = 1 @@ -423,14 +455,22 @@ def BreakpointsAsQuickFix( self ): desc = "Instruction" sfx = f" at { utils.Hex( bp.get( 'address', '' ) ) }" + if msg: + msg = list( filter( lambda x: x, msg ) ) + + if msg: + msg = f"{ ', '.join( msg ) } - " + else: + msg = '' + qf.append( { 'filename': file_name, 'lnum': line, 'col': 1, 'type': 'L', 'valid': valid, - 'text': ( f"{desc} breakpoint{sfx} - " - f"{state}: {json.dumps( bp['options'] )}" + 'text': ( f"{desc} breakpoint{sfx} - {state}: {msg}" + f"{json.dumps( bp['options'] )}" f"\t{ line_value }" ) } ) for bp in self._func_breakpoints: @@ -448,6 +488,37 @@ def BreakpointsAsQuickFix( self ): bp[ 'function' ], json.dumps( bp[ 'options' ] ) ) } ) + for bp in self._data_breakponts: + msg = '' + if 'server_bp' in bp: + state = 'PENDING' + for conn, server_bp in bp[ 'server_bp' ].items(): + if conn != bp[ 'conn' ]: + continue + msg = server_bp.get( 'message' ) + if server_bp[ 'verified' ]: + state = 'VERIFIED' + break + else: + state = bp[ 'state' ] + + if msg: + msg = f"{ msg } - " + else: + msg = '' + + qf.append( { + 'filename': bp[ 'info' ][ 'description' ], + 'data_id': bp[ 'info' ][ 'dataId' ], + 'session_id': bp[ 'conn' ], + 'lnum': 1, + 'col': 1, + 'type': 'D', + 'valid': 0, + 'text': f"{ bp['name'] }: Data breakpoint - {state}: {msg}" + f"{ bp['info' ][ 'description' ] }: " + + json.dumps( bp[ 'options' ] ) + } ) return qf @@ -523,23 +594,21 @@ def _ClearServerBreakpointData( self, conn: DebugAdapterConnection ): if not bp[ 'server_bp' ]: del bp[ 'server_bp' ] - # Clear all instruction breakpoints because they aren't truly portable # across sessions. - # - # TODO: It might be possible to re-resolve the address stored in the - # breakpoint, though this would only work in a limited way (as load - # addresses will frequently not be the same across runs) - - def ShouldKeep( bp ): + def ShouldKeepInsBP( bp ): if not bp[ 'is_instruction_breakpoint' ]: return True if 'address' in bp and bp[ 'session_id' ] != conn.GetSessionId(): return True return False - breakpoints[ : ] = [ bp for bp in breakpoints if ShouldKeep( bp ) ] + breakpoints[ : ] = [ bp for bp in breakpoints if ShouldKeepInsBP( bp ) ] + + # Erase any data breakpoints for this connection too + self._data_breakponts[ : ] = [ bp for bp in self._data_breakponts + if bp[ 'conn' ] != conn.GetSessionId() ] def _CopyServerLineBreakpointProperties( self, @@ -559,7 +628,7 @@ def UpdatePostedBreakpoint( self, bp = self._FindPostedBreakpoint( conn, server_bp.get( 'id' ) ) if bp is None: self._logger.warn( "Unexpected update to breakpoint with id %s:" - "breakpiont not found. %s", + "breakpoint not found. %s", server_bp.get( 'id' ), server_bp ) # FIXME ? self.AddPostedBreakpoint( server_bp ) @@ -736,7 +805,7 @@ def ClearTemporaryBreakpoint( self, file_name, line_num ): # FIXME: We should use the _FindPostedBreakpoint here instead, as that's way # more accurate at this point. Some servers can now identifyt he breakpoint # ID that actually triggered too. For now, we still have - # _UpdateServerBreakpoints change the _user_ breakpiont line and we check + # _UpdateServerBreakpoints change the _user_ breakpoint line and we check # for that _here_, though we could check ['server_bp']['line'] updates = False for bp, index in self._AllBreakpointsOnLine( file_name, line_num ): @@ -776,7 +845,7 @@ def _UpdateServerBreakpoints( self, conn, breakpoints, bp_idxs ): is_temporary = bool( user_bp[ 'options' ].get( 'temporary' ) ) if not is_temporary: - # We don't modify the 'user" breakpiont + # We don't modify the 'user" breakpoint continue # FIXME: Tempoarary instruction breakpoints would not have a line; we @@ -807,7 +876,49 @@ def AddFunctionBreakpoint( self, function, options ): # 'condition': ..., # 'hitCondition': ..., } ) + self.UpdateUI() + + + def AddDataBreakpoint( self, + conn: DebugAdapterConnection, + name, + info, + options ): + self._data_breakponts.append( { + 'state': 'ENABLED', + 'conn': conn.GetSessionId(), + 'name': name, + 'info': info, + 'options': options, + 'is_instruction_breakpoint': False + } ) + # We don't have a way to render breakpoints in the variables view right now, + # so instead when you add a data breakpoint, we force-show the breakpoints + # window + self.ShowBreakpointsView() + self.UpdateUI() + + + def ToggleDataBreakpoint( self, session_id, data_id ): + for dbp in self._data_breakponts: + if dbp[ 'conn' ] != session_id: + continue + if dbp[ 'info' ][ 'dataId' ] != data_id: + continue + if dbp[ 'state' ] == 'ENABLED': + dbp[ 'state' ] = 'DISABLED' + else: + dbp[ 'state' ] = 'ENABLED' + self.UpdateUI() + return + + + def ClearDataBreakpoint( self, session_id, data_id ): + self._data_breakponts = [ + item for item in self._data_breakponts + if item[ 'conn' ] != session_id or item[ 'info' ][ 'dataId' ] != data_id + ] self.UpdateUI() @@ -942,9 +1053,9 @@ def response_handler( conn, msg, bp_idxs = [] ): # function breakpoint as well as every line breakpoint. We need to # implement that: # - pass the indices in here - # - make _FindPostedBreakpoint also search function breakpionts + # - make _FindPostedBreakpoint also search function breakpoints # - make sure that ConnectionClosed also cleares the server_bp data for - # function breakpionts + # function breakpoints # - make sure that we have tests for this, because i'm sure we don't! for connection in self._connections: self._awaiting_bp_responses += 1 @@ -1014,6 +1125,42 @@ def response_handler( conn, msg, bp_idxs = [] ): failure_handler = response_received ) + if self._server_capabilities.get( 'supportsDataBreakpoints' ): + connection: DebugAdapterConnection + for connection in self._connections: + breakpoints = [] + bp_idxs = [] + for bp in self._data_breakponts: + if bp[ 'conn' ] != connection.GetSessionId(): + continue + if not bp[ 'info' ].get( 'dataId' ): + continue + + bp.pop( 'server_bp', None ) + + if bp[ 'state' ] != 'ENABLED': + continue + + data_bp = {} + data_bp.update( bp[ 'options' ] ) + data_bp[ 'dataId' ] = bp[ 'info' ][ 'dataId' ] + bp_idxs.append( ( len( breakpoints ), bp ) ) + breakpoints.append( data_bp ) + + self._awaiting_bp_responses += 1 + connection.DoRequest( + lambda msg, conn=connection: response_handler( conn, + msg, + bp_idxs ), + { + 'command': 'setDataBreakpoints', + 'arguments': { + 'breakpoints': breakpoints, + }, + }, + failure_handler = response_received + ) + if self._exception_breakpoints: for connection in self._connections: self._awaiting_bp_responses += 1 @@ -1112,6 +1259,13 @@ def Save( self ): if bps: line[ file_name ] = bps + # TODO: Some way to persis data breakpoints? Currently they require + # variablesReference, which is clearly not something that can be persisted + # + # That said, the spec now seems to support data bps on expressions, though i + # can't see any servers which support that. + # + # There's now even a 'canPersist' field on the DataBreakpointInfoResponse return { 'line': line, 'function': self._func_breakpoints, @@ -1183,6 +1337,11 @@ def _HideBreakpoints( self ): signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) del bp[ 'sign_id' ] + # TODO could/should we show a sign in the variables view when there's a data + # brakpoint on the variable? Not sure how best to actually do that, but + # maybe the variable view can pass that info when calling AddDataBreakpoint, + # such as the variablesReference/name + def _SignToLine( self, file_name, bp ): if bp[ 'is_instruction_breakpoint' ]: diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 941a5f4cb..afa210a03 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -32,6 +32,7 @@ install, output, stack_trace, + session_manager, utils, variables, settings, @@ -62,6 +63,17 @@ def wrapper( self: "DebugSession", *args, **kwargs ): return wrapper return decorator + def ParentSession(): + def decorator( fct ): + @functools.wraps( fct ) + def wrapper( self: "DebugSession", *args, **kwargs ): + current = self + while current.parent_session: + current = current.parent_session + return fct( current, *args, **kwargs ) + return wrapper + return decorator + def ParentOnly( otherwise=None ): def decorator( fct ): @functools.wraps( fct ) @@ -1016,6 +1028,115 @@ def OnDisassemblyWindowScrolled( self, win_id ): self._disassemblyView.OnWindowScrolled( win_id ) + @ParentSession() + def AddDataBreakpoint( self, opts, buf = None, line_num = None ): + # Use the parent session, because the _connection_ comes from the + # variable/watch result that is actually chosen + + def add_bp( conn, name, msg ): + breakpoint_info = msg.get( 'body' ) + if not breakpoint_info: + utils.UserMessage( "Can't set data breakpoint here" ) + return + + if breakpoint_info[ 'dataId' ] is None: + utils.UserMessage( + f"Can't set data breakpoint here: {breakpoint_info[ 'description' ]}" + ) + return + + access_types = breakpoint_info.get( 'accessTypes' ) + if access_types and 'accessType' not in opts: + access_type = utils.SelectFromList( f'What type of access for {name}?', + access_types ) + if not access_type: + return + opts[ 'accessType' ] = access_type + + self._breakpoints.AddDataBreakpoint( conn, + name, + breakpoint_info, + opts ) + + con: debug_adapter_connection.DebugAdapterConnection = None + arguments: dict = None + + # Check if we were requesting on a specific child variable in the + # watch/locals window. If so, use variablesReference. + con, arguments = self._variablesView.GetDataBreakpointInfoRequest( + buf, + line_num ) + + if not con: + # No watch variable was found, so enter an expression and pass it in + # 'name', with optional 'bytes' and 'asAddress' arguments. + arguments = {} + con = self._stackTraceView.GetCurrentSession().Connection() + + if not con: + return + + address_allowed = bool( session_manager.Get().GetSession( + con.GetSessionId() )._server_capabilities.get( + 'supportsDataBreakpointBytes' ) ) + + if address_allowed: + expr = utils.AskForInput( + 'Expression to watch (or empty for address): ' ) + + if expr is None: + return + + if not expr: + expr = utils.AskForInput( 'Address to watch: ' ) + arguments[ 'asAddress' ] = True + + if not expr: + return + + size = utils.AskForInput( 'Bytes to watch (empty for default): ' ) + if size is None: + return + + if size != '': + try: + arguments[ 'bytes' ] = int( size ) + except ValueError: + utils.UserMessage( "Invalid size", error=True ) + return + else: + expr = utils.AskForInput( 'Expression to watch: ' ) + + if not expr: + return + + arguments = { + 'name': expr, + 'frameId': self._stackTraceView.GetCurrentFrame()[ 'id' ] + } | arguments + + if not con or not arguments: + utils.UserMessage( "Nothing set" ) + return + + if not session_manager.Get().GetSession( + con.GetSessionId() )._server_capabilities.get( + 'supportsDataBreakpoints' ): + utils.UserMessage( "Server does not support data breakpoints" ) + return + + con.DoRequest( + lambda msg: add_bp( con, arguments[ 'name' ], msg ), { + 'command': 'dataBreakpointInfo', + 'arguments': arguments, + }, + failure_handler = lambda reason, msg: utils.UserMessage( + reason, + error=True + ) + ) + + @CurrentSession() @IfConnected() def AddWatch( self, expression ): diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 26d6258b3..a4295048e 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -92,6 +92,7 @@ 'delete': [ '' ], 'set_value': [ '', '' ], 'read_memory': [ 'm' ], + 'add_data_breakpoint': [ '' ], }, 'stack_trace': { 'expand_or_jump': [ '', '<2-LeftMouse>' ], diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 4aa9262d6..4bbed1e23 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -62,13 +62,25 @@ def IsContained( self ): def VariablesReference( self ): assert False + @abc.abstractmethod + def FrameID( self ): + assert False + + @abc.abstractmethod + def Name( self ): + assert False + + @abc.abstractmethod def MemoryReference( self ): - assert None + assert False @abc.abstractmethod def HoverText( self ): return "" + def Update( self, connection ): + self.connection = connection + class Scope( Expandable ): """Holds an expandable scope (a DAP scope dict), with expand/collapse state""" @@ -82,7 +94,14 @@ def VariablesReference( self ): def MemoryReference( self ): return None - def Update( self, scope ): + def FrameID( self ): + return None + + def Name( self ): + return self.scope[ 'name' ] + + def Update( self, connection, scope ): + super().Update( connection ) self.scope = scope def HoverText( self ): @@ -91,8 +110,12 @@ def HoverText( self ): class WatchResult( Expandable ): """Holds the result of a Watch expression with expand/collapse.""" - def __init__( self, connection: DebugAdapterConnection, result: dict ): + def __init__( self, + connection: DebugAdapterConnection, + watch, + result: dict ): super().__init__( connection ) + self.watch = watch self.result = result # A new watch result is marked as changed self.changed = True @@ -103,9 +126,15 @@ def VariablesReference( self ): def MemoryReference( self ): return self.result.get( 'memoryReference' ) + def FrameID( self ): + return self.watch.expression.get( 'frameId' ) + + def Name( self ): + return self.watch.expression.get( 'expression' ) + def Update( self, connection, result ): + super().Update( connection ) self.changed = False - self.connection = connection if self.result[ 'result' ] != result[ 'result' ]: self.changed = True self.result = result @@ -121,8 +150,8 @@ def HoverText( self ): class WatchFailure( WatchResult ): - def __init__( self, connection: DebugAdapterConnection, reason ): - super().__init__( connection, { 'result': reason } ) + def __init__( self, connection: DebugAdapterConnection, watch, reason ): + super().__init__( connection, watch, { 'result': reason } ) self.changed = True @@ -130,7 +159,8 @@ class Variable( Expandable ): """Holds one level of an expanded value tree. Also itself expandable.""" def __init__( self, connection: DebugAdapterConnection, - container: Expandable, variable: dict ): + container: Expandable, + variable: dict ): super().__init__( connection = connection, container = container ) self.variable = variable # A new variable appearing is marked as changed @@ -142,9 +172,15 @@ def VariablesReference( self ): def MemoryReference( self ): return self.variable.get( 'memoryReference' ) + def FrameID( self ): + return self.container.FrameID() + + def Name( self ): + return self.variable[ 'name' ] + def Update( self, connection, variable ): + super().Update( connection ) self.changed = False - self.connection = connection if self.variable[ 'value' ] != variable[ 'value' ]: self.changed = True self.variable = variable @@ -171,6 +207,11 @@ def __init__( self, connection: DebugAdapterConnection, expression: dict ): self.result = None def SetCurrentFrame( self, connection, frame ): + if connection is None: + self.connection = None + self.result.connection = None + return + if self.connection is None: self.connection = connection elif self.connection != connection: @@ -227,6 +268,9 @@ def AddExpandMappings( mappings = None ): for mapping in utils.GetVimList( mappings, 'read_memory' ): vim.command( f'nnoremap { mapping } ' ':call vimspector#ReadMemory()' ) + for mapping in utils.GetVimList( mappings, 'add_data_breakpoint' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#AddDataBreakpoint()' ) @@ -323,7 +367,7 @@ def ConnectionClosed( self, connection ): ] for w in self._watches: if w.connection == connection: - w.connection = None + w.SetCurrentFrame( None, None ) def Reset( self ): @@ -363,7 +407,7 @@ def scopes_consumer( message ): if not found: scope = Scope( connection, scope_body ) else: - scope.Update( scope_body ) + scope.Update( connection, scope_body ) new_scopes.append( scope ) @@ -434,7 +478,7 @@ def handler( message ): watch = self._variable_eval if watch.result is None or watch.result.connection != connection: - watch.result = WatchResult( connection, message[ 'body' ] ) + watch.result = WatchResult( connection, watch, message[ 'body' ] ) else: watch.result.Update( connection, message[ 'body' ] ) @@ -543,7 +587,9 @@ def _UpdateWatchExpression( self, watch: Watch, message: dict ): if watch.result is not None: watch.result.Update( watch.connection, message[ 'body' ] ) else: - watch.result = WatchResult( watch.connection, message[ 'body' ] ) + watch.result = WatchResult( watch.connection, + watch, + message[ 'body' ] ) if ( watch.result.IsExpandable() and watch.result.IsExpanded() ): @@ -563,7 +609,7 @@ def _WatchExpressionFailed( self, reason: str, watch: Watch ): # We already have a result for this watch. Wut ? return - watch.result = WatchFailure( watch.connection, reason ) + watch.result = WatchFailure( watch.connection, watch, reason ) self._DrawWatches() def _GetVariable( self, buf = None, line_num = None ): @@ -677,7 +723,6 @@ def GetMemoryReference( self ): if variable is None: return None, None - # TODO: Return the connection too! return variable.connection, variable.MemoryReference() @@ -853,4 +898,28 @@ def SetSyntax( self, syntax ): syntax, self._vars.buf, self._watch.buf ) + + + def GetDataBreakpointInfoRequest( self, buf, line_num ): + variable: Expandable + view: View + + variable, view = self._GetVariable( buf, line_num ) + if variable is None: + return None, None + + arguments = { + 'name': variable.Name() + } + frameId = variable.FrameID() + if frameId: + arguments[ 'frameId' ] = frameId + + if variable.IsContained(): + arguments[ 'variablesReference' ] = ( + variable.container.VariablesReference() ) + + return variable.connection, arguments + + # vim: sw=2 diff --git a/support/test/cpp/simple_c_program/.vimspector.json b/support/test/cpp/simple_c_program/.vimspector.json index c72f550bd..fdbe4a92d 100644 --- a/support/test/cpp/simple_c_program/.vimspector.json +++ b/support/test/cpp/simple_c_program/.vimspector.json @@ -1,10 +1,32 @@ { "adapters": { + "lldb-dap": { + "attach": { + "pidProperty": "pid", + "pidSelect": "ask" + }, + "command": [ + "/opt/homebrew/opt/llvm/bin/lldb-dap" + ], + "env": { + "LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY": "YES" + }, + "name": "lldb", + "configuration": { + "runInTerminal": true + } + }, + "custom-lldb-dap": { + "extends": "lldb-dap", + "command": [ + "$HOME/Development/llvm-project/build/bin/lldb-dap" + ] + }, "custom-codelldb": { "extends": "CodeLLDB", "command": [ "$HOME/Development/vimspector/CodeLLDB/build/adapter/codelldb", - "--port", "${unusedLocalPort}" + "--liblldb", "$HOME/Development/llvm-project/build/lib/liblldb.dylib" ] }, "CodeLLDB - StopOnEntry": { @@ -23,6 +45,7 @@ "adapter": "custom-codelldb", "configuration": { "request": "launch", + "expressions": "native", "program": "${workspaceRoot}/test" } }, @@ -47,6 +70,17 @@ "stopOnEntry": true } }, + "lldb-dap": { + "adapter": "lldb-dap", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/test" + } + }, + "lldb-dap-custom": { + "extends": "lldb-dap", + "adapter": "custom-lldb-dap" + }, "lldb-vscode": { "adapter": "lldb-vscode", "variables": { @@ -64,7 +98,7 @@ "adapter": "vscode-cpptools", "variables": { "BUILDME": { - "shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${workspaceRoot}/test_c.cpp" + "shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${file}" }, "arch": { "shell": "uname -m" @@ -86,7 +120,7 @@ "adapter": "vscode-cpptools", "variables": { "BUILDME": { - "shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${workspaceRoot}/test_c.cpp" + "shell": "g++ -o ${workspaceRoot}/test -g -std=c++17 ${file}" } }, "configuration": { diff --git a/support/test/cpp/simple_c_program/memory.cpp b/support/test/cpp/simple_c_program/memory.cpp new file mode 100644 index 000000000..cac551ba1 --- /dev/null +++ b/support/test/cpp/simple_c_program/memory.cpp @@ -0,0 +1,24 @@ +struct Test +{ + int x; + int y; +}; + +int main( int argc , char ** argv ) +{ + Test x[] = { + { 1, 2 }, + { 3, 4 }, + { 5, 6 }, + }; + + Test y = { 7, 8 }; + + x[0].x += argc; + argv[ 0 ][ 0 ] = 'x' ; + + y.x += **argv; + y.y += argc * **argv; + + return argc; +} diff --git a/support/test/cpp/simple_c_program/test_c.cpp b/support/test/cpp/simple_c_program/test_c.cpp index cd696b18d..5551e884f 100644 --- a/support/test/cpp/simple_c_program/test_c.cpp +++ b/support/test/cpp/simple_c_program/test_c.cpp @@ -12,6 +12,8 @@ namespace Test int somethingInt; char somethingChar; } something; + + char big[ sizeof(void*) * 100 ]; }; TestStruct _t; @@ -25,7 +27,7 @@ namespace Test void foo( TestStruct m ) { - TestStruct t{ true, {11} }; + TestStruct t{ true, {11}, {} }; bar( t ); } } @@ -37,10 +39,20 @@ int main ( int argc, char ** argv ) printf( "HOME: %s\n", getenv( "HOME" ) ); - Test::TestStruct t{ true, {99} }; + Test::TestStruct t{ true, {99}, { ' ', } }; foo( t ); // ADL! for ( int i = 0; i < 100 ; ++i ) { - Test::foo( { true, {i} } ); + Test::foo( { true, {i}, {0} } ); } + + t.big[ sizeof(void*) * 0 ] = 'r'; + t.big[ sizeof(void*) * 1 ] = 'o'; + t.big[ sizeof(void*) * 2 ] = 'f'; + t.big[ sizeof(void*) * 3 ] = 'l'; + //t.big[ sizeof(void*) * 4 ] = '0'; + + std::cout << t.big << '\n'; + + return 0; } diff --git a/support/test/go/structs/.gitignore b/support/test/go/structs/.gitignore new file mode 100644 index 000000000..242c034c1 --- /dev/null +++ b/support/test/go/structs/.gitignore @@ -0,0 +1 @@ +hello_world diff --git a/support/test/go/structs/.vimspector.json b/support/test/go/structs/.vimspector.json new file mode 100644 index 000000000..da54999a1 --- /dev/null +++ b/support/test/go/structs/.vimspector.json @@ -0,0 +1,29 @@ +{ + "configurations": { + "run-legacy": { + "adapter": "vscode-go", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/hello-world.go", + "mode": "debug", + "trace": true, + "env": { "GO111MODULE": "off" } + } + }, + "run-delve": { + "adapter": "delve", + "configuration": { + "request": "launch", + "env": { "GO111MODULE": "off" }, + + "mode": "debug", // debug|test + "program": "${workspaceRoot}/hello-world.go" + + // "args": [], + // "buildFlags": ... + // "stackTraceDepth": ..., + // "showGlobalVariables": true, + } + } + } +} diff --git a/support/test/go/structs/__debug_bin b/support/test/go/structs/__debug_bin new file mode 100755 index 000000000..919aef79b Binary files /dev/null and b/support/test/go/structs/__debug_bin differ diff --git a/support/test/go/structs/hello-world.go b/support/test/go/structs/hello-world.go new file mode 100644 index 000000000..efff2c069 --- /dev/null +++ b/support/test/go/structs/hello-world.go @@ -0,0 +1,25 @@ +package main +import "fmt" + +type Toaster struct { + Power int + Colour string +} + +type Test struct { + Test string + Toast Toaster +} + +func main() { + var v = "test" + test := Test{ + Test: "This is\na\ntest", + Toast: Toaster{ + Power: 10, + Colour: "green", + }, + } + fmt.Println("hello world: " + v) + fmt.Println("Hi " + test.Test) +} diff --git a/support/test/java/test_project/.vimspector.json b/support/test/java/test_project/.vimspector.json index 39f4639ba..58219a917 100644 --- a/support/test/java/test_project/.vimspector.json +++ b/support/test/java/test_project/.vimspector.json @@ -7,6 +7,7 @@ "mainClass": "com.vimspector.test.TestApplication", "sourcePaths": [ "${workspaceRoot}/src/main/java" ], "classPaths": [ "${workspaceRoot}/target/classes" ], + "project": "TestApplication", "args": "hello world!", "stopOnEntry": true, "console": "integratedTerminal", @@ -20,6 +21,7 @@ "configuration": { "request": "attach", "sourcePaths": [ "${workspaceRoot}/src/main/java" ], + "project": "TestApplication", "stopOnEntry": true, "hostName": "localhost", "port": "${JVMDebugPort}", @@ -32,6 +34,7 @@ "adapter": "vscode-javac", "configuration": { "request": "attach", + "project": "TestApplication", "port": "${debugPort}", "sourceRoots": [ "${workspaceRoot}/src/main/java" diff --git a/support/test/rust/vimspector_test/src/main.rs b/support/test/rust/vimspector_test/src/main.rs index 509a6d43c..9cea16605 100644 --- a/support/test/rust/vimspector_test/src/main.rs +++ b/support/test/rust/vimspector_test/src/main.rs @@ -1,10 +1,26 @@ +struct Point { + x: i32, + y: i32, +} + struct Foo { x: i32, } fn main() { let s = "World!"; + println!("Hello, {}!", s); + let f = Foo { x: 42 }; let g = &f; println!("Hello, {} {} {}!", s, g.x, f.x); + + let mut p = Point{ x: 1, y: 11 }; + + p.x = 11; + p.y = 11; + p.x += 11; + p.y += 11; + + println!("Point: {}, {}", p.x, p.y); } diff --git a/support/vimspector_process_list/go.mod b/support/vimspector_process_list/go.mod index dacd189d8..43c52115c 100644 --- a/support/vimspector_process_list/go.mod +++ b/support/vimspector_process_list/go.mod @@ -2,15 +2,15 @@ module github.com/puremourning/vimspector/support/vimspector_process_list go 1.19 -require github.com/shirou/gopsutil/v3 v3.23.4 +require github.com/shirou/gopsutil/v3 v3.24.5 require ( - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/shoenig/go-m1cpu v0.1.5 // indirect - github.com/tklauser/go-sysconf v0.3.11 // indirect - github.com/tklauser/numcpus v0.6.0 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/sys v0.7.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.9.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/sys v0.26.0 // indirect ) diff --git a/support/vimspector_process_list/go.sum b/support/vimspector_process_list/go.sum index e12874754..9babb46ef 100644 --- a/support/vimspector_process_list/go.sum +++ b/support/vimspector_process_list/go.sum @@ -3,19 +3,30 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o= github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ= github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c= github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -27,15 +38,24 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo= +github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tests/testdata/cpp/simple/.vimspector.json b/tests/testdata/cpp/simple/.vimspector.json index 3cf58c2d0..79003517e 100644 --- a/tests/testdata/cpp/simple/.vimspector.json +++ b/tests/testdata/cpp/simple/.vimspector.json @@ -76,6 +76,22 @@ "MIMode": "lldb" } }, + "lldb-dap": { + "adapter": "lldb-dap", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/${fileBasenameNoExtension}", + "args": [ + "A", "eh", + "B", "bee", + "C", "Sea", + "D", "Ceedy" + ], + "stopOnEntry": true, + "runInTerminal": true, + "cwd": "${workspaceRoot}" + } + }, "CodeLLDB": { "adapter": "CodeLLDB", // "adapter": { @@ -129,12 +145,21 @@ }, "name": "lldb" }, + "lldb-dap": { + "variables": { + "LLVM": { + "shell": "brew --prefix llvm" + } + }, + "command": [ + "${LLVM}/bin/lldb-dap" + ], + "name": "lldb" + }, "CodeLLDB-localbuild": { "extends": "CodeLLDB", "command": [ - "$HOME/Development/vimspector/CodeLLDB/build/adapter/codelldb", - "--port", - "${unusedLocalPort}" + "$HOME/Development/vimspector/CodeLLDB/build/adapter/codelldb" ] } }