Skip to content

V5 Debugger Server Protocol v1

All programming, documentation, and spec authoring was done by Hunter Stasonis

This program and its documentation have been placed under the MIT License, and is provided without warranty of any kind

Constants and Definitions

Throughout this document many words many be underlined with a dotted line, hovering over these words will show their definition.

Communication

v5dbg communicates over USB serial. The debug server opens the pseudofile provided by the PROS kernel located at /ser/sout in write mode, messages are serialized and written to this file using fwrite. COBS and stream multiplexing should be disabled for this file so data can be read raw by the debugger.

Messages

Messages are payloads of data sent from either the debugger, or debug server. All messages are formatted as follows, left to right.

  • Message begin
  • Protocol version
  • Message type
  • Message payload
  • Newline

An example message can be seen as %1:1:0, message type one is program suspend. It should be noted that if more than two message separator characters are used then any located after the third are ignored and are merged into the message payload.

If we have the message %1:2:0:1:2:3 then the message payload should be 0:1:2:3, the extra message separator characters do not mess up the parser state and does not throw a warning or error of any kind

Message Types

Message types are defined as enums within v5dbg/protocol.h (include directory), the DEBUGGER_MESSAGE_MAX is used as a basic check to determine if a message type is invalid.

Message IDs can be considered unsigned ints since they never can be negative, and a message should be assumed invalid or corrupted if so.

enum v5dbg_message_type_e
{
  /// @brief  Connection opened
  DEBUGGER_MESSAGE_OPEN = 0,

  /// @brief  Request program suspension, assume it has occurred when a DEBUG_MESSAGE_RSUSPEND is parsed
  DEBUGGER_MESSAGE_SUSPEND = 1,

  /// @brief  Connection closed
  DEBUGGER_MESSAGE_CLOSE = 2,

  /// @brief  Allocate a string, can be processed by the debugger however it likes
  DEBUGGER_MESSAGE_ALLOCATE_STRING = 3,

  /// @brief  Resume the program from a suspended state
  DEBUGGER_MESSAGE_RESUME = 4,

  /// @brief  Respond with DEBUGGER_MESSAGE_RTHREADS containing a comma separated list of every thread being managed by
  /// v5dbg
  DEBUGGER_MESSAGE_THREADS = 5,

  /// @brief  Return message for DEBUGGER_MESSAGE_THREADS
  DEBUGGER_MESSAGE_RTHREADS = 6,

  /// @brief  Get the vstack for the given thread index
  DEBUGGER_MESSAGE_VSTACK_FOR = 7,

  /// @brief  Return message for DEBUGGER_MESSAGE_VSTACK_FOR, keep accepting messages until DEBUGGER_MESSAGE_VSTACK_END
  DEBUGGER_MESSAGE_RVSTACK = 8,

  /// @brief  Stop accepting DEBUGGER_MESSAGE_RVSTACK messages
  DEBUGGER_MESSAGE_VSTACK_END = 9,

  /// @brief  List memory for the given stack frame with thread ID
  DEBUGGER_MESSAGE_LMEM_FOR = 10,

  /// @brief  Returned message for DEBUGGER_MESSAGE_LMEM_FOR, keep accepting messages until DEBUGGER_MESSAGE_LMEM_END
  DEBUGGER_MESSAGE_RLMEM = 11,

  /// @brief  Stop accepting DEBUGGER_MESSAGE_RLMEM messages
  DEBUGGER_MESSAGE_LMEM_END = 12,

  /// @brief  Max debugger message ID
  DEBUGGER_MESSAGE_MAX = 13
};

Subarguments

Subarguments introduce a way for messages to include more complex data in their 3rd data field. Since the message parser only parses up to the 2nd message separator character the third data field can have message separators located in it.

The subargs parser allows for another array of elements to be placed into the data field, the simplest way to perform this would be to split the data field on the message separator character. If we want to include data inside the data field that has message separators(such as C++ typenames) we need an escape character.

The [ and ] character

When the first character of an element is an [ we ignore any message separator characters we come across until we encounter a ] character located before a message separator or a newline.

