Skip to content
snare edited this page Aug 1, 2014 · 4 revisions

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
  2. Wait
  3. State
  4. Targets
  5. Registers
  6. Memory
  7. Stack
  8. Disassemble
  9. Command
  10. Backtrace
  11. Breakpoints
  12. Connect FD

All requests (except wait) can return a busy error if the debugger is busy and unable to respond.

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"
    }
}

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
    },
}

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"
    }
}

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"
            }
        ]
    }
}

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,
            ...
        }
    }
}

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..."
    }
}

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.

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
    }
}

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..."
    }
}

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
        ]
    }
}

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}
        ]
    }
}

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

Clone this wiki locally