1085 lines
40 KiB
Text
1085 lines
40 KiB
Text
*vimspector-ref*
|
|
|
|
===============================================================================
|
|
Contents ~
|
|
|
|
1. Introduction |vimspector-ref-introduction|
|
|
2. title: Configuration |vimspector-ref-title-configuration|
|
|
3. Concepts |vimspector-ref-concepts|
|
|
1. Debug adapter configuration |vimspector-ref-debug-adapter-configuration|
|
|
2. Debug profile configuration |vimspector-ref-debug-profile-configuration|
|
|
3. Replacements and variables |vimspector-ref-replacements-variables|
|
|
1. The splat operator |vimspector-ref-splat-operator|
|
|
2. Default values |vimspector-ref-default-values|
|
|
3. Coercing Types |vimspector-ref-coercing-types|
|
|
4. Configuration Format |vimspector-ref-configuration-format|
|
|
5. Files and locations |vimspector-ref-files-locations|
|
|
6. Adapter configurations |vimspector-ref-adapter-configurations|
|
|
7. Debug configurations |vimspector-ref-debug-configurations|
|
|
1. Configuration selection |vimspector-ref-configuration-selection|
|
|
1. Specifying a default configuration |vimspector-ref-specifying-default-configuration|
|
|
2. Preventing automatic selection |vimspector-ref-preventing-automatic-selection|
|
|
2. Exception Breakpoints |vimspector-ref-exception-breakpoints|
|
|
8. Predefined Variables |vimspector-ref-predefined-variables|
|
|
9. Remote Debugging Support |vimspector-ref-remote-debugging-support|
|
|
1. Python (debugpy) Example |vimspector-ref-python-example|
|
|
2. C-family (gdbserver) Example |vimspector-ref-c-family-example|
|
|
3. Docker Example |vimspector-ref-docker-example|
|
|
10. Appendix: Configuration file format |vimspector-ref-appendix-configuration-file-format|
|
|
11. Appendix: Editor configuration |vimspector-ref-appendix-editor-configuration|
|
|
12. References |vimspector-ref-references|
|
|
|
|
===============================================================================
|
|
*vimspector-ref-introduction*
|
|
Introduction ~
|
|
|
|
===============================================================================
|
|
*vimspector-ref-title-configuration*
|
|
title: Configuration ~
|
|
|
|
This document defines the supported format for project and adapter
|
|
configuration for Vimspector.
|
|
|
|
- Concepts
|
|
|
|
- Debug adapter configuration
|
|
- Debug profile configuration
|
|
- Replacements and variables
|
|
- The splat operator
|
|
- Default values
|
|
- Coercing Types
|
|
|
|
- Configuration Format
|
|
- Files and locations
|
|
- Adapter configurations
|
|
- Debug configurations
|
|
|
|
- Configuration selection
|
|
|
|
- Specifying a default configuration
|
|
- Preventing automatic selection
|
|
|
|
- Exception Breakpoints
|
|
|
|
- Predefined Variables
|
|
- Remote Debugging Support
|
|
|
|
- Python (debugpy) Example
|
|
- C-family (gdbserver) Example
|
|
- Docker Example
|
|
|
|
- Appendix: Configuration file format
|
|
- Appendix: Editor configuration
|
|
|
|
===============================================================================
|
|
*vimspector-ref-concepts*
|
|
Concepts ~
|
|
|
|
As Vimspector supports debugging arbitrary projects, you need to tell it a few
|
|
details about what you want to debug, and how to go about doing that.
|
|
|
|
In order to debug things, Vimspector requires a Debug Adapter which bridges
|
|
between Vimspector and the actual debugger tool. Vimspector can be used with
|
|
any debug adapter that implements the Debug Adapter Protocol [1].
|
|
|
|
For each debugging session, you provide a _debug configuration_ which includes
|
|
things like:
|
|
|
|
- The debug adapter to use (and possibly how to launch and configure it).
|
|
- How to connect to the remote host, if remote debugging.
|
|
- How to launch or attach to your process.
|
|
|
|
Along with optional additional configuration for things like:
|
|
|
|
- Exception breakpoints
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-debug-adapter-configuration*
|
|
Debug adapter configuration ~
|
|
|
|
The adapter to use for a particular debug session can be specified inline
|
|
within the _debug configuration_, but more usually the debug adapter is defined
|
|
separately and just referenced from the _debug configuration_.
|
|
|
|
The adapter configuration includes things like:
|
|
|
|
- How to launch or connect to the debug adapter
|
|
- How to configure it for PID attachment
|
|
- How to set up remote debugging, such as how to launch the process remotely
|
|
(for example, under 'gdbserver', 'ptvsd', etc.)
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-debug-profile-configuration*
|
|
Debug profile configuration ~
|
|
|
|
Projects can have many different debug profiles. For example you might have all
|
|
of the following, for a given source tree:
|
|
|
|
- Remotely launch c++ the process, and break on 'main'
|
|
- Locally Python test and break exception
|
|
- Remotely attach to a c++ process
|
|
- Locally launch a bash script
|
|
- Attach to a JVM listening on a port
|
|
|
|
Each of these represents a different use case and a different _debug
|
|
configuration_. As mentioned above, a _debug configuration_ is essentially:
|
|
|
|
- The adapter to use
|
|
|
|
- The type of session (launch or attach), and whether or not to do it
|
|
remotely
|
|
|
|
- The configuration to pass to the adapter in order to launch or attach to
|
|
the process.
|
|
|
|
The bulk of the configuration is the last of these, which comprises
|
|
adapter-specific options, as the Debug Adapter Protocol does not specify any
|
|
standard for launch or attach configuration.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-replacements-variables*
|
|
Replacements and variables ~
|
|
|
|
Vimspector _debug configuration_ is intended to be as general as possible, and
|
|
to be committed to source control so that debugging your applications becomes a
|
|
simple, quick and pain-free habit (e.g. answering questions like "what happens
|
|
if..." with "just hit F5 and step through!").
|
|
|
|
Therefore it's important to abstract certain details, like runtime and
|
|
build-time paths, and to parameterise the _debug configuration_. Vimspector
|
|
provides a simple mechanism to do this with '${replacement}' style
|
|
replacements.
|
|
|
|
The values available within the '${...}' are defined below, but in summary the
|
|
following are supported:
|
|
|
|
- Environment variables, such as '${PATH}'
|
|
- Predefined variables, such as '${workspaceRoot}', '${file}' etc.
|
|
- Configuration-defined variables, either provided by the adapter
|
|
configuration or debug configuration, or from running a simple shell
|
|
command.
|
|
- Anything else you like - the user will be asked to provide a value.
|
|
|
|
If the latter 2 are confusing, for now, suffice to say that they are how
|
|
Vimspector allows parameterisation of debug sessions. The [Vimspector
|
|
website][website-getting-started] has a good example of where this sort of
|
|
thing is useful: accepting the name of a test to run.
|
|
|
|
But for now, consider the following example snippet:
|
|
>
|
|
{
|
|
"configurations": {
|
|
"example-debug-configuration": {
|
|
// This is a single-line comment explaining the purpose
|
|
"adapter": "example-adapter-name",
|
|
"variables": {
|
|
"SecretToken": { // Variables should start with upper-case letters
|
|
"shell" : [ "cat", "${HOME}/.secret_token" ]
|
|
}
|
|
},
|
|
"configuration": {
|
|
"request": "launch" /* or it could be "attach" */,
|
|
"program": [
|
|
"${fileBasenameNoExtension}",
|
|
"-c", "configuration_file.cfg",
|
|
"-u", "${USER}",
|
|
"--test-identifier", "${TestIdentifier}",
|
|
"--secret-token", "${SecretToken}"
|
|
]
|
|
},
|
|
"breakpoints": {
|
|
"exception": {
|
|
"caught": "",
|
|
"uncaught": "Y"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
<
|
|
In this (fictitious) example the 'program' launch configuration item contains
|
|
the following variable substitutions:
|
|
|
|
- '${fileBasenameNoExtension}' - this is a Predefined Variable, set by
|
|
Vimspector to the base name of the file that's opened in Vim, with its
|
|
extension removed ('/path/to/xyz.cc' -> 'xyz').
|
|
|
|
- '${USER}' - this refers to the Environment Variable 'USER'.
|
|
|
|
- '${TestIdentifier}' - this variable is not defined, so the user is asked to
|
|
provide a value interactively when starting debugging. Vimspector remembers
|
|
what they said and provides it as the default should they debug again.
|
|
|
|
- '${SecretToken}' - this variable is provided by the configuration's
|
|
'variables' block. Its value is taken from the 'strip''d result of running
|
|
the shell command. Note these variables can be supplied by both the debug
|
|
and adapter configurations and can be either static strings or shell
|
|
commands.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-splat-operator*
|
|
The splat operator ~
|
|
|
|
Often we want to create a single '.vimspector.json' entry which encompasses
|
|
many use cases, as it is tedious to write every use case/start up option in
|
|
JSON. This is why we have the replacement variables after all.
|
|
|
|
Frequently debug adapters request command arguments as a JSON array, for
|
|
example:
|
|
>
|
|
"args": [ "one", "two three", "four" ],
|
|
<
|
|
To help with this sort of case, Vimspector supports a 'splat' operator for
|
|
replacement variables operating within lists. The syntax is: '"*${var}', which
|
|
means roughly "splice the contents of '${var}' into the list at this position".
|
|
'${var}' is parsed like a shell command (using python's 'shlex' parser) and
|
|
each word is added as a list item.
|
|
|
|
For example:
|
|
>
|
|
"args": [ "*${CommandLineArgs}" ]
|
|
<
|
|
This would:
|
|
|
|
- Ask the user to provide the variable 'CommandLineArgs'. Let's say they
|
|
entered 'one "two three" four'
|
|
- Split 'CommandLineArgs' like shell arguments: 'one', 'two three' and 'four'
|
|
- Set 'args' in the settings dict to: '[ "one", "two three", "four" ]'
|
|
|
|
You can also combine with static values:
|
|
>
|
|
"args": [ "First", "*${CommandLineArgs}", "Last" ]
|
|
<
|
|
This would yield the intuitive result: '[ "First", "one", "two three", "four",
|
|
"Last" ]'
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-default-values*
|
|
Default values ~
|
|
|
|
You can specify replacements with default values. In this case if the user has
|
|
not specified a value, they are prompted but with the default value
|
|
pre-populated, allowing them to just press return to accept the default.
|
|
|
|
The syntax is '${variableName:default value}'. The default value can contain
|
|
any character, but to include a '}' you must escape it with a backslash. To
|
|
include a backslash in the JSON you must write '\\', as in:
|
|
>
|
|
{ "key": "${value:default {\\} stuff}" }
|
|
<
|
|
The default value can also be a replacement variable. However, this _must_ be a
|
|
variable that's already defined, such as one of the predefined variables, or
|
|
one specified in a 'variables' block. In order to reference them, you _must_
|
|
use '${var}' syntax and you _must_ escape the closing '}'. For example, the is
|
|
a common and useful case:
|
|
>
|
|
{
|
|
"configuration": {
|
|
"program": "${script:${file\\}}"
|
|
}
|
|
}
|
|
<
|
|
This will prompt the user to specify 'script', but it will default to the path
|
|
to the current file.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-coercing-types*
|
|
Coercing Types ~
|
|
|
|
Sometimes, you want to provide an option for a boolean parameter, or want to
|
|
allow the user to specify more than just strings. Vimspector allows you to do
|
|
this, ensuring that the resulting JSON is valid. This is done by interpreting a
|
|
value as a JSON string and substituting the resulting JSON value in its place.
|
|
|
|
This is easier to explain with an example. Let's say we want to offer the
|
|
ability to break on entry, as an option for the user. The launch configuration
|
|
requires 'stopOnEntry' to be a bool. This doesn't work:
|
|
>
|
|
"stopOnEntry": "${StopOnEntry}"
|
|
<
|
|
The reason is that if the user types 'true', the resulting object is:
|
|
>
|
|
"stopOnEntry": "true"
|
|
<
|
|
The problem being that is a string, not a boolean. So Vimspector allows you to
|
|
re-interpret the string as a JSON value and use that instead. To do this, add
|
|
'#json' to the key's name. You can even add a default, like this:
|
|
>
|
|
"stopOnEntry#json": "${stopOnEntry:true}"
|
|
<
|
|
If the user accepts the default, the resulting string '"true"' is coerced to a
|
|
JSON value 'true', and the suffix is stripped fom the key, resulting in the
|
|
following:
|
|
>
|
|
"stopOnEntry#json": true
|
|
<
|
|
Which is what we need.
|
|
|
|
If you happen to have a key that already ends in '#json' (unlikely!), then you
|
|
can force Vimspector to treat the value as a string by appending '#s', as in:
|
|
>
|
|
"unlikelyKeyName#json#s": "this is a string, not JSON data"
|
|
<
|
|
**_Advanced usage:_**
|
|
|
|
The most common usage for this is for number and bool types, but it works for
|
|
objects too. If you want to be able to specify a whole object (e.g. a whole
|
|
'env' dict), then you can do that too:
|
|
>
|
|
"env#json": "${Environment:{\\}}"
|
|
<
|
|
The default value here is '{}' (note the '}' must be escaped!). The user can
|
|
then enter something like '{ "MYVAR": "MyValue", "OTHER": "Other" }' and the
|
|
resulting object would be:
|
|
>
|
|
"env": {
|
|
"MYVAR": "MyValue",
|
|
"OTHER": "Other"
|
|
}
|
|
<
|
|
It also works for lists, though the splat operator is usually more convenient
|
|
for that.
|
|
|
|
===============================================================================
|
|
*vimspector-ref-configuration-format*
|
|
Configuration Format ~
|
|
|
|
All Vimspector configuration is defined in a JSON object. The complete
|
|
specification of this object is available in the JSON Schema [2], but the basic
|
|
format for the configuration object is:
|
|
>
|
|
{
|
|
"adapters": { <object mapping name to <adapter configuration> },
|
|
"configurations": { <object mapping name to <debug configuration> }
|
|
}
|
|
<
|
|
The 'adapters' key is actually optional, as '<adapter configuration>' can be
|
|
embedded within '<debug configuration>', though this is not recommended usage.
|
|
|
|
===============================================================================
|
|
*vimspector-ref-files-locations*
|
|
Files and locations ~
|
|
|
|
The above configuration object is constructed from a number of configuration
|
|
files, by merging objects in a specified order.
|
|
|
|
In a minimal sense, the only file required is a '.vimspector.json' file in the
|
|
root of your project which defines the full configuration object [2], but it is
|
|
usually useful to split the 'adapters' configuration into a separate file (or
|
|
indeed one file per debug adapter).
|
|
|
|
The following sections describe the files that are read and use the following
|
|
abbreviations:
|
|
|
|
- '<vimspector home>' means the path to the Vimspector installation (such as
|
|
'$HOME/.vim/pack/vimspector/start/vimspector')
|
|
|
|
- '<OS>' is either 'macos' or 'linux' depending on the host operating system.
|
|
|
|
- '<filetype>' is the Vim filetype. Where multiple filetypes are in effect,
|
|
typically all filetypes are checked.
|
|
|
|
===============================================================================
|
|
*vimspector-ref-adapter-configurations*
|
|
Adapter configurations ~
|
|
|
|
Vimspector reads a series of files to build the 'adapters' object. The
|
|
'adapters' objects are merged in such a way that a definition for an adapter
|
|
named 'example-adapter' in a later file _completely replaces_ a previous
|
|
definition.
|
|
|
|
- '<vimspector home>/gadgets/<OS>/.gadgets.json' - the file written by
|
|
'install_gadget.py' and not usually edited by users.
|
|
|
|
- '<vimspector home>/gadgets/<OS>/.gadgets.d/*.json' (sorted alphabetically).
|
|
These files are user-supplied and override the above.
|
|
|
|
- The first such '.gadgets.json' file found in all parent directories of the
|
|
file open in Vim.
|
|
|
|
- The '.vimspector.json' and any filetype-specific configurations (see below)
|
|
|
|
In all cases, the required format is:
|
|
>
|
|
{
|
|
"$schema": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json#",
|
|
"adapters": {
|
|
"<adapter name>": {
|
|
<adapter configuration>
|
|
}
|
|
}
|
|
}
|
|
<
|
|
Each adapters block can define any number of adapters. As mentioned, if the
|
|
same adapter name exists in multiple files, the last one read takes precedence
|
|
and _completely replaces_ the previous configuration. In particular that means
|
|
you can't just override one option, you have to override the whole block.
|
|
|
|
Adapter configurations are re-read at the start of each debug session.
|
|
|
|
The specification for the gadget object is defined in the [gadget schema][].
|
|
|
|
===============================================================================
|
|
*vimspector-ref-debug-configurations*
|
|
Debug configurations ~
|
|
|
|
There are two locations for debug configurations for a project:
|
|
|
|
- '<vimspector home>/configurations/<OS>/<filetype>/*.json'
|
|
- '.vimspector.json' in the project source
|
|
|
|
Typically, the debug configurations are read from '.vimspector.json'. The file
|
|
is found (like '.gadgets.json' above) by recursively searching up the directory
|
|
hierarchy from the directory of the file open in Vim. The first file found is
|
|
read and no further searching is done.
|
|
|
|
Only a single '.vimspector.json' is read. If one is found, the location of this
|
|
file is used for '${workspaceRoot}' and other workspace-relative paths.
|
|
|
|
In addition, users can create filetype-specific configurations in the
|
|
Vimspector installation directory. This can be useful where the parameters for
|
|
the debug session for a particular filetype are always known in advance, or can
|
|
always be entered by the user. This allows for debugging to "just work" without
|
|
any modification to the project source (no need to add a '.vimspector.json').
|
|
In this case, the '${workspaceRoot}' and workspace-relative paths are
|
|
interpreted relative to the file open in Vim. This isn't ideal, but there is no
|
|
other obvious way to default this variable.
|
|
|
|
As with gadgets, any debug configurations appearing within '.vimspector.json'
|
|
override any that appear in the common configuration dir.
|
|
|
|
Debug configurations are re-read at the start of each debug session, so
|
|
modifications are picked up without any restarts of Vim.
|
|
|
|
The specification for the gadget object is defined in the schema [2], but a
|
|
typical example looks like this:
|
|
>
|
|
{
|
|
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
|
|
"configurations": {
|
|
"<configuation name>": {
|
|
"adapter": "<adapter name>",
|
|
"configuration": {
|
|
"request": "<launch or attach>",
|
|
<debug configutation>
|
|
}
|
|
}
|
|
}
|
|
}
|
|
<
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-configuration-selection*
|
|
Configuration selection ~
|
|
|
|
When starting debugging, you can specify which debug configuration to launch
|
|
with "call vimspector#LaunchWithSettings( #{ configuration: 'name here' } )".
|
|
|
|
Otherwise, if there's only one configuration found, Vimspector will use that
|
|
configuration, unless it contains a key '"autoselect": false'.
|
|
|
|
If multiple debug configurations are found, and no explicit configuration was
|
|
selected on Launch, the user is prompted to select a configuration, unless a
|
|
single debug configuration is found with a key '"default": true'.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-specifying-default-configuration*
|
|
Specifying a default configuration ~
|
|
|
|
As noted, you can specify a default configuration with '"default": true':
|
|
>
|
|
{
|
|
"configurations": {
|
|
"use this one": {
|
|
"default": true,
|
|
"adapter": " ... ",
|
|
"configuation": {
|
|
// ...
|
|
}
|
|
},
|
|
"don't use this one": {
|
|
// ...
|
|
}
|
|
}
|
|
}
|
|
<
|
|
If multiple configurations are found with 'default' set to 'true', then the
|
|
user is prompted anyway.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-preventing-automatic-selection*
|
|
Preventing automatic selection ~
|
|
|
|
If you don't want a configuration to be selected automatically, then set
|
|
'"autoselect": false'. This particularly useful for configurations in the
|
|
central (as opposed to project-local) directory. For example:
|
|
>
|
|
"configurations": {
|
|
"Don't use this by default!": {
|
|
"autoselect": false,
|
|
"adapter": " ... ",
|
|
"configuation": {
|
|
// ...
|
|
}
|
|
}
|
|
}
|
|
<
|
|
Setting 'autoselect' to 'false' overrides setting 'default' to 'true'.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-exception-breakpoints*
|
|
Exception Breakpoints ~
|
|
|
|
Debug adapters have arbitrary configuration for exception breakpoints. Normally
|
|
this is presented as a series of question to the user on starting the debug
|
|
session. The question includes the name of the exception breakpoint option, the
|
|
default and the list of valid responses (usually 'Y' or 'N').
|
|
|
|
You can pre-configure the answers to these questions in the 'breakpoints'
|
|
section of the debug configuration. For each question, take the name provided
|
|
and configure the response 'exception' mapping in the 'breakpoints' mapping. If
|
|
the configured response is empty string, the debug adapter default will be
|
|
used.
|
|
|
|
Referring to the above example, the following tells the debug adapter to use
|
|
the default value for 'caught' exceptions and to break on 'uncaught' exception:
|
|
>
|
|
{
|
|
"configurations": {
|
|
"example-debug-configuration": {
|
|
"adapter": "example-adapter-name",
|
|
"breakpoints": {
|
|
"exception": {
|
|
"caught": "",
|
|
"uncaught": "Y"
|
|
}
|
|
},
|
|
...
|
|
<
|
|
The keys in the 'exception' mapping are what Vimspector includes in the prompt.
|
|
For example, when prompted with the following:
|
|
>
|
|
cpp_throw: Break on C++: on throw (Y/N/default: Y)?
|
|
<
|
|
The exception breakpoint "type" is 'cpp_throw' and the default is 'Y'.
|
|
|
|
Similarly:
|
|
>
|
|
cpp_catch: Break on C++: on catch (Y/N/default: N)?
|
|
<
|
|
The exception breakpoint "type" is 'cpp_catch' and the default is 'N'.
|
|
|
|
Use the following to set the values in configuration and not get asked:
|
|
>
|
|
"configurations": {
|
|
"example-debug-configuration": {
|
|
"adapter": "example-adapter-name",
|
|
"breakpoints": {
|
|
"exception": {
|
|
"cpp_throw": "Y",
|
|
"cpp_catch": "Y"
|
|
}
|
|
},
|
|
<
|
|
To just accept the defaults for these exception breakpoint types, don't specify
|
|
a value, as in :
|
|
>
|
|
"configurations": {
|
|
"example-debug-configuration": {
|
|
"adapter": "example-adapter-name",
|
|
"breakpoints": {
|
|
"exception": {
|
|
"cpp_throw": "",
|
|
"cpp_catch": ""
|
|
}
|
|
},
|
|
<
|
|
===============================================================================
|
|
*vimspector-ref-predefined-variables*
|
|
Predefined Variables ~
|
|
|
|
The following variables are provided:
|
|
|
|
- '${dollar}' - has the value '$', can be used to enter a literal dollar
|
|
- '$$' - a literal dollar
|
|
- '${workspaceRoot}' - the path of the folder where '.vimspector.json' was
|
|
found
|
|
- '${workspaceFolder}' - the path of the folder where '.vimspector.json' was
|
|
found
|
|
- '${gadgetDir}' - path to the OS-specific gadget dir ('<vimspector
|
|
home>/gadgets/<OS>')
|
|
- '${file}' - the current opened file
|
|
- '${relativeFile}' - the current opened file relative to 'workspaceRoot'
|
|
- '${fileBasename}' - the current opened file's 'basename'
|
|
- '${fileBasenameNoExtension}' - the current opened file's 'basename' with no
|
|
file extension
|
|
- '${fileDirname}' - the current opened file's 'dirname'
|
|
- '${fileExtname}' - the current opened file's extension
|
|
- '${cwd}' - the current working directory of the active window on launch
|
|
- '${unusedLocalPort}' - an unused local TCP port
|
|
|
|
===============================================================================
|
|
*vimspector-ref-remote-debugging-support*
|
|
Remote Debugging Support ~
|
|
|
|
Vimspector has in-built support for executing remote debuggers (such as
|
|
'gdbserver', 'debugpy', 'llvm-server' etc.). This is useful for environments
|
|
where the development is done on one host and the runtime is some other host,
|
|
account, container, etc.
|
|
|
|
In order for it to work, you have to set up paswordless SSH between the local
|
|
and remote machines/accounts. Then just tell Vimspector how to remotely launch
|
|
and/or attach to the app.
|
|
|
|
This is presented as examples with commentary, as it's a fairly advanced/niche
|
|
case. If you're not already familiar with remote debugging tools (such as
|
|
gdbserver) or not familiar with ssh or such, you might need to independently
|
|
research that.
|
|
|
|
Vimspector's tools are intended to automate your existing process for setting
|
|
this up rather than to offer batteries-included approach. Ultimately, all
|
|
Vimspector is going to do is run your commands over SSH, or docker, and
|
|
co-ordinate with the adapter.
|
|
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-python-example*
|
|
Python (debugpy) Example ~
|
|
|
|
Here is some examples using the Vimspector built-in remote support (using SSH)
|
|
to remotely launch and attach a python application and connect to it using
|
|
debugpy.
|
|
|
|
The usage pattern is to hit '<F5>', enter 'host' (the host where your app
|
|
runs), 'account' (the account it runs under), and 'port' (a port that will be
|
|
opened on the remote host). Vimspector also supports exec'ing into Docker run
|
|
containers with 'container' (the container name or id your app is running in).
|
|
Vimspector then orchestrates the various tools to set you up.
|
|
>
|
|
{
|
|
"adapters": {
|
|
"python-remote": {
|
|
"port": "${port}",
|
|
"host": "${host}",
|
|
"launch": {
|
|
"remote": {
|
|
"host": "${host}", // Remote host to ssh to (mandatory if not using container)
|
|
"account": "${account}", // User to connect as (optional)
|
|
|
|
// Optional.... Manual additional arguments for ssh
|
|
// "ssh": {
|
|
// "args": [ "-o", "StrictHostKeyChecking=no" ]
|
|
// },
|
|
|
|
// Command to launch the debugee and attach the debugger;
|
|
// %CMD% replaced with the remote-cmdLine configured in the launch
|
|
// configuration. (mandatory)
|
|
"runCommand": [
|
|
"python", "-m", "debugpy",
|
|
"--listen", "0.0.0.0:${port}",
|
|
"--wait-for-client",
|
|
"%CMD%"
|
|
]
|
|
|
|
// Optional alternative to runCommand (if you need to run multiple
|
|
// commands)
|
|
// "runCommands": [
|
|
// [ /* first command */ ],
|
|
// [ /* second command */ ]
|
|
// ]
|
|
|
|
}
|
|
|
|
// optional delay to wait after running runCommand(s). This is often
|
|
// needed because of the way docker handles TCP, or if you're using some
|
|
// wrapper (e.g. to start the JVM)
|
|
// "delay": "1000m" // format as per :help sleep
|
|
},
|
|
"attach": {
|
|
"remote": {
|
|
"host": "${host}", // Remote host to ssh to (mandatory if not using container)
|
|
"account": "${account}", // User to connect as (optional)
|
|
// Command to get the PID of the process to attach (mandatory)
|
|
"pidCommand": [
|
|
//
|
|
// Remember taht you can use ${var} to ask for input. I use this to
|
|
// call a custom command to returm the PID for a named service, so
|
|
// here's an examle:
|
|
//
|
|
"/path/to/secret/script/GetPIDForService", "${ServiceName}"
|
|
],
|
|
|
|
// Command to attach the debugger; %PID% replaced with output of
|
|
// pidCommand above (mandatory)
|
|
"attachCommand": [
|
|
"python", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
|
|
"--pid", "%PID%"
|
|
]
|
|
|
|
// Optional alternative to attachCommand (if you need to run multiple
|
|
// commands)
|
|
// "attachCommands": [
|
|
// [ /* first command */ ],
|
|
// [ /* second command */ ]
|
|
// ],
|
|
|
|
// Optional.... useful with buggy gdbservers to kill -TRAP %PID%
|
|
// "initCompleteCommand": [
|
|
// /* optional command to run after initialized */
|
|
// ]
|
|
|
|
// Optional.... Manual additional arguments for ssh
|
|
// "ssh": {
|
|
// "args": [ "-o", "StrictHostKeyChecking=no" ]
|
|
// },
|
|
}
|
|
// optional delay to wait after running runCommand(s). This is often
|
|
// needed because of the way docker handles TCP, or if you're using some
|
|
// wrapper (e.g. to start the JVM)
|
|
// "delay": "1000m" // format as per :help sleep
|
|
}
|
|
}
|
|
},
|
|
"configurations": {
|
|
"remote-launch": {
|
|
"adapter": "python-remote",
|
|
|
|
"remote-request": "launch",
|
|
"remote-cmdLine": [
|
|
"${RemoteRoot}/${fileBasename}", "*${args}"
|
|
],
|
|
|
|
"configuration": {
|
|
"request": "attach",
|
|
"pathMappings": [
|
|
{
|
|
"localRoot": "${workspaceRoot}",
|
|
"remoteRoot": "${RemoteRoot}"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"remote-attach": {
|
|
"variables": {
|
|
// Just an example of how to specify a variable manually rather than
|
|
// vimspector asking for input from the user
|
|
"ServiceName": "${fileBasenameNoExtention}"
|
|
},
|
|
|
|
"adapter": "python-remote",
|
|
"remote-request": "attach",
|
|
|
|
"configuration": {
|
|
"request": "attach",
|
|
"pathMappings": [
|
|
{
|
|
"localRoot": "${workspaceRoot}",
|
|
"remoteRoot": "${RemoteRoot}"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
<
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-c-family-example*
|
|
C-family (gdbserver) Example ~
|
|
|
|
This example uses Vimspector to remotely launch or attach to a binary using
|
|
'gdbserver' and then instructs vscode-cpptools to attach to that 'gdbserver'.
|
|
|
|
The approach is very similar to the above for python, just that we use
|
|
gdbserver and have to tell cpptools a few more options.
|
|
>
|
|
{
|
|
"adapters": {
|
|
"cpptools-remote": {
|
|
"command": [
|
|
"${gadgetDir}/vscode-cpptools/debugAdapters/OpenDebugAD7"
|
|
],
|
|
"name": "cppdbg",
|
|
"configuration": {
|
|
"type": "cppdbg"
|
|
},
|
|
"launch": {
|
|
"remote": {
|
|
"host": "${host}",
|
|
"account": "${account}",
|
|
"runCommand": [
|
|
"gdbserver",
|
|
"--once",
|
|
"--no-startup-with-shell",
|
|
"--disable-randomisation",
|
|
"0.0.0.0:${port}",
|
|
"%CMD%"
|
|
}
|
|
},
|
|
"attach": {
|
|
"remote": {
|
|
"host": "${host}",
|
|
"account": "${account}",
|
|
"pidCommand": [
|
|
"/path/to/secret/script/GetPIDForService", "${ServiceName}"
|
|
],
|
|
"attachCommand": [
|
|
"gdbserver",
|
|
"--once",
|
|
"--attach",
|
|
"0.0.0.0:${port}",
|
|
"%PID%"
|
|
],
|
|
//
|
|
// If your application is started by a wrapper script, then you might
|
|
// need the followin. GDB can't pause an application because it only
|
|
// sends the signal to the process group leader. Or something.
|
|
// Basically, if you find that everything just hangs and the
|
|
// application never attaches, try using the following to manually
|
|
// force the trap signal.
|
|
//
|
|
"initCompleteCommand": [
|
|
"kill",
|
|
"-TRAP",
|
|
"%PID%"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"configurations": {
|
|
"remote launch": {
|
|
"adapter": "cpptools-remote",
|
|
"remote-cmdLine": [ "/path/to/the/remote/executable", "args..." ],
|
|
"remote-request": "launch",
|
|
"configuration": {
|
|
"request": "attach", // yes, attach!
|
|
|
|
"program": "/path/to/the/local/executable",
|
|
"MIMode": "gdb",
|
|
"miDebuggerAddress": "${host}:${port}"
|
|
}
|
|
},
|
|
"remote attach": {
|
|
"adapter": "cpptools-remote",
|
|
"remote-request": "attach",
|
|
"configuration": {
|
|
"request": "attach",
|
|
|
|
"program": "/path/to/the/local/executable",
|
|
"MIMode": "gdb",
|
|
"miDebuggerAddress": "${host}:${port}"
|
|
}
|
|
}
|
|
}
|
|
<
|
|
-------------------------------------------------------------------------------
|
|
*vimspector-ref-docker-example*
|
|
Docker Example ~
|
|
|
|
This example uses Vimspector to remotely launch or attach to a docker container
|
|
port.
|
|
>
|
|
{
|
|
"adapters": {
|
|
"python-remote": {
|
|
"port": "${port}",
|
|
"launch": {
|
|
"remote": {
|
|
"container": "${container}", // Docker container id or name to exec into to.
|
|
|
|
// Command to launch the debugee and attach the debugger;
|
|
// %CMD% replaced with the remote-cmdLine configured in the launch
|
|
// configuration. (mandatory)
|
|
"runCommand": [
|
|
"python", "-m", "debugpy",
|
|
"--listen", "0.0.0.0:${port}",
|
|
"--wait-for-client",
|
|
"%CMD%"
|
|
]
|
|
|
|
// Optional alternative to runCommand (if you need to run multiple
|
|
// commands)
|
|
// "runCommands": [
|
|
// [ /* first command */ ],
|
|
// [ /* second command */ ]
|
|
// ]
|
|
|
|
}
|
|
|
|
// optional delay to wait after running runCommand(s). This is often
|
|
// needed because of the way docker handles TCP
|
|
"delay": "1000m" // format as per :help sleep
|
|
},
|
|
"attach": {
|
|
"remote": {
|
|
"container": "${container}", // Docker container id or name to exec into.
|
|
// Command to get the PID of the process to attach (mandatory)
|
|
// This command gets appended to "docker exec ${container}"
|
|
"pidCommand": [
|
|
//
|
|
// Remember taht you can use ${var} to ask for input. I use this to
|
|
// call a custom command to returm the PID for a named service, so
|
|
// here's an examle:
|
|
//
|
|
"sh", "-c", "pgrep", "-f", "${filename}"
|
|
],
|
|
|
|
// Command to attach the debugger; %PID% replaced with output of
|
|
// pidCommand above (mandatory)
|
|
"attachCommand": [
|
|
"sh", "-c", "python", "-m", "debugpy", "--listen", "0.0.0.0:${port}",
|
|
"--pid", "%PID%"
|
|
]
|
|
|
|
// Optional alternative to attachCommand (if you need to run multiple
|
|
// commands)
|
|
// "attachCommands": [
|
|
// [ /* first command */ ],
|
|
// [ /* second command */ ]
|
|
// ],
|
|
|
|
// Optional.... useful with buggy gdbservers to kill -TRAP %PID%
|
|
// "initCompleteCommand": [
|
|
// /* optional command to run after initialized */
|
|
// ]
|
|
|
|
}
|
|
|
|
// optional delay to wait after running runCommand(s). This is often
|
|
// needed because of the way docker handles TCP, or if you're using some
|
|
// wrapper (e.g. to start the JVM)
|
|
"delay": "1000m" // format as per :help sleep
|
|
}
|
|
}
|
|
},
|
|
"configurations": {
|
|
"remote-launch": {
|
|
"adapter": "python-remote",
|
|
|
|
"remote-request": "launch",
|
|
"remote-cmdLine": [
|
|
"${RemoteRoot}/${fileBasename}", "*${args}"
|
|
],
|
|
|
|
"configuration": {
|
|
"request": "attach",
|
|
"pathMappings": [
|
|
{
|
|
"localRoot": "${workspaceRoot}",
|
|
"remoteRoot": "${RemoteRoot}"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"remote-attach": {
|
|
"variables": {
|
|
// Just an example of how to specify a variable manually rather than
|
|
// vimspector asking for input from the user
|
|
"FileName": "${fileName}"
|
|
},
|
|
|
|
"adapter": "python-remote",
|
|
"remote-request": "attach",
|
|
|
|
"configuration": {
|
|
"request": "attach",
|
|
"pathMappings": [
|
|
{
|
|
"localRoot": "${workspaceRoot}",
|
|
"remoteRoot": "${RemoteRoot}"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
<
|
|
===============================================================================
|
|
*vimspector-ref-appendix-configuration-file-format*
|
|
Appendix: Configuration file format ~
|
|
|
|
The configuration files are text files which must be UTF-8 encoded. They
|
|
contain a single JSON object, along with optional comments.
|
|
|
|
Comments are "c-style", i.e.:
|
|
|
|
- '// single line comment ...'
|
|
- '/* inline comment */'
|
|
|
|
There is much debate about whether JSON files should contain comments. I have
|
|
added them because they are useful in the context of configuration files.
|
|
Unforutnately this may mean your editor doesn't like them (they are strictly
|
|
invalid JSON) so it's up to you if you use them.
|
|
|
|
Technically, Vimspector uses JSON minify [3] to strip comments before parsing
|
|
the JSON.
|
|
|
|
===============================================================================
|
|
*vimspector-ref-appendix-editor-configuration*
|
|
Appendix: Editor configuration ~
|
|
|
|
If you would like some assistance with writing the JSON files, and your editor
|
|
of choice has a way to use a language server, you can use the VSCode JSON
|
|
language server [4].
|
|
|
|
It is recommended to include the '$schema' declaration as in the above
|
|
examples, but if that isn't present, the following JSON language server
|
|
configuration [5] is recommened to load the schema from the Internet:
|
|
>
|
|
{
|
|
"json": {
|
|
"schemas": [
|
|
{
|
|
"fileMatch": [ ".vimspector.json" ],
|
|
"url": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json"
|
|
},
|
|
{
|
|
"fileMatch": [ ".gadgets.json", ".gadgets.d/*.json" ],
|
|
"url": "https://puremourning.github.io/vimspector/schema/gadgets.schema.json"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
<
|
|
If your language server client of choice happens to be YouCompleteMe [6], then
|
|
the following '.ycm_extra_conf.py' is good enough to get you going, after
|
|
following the instructions in the lsp-examples [7] repo to get the server set
|
|
up:
|
|
>
|
|
VIMSPECTOR_HOME = '/path/to/vimspector' # TODO: Change this
|
|
|
|
def Settings( **kwargs ):
|
|
if kwargs[ 'language' ] == 'json':
|
|
return {
|
|
'ls': {
|
|
'json': {
|
|
'schemas': [
|
|
{
|
|
'fileMatch': [ '.vimspector.json' ],
|
|
'url': f'file://{VIMSPECTOR_HOME}/docs/schema/vimspector.schema.json'
|
|
},
|
|
{
|
|
'fileMatch': [ '.gadgets.json', '.gadgets.d/*.json' ],
|
|
'url': f'file://{VIMSPECTOR_HOME}/docs/schema/gadgets.schema.json'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
return None # Or your existing Settings definition....
|
|
<
|
|
This configuration can be adapted to any other LSP-based editor configuration
|
|
and is provided just as an example.
|
|
|
|
===============================================================================
|
|
*vimspector-ref-references*
|
|
References ~
|
|
|
|
[1] https://microsoft.github.io/debug-adapter-protocol/
|
|
[2] http://puremourning.github.io/vimspector/schema/vimspector.schema.json
|
|
[3] https://github.com/getify/JSON.minify
|
|
[4] https://github.com/vscode-langservers/vscode-json-languageserver
|
|
[5] https://github.com/vscode-langservers/vscode-json-languageserver#settings
|
|
[6] https://github.com/ycm-core/YouCompleteMe
|
|
[7] https://github.com/ycm-core/lsp-examples
|
|
|
|
vim: ft=help
|