Without subarguments

Imagine we have this message:

%1:1:std::vector<int>:helloWorld

It's parameters are broken down into:

  • 1
  • 1
  • std::vector<int>:helloWorld

When we split the data parameter of the message by the message separator we get:

  • std
  • :vector<int>
  • helloWorld

This IS NOT what we want!

With subarguments

Taking our old string from the previous example and adding subargumrnts we get:

%1:1:[std::vector<int>]:helloWorld

It's parameters are broken down the same way during message parsing

Now instead of splitting on the message seperator the subargument parser is called creating:

  • std::vector<int>
  • helloWorld

This IS what we want!

References

You can find the official implementation of a subargs parser on GitHub

Behaviors

Messages may be marked with icons, hovering over them will show information about the specific message type. Messages that do not state their argument type can be assumed to not use subarguments, or comma splitting.

If messages have more than one argument they will specify their argument type next to their enum name in the title.

DEBUGGER_MESSAGE_OPEN

Sent by the debug server to the debugger at program startup and every 2 seconds that the program is running. The debugger will only connect to the debug server if it can detect and read this message, the debugger will also print a hang message if it fails to receive an OPEN message atleast once every 5 seconds.

DEBUGGER_MESSAGE_ALLOCATE_STRING

When sent to the debug server this message sent unaltered back to the debugger, this was used for testing.

DEBUGGER_MESSAGE_SUSPEND

Requests the debug server to suspend all of it's supervised tasks, no response is sent to the client.

DEBUGGER_MESSAGE_RESUME

Requests the debug server to resume all of it's supervised tasks, no response is sent to the client.

DEBUGGER_MESSAGE_THREADS

Responds with a single DEBUGGER_MESSAGE_RTHREADS message containing all the debugger's supervised threads.

DEBUGGER_MESSAGE_RTHREADS

Response to a DEBUGGER_MESSAGE_THREADS message.

The following parameters repeat for every thread.

Parameter Docs
Index 0 String Name of the thread
Index 1 unsigned int ID of the thread
Example message

%1:6:Worker Thread,0,Odom Thread,1,OpControl,2

DEBUGGER_MESSAGE_VSTACK_FOR

When sent to the debug server it returns a series of DEBUGGER_MESSAGE_RVSTACK messages for each frame of the stack before ending with a DEBUGGER_MESSAGE_VSTACK_END message.

Parameter Docs
Index 0 unsigned int Thread ID to grab the callstack for

DEBUGGER_MESSAGE_RVSTACK

This message is sent repeatedly until a DEBUGGER_MESSAGE_VSTACK_END message is sent.

Parameter Docs
Index 0 unsigned int of the frame ID
Index 1 String Name of the function
Index 2 String File path to the function
Index 3 unsigned int Line number inside of the file where this function begins

DEBUGGER_MESSAGE_VSTACK_END

This message is sent to signal that the debugger can stop waiting for DEBUGGER_MESSAGE_RVSTACK messages.

The data field may be filled with any data the server wishes but it can be ignored.

DEBUGGER_MESSAGE_LMEM_FOR

This message is sent to the debug server to request a series of DEBUGGER_MESSAGE_RLMEM messages terminated by a DEBUGGER_MESSAGE_LMEM_END which contains captured local memory.

Parameter Docs
Index 0 unsigned int Frame ID
Index 1 unsigned int Thread ID

DEBUGGER_MESSAGE_RLMEM

This message is sent repeatedly until a DEBUGGER_MESSAGE_LMEM_END message is sent.

Parameter Docs
Index 0 String C++ typename of this variable
Index 1 String Name of this variable
Index 2 String Path to the file this variable is in
Index 3 unsigned int Line number this variable was exposed on
Index 4 String Pretty printed buffer for this variable

DEBUGGER_MESSAGE_LMEM_END

This message is sent to signal that the debugger can stop waiting for DEBUGGER_MESSAGE_RLMEM messages.

The data field may be filled with any data the server wishes but it can be ignored.