JSON API reference

snare edited this page Apr 9, 2016 · 4 revisions

JSON API

Voltron clients communicate with the back end by means of an HTTP/JSON API which is exposed over a UNIX domain socket (~/.voltron/voltron.sock), and/or a TCP socket.

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

Blocking

Requests may also specify a block parameter. Some debugger hosts support asynchronous requests (LLDB) and some only support synchronous requests (GDB). This capability is indicated by the capabilities field returned in a version response. If block is true, the request will be deferred until the next time the debugger stops (for example, until the user steps over another instruction, or until the user interrupts the debugger or a breakpoint is hit if the inferior is currently running). The debugger then processes any blocking requests on its main thread, and the response will be sent.

The request may also specify a timeout parameter for blocking requests. This defaults to 30 seconds if not specified. If block is true, the HTTP server will block until either the request has been processed, or the timeout is hit. If the request times out, an error response will be returned. If the request is fulfilled, the appropriate response will be returned.

Here is an example of a blocking request with a timeout of 10 seconds:

{
    "type":         "request",
    "request":      "registers",
    "block": true,
    "timeout": 10,
    "data": {
        "target_id":0
    }
}

If the timeout is hit, an error response like this is sent:

{
    "type": "response",
    "status": "error",
    "data": {
        "message": "The request timed out",
        "code": 4100
    }
}

Voltron's built-in views support both asynchronous and blocking methods, which means that they can update immediately upon connecting to LLDB, or wait until the debugger is stepped for GDB (so we don't crash GDB by poking at the inferior from a background thread). Have a look at them for examples of how to support both methods.

Requests

The following requests are defined in the core API:

  1. Version
  2. State
  3. Targets
  4. Registers
  5. Memory
  6. Stack
  7. Disassemble
  8. Command
  9. Backtrace
  10. Breakpoints

All requests may 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"
    }
}

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

Request

{
    "type":         "request",
    "request":      "breakpoints",
    "data": {
        "target_id":    0
    }
}

Response

{
    "type":         "response",
    "status":       "success",
    "data": {
        "breakpoints": [
            {
                "id":           1,
                "enabled":      True,
                "one_shot":     False,
                "hit_count":    5,
                "locations": [
                    {
                        "address":  0x100000cf0,
                        "name":     "main"
                    }
                ]
            }
        ]

    }
}

Errors

XXX: List all the error types here