[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:
+5
-3
@@ -68,9 +68,11 @@ When using the extension-provided WebSocket transport layer, the
|
||||
debugger can be enabled by calling `jerryx_debugger_after_connect
|
||||
(jerryx_debugger_tcp_create (debug_port) && jerryx_debugger_ws_create ())`
|
||||
after the `jerry_init ()` function. It initializes the debugger and
|
||||
blocks until a client connects. (Custom transport layers may be
|
||||
implemented and initialized similarly. Currently, `jerryx_debugger_rp_create()` for
|
||||
raw packet transport layer is also available.)
|
||||
blocks until a client connects.
|
||||
(Custom transport layers may be implemented and initialized similarly.
|
||||
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
|
||||
to identify the resource name of the source code. This resource name
|
||||
|
||||
@@ -193,6 +193,8 @@ jerry_cleanup (void)
|
||||
#ifdef JERRY_DEBUGGER
|
||||
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
||||
{
|
||||
jerry_debugger_send_type (JERRY_DEBUGGER_CLOSE_CONNECTION);
|
||||
|
||||
jerry_debugger_transport_close ();
|
||||
}
|
||||
#endif /* JERRY_DEBUGGER */
|
||||
|
||||
@@ -38,9 +38,9 @@ typedef struct
|
||||
* The number of message types in the debugger should reflect the
|
||||
* 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_VERSION == 8,
|
||||
&& JERRY_DEBUGGER_VERSION == 9,
|
||||
debugger_version_correlates_to_message_type_count);
|
||||
|
||||
/**
|
||||
|
||||
@@ -156,6 +156,7 @@ typedef enum
|
||||
JERRY_DEBUGGER_SCOPE_CHAIN_END = 29, /**< last output of scope chain */
|
||||
JERRY_DEBUGGER_SCOPE_VARIABLES = 30, /**< 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 */
|
||||
|
||||
/* Messages sent by the client to server. */
|
||||
|
||||
@@ -31,7 +31,7 @@ extern "C"
|
||||
/**
|
||||
* JerryScript debugger protocol version.
|
||||
*/
|
||||
#define JERRY_DEBUGGER_VERSION (8)
|
||||
#define JERRY_DEBUGGER_VERSION (9)
|
||||
|
||||
/**
|
||||
* Types for the client source wait and run method.
|
||||
|
||||
@@ -24,6 +24,11 @@ import logging
|
||||
import time
|
||||
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):
|
||||
print(string, end='')
|
||||
|
||||
@@ -246,14 +251,40 @@ def src_check_args(args):
|
||||
print("Error: Non-negative integer number expected: %s" % (val_errno))
|
||||
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():
|
||||
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
|
||||
|
||||
logging.debug("Connected to JerryScript on %d port", debugger.port)
|
||||
logging.debug("Connected to JerryScript")
|
||||
|
||||
prompt = DebuggerPrompt(debugger)
|
||||
prompt.prompt = "(jerry-debugger) "
|
||||
|
||||
@@ -21,12 +21,9 @@ import re
|
||||
import select
|
||||
import struct
|
||||
import sys
|
||||
from jerry_client_websocket import WebSocket
|
||||
from jerry_client_rawpacket import RawPacket
|
||||
from jerry_client_tcp import Socket
|
||||
|
||||
# Expected debugger protocol version.
|
||||
JERRY_DEBUGGER_VERSION = 8
|
||||
JERRY_DEBUGGER_VERSION = 9
|
||||
|
||||
# Messages sent by the server to client.
|
||||
JERRY_DEBUGGER_CONFIGURATION = 1
|
||||
@@ -60,6 +57,7 @@ JERRY_DEBUGGER_SCOPE_CHAIN = 28
|
||||
JERRY_DEBUGGER_SCOPE_CHAIN_END = 29
|
||||
JERRY_DEBUGGER_SCOPE_VARIABLES = 30
|
||||
JERRY_DEBUGGER_SCOPE_VARIABLES_END = 31
|
||||
JERRY_DEBUGGER_CLOSE_CONNECTION = 32
|
||||
|
||||
# Debugger option flags
|
||||
JERRY_DEBUGGER_LITTLE_ENDIAN = 0x1
|
||||
@@ -123,7 +121,7 @@ def arguments_parse():
|
||||
parser = argparse.ArgumentParser(description="JerryScript debugger client")
|
||||
|
||||
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,
|
||||
help="increase verbosity (default: %(default)s)")
|
||||
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")
|
||||
parser.add_argument("--channel", choices=["websocket", "rawpacket"], default="websocket",
|
||||
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()
|
||||
|
||||
if args.verbose:
|
||||
@@ -266,17 +268,7 @@ class DebuggerAction(object):
|
||||
|
||||
class JerryDebugger(object):
|
||||
# pylint: disable=too-many-instance-attributes,too-many-statements,too-many-public-methods,no-self-use,redefined-variable-type
|
||||
def __init__(self, address, 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))
|
||||
|
||||
def __init__(self, channel):
|
||||
self.prompt = False
|
||||
self.function_list = {}
|
||||
self.source = ''
|
||||
@@ -304,15 +296,7 @@ class JerryDebugger(object):
|
||||
self.non_interactive = False
|
||||
self.current_out = b""
|
||||
self.current_log = b""
|
||||
self.channel = None
|
||||
|
||||
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")
|
||||
self.channel = channel
|
||||
|
||||
config_size = 8
|
||||
# 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
|
||||
def process_messages(self):
|
||||
result = ""
|
||||
|
||||
while True:
|
||||
data = self.channel.get_message(False)
|
||||
if not self.non_interactive:
|
||||
@@ -846,6 +829,9 @@ class JerryDebugger(object):
|
||||
|
||||
return DebuggerAction(DebuggerAction.TEXT, result)
|
||||
|
||||
elif JERRY_DEBUGGER_CLOSE_CONNECTION:
|
||||
return DebuggerAction(DebuggerAction.END, "")
|
||||
|
||||
else:
|
||||
raise Exception("Unknown message")
|
||||
|
||||
|
||||
@@ -20,14 +20,13 @@ MAX_BUFFER_SIZE = 256
|
||||
|
||||
class RawPacket(object):
|
||||
""" Simplified transmission layer. """
|
||||
def __init__(self, address, protocol):
|
||||
def __init__(self, protocol):
|
||||
self.protocol = protocol
|
||||
self.data_buffer = b""
|
||||
self.address = address
|
||||
|
||||
def connect(self, config_size):
|
||||
""" Create connection. """
|
||||
self.protocol.connect(self.address)
|
||||
self.protocol.connect()
|
||||
self.data_buffer = b""
|
||||
|
||||
# It will return with the Network configurations, which has the following struct:
|
||||
|
||||
@@ -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
|
||||
@@ -17,17 +17,20 @@
|
||||
import socket
|
||||
import select
|
||||
|
||||
# pylint: disable=too-many-arguments,superfluous-parens
|
||||
class Socket(object):
|
||||
""" 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)
|
||||
|
||||
def connect(self, address):
|
||||
def connect(self):
|
||||
"""
|
||||
Connect to a remote socket at address (host, port).
|
||||
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):
|
||||
"""" Mark the socket closed. """
|
||||
|
||||
@@ -21,11 +21,10 @@ WEBSOCKET_BINARY_FRAME = 2
|
||||
WEBSOCKET_FIN_BIT = 0x80
|
||||
|
||||
class WebSocket(object):
|
||||
def __init__(self, address, protocol):
|
||||
def __init__(self, protocol):
|
||||
|
||||
self.data_buffer = b""
|
||||
self.protocol = protocol
|
||||
self.address = address
|
||||
|
||||
def __handshake(self):
|
||||
""" Client Handshake Request. """
|
||||
@@ -55,7 +54,7 @@ class WebSocket(object):
|
||||
|
||||
def connect(self, config_size):
|
||||
""" WebSockets connection. """
|
||||
self.protocol.connect(self.address)
|
||||
self.protocol.connect()
|
||||
self.data_buffer = b""
|
||||
self.__handshake()
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
bool jerryx_debugger_tcp_create (uint16_t port);
|
||||
bool jerryx_debugger_serial_create (const char *config);
|
||||
|
||||
/*
|
||||
* Message encoding interfaces.
|
||||
|
||||
+45
-7
@@ -318,6 +318,8 @@ typedef enum
|
||||
OPT_DEBUG_SERVER,
|
||||
OPT_DEBUG_PORT,
|
||||
OPT_DEBUG_CHANNEL,
|
||||
OPT_DEBUG_PROTOCOL,
|
||||
OPT_DEBUG_SERIAL_CONFIG,
|
||||
OPT_DEBUGGER_WAIT_SOURCE,
|
||||
OPT_EXEC_SNAP,
|
||||
OPT_EXEC_SNAP_FUNC,
|
||||
@@ -349,6 +351,10 @@ static const cli_opt_t main_opts[] =
|
||||
.help = "debug server port (default: 5001)"),
|
||||
CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel", .meta = "[websocket|rawpacket]",
|
||||
.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",
|
||||
.help = "wait for an executable source from the client"),
|
||||
CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE",
|
||||
@@ -422,21 +428,33 @@ context_alloc (size_t size,
|
||||
static void
|
||||
init_engine (jerry_init_flag_t flags, /**< initialized flags for the engine */
|
||||
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);
|
||||
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"))
|
||||
{
|
||||
jerryx_debugger_after_connect (tcp_created && jerryx_debugger_rp_create ());
|
||||
jerryx_debugger_after_connect (protocol && jerryx_debugger_rp_create ());
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
uint16_t debug_port = 5001;
|
||||
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_wait_mode = false;
|
||||
@@ -542,6 +562,24 @@ main (int argc,
|
||||
}
|
||||
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:
|
||||
{
|
||||
if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
|
||||
@@ -640,7 +678,7 @@ main (int argc,
|
||||
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 ();
|
||||
|
||||
@@ -758,7 +796,7 @@ main (int argc,
|
||||
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 ();
|
||||
}
|
||||
@@ -802,7 +840,7 @@ main (int argc,
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user