[Debugger] Implementation of transport over serial connection (#2800)

JerryScript-DCO-1.0-Signed-off-by: Robert Sipka rsipka.uszeged@partner.samsung.com
This commit is contained in:
Robert Sipka
2019-03-26 09:39:51 +01:00
committed by GitHub
parent 0981985134
commit 3d656cdf57
14 changed files with 577 additions and 51 deletions
+5 -3
View File
@@ -68,9 +68,11 @@ When using the extension-provided WebSocket transport layer, the
debugger can be enabled by calling `jerryx_debugger_after_connect debugger can be enabled by calling `jerryx_debugger_after_connect
(jerryx_debugger_tcp_create (debug_port) && jerryx_debugger_ws_create ())` (jerryx_debugger_tcp_create (debug_port) && jerryx_debugger_ws_create ())`
after the `jerry_init ()` function. It initializes the debugger and after the `jerry_init ()` function. It initializes the debugger and
blocks until a client connects. (Custom transport layers may be blocks until a client connects.
implemented and initialized similarly. Currently, `jerryx_debugger_rp_create()` for (Custom transport layers may be implemented and initialized similarly.
raw packet transport layer is also available.) Currently, `jerryx_debugger_rp_create ()` for raw packet transport layer and
`jerryx_debugger_serial_create (const char* config)` for serial protocol
are also available.)
The resource name provided to `jerry_parse ()` is used by the client The resource name provided to `jerry_parse ()` is used by the client
to identify the resource name of the source code. This resource name to identify the resource name of the source code. This resource name
+2
View File
@@ -193,6 +193,8 @@ jerry_cleanup (void)
#ifdef JERRY_DEBUGGER #ifdef JERRY_DEBUGGER
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{ {
jerry_debugger_send_type (JERRY_DEBUGGER_CLOSE_CONNECTION);
jerry_debugger_transport_close (); jerry_debugger_transport_close ();
} }
#endif /* JERRY_DEBUGGER */ #endif /* JERRY_DEBUGGER */
+2 -2
View File
@@ -38,9 +38,9 @@ typedef struct
* The number of message types in the debugger should reflect the * The number of message types in the debugger should reflect the
* debugger versioning. * debugger versioning.
*/ */
JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT == 32 JERRY_STATIC_ASSERT (JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT == 33
&& JERRY_DEBUGGER_MESSAGES_IN_MAX_COUNT == 21 && JERRY_DEBUGGER_MESSAGES_IN_MAX_COUNT == 21
&& JERRY_DEBUGGER_VERSION == 8, && JERRY_DEBUGGER_VERSION == 9,
debugger_version_correlates_to_message_type_count); debugger_version_correlates_to_message_type_count);
/** /**
+1
View File
@@ -156,6 +156,7 @@ typedef enum
JERRY_DEBUGGER_SCOPE_CHAIN_END = 29, /**< last output of scope chain */ JERRY_DEBUGGER_SCOPE_CHAIN_END = 29, /**< last output of scope chain */
JERRY_DEBUGGER_SCOPE_VARIABLES = 30, /**< scope variables */ JERRY_DEBUGGER_SCOPE_VARIABLES = 30, /**< scope variables */
JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31, /**< last output of scope variables */ JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31, /**< last output of scope variables */
JERRY_DEBUGGER_CLOSE_CONNECTION = 32, /**< close connection with the client */
JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT, /**< number of different type of output messages by the debugger */ JERRY_DEBUGGER_MESSAGES_OUT_MAX_COUNT, /**< number of different type of output messages by the debugger */
/* Messages sent by the client to server. */ /* Messages sent by the client to server. */
+1 -1
View File
@@ -31,7 +31,7 @@ extern "C"
/** /**
* JerryScript debugger protocol version. * JerryScript debugger protocol version.
*/ */
#define JERRY_DEBUGGER_VERSION (8) #define JERRY_DEBUGGER_VERSION (9)
/** /**
* Types for the client source wait and run method. * Types for the client source wait and run method.
+34 -3
View File
@@ -24,6 +24,11 @@ import logging
import time import time
import jerry_client_main import jerry_client_main
from jerry_client_websocket import WebSocket
from jerry_client_rawpacket import RawPacket
from jerry_client_tcp import Socket
from jerry_client_serial import Serial
def write(string): def write(string):
print(string, end='') print(string, end='')
@@ -246,14 +251,40 @@ def src_check_args(args):
print("Error: Non-negative integer number expected: %s" % (val_errno)) print("Error: Non-negative integer number expected: %s" % (val_errno))
return -1 return -1
# pylint: disable=too-many-branches,too-many-locals,too-many-statements # pylint: disable=too-many-branches,too-many-locals,too-many-statements,redefined-variable-type
def main(): def main():
args = jerry_client_main.arguments_parse() args = jerry_client_main.arguments_parse()
debugger = jerry_client_main.JerryDebugger(args.address, args.channel) channel = None
protocol = None
if args.protocol == "tcp":
address = None
if ":" not in args.address:
address = (args.address, 5001) # use default port
else:
host, port = args.address.split(":")
address = (host, int(port))
protocol = Socket(address)
elif args.protocol == "serial":
protocol = Serial(args.serial_config)
else:
print("Unsupported transmission protocol")
return -1
if args.channel == "websocket":
channel = WebSocket(protocol=protocol)
elif args.channel == "rawpacket":
channel = RawPacket(protocol=protocol)
else:
print("Unsupported communication channel")
return -1
debugger = jerry_client_main.JerryDebugger(channel)
debugger.non_interactive = args.non_interactive debugger.non_interactive = args.non_interactive
logging.debug("Connected to JerryScript on %d port", debugger.port) logging.debug("Connected to JerryScript")
prompt = DebuggerPrompt(debugger) prompt = DebuggerPrompt(debugger)
prompt.prompt = "(jerry-debugger) " prompt.prompt = "(jerry-debugger) "
+12 -26
View File
@@ -21,12 +21,9 @@ import re
import select import select
import struct import struct
import sys import sys
from jerry_client_websocket import WebSocket
from jerry_client_rawpacket import RawPacket
from jerry_client_tcp import Socket
# Expected debugger protocol version. # Expected debugger protocol version.
JERRY_DEBUGGER_VERSION = 8 JERRY_DEBUGGER_VERSION = 9
# Messages sent by the server to client. # Messages sent by the server to client.
JERRY_DEBUGGER_CONFIGURATION = 1 JERRY_DEBUGGER_CONFIGURATION = 1
@@ -60,6 +57,7 @@ JERRY_DEBUGGER_SCOPE_CHAIN = 28
JERRY_DEBUGGER_SCOPE_CHAIN_END = 29 JERRY_DEBUGGER_SCOPE_CHAIN_END = 29
JERRY_DEBUGGER_SCOPE_VARIABLES = 30 JERRY_DEBUGGER_SCOPE_VARIABLES = 30
JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31 JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31
JERRY_DEBUGGER_CLOSE_CONNECTION = 32
# Debugger option flags # Debugger option flags
JERRY_DEBUGGER_LITTLE_ENDIAN = 0x1 JERRY_DEBUGGER_LITTLE_ENDIAN = 0x1
@@ -123,7 +121,7 @@ def arguments_parse():
parser = argparse.ArgumentParser(description="JerryScript debugger client") parser = argparse.ArgumentParser(description="JerryScript debugger client")
parser.add_argument("address", action="store", nargs="?", default="localhost:5001", parser.add_argument("address", action="store", nargs="?", default="localhost:5001",
help="specify a unique network address for connection (default: %(default)s)") help="specify a unique network address for tcp connection (default: %(default)s)")
parser.add_argument("-v", "--verbose", action="store_true", default=False, parser.add_argument("-v", "--verbose", action="store_true", default=False,
help="increase verbosity (default: %(default)s)") help="increase verbosity (default: %(default)s)")
parser.add_argument("--non-interactive", action="store_true", default=False, parser.add_argument("--non-interactive", action="store_true", default=False,
@@ -138,6 +136,10 @@ def arguments_parse():
help="specify a javascript source file to execute") help="specify a javascript source file to execute")
parser.add_argument("--channel", choices=["websocket", "rawpacket"], default="websocket", parser.add_argument("--channel", choices=["websocket", "rawpacket"], default="websocket",
help="specify the communication channel (default: %(default)s)") help="specify the communication channel (default: %(default)s)")
parser.add_argument("--protocol", choices=["tcp", "serial"], default="tcp",
help="specify the transmission protocol over the communication channel (default: %(default)s)")
parser.add_argument("--serial-config", metavar="CONFIG_STRING", default="/dev/ttyUSB0,115200,8,N,1",
help="Configure parameters for serial port (default: %(default)s)")
args = parser.parse_args() args = parser.parse_args()
if args.verbose: if args.verbose:
@@ -266,17 +268,7 @@ class DebuggerAction(object):
class JerryDebugger(object): class JerryDebugger(object):
# pylint: disable=too-many-instance-attributes,too-many-statements,too-many-public-methods,no-self-use,redefined-variable-type # pylint: disable=too-many-instance-attributes,too-many-statements,too-many-public-methods,no-self-use,redefined-variable-type
def __init__(self, address, channel): def __init__(self, channel):
if ":" not in address:
self.host = address
self.port = 5001 # use default port
else:
self.host, self.port = address.split(":")
self.port = int(self.port)
print("Connecting to: %s:%s" % (self.host, self.port))
self.prompt = False self.prompt = False
self.function_list = {} self.function_list = {}
self.source = '' self.source = ''
@@ -304,15 +296,7 @@ class JerryDebugger(object):
self.non_interactive = False self.non_interactive = False
self.current_out = b"" self.current_out = b""
self.current_log = b"" self.current_log = b""
self.channel = None self.channel = channel
protocol = Socket()
if channel == "websocket":
self.channel = WebSocket(address=(self.host, self.port), protocol=protocol)
elif channel == "rawpacket":
self.channel = RawPacket(address=(self.host, self.port), protocol=protocol)
else:
raise Exception("Unsupported communication channel")
config_size = 8 config_size = 8
# The server will send the configuration message after connection established # The server will send the configuration message after connection established
@@ -697,7 +681,6 @@ class JerryDebugger(object):
# pylint: disable=too-many-branches,too-many-locals,too-many-statements,too-many-return-statements # pylint: disable=too-many-branches,too-many-locals,too-many-statements,too-many-return-statements
def process_messages(self): def process_messages(self):
result = "" result = ""
while True: while True:
data = self.channel.get_message(False) data = self.channel.get_message(False)
if not self.non_interactive: if not self.non_interactive:
@@ -846,6 +829,9 @@ class JerryDebugger(object):
return DebuggerAction(DebuggerAction.TEXT, result) return DebuggerAction(DebuggerAction.TEXT, result)
elif JERRY_DEBUGGER_CLOSE_CONNECTION:
return DebuggerAction(DebuggerAction.END, "")
else: else:
raise Exception("Unknown message") raise Exception("Unknown message")
+2 -3
View File
@@ -20,14 +20,13 @@ MAX_BUFFER_SIZE = 256
class RawPacket(object): class RawPacket(object):
""" Simplified transmission layer. """ """ Simplified transmission layer. """
def __init__(self, address, protocol): def __init__(self, protocol):
self.protocol = protocol self.protocol = protocol
self.data_buffer = b"" self.data_buffer = b""
self.address = address
def connect(self, config_size): def connect(self, config_size):
""" Create connection. """ """ Create connection. """
self.protocol.connect(self.address) self.protocol.connect()
self.data_buffer = b"" self.data_buffer = b""
# It will return with the Network configurations, which has the following struct: # It will return with the Network configurations, which has the following struct:
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# Copyright JS Foundation and other contributors, http://js.foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import select
import serial
class Serial(object):
""" Create a new socket using the given address family, socket type and protocol number. """
def __init__(self, serial_config):
config = serial_config.split(',')
config_size = len(config)
port = config[0] if config_size > 0 else "/dev/ttyUSB0"
baudrate = config[1] if config_size > 1 else 115200
bytesize = int(config[2]) if config_size > 2 else 8
parity = config[3] if config_size > 3 else 'N'
stopbits = int(config[4]) if config_size > 4 else 1
self.ser = serial.Serial(port=port, baudrate=baudrate, parity=parity,
stopbits=stopbits, bytesize=bytesize, timeout=1)
def connect(self):
""" Connect to the server, write a 'c' to the serial port """
self.send_data('c')
def close(self):
"""" close the serial port. """
self.ser.close()
def receive_data(self, max_size=1024):
""" The maximum amount of data to be received at once is specified by max_size. """
return self.ser.read(max_size)
def send_data(self, data):
""" Write data to the serial port. """
return self.ser.write(data)
def ready(self):
""" Monitor the file descriptor. """
result = select.select([self.ser.fileno()], [], [], 0)[0]
return self.ser.fileno() in result
+6 -3
View File
@@ -17,17 +17,20 @@
import socket import socket
import select import select
# pylint: disable=too-many-arguments,superfluous-parens
class Socket(object): class Socket(object):
""" Create a new socket using the given address family, socket type and protocol number. """ """ Create a new socket using the given address family, socket type and protocol number. """
def __init__(self, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM, proto=0, fileno=None): def __init__(self, address, socket_family=socket.AF_INET, socket_type=socket.SOCK_STREAM, proto=0, fileno=None):
self.address = address
self.socket = socket.socket(socket_family, socket_type, proto, fileno) self.socket = socket.socket(socket_family, socket_type, proto, fileno)
def connect(self, address): def connect(self):
""" """
Connect to a remote socket at address (host, port). Connect to a remote socket at address (host, port).
The format of address depends on the address family. The format of address depends on the address family.
""" """
self.socket.connect(address) print("Connecting to: %s:%s" % (self.address[0], self.address[1]))
self.socket.connect(self.address)
def close(self): def close(self):
"""" Mark the socket closed. """ """" Mark the socket closed. """
+2 -3
View File
@@ -21,11 +21,10 @@ WEBSOCKET_BINARY_FRAME = 2
WEBSOCKET_FIN_BIT = 0x80 WEBSOCKET_FIN_BIT = 0x80
class WebSocket(object): class WebSocket(object):
def __init__(self, address, protocol): def __init__(self, protocol):
self.data_buffer = b"" self.data_buffer = b""
self.protocol = protocol self.protocol = protocol
self.address = address
def __handshake(self): def __handshake(self):
""" Client Handshake Request. """ """ Client Handshake Request. """
@@ -55,7 +54,7 @@ class WebSocket(object):
def connect(self, config_size): def connect(self, config_size):
""" WebSockets connection. """ """ WebSockets connection. """
self.protocol.connect(self.address) self.protocol.connect()
self.data_buffer = b"" self.data_buffer = b""
self.__handshake() self.__handshake()
+409
View File
@@ -0,0 +1,409 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jerryscript-debugger-transport.h"
#include "jerryscript-ext/debugger.h"
#include "jext-common.h"
#if defined JERRY_DEBUGGER && !defined WIN32
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
/* Max size of configuration string */
#define CONFIG_SIZE (255)
/**
* Implementation of transport over serial connection.
*/
typedef struct
{
jerry_debugger_transport_header_t header; /**< transport header */
int fd; /**< file descriptor */
} jerryx_debugger_transport_serial_t;
/**
* Configure parameters for a serial port.
*/
typedef struct
{
char *device_id;
uint32_t baud_rate; /**< specify the rate at which bits are transmitted for the serial interface */
uint32_t data_bits; /**< specify the number of data bits to transmit over the serial interface */
char parity; /**< specify how you want to check parity bits in the data bits transmitted via the serial port */
uint32_t stop_bits; /**< specify the number of bits used to indicate the end of a byte. */
} jerryx_debugger_transport_serial_config_t;
/**
* Correctly close a file descriptor.
*/
static inline void
jerryx_debugger_serial_close_fd (int fd) /**< file descriptor to close */
{
if (close (fd) != 0)
{
JERRYX_ERROR_MSG ("Error while closing the file descriptor: %d\n", errno);
}
} /* jerryx_debugger_serial_close_fd */
/**
* Set a file descriptor to blocking or non-blocking mode.
*
* @return true if everything is ok
* false if there was an error
**/
static bool
jerryx_debugger_serial_set_blocking (int fd, bool blocking)
{
/* Save the current flags */
int flags = fcntl (fd, F_GETFL, 0);
if (flags == -1)
{
JERRYX_ERROR_MSG ("Error %d during get flags from file descriptor\n", errno);
return false;
}
if (blocking)
{
flags &= ~O_NONBLOCK;
}
else
{
flags |= O_NONBLOCK;
}
if (fcntl (fd, F_SETFL, flags) == -1)
{
JERRYX_ERROR_MSG ("Error %d during set flags from file descriptor\n", errno);
return false;
}
return true;
} /* jerryx_debugger_serial_set_blocking */
/**
* Configure the file descriptor used by the serial communcation.
*
* @return true if everything is ok
* false if there was an error
*/
static inline bool
jerryx_debugger_serial_configure_attributes (int fd, jerryx_debugger_transport_serial_config_t serial_config)
{
struct termios options;
memset (&options, 0, sizeof (options));
/* Get the parameters associated with the file descriptor */
if (tcgetattr (fd, &options) != 0)
{
JERRYX_ERROR_MSG ("Error %d from tggetattr\n", errno);
return false;
}
/* Set the input and output baud rates */
cfsetispeed (&options, serial_config.baud_rate);
cfsetospeed (&options, serial_config.baud_rate);
/* Set the control modes */
options.c_cflag &= (uint32_t) ~CSIZE; // character size mask
options.c_cflag |= (CLOCAL | CREAD); // ignore modem control lines and enable the receiver
switch (serial_config.data_bits)
{
case 5:
{
options.c_cflag |= CS5; // set character size mask to 5-bit chars
break;
}
case 6:
{
options.c_cflag |= CS6; // set character size mask to 6-bit chars
break;
}
case 7:
{
options.c_cflag |= CS7; // set character size mask to 7-bit chars
break;
}
case 8:
{
options.c_cflag |= CS8; // set character size mask to 8-bit chars
break;
}
default:
{
JERRYX_ERROR_MSG ("Unsupported data bits: %d\n", serial_config.data_bits);
return false;
}
}
switch (serial_config.parity)
{
case 'N':
{
options.c_cflag &= (unsigned int) ~(PARENB | PARODD);
break;
}
case 'O':
{
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
}
case 'E':
{
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
}
default:
{
JERRYX_ERROR_MSG ("Unsupported parity: %c\n", serial_config.parity);
return false;
}
}
switch (serial_config.stop_bits)
{
case 1:
{
options.c_cflag &= (uint32_t) ~CSTOPB; // set 1 stop bits
break;
}
case 2:
{
options.c_cflag |= CSTOPB; // set 2 stop bits
break;
}
default:
{
JERRYX_ERROR_MSG ("Unsupported stop bits: %d\n", serial_config.stop_bits);
return false;
}
}
/* Set the input modes */
options.c_iflag &= (uint32_t) ~IGNBRK; // disable break processing
options.c_iflag &= (uint32_t) ~(IXON | IXOFF | IXANY); // disable xon/xoff ctrl
/* Set the output modes: no remapping, no delays */
options.c_oflag = 0;
/* Set the local modes: no signaling chars, no echo, no canoncial processing */
options.c_lflag = 0;
/* Read returns when at least one byte of data is available. */
options.c_cc[VMIN] = 1; // read block
options.c_cc[VTIME] = 5; // 0.5 seconds read timeout
/* Set the parameters associated with the file descriptor */
if (tcsetattr (fd, TCSANOW, &options) != 0)
{
JERRYX_ERROR_MSG ("Error %d from tcsetattr", errno);
return false;
}
/* Flushes both data received but not read, and data written but not transmitted */
if (tcflush (fd, TCIOFLUSH) != 0)
{
JERRYX_ERROR_MSG ("Error %d in tcflush() :%s\n", errno, strerror (errno));
jerryx_debugger_serial_close_fd (fd);
return false;
}
return true;
} /* jerryx_debugger_serial_configure_attributes */
/**
* Close a serial connection.
*/
static void
jerryx_debugger_serial_close (jerry_debugger_transport_header_t *header_p) /**< serial implementation */
{
JERRYX_ASSERT (!jerry_debugger_transport_is_connected ());
jerryx_debugger_transport_serial_t *serial_p = (jerryx_debugger_transport_serial_t *) header_p;
JERRYX_DEBUG_MSG ("Serial connection closed.\n");
jerryx_debugger_serial_close_fd (serial_p->fd);
jerry_heap_free ((void *) header_p, sizeof (jerryx_debugger_transport_serial_t));
} /* jerryx_debugger_serial_close */
/**
* Send data over a serial connection.
*
* @return true - if the data has been sent successfully
* false - otherwise
*/
static bool
jerryx_debugger_serial_send (jerry_debugger_transport_header_t *header_p, /**< serial implementation */
uint8_t *message_p, /**< message to be sent */
size_t message_length) /**< message length in bytes */
{
JERRYX_ASSERT (jerry_debugger_transport_is_connected ());
jerryx_debugger_transport_serial_t *serial_p = (jerryx_debugger_transport_serial_t *) header_p;
do
{
ssize_t sent_bytes = write (serial_p->fd, message_p, message_length);
if (sent_bytes < 0)
{
if (errno == EWOULDBLOCK)
{
continue;
}
JERRYX_ERROR_MSG ("Error: write to file descriptor: %d\n", errno);
jerry_debugger_transport_close ();
return false;
}
message_p += sent_bytes;
message_length -= (size_t) sent_bytes;
}
while (message_length > 0);
return true;
} /* jerryx_debugger_serial_send */
/**
* Receive data from a serial connection.
*/
static bool
jerryx_debugger_serial_receive (jerry_debugger_transport_header_t *header_p, /**< serial implementation */
jerry_debugger_transport_receive_context_t *receive_context_p) /**< receive context */
{
jerryx_debugger_transport_serial_t *serial_p = (jerryx_debugger_transport_serial_t *) header_p;
uint8_t *buffer_p = receive_context_p->buffer_p + receive_context_p->received_length;
size_t buffer_size = JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE - receive_context_p->received_length;
ssize_t length = read (serial_p->fd, buffer_p, buffer_size);
if (length <= 0)
{
if (errno != EWOULDBLOCK || length == 0)
{
jerry_debugger_transport_close ();
return false;
}
length = 0;
}
receive_context_p->received_length += (size_t) length;
if (receive_context_p->received_length > 0)
{
receive_context_p->message_p = receive_context_p->buffer_p;
receive_context_p->message_length = receive_context_p->received_length;
}
return true;
} /* jerryx_debugger_serial_receive */
/**
* Create a serial connection.
*
* @return true if successful,
* false otherwise
*/
bool
jerryx_debugger_serial_create (const char *config) /**< specify the configuration */
{
/* Parse the configuration string */
char tmp_config[CONFIG_SIZE];
strncpy (tmp_config, config, CONFIG_SIZE);
jerryx_debugger_transport_serial_config_t serial_config;
char *token = strtok (tmp_config, ",");
serial_config.device_id = token ? token : "/dev/ttyS0";
serial_config.baud_rate = (token = strtok (NULL, ",")) ? (uint32_t) strtoul (token, NULL, 10) : 115200;
serial_config.data_bits = (token = strtok (NULL, ",")) ? (uint32_t) strtoul (token, NULL, 10) : 8;
serial_config.parity = (token = strtok (NULL, ",")) ? token[0] : 'N';
serial_config.stop_bits = (token = strtok (NULL, ",")) ? (uint32_t) strtoul (token, NULL, 10) : 1;
int fd = open (serial_config.device_id, O_RDWR);
if (fd < 0)
{
JERRYX_ERROR_MSG ("Error %d opening %s: %s", errno, serial_config.device_id, strerror (errno));
return false;
}
if (!jerryx_debugger_serial_configure_attributes (fd, serial_config))
{
jerryx_debugger_serial_close_fd (fd);
return false;
}
JERRYX_DEBUG_MSG ("Waiting for client connection\n");
/* Client will sent a 'c' char to initiate the connection. */
uint8_t conn_char;
ssize_t t = read (fd, &conn_char, 1);
if (t != 1 || conn_char != 'c' || !jerryx_debugger_serial_set_blocking (fd, false))
{
return false;
}
JERRYX_DEBUG_MSG ("Client connected\n");
size_t size = sizeof (jerryx_debugger_transport_serial_t);
jerry_debugger_transport_header_t *header_p;
header_p = (jerry_debugger_transport_header_t *) jerry_heap_alloc (size);
if (!header_p)
{
jerryx_debugger_serial_close_fd (fd);
return false;
}
header_p->close = jerryx_debugger_serial_close;
header_p->send = jerryx_debugger_serial_send;
header_p->receive = jerryx_debugger_serial_receive;
((jerryx_debugger_transport_serial_t *) header_p)->fd = fd;
jerry_debugger_transport_add (header_p,
0,
JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE,
0,
JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE);
return true;
} /* jerryx_debugger_serial_create */
#else /* !JERRY_DEBUGGER || WIN32 */
/**
* Dummy function when debugger is disabled.
*
* @return false
*/
bool
jerryx_debugger_serial_create (const char *config)
{
JERRYX_UNUSED (config);
return false;
} /* jerryx_debugger_serial_create */
#endif /* JERRY_DEBUGGER */
@@ -30,6 +30,7 @@ void jerryx_debugger_after_connect (bool success);
* Message transmission interfaces. * Message transmission interfaces.
*/ */
bool jerryx_debugger_tcp_create (uint16_t port); bool jerryx_debugger_tcp_create (uint16_t port);
bool jerryx_debugger_serial_create (const char *config);
/* /*
* Message encoding interfaces. * Message encoding interfaces.
+45 -7
View File
@@ -318,6 +318,8 @@ typedef enum
OPT_DEBUG_SERVER, OPT_DEBUG_SERVER,
OPT_DEBUG_PORT, OPT_DEBUG_PORT,
OPT_DEBUG_CHANNEL, OPT_DEBUG_CHANNEL,
OPT_DEBUG_PROTOCOL,
OPT_DEBUG_SERIAL_CONFIG,
OPT_DEBUGGER_WAIT_SOURCE, OPT_DEBUGGER_WAIT_SOURCE,
OPT_EXEC_SNAP, OPT_EXEC_SNAP,
OPT_EXEC_SNAP_FUNC, OPT_EXEC_SNAP_FUNC,
@@ -349,6 +351,10 @@ static const cli_opt_t main_opts[] =
.help = "debug server port (default: 5001)"), .help = "debug server port (default: 5001)"),
CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel", .meta = "[websocket|rawpacket]", CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel", .meta = "[websocket|rawpacket]",
.help = "Specify the debugger transmission channel (default: websocket)"), .help = "Specify the debugger transmission channel (default: websocket)"),
CLI_OPT_DEF (.id = OPT_DEBUG_PROTOCOL, .longopt = "debug-protocol", .meta = "PROTOCOL",
.help = "Specify the transmission protocol over the communication channel (tcp|serial, default: tcp)"),
CLI_OPT_DEF (.id = OPT_DEBUG_SERIAL_CONFIG, .longopt = "serial-config", .meta = "OPTIONS_STRING",
.help = "Configure parameters for serial port (default: /dev/ttyS0,115200,8,N,1)"),
CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source", CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source",
.help = "wait for an executable source from the client"), .help = "wait for an executable source from the client"),
CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE", CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE",
@@ -422,21 +428,33 @@ context_alloc (size_t size,
static void static void
init_engine (jerry_init_flag_t flags, /**< initialized flags for the engine */ init_engine (jerry_init_flag_t flags, /**< initialized flags for the engine */
char *debug_channel, /**< enable the debugger init or not */ char *debug_channel, /**< enable the debugger init or not */
uint16_t debug_port) /**< the debugger port */ char *debug_protocol, /**< enable the debugger init or not */
uint16_t debug_port, /**< the debugger port for tcp protocol */
char *debug_serial_config) /**< configuration string for serial protocol */
{ {
jerry_init (flags); jerry_init (flags);
if (strcmp (debug_channel, "")) if (strcmp (debug_channel, ""))
{ {
bool tcp_created = jerryx_debugger_tcp_create (debug_port); bool protocol = false;
if (!strcmp (debug_protocol, "tcp"))
{
protocol = jerryx_debugger_tcp_create (debug_port);
}
else
{
assert (!strcmp (debug_protocol, "serial"));
protocol = jerryx_debugger_serial_create (debug_serial_config);
}
if (!strcmp (debug_channel, "rawpacket")) if (!strcmp (debug_channel, "rawpacket"))
{ {
jerryx_debugger_after_connect (tcp_created && jerryx_debugger_rp_create ()); jerryx_debugger_after_connect (protocol && jerryx_debugger_rp_create ());
} }
else else
{ {
assert (!strcmp (debug_channel, "websocket")); assert (!strcmp (debug_channel, "websocket"));
jerryx_debugger_after_connect (tcp_created && jerryx_debugger_ws_create ()); jerryx_debugger_after_connect (protocol && jerryx_debugger_ws_create ());
} }
} }
@@ -464,6 +482,8 @@ main (int argc,
bool start_debug_server = false; bool start_debug_server = false;
uint16_t debug_port = 5001; uint16_t debug_port = 5001;
char *debug_channel = "websocket"; char *debug_channel = "websocket";
char *debug_protocol = "tcp";
char *debug_serial_config = "/dev/ttyS0,115200,8,N,1";
bool is_repl_mode = false; bool is_repl_mode = false;
bool is_wait_mode = false; bool is_wait_mode = false;
@@ -542,6 +562,24 @@ main (int argc,
} }
break; break;
} }
case OPT_DEBUG_PROTOCOL:
{
if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
{
debug_protocol = (char *) cli_consume_string (&cli_state);
check_usage (!strcmp (debug_protocol, "tcp") || !strcmp (debug_protocol, "serial"),
argv[0], "Error: invalid value for --debug-protocol: ", cli_state.arg);
}
break;
}
case OPT_DEBUG_SERIAL_CONFIG:
{
if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
{
debug_serial_config = (char *) cli_consume_string (&cli_state);
}
break;
}
case OPT_DEBUGGER_WAIT_SOURCE: case OPT_DEBUGGER_WAIT_SOURCE:
{ {
if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
@@ -640,7 +678,7 @@ main (int argc,
debug_channel = ""; debug_channel = "";
} }
init_engine (flags, debug_channel, debug_port); init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config);
jerry_value_t ret_value = jerry_create_undefined (); jerry_value_t ret_value = jerry_create_undefined ();
@@ -758,7 +796,7 @@ main (int argc,
break; break;
} }
init_engine (flags, debug_channel, debug_port); init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config);
ret_value = jerry_create_undefined (); ret_value = jerry_create_undefined ();
} }
@@ -802,7 +840,7 @@ main (int argc,
jerry_cleanup (); jerry_cleanup ();
init_engine (flags, debug_channel, debug_port); init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config);
ret_value = jerry_create_undefined (); ret_value = jerry_create_undefined ();
} }