mirror of https://github.com/snare/voltron.git
Initial docs for newtron
parent
0b3f177165
commit
bc41f844db
|
@ -0,0 +1,3 @@
|
|||
# Building API plugins
|
||||
|
||||
To be completed.
|
|
@ -0,0 +1,3 @@
|
|||
# Building debugger adaptor plugins
|
||||
|
||||
To be completed.
|
|
@ -0,0 +1,3 @@
|
|||
# Building view plugins
|
||||
|
||||
To be completed.
|
|
@ -0,0 +1,3 @@
|
|||
# Building web plugins
|
||||
|
||||
To be completed.
|
|
@ -0,0 +1,15 @@
|
|||
# Extending Voltron
|
||||
|
||||
[Plugin architecture](Plugin-architecture)
|
||||
|
||||
[Building debugger adaptor plugins](Building-debugger-adaptor-plugins)
|
||||
|
||||
[Building API plugins](Building-API-plugins)
|
||||
|
||||
[Building view plugins](Building-view-plugins)
|
||||
|
||||
[Building web plugins](Building-web-plugins)
|
||||
|
||||
[Building custom clients](Building-custom-clients)
|
||||
|
||||
[JSON API reference](JSON-API-reference)
|
7
Home.md
7
Home.md
|
@ -1 +1,6 @@
|
|||
Welcome to the voltron wiki!
|
||||
# Voltron
|
||||
|
||||
Voltron is a debugger thingy.
|
||||
|
||||
The info on this wiki relates to the new version on the `lionforce` branch.
|
||||
|
||||
|
|
|
@ -0,0 +1,453 @@
|
|||
# JSON API
|
||||
|
||||
Voltron clients communicate with the back end by means of a JSON API which is exposed over a UNIX domain socket (`~/.voltron/voltron.sock`), and optionally a TCP socket and HTTP server. All of these methods expose the same API.
|
||||
|
||||
## API messages
|
||||
|
||||
All API messages are valid JSON objects containing at least a `type` field, which specifies whether the message is a request or response message.
|
||||
|
||||
Messages may contain an optional `data` field, which is a hash containing message-specific data.
|
||||
|
||||
{
|
||||
"type": "<message_type>",
|
||||
...
|
||||
"data": {
|
||||
"<message_specific_field>": "some data"
|
||||
}
|
||||
}
|
||||
|
||||
### Requests
|
||||
|
||||
A request contains at least a `type` field (which is always "request") and a `request` field that defines the type of request.
|
||||
|
||||
For example, a `state` request to get the current state of a debugger target:
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "state"
|
||||
}
|
||||
|
||||
Requests may also contain a `data` section which is used to pass request-specific parameters to the back end. For example, many request types accept a `target_id` field in the `data` section to specify a debugger target ("inferior" in GDB parlance).
|
||||
|
||||
For example, a `registers` request with a `target_id` field:
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "registers",
|
||||
"data": {
|
||||
"target_id":0
|
||||
}
|
||||
}
|
||||
|
||||
### Responses
|
||||
|
||||
Responses always have a `data` section, which either contains the result of the request, or an error code and message.
|
||||
|
||||
For example, the response to a `targets` request with an array containing the info for one target:
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"targets": [
|
||||
{
|
||||
"id": 0,
|
||||
"file": "/bin/ls",
|
||||
"arch": "x86_64"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
An error response:
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "error",
|
||||
"data": {
|
||||
"code": 0x1000,
|
||||
"message": "An error occurred"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
## Request types
|
||||
|
||||
The following request types are defined in the core API:
|
||||
|
||||
1. [Version](#version)
|
||||
2. [Wait](#wait)
|
||||
3. [State](#state)
|
||||
4. [Targets](#targets)
|
||||
5. [Registers](#registers)
|
||||
6. [Memory](#memory)
|
||||
7. [Stack](#stack)
|
||||
8. [Disassemble](#disassemble)
|
||||
9. [Command](#command)
|
||||
10. [Backtrace](#backtrace)
|
||||
11. [Breakpoints](#breakpoints)
|
||||
12. [Connect FD](#connect_fd)
|
||||
|
||||
All requests (except `wait`) can return a busy error if the debugger is busy and unable to respond.
|
||||
|
||||
## <a name="version"/>Version
|
||||
|
||||
Get the API and debugger host version.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "version"
|
||||
}
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"api_version": 1.0,
|
||||
"host_version": "lldb-something"
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="wait"/>Wait
|
||||
|
||||
Block until one of the requested state changes occurs or the timeout is up, in which case it will return an error.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "wait",
|
||||
"data" {
|
||||
"state_changes": [
|
||||
"stopped"
|
||||
],
|
||||
"timeout": 10
|
||||
}
|
||||
}
|
||||
|
||||
`state_changes` - an array of state changes to wait for. Currently only "stopped" is supported. Optional. Default is `["stopped"]`.
|
||||
`timeout` - timeout value after which the server will return an error response. Optional. Default is block indefinitely.
|
||||
|
||||
### Success response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"state": "stopped"
|
||||
}
|
||||
}
|
||||
|
||||
### Error response if the timeout is reached
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "error",
|
||||
"data": {
|
||||
"message": "The request timed out",
|
||||
"code": 4100
|
||||
},
|
||||
}
|
||||
|
||||
## <a name="state"/>State
|
||||
|
||||
Get a the state of a debugger target.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "state",
|
||||
"data": {
|
||||
"target_id": 0
|
||||
}
|
||||
}
|
||||
|
||||
### Success response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"state": "stopped"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
## <a name="targets"/>Targets
|
||||
|
||||
Get a list of the debugger's targets.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "targets"
|
||||
}
|
||||
|
||||
### Success response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"targets": [
|
||||
{
|
||||
"id": 0,
|
||||
"file": "/bin/ls",
|
||||
"arch": "x86_64"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="registers"/>Registers
|
||||
|
||||
Get the values of the CPU registers for a given target and thread.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "registers",
|
||||
"data": {
|
||||
"target_id": 0,
|
||||
"thread_id": 1234
|
||||
}
|
||||
}
|
||||
|
||||
`target_id` - the target ID from which to read register values. Optional. Default is the first target.
|
||||
`thread_id` - the thread ID from which to read register values. Optional. Default is the current thread.
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"registers": {
|
||||
"rip": 0xffffff8012341234,
|
||||
"rax": 0x4141414141414141,
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="memory"/>Memory
|
||||
|
||||
Read memory from the inferior.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "read_memory",
|
||||
"data": {
|
||||
"target_id": 0,
|
||||
"address": 0xffffff8012341234,
|
||||
"bytes": 1024,
|
||||
}
|
||||
}
|
||||
|
||||
`target_id` - the target ID from which to read memory. Optional. Default is the first target.
|
||||
`bytes` - the number of bytes to read. Required.
|
||||
`address` - the address at which to start reading.
|
||||
`register` - the register which contains the address at which to start reading.
|
||||
|
||||
Either `address` or `register` must be specified.
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"memory": "\x41\x41\x41\x41...",
|
||||
"bytes": 1024
|
||||
}
|
||||
}
|
||||
|
||||
### Error response with partial read:
|
||||
|
||||
XXX: This isn't implemented yet, probably do it
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "error",
|
||||
"data": {
|
||||
"code": 666,
|
||||
"message": "Read failed at 0xffffff8012341266, only 50 bytes read",
|
||||
"bytes": 50,
|
||||
"memory": "\x41\x41\x41\x41..."
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="stack"/>Stack
|
||||
|
||||
Read memory starting from the value contained in the inferior's stack pointer register.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "stack",
|
||||
"data": {
|
||||
"target_id": 0,
|
||||
"bytes": 512
|
||||
}
|
||||
}
|
||||
|
||||
`target_id` - the target ID from which to read stack memory. Optional. Default is the first target.
|
||||
`bytes` - the number of bytes to read. Required.
|
||||
|
||||
### Response
|
||||
|
||||
See [Memory](#memory).
|
||||
|
||||
## <a name="disassemble"/>Disassemble
|
||||
|
||||
Disassemble instructions from the inferior's memory.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "disassemble",
|
||||
"data": {
|
||||
"target_id": 0,
|
||||
"address": 0xffffff8012341234,
|
||||
"count": 16
|
||||
}
|
||||
}
|
||||
|
||||
`target_id` - the target ID. Optional.
|
||||
`address` - the address at which to start disassembling. Optional.
|
||||
`count` - the number of instructions to disassemble. Required.
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"output": "mov blah blah",
|
||||
"bytes": 1024
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="command"/>Command
|
||||
|
||||
Execute a command in the debugger host and return the output.
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "execute_command",
|
||||
"data": {
|
||||
"command": "x/32x $rsp"
|
||||
}
|
||||
}
|
||||
|
||||
`command` - the command to execute.
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"output": "0x12341234 0x12341234..."
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="backtrace"/>Backtrace
|
||||
|
||||
Get a list of the current stack of function calls in a given thread.
|
||||
|
||||
XXX: Not implemented yet
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "backtrace",
|
||||
"data": {
|
||||
"target_id": 0,
|
||||
"thread_id": 1234
|
||||
}
|
||||
}
|
||||
|
||||
### Response
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"frames": [
|
||||
etc
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="breakpoints"/>Breakpoints
|
||||
|
||||
Get a list of the breakpoints set in an inferior.
|
||||
|
||||
XXX: Not implemented yet
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "list_breakpoints",
|
||||
"data": {
|
||||
"target_id": 0
|
||||
}
|
||||
}
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success",
|
||||
"data": {
|
||||
"breakpoints": [
|
||||
{"address": 0xffffff8012341234, "enabled": true}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
## <a name="connect_fd"/>Connect FD
|
||||
|
||||
This is a special request that connects this client session directly to a file descriptor belonging to the inferior.
|
||||
|
||||
Once the response has been sent the socket will be connected to the file descriptor. Once a session is connected to an fd, it cannot be disconnected without closing the socket.
|
||||
|
||||
If the request fails, an error response will be sent to the client.
|
||||
|
||||
XXX: Not implemented yet
|
||||
|
||||
### Request
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"request": "connect_fd",
|
||||
"data": {
|
||||
"fd": 0
|
||||
}
|
||||
}
|
||||
|
||||
### Response
|
||||
|
||||
{
|
||||
"type": "response",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
# Errors
|
||||
|
||||
XXX: List all the error types here
|
|
@ -0,0 +1,91 @@
|
|||
# Plugin architecture
|
||||
|
||||
Voltron uses a plugin architecture for implementing debugger host suppprt, API methods, and UI views. Much of the core of Voltron is implemented using this plugin architecture, and it can also be used to extend Voltron to support new debugger hosts and add new custom API methods and views.
|
||||
|
||||
## Types of plugins
|
||||
|
||||
There are four types of plugins supported in Voltron.
|
||||
|
||||
### Debugger adaptor plugins
|
||||
|
||||
Debugger adaptor plugins implement the majority of the support for a given debugger host platform. There are two core debugger adaptor plugins included with Voltron which provide support for LLDB and GDB. These plugins both implement a common set of methods that are used by the core API plugins. If you want to add support for a new debugger host, you'll need to look at the code for one of these plugins in `voltron/plugins/debugger/{dbg_lldb|dbg_gdb}.py` and implement the same methods. They are reasonably well-documented and should provide a reasonable starting point.
|
||||
|
||||
More information on building API plugins can be found [here](Building-debugger-adaptor-plugins).
|
||||
|
||||
### API plugins
|
||||
|
||||
API plugins implement an API method which is accessible via the various configured listeners. These define a request and response class, and interact with a package-wide instance of one of the above debugger adaptor plugins. When an incoming API request is received, its `request` field is used to look up the appropraite API plugin to handle the request. See the included core API plugins in `voltron/plugins/api/*.py`.
|
||||
|
||||
More information on building API plugins can be found [here](Building-API-plugins).
|
||||
|
||||
### View plugins
|
||||
|
||||
View plugins provide views that can be run from the `voltron` command-line entry point. Typically these are, as are the included views, terminal-based. These plugins define a view class, which is instantiated and run by the command-line entry point program. See the included views in `voltron/plugins/views/*.py`.
|
||||
|
||||
More information on building API plugins can be found [here](Building-view-plugins).
|
||||
|
||||
### Web plugins
|
||||
|
||||
Web plugins provide views or other features that are accessible via the embedded CherryPy web server. They typically define a Flask app or just provide a directory of static which is served by the embedded web server, and use JavaScript to talk to the API back end. No web plugins are included in the core at the moment, but see the `examples` directory.
|
||||
|
||||
More information on building API plugins can be found [here](Building-web-plugins).
|
||||
|
||||
## Anatomy of a plugin
|
||||
|
||||
The bare minimum for a Voltron plugin is a Python module containing a subclass of one of the plugin classes - `APIPlugin`, `DebuggerAdaptorPlugin`, `ViewPlugin` or `WebPlugin` (see `voltron/plugin.py`). The plugin class is the top-level object in a plugin. It usually has a name and references to other resources in the plugin. For example an `APIPlugin` has a name like `wait`, and references to an `APIRequest` subclass and an `APIResponse` subclass used to handle the API request. This is what a sample API plugin class might look like:
|
||||
|
||||
class SampleAPIPlugin(APIPlugin):
|
||||
name = 'sample'
|
||||
request_class = SampleAPIRequest
|
||||
response_class = SampleAPIResponse
|
||||
|
||||
The plugin class itself typically does not contain any methods, it is just used as the top-level object to reference a plugin. The common parent of all the plugin classes, `Plugin` (in [Scruffy](https://github.com/snarez/scruffy)) utilises a metaclass, `PluginRegistry`, to collect all its subclasses as they're loaded by Scruffy. Voltron implements its own `PluginManager` class which is used as an interface to the `PluginRegistry`.
|
||||
|
||||
## Installing plugins
|
||||
|
||||
### User plugins directory
|
||||
|
||||
When Voltron is loaded into the debugger, the [Scruffy](https://github.com/snarez/scruffy) environment is initialised and plugins are discovered in two locations: the `plugins` directory within the `voltron` package, and the user plugins directory in `~/.voltron/plugins`. The latter is where custom plugins should be installed. These directories are searched recursively, and any Python module discovered within is loaded. Classes in these modules that inherit from one of the Voltron plugin classes will be recognised and loaded into the plugin registry.
|
||||
|
||||
### Registering programmatically
|
||||
|
||||
Plugins can also be loaded and registered programmatically. This may be useful for custom clients that aren't using the standard view architecture, or for loading plugins from the debugger's init script.
|
||||
|
||||
After Voltron has been loaded into the debugger, a package-wide shared instance of the `PluginManager` class will be available at `voltron.plugin.pm`. This instance can be used to programmatically register plugins like so:
|
||||
|
||||
from myplugin import MyAPIPlugin, MyAPIRequest, MyAPIResponse
|
||||
voltron.plugin.pm.register_plugin(MyAPIPlugin)
|
||||
|
||||
This will make an instance of the plugin class available through the shared `PluginManager` instance.
|
||||
|
||||
### Accessing plugins
|
||||
|
||||
The shared `PluginManager` instance `voltron.plugin.pm` can also be used to access plugins programmatically from your custom plugins or Voltron clients. There are convenience methods in `voltron/plugin.py` to make this easier.
|
||||
|
||||
For example, to instantiate an API request:
|
||||
|
||||
req = api_request('disassemble', count=16)
|
||||
|
||||
An API response:
|
||||
|
||||
res = api_response('disassemble', disassembly="push r...")
|
||||
|
||||
To allocate a view class:
|
||||
|
||||
view = view('register')
|
||||
|
||||
To allocate a new debugger adaptor instance (this would only really be useful if you were building another tool on top of Voltron):
|
||||
|
||||
adaptor = debugger_adaptor('lldb')
|
||||
|
||||
If you want to access the plugin classes themselves directly, you can call the `PluginManager` methods:
|
||||
|
||||
plugin = voltron.plugin.pm.api_plugin_for_request('wait')
|
||||
|
||||
plugin = voltron.plugin.pm.debugger_plugin_for_host('lldb')
|
||||
|
||||
plugin = voltron.plugin.pm.view_plugin_with_name('register')
|
||||
|
||||
plugin = voltron.plugin.pm.web_plugin_with_name('angularview')
|
||||
|
||||
The package-wide debugger adaptor is located at `voltron.debugger`. This can be used from your custom API plugins to query the debugger host back end. See some of the core API plugins and the included LLDB and GDB host adaptor plugins for usage.
|
Loading…
Reference in New Issue