Skip to content

Debugger Transport APIs and Zephyr port #2247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions jerry-core/api/jerry-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,44 @@ jerry_debugger_stop_at_breakpoint (bool enable_stop_at_breakpoint) /**< enable/d
* Debugger server initialization. Must be called after jerry_init.
*/
void
jerry_debugger_init (uint16_t port) /**< server port number */
jerry_debugger_init (jerry_debugger_transport_t *transport_p) /**< transport */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this port specific definition part of the public API?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the jerry_debugger_transport_t type? Yes, this is intended and part of the jerry_port_ api where each platform have to provide their own definition, hence why it was forward declared, so it can build.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately "it can build" is not a strong enough reason for something to be part of the public API.

While I am ok with having a structure for communication, the public API part needs to be independent of everything: no jerry port dependency, no compiler define dependency, etc.

I think the first question is: do we want multiple transport layer support, or the recv/send/close could be provided by jerry port.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zherczeg When I originally started the the port, it was a simpler patch to just port the socket APIs, but I think @martijnthe really like to support multiple transports, so we kinds modified it to take a struct of handlers. I mean we can submit a simpler patch to ports only socket calls and then a separate patch later to rework the APIs to support multiple transports, I think it's up to you and the JerryScript maintainers to make this kind of decision. Honestly, my goal is to at least get the websockets working on Zephyr as part of this effort. If we move the the struct definitions and APIs to from jerryscript-port to jerryscript-debugger.h, would that work for you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this port specific definition part of the public API?

The definition should NOT be part of the public API, only the forward declaration should be. That way there is no dependency. (Until one actually want to use the debugger feature, in which case one has to pick an implementation which would contain the definition of the struct as well.)

I think the first question is: do we want multiple transport layer support

Yes, at least in our project we want to mix and match between 3 transports at run-time.

IMHO, a stronger reason for wanting a more dynamic transport interface as opposed to extending the "port surface" with static functions for the transport, is that static functions would make combining, composing and re-using transport implementations harder.

For example in our case, we want USB, BLE and a "pseudo transport" that just gathers the debug info (i.e. for printing human consumable exception traces). Imagine that someone already wrote each of these transports separately. Each transport would define the same set of functions/symbols. If I want to create a new transport that combines/chains/... them, I'd have to rename each function to avoid conflicts. Even if a simple "switch" between 2 transports would involve renaming each function.

{
#ifdef JERRY_DEBUGGER
JERRY_CONTEXT (debugger_port) = port;
JERRY_CONTEXT (debugger_transport_p) = transport_p;
JERRY_ASSERT (JERRY_CONTEXT (debugger_transport_p) != NULL);
JERRY_ASSERT (JERRY_CONTEXT (debugger_transport_p)->accept_connection != NULL);
JERRY_ASSERT (JERRY_CONTEXT (debugger_transport_p)->close_connection != NULL);
JERRY_ASSERT (JERRY_CONTEXT (debugger_transport_p)->send != NULL);
JERRY_ASSERT (JERRY_CONTEXT (debugger_transport_p)->receive != NULL);
jerry_debugger_accept_connection ();
#else /* !JERRY_DEBUGGER */
JERRY_UNUSED (port);
JERRY_UNUSED (transport_p);
#endif /* JERRY_DEBUGGER */
} /* jerry_debugger_init */

/**
* Debugger transport transmission sizes, each transport will need to set these.
*/
void
jerry_debugger_set_transmit_sizes (size_t send_header_size, /**< transport send header size */
size_t max_send_size, /**< transport max send size */
size_t receive_header_size, /**< transport receive header size */
size_t max_receive_size) /**< transport max receive size */
{
#ifdef JERRY_DEBUGGER
JERRY_CONTEXT (debugger_send_header_size) = (uint8_t) send_header_size;
JERRY_CONTEXT (debugger_max_send_size) = (uint8_t) max_send_size;
JERRY_CONTEXT (debugger_receive_header_size) = (uint8_t) receive_header_size;
JERRY_CONTEXT (debugger_max_receive_size) = (uint8_t) max_receive_size;
#else /* !JERRY_DEBUGGER */
JERRY_UNUSED (send_header_size);
JERRY_UNUSED (max_send_size);
JERRY_UNUSED (receive_header_size);
JERRY_UNUSED (max_receive_size);
#endif /* JERRY_DEBUGGER */
} /* jerry_debugger_set_transmit_sizes */


/**
* Sets whether the engine should wait and run a source.
*
Expand Down
8 changes: 1 addition & 7 deletions jerry-core/debugger/debugger-ws.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,12 @@ typedef struct
/**
* Byte data for evaluating expressions and receiving client source.
*/
typedef struct
typedef struct jerry_debugger_uint8_data_t
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems to be unjustified. Current code base adds names to those structs only that are recursive. Here, it's not the case.

{
uint32_t uint8_size; /**< total size of the client source */
uint32_t uint8_offset; /**< current offset in the client source */
} jerry_debugger_uint8_data_t;

bool jerry_debugger_accept_connection (void);
void jerry_debugger_close_connection (void);

bool jerry_debugger_send (size_t data_size);
bool jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p);

void jerry_debugger_compute_sha1 (const uint8_t *input1, size_t input1_len,
const uint8_t *input2, size_t input2_len,
uint8_t output[20]);
Expand Down
162 changes: 162 additions & 0 deletions jerry-core/debugger/debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -1068,4 +1068,166 @@ jerry_debugger_send_exception_string (void)
return result;
} /* jerry_debugger_send_exception_string */

/**
* Initialize the debugger connection.
*
* @return true - if the connection succeeded
* false - otherwise
*/
bool
jerry_debugger_accept_connection (void)
{
uint8_t *payload_p = JERRY_CONTEXT (debugger_send_buffer) + JERRY_CONTEXT (debugger_send_header_size);
JERRY_CONTEXT (debugger_send_buffer_payload_p) = payload_p;

uint8_t max_send_size = (uint8_t) (JERRY_DEBUGGER_MAX_BUFFER_SIZE -
JERRY_CONTEXT (debugger_send_header_size));
if (max_send_size <= JERRY_CONTEXT (debugger_max_send_size))
{
JERRY_CONTEXT (debugger_max_send_size) = max_send_size;
}

uint8_t max_receive_size = (uint8_t) (JERRY_DEBUGGER_MAX_BUFFER_SIZE -
JERRY_CONTEXT (debugger_receive_header_size));
if (max_receive_size <= JERRY_CONTEXT (debugger_max_send_size))
{
JERRY_CONTEXT (debugger_max_receive_size) = max_receive_size;
}

if (!JERRY_CONTEXT (debugger_transport_p)->accept_connection (JERRY_CONTEXT (debugger_transport_p)))
{
return false;
}

JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_CONNECTED);

if (!jerry_debugger_send_configuration (max_receive_size))
{
return false;
}

JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_STOP);
JERRY_CONTEXT (debugger_stop_context) = NULL;
return true;
} /* jerry_debugger_accept_connection */

/**
* Close the connection to the client.
*/
void
jerry_debugger_close_connection (void)
{
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
JERRY_CONTEXT (debugger_flags) = JERRY_DEBUGGER_VM_IGNORE;

JERRY_CONTEXT (debugger_transport_p)->close_connection (JERRY_CONTEXT (debugger_transport_p));
jerry_debugger_free_unreferenced_byte_code ();
} /* jerry_debugger_close_connection */

/**
* Send message to the client side
*
* @return true - if the data was sent successfully to the debugger client,
* false - otherwise
*/
bool
jerry_debugger_send (size_t data_size) /**< data size */
{
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
JERRY_ASSERT (data_size <= JERRY_CONTEXT (debugger_max_send_size));

if (!JERRY_CONTEXT (debugger_transport_p)->send (JERRY_CONTEXT (debugger_transport_p),
JERRY_CONTEXT (debugger_send_buffer),
data_size))
{
jerry_debugger_close_connection ();
return false;
}

return true;
} /* jerry_debugger_send */

/**
* Receive message from the client.
*
* Note:
* If the function returns with true, the value of
* JERRY_DEBUGGER_VM_STOP flag should be ignored.
*
* @return true - if execution should be resumed,
* false - otherwise
*/
bool
jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p) /**< [out] data received from client */
{
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
JERRY_ASSERT (message_data_p != NULL ? !!(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE)
: !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_RECEIVE_DATA_MODE));

JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY;

bool resume_exec = false;
uint8_t expected_message_type = 0;
size_t message_size;

while (true)
{
uint32_t offset = JERRY_CONTEXT (debugger_receive_buffer_offset);
bool success = JERRY_CONTEXT (debugger_transport_p)->receive (JERRY_CONTEXT (debugger_transport_p),
JERRY_CONTEXT (debugger_receive_buffer),
&message_size,
&offset);

if (!success)
{
jerry_debugger_close_connection ();
return true;
}

if (offset < JERRY_CONTEXT (debugger_receive_header_size))
{
if (expected_message_type != 0)
{
continue;
}

return resume_exec;
}

uint32_t message_total_size = (uint32_t) (message_size +
JERRY_CONTEXT (debugger_receive_header_size));

if (offset < message_total_size)
{
if (expected_message_type != 0)
{
continue;
}

return resume_exec;
}

/* The jerry_debugger_process_message function is inlined
* so passing these arguments is essentially free. */
if (!jerry_debugger_process_message (JERRY_CONTEXT (debugger_receive_buffer) +
JERRY_CONTEXT (debugger_receive_header_size),
(uint32_t) message_size,
&resume_exec,
&expected_message_type,
(jerry_debugger_uint8_data_t**) message_data_p))
{
return true;
}

if (message_total_size < offset)
{
memmove (JERRY_CONTEXT (debugger_receive_buffer),
JERRY_CONTEXT (debugger_receive_buffer) + message_total_size,
offset - message_total_size);
}

JERRY_CONTEXT (debugger_receive_buffer_offset) = (uint16_t) (offset - message_total_size);
}
} /* jerry_debugger_receive */

#endif /* JERRY_DEBUGGER */
11 changes: 11 additions & 0 deletions jerry-core/debugger/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ typedef enum
JERRY_DEBUGGER_CONTEXT_RESET_MODE = 1u << 10, /**< debugger and engine reinitialization mode */
} jerry_debugger_flags_t;

/**
* Waiting for data from the client.
*/
#define JERRY_DEBUGGER_RECEIVE_DATA_MODE \
(JERRY_DEBUGGER_BREAKPOINT_MODE | JERRY_DEBUGGER_CLIENT_SOURCE_MODE)

/**
* Set debugger flags.
*/
Expand Down Expand Up @@ -415,6 +421,11 @@ bool jerry_debugger_send_parse_function (uint32_t line, uint32_t column);
void jerry_debugger_send_memstats (void);
bool jerry_debugger_send_exception_string (void);

bool jerry_debugger_accept_connection (void);
void jerry_debugger_close_connection (void);
bool jerry_debugger_send (size_t data_size);
bool jerry_debugger_receive (jerry_debugger_uint8_data_t **message_data_p);

#endif /* JERRY_DEBUGGER */

#endif /* !DEBUGGER_H */
8 changes: 7 additions & 1 deletion jerry-core/include/jerryscript-debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@ typedef jerry_value_t (*jerry_debugger_wait_for_source_callback_t) (const jerry_
/**
* Engine debugger functions.
*/
void jerry_debugger_init (uint16_t port);
typedef struct jerry_debugger_transport_t jerry_debugger_transport_t;

void jerry_debugger_init (jerry_debugger_transport_t *transport_p);
void jerry_debugger_set_transmit_sizes (size_t send_header_size,
size_t max_send_size,
size_t receive_header_size,
size_t max_receive_size);
bool jerry_debugger_is_connected (void);
void jerry_debugger_stop (void);
void jerry_debugger_continue (void);
Expand Down
24 changes: 24 additions & 0 deletions jerry-core/include/jerryscript-port.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,30 @@ double jerry_port_get_current_time (void);
*/
struct jerry_instance_t *jerry_port_get_current_instance (void);

/*
* Debugger Port API
*/

/* forward declaration */
struct jerry_debugger_uint8_data_t;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forward declaration also seems to be unnecessary, especially in this public header. The type is only used in sources internal to jerry-core. If I see it right.


/**
* Transport APIs, different transport will define
* the real implementation of these APIs
*/
struct jerry_debugger_transport_t
{
bool (*accept_connection) (struct jerry_debugger_transport_t *transport_p);
void (*close_connection) (struct jerry_debugger_transport_t *transport_p);
bool (*send) (struct jerry_debugger_transport_t *transport_p,
uint8_t *message_data_p,
size_t data_size);
bool (*receive) (struct jerry_debugger_transport_t *transport_p,
uint8_t *message_data_p,
size_t *data_size,
uint32_t *buffer_offset);
};

/**
* Makes the process sleep for a given time.
*
Expand Down
5 changes: 3 additions & 2 deletions jerry-core/jcontext/jcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,12 @@ typedef struct
jmem_cpointer_t debugger_byte_code_free_tail; /**< tail of byte code free linked list */
uint32_t debugger_flags; /**< debugger flags */
uint16_t debugger_receive_buffer_offset; /**< receive buffer offset */
uint16_t debugger_port; /**< debugger socket communication port */
uint8_t debugger_message_delay; /**< call receive message when reaches zero */
uint8_t debugger_send_header_size; /**< header size reserved when sending */
uint8_t debugger_receive_header_size; /**< header size reserved when receiving */
uint8_t debugger_max_send_size; /**< maximum amount of data that can be written */
uint8_t debugger_max_receive_size; /**< maximum amount of data that can be received */
int debugger_connection; /**< holds the file descriptor of the socket communication */
jerry_debugger_transport_t *debugger_transport_p; /**< holds the pointer to the debugger transport */
#endif /* JERRY_DEBUGGER */

#ifdef JERRY_ENABLE_LINE_INFO
Expand Down
4 changes: 2 additions & 2 deletions jerry-main/main-unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ main (int argc,
jerry_init (flags);
if (start_debug_server)
{
jerry_debugger_init (debug_port);
jerry_debugger_init (jerry_port_default_init_socket_transport (debug_port));
}

register_js_function ("assert", jerryx_handler_assert);
Expand Down Expand Up @@ -710,7 +710,7 @@ main (int argc,
jerry_cleanup ();

jerry_init (flags);
jerry_debugger_init (debug_port);
jerry_debugger_init (jerry_port_default_init_socket_transport (debug_port));

register_js_function ("assert", jerryx_handler_assert);
register_js_function ("gc", jerryx_handler_gc);
Expand Down
Loading