diff --git a/.travis.yml b/.travis.yml index 7c2d0b6b8a..80d359e421 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,10 +14,12 @@ matrix: - name: "Checks" env: - OPTS="--check-signed-off=travis --check-cppcheck --check-doxygen --check-vera --check-license --check-magic-strings --check-pylint" - install: pip install --user pylint==1.6.5 + install: + - pip install --user pylint==1.6.5 + - pip install --user pybluez addons: apt: - packages: [doxygen, cppcheck, vera++] + packages: [doxygen, cppcheck, vera++, libbluetooth-dev, python-dev] - name: "Linux/x86-64 Build & Correctness Tests" env: diff --git a/docs/07.DEBUGGER.md b/docs/07.DEBUGGER.md index 3c119f7f0d..0a43ba1383 100644 --- a/docs/07.DEBUGGER.md +++ b/docs/07.DEBUGGER.md @@ -9,7 +9,7 @@ This simple application demonstrates the communication protocol between the client and server, and can be reused by integrated development environments. -## Setting up the debugger server +## Setting up TCP/IP debugger server The following arguments must be passed to `tools/build.py`: @@ -21,7 +21,23 @@ JerryScript extension, which transmits messages over TCP/IP networks. If necessary/implemented, any reliable stream or datagram based protocol can be used for transmitting debugger messages. -## Debugging JavaScript applications +## Setting up Bluetooth debugger server + +The following arguments must be passed to `tools/build.py`: + +`--jerry-bt-debugger=on` + +The transport layer of the communication protocol is pluggable. +At the moment, a WebSocket-based implementation is provided as a +JerryScript extension, which transmits messages over Bluetooth network. + +The following library is needed to build JerryScript +- libbluetooth-dev + +The following python commands needed to run jerry-client.py +- sudo apt-get install -q python-dev +- pip install --user pylint==1.6.5 pybluez + The debugger client must be connected to the server before the JavaScript application runs. On-the-fly attachment is supported @@ -43,6 +59,12 @@ source code: `--debugger-wait-source` +The following argument makes JerryScript create a server with +the given type. There are two choices, tcp or bluetooth. +(default: tcp) + +`--server-type bluetooth` + It is also recommended to increase the log level to see the *Waiting for client connection* message: diff --git a/jerry-debugger/jerry_client.py b/jerry-debugger/jerry_client.py index e56ca96c67..8613fd3e8c 100755 --- a/jerry-debugger/jerry_client.py +++ b/jerry-debugger/jerry_client.py @@ -18,10 +18,10 @@ from cmd import Cmd from pprint import pprint import math -import socket import sys import logging import time +import re import jerry_client_ws def write(string): @@ -291,8 +291,14 @@ def main(): if __name__ == "__main__": try: main() - except socket.error as error_msg: + except IOError as error_msg: ERRNO = error_msg.errno + if not ERRNO: + # if there is no ERRNO then try to get it from the error arguments + MATCH = re.search(r"(\d+)", error_msg.args[0]) + if MATCH: + ERRNO = int(MATCH.group(1)) + MSG = str(error_msg) if ERRNO == 111: sys.exit("Failed to connect to the JerryScript debugger.") diff --git a/jerry-debugger/jerry_client_ws.py b/jerry-debugger/jerry_client_ws.py index defee5b7d8..75a2211ca4 100644 --- a/jerry-debugger/jerry_client_ws.py +++ b/jerry-debugger/jerry_client_ws.py @@ -266,15 +266,24 @@ def get_text(self): class JerryDebugger(object): - # pylint: disable=too-many-instance-attributes,too-many-statements,too-many-public-methods,no-self-use + # pylint: disable=too-many-instance-attributes,too-many-statements,too-many-public-methods,no-self-use,too-many-branches def __init__(self, address): - + temp = address.split(":") if ":" not in address: self.host = address self.port = 5001 # use default port + self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + elif len(temp) == 2: + self.host = temp[0] + self.port = int(temp[1]) + self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + elif len(temp) == 6: + import bluetooth + self.host = address + self.port = 1 # use default port + self.client_socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM) else: - self.host, self.port = address.split(":") - self.port = int(self.port) + raise Exception("Wrong address type") print("Connecting to: %s:%s" % (self.host, self.port)) @@ -303,7 +312,6 @@ def __init__(self, address): self.nocolor = '' self.src_offset = 0 self.src_offset_diff = 0 - self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client_socket.connect((self.host, self.port)) self.non_interactive = False self.current_out = b"" @@ -385,7 +393,11 @@ def __init__(self, address): self.message_data = result[len_expected:] def __del__(self): - self.client_socket.close() + try: + self.client_socket.close() + except AttributeError: + pass + def _exec_command(self, command_id): self.send_command(command_id) diff --git a/jerry-ext/CMakeLists.txt b/jerry-ext/CMakeLists.txt index 4a3f0504f7..33467c8204 100644 --- a/jerry-ext/CMakeLists.txt +++ b/jerry-ext/CMakeLists.txt @@ -18,9 +18,13 @@ project (${JERRY_EXT_NAME} C) # Optional features set(FEATURE_INIT_FINI OFF CACHE BOOL "Enable init/fini arrays?") +set(FEATURE_BT_DEBUGGER OFF CACHE BOOL "Enable JerryScript bluetooth?") + # Status messages message(STATUS "FEATURE_INIT_FINI " ${FEATURE_INIT_FINI}) +message(STATUS "FEATURE_BT_DEBUGGER " ${FEATURE_BT_DEBUGGER}) + # Include directories set(INCLUDE_EXT_PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") @@ -32,6 +36,10 @@ if(FEATURE_INIT_FINI) set(DEFINES_EXT ${DEFINES_EXT} ENABLE_INIT_FINI) endif() +if(FEATURE_BT_DEBUGGER AND FEATURE_DEBUGGER) + set(DEFINES_EXT ${DEFINES_EXT} JERRY_BT_DEBUGGER) +endif() + # Source directories file(GLOB SOURCE_EXT arg/*.c @@ -49,3 +57,7 @@ target_link_libraries(${JERRY_EXT_NAME} jerry-core) install(TARGETS ${JERRY_EXT_NAME} DESTINATION lib) install(DIRECTORY ${INCLUDE_EXT_PUBLIC}/ DESTINATION include) + +if(FEATURE_BT_DEBUGGER) + target_link_libraries(${JERRY_EXT_NAME} -lbluetooth) +endif() diff --git a/jerry-ext/debugger/debugger-bluetooth.c b/jerry-ext/debugger/debugger-bluetooth.c new file mode 100644 index 0000000000..5f9bc7c3d3 --- /dev/null +++ b/jerry-ext/debugger/debugger-bluetooth.c @@ -0,0 +1,264 @@ +/* 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" + +#ifdef JERRY_BT_DEBUGGER + +#include +#include +#include +#include +#include +#include + +/** + * Implementation of transport over bluetooth. + */ +typedef struct +{ + jerry_debugger_transport_header_t header; /**< transport header */ + int bluetooth_socket; /**< bluetooth socket */ +} jerryx_debugger_transport_bluetooth_t; + +/** + * Log bluetooth error message. + */ +static void +jerryx_debugger_bt_log_error (int err_val) +{ + JERRYX_ERROR_MSG ("bluetooth Error: %s\n", strerror (err_val)); +} /* jerryx_debugger_bt_log_error */ + +/** + * Close a bluetooth connection. + */ +static void +jerryx_debugger_bt_close (jerry_debugger_transport_header_t *header_p) /**< bluetooth implementation */ +{ + JERRYX_ASSERT (!jerry_debugger_transport_is_connected ()); + + jerryx_debugger_transport_bluetooth_t *bluetooth_p = (jerryx_debugger_transport_bluetooth_t *) header_p; + + JERRYX_DEBUG_MSG ("bluetooth connection closed\n"); + + close (bluetooth_p->bluetooth_socket); + + jerry_heap_free ((void *) header_p, sizeof (jerryx_debugger_transport_bluetooth_t)); +} /* jerryx_debugger_bt_close */ + +/** + * Send data over a bluetooth connection. + * + * @return true - if the data has been sent successfully + * false - otherwise + */ +static bool +jerryx_debugger_bt_send (jerry_debugger_transport_header_t *header_p, /**< bluetooth 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_bluetooth_t *bluetooth_p = (jerryx_debugger_transport_bluetooth_t *) header_p; + + do + { + ssize_t is_err = recv (bluetooth_p->bluetooth_socket, NULL, 0, MSG_PEEK); + + if (is_err == 0 && errno != EWOULDBLOCK) + { + int err_val = errno; + jerry_debugger_transport_close (); + jerryx_debugger_bt_log_error (err_val); + return false; + } + ssize_t sent_bytes = send (bluetooth_p->bluetooth_socket, message_p, message_length, 0); + + if (sent_bytes < 0) + { + if (errno == EWOULDBLOCK) + { + continue; + } + + int err_val = errno; + jerry_debugger_transport_close (); + jerryx_debugger_bt_log_error (err_val); + return false; + } + + message_p += sent_bytes; + message_length -= (size_t) sent_bytes; + } + while (message_length > 0); + + return true; +} /* jerryx_debugger_bt_send */ + +/** + * Receive data from a bluetooth connection. + */ +static bool +jerryx_debugger_bt_receive (jerry_debugger_transport_header_t *header_p, /**< bluetooth implementation */ + jerry_debugger_transport_receive_context_t *receive_context_p) /**< receive context */ +{ + jerryx_debugger_transport_bluetooth_t *bluetooth_p = (jerryx_debugger_transport_bluetooth_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 = recv (bluetooth_p->bluetooth_socket, buffer_p, buffer_size, 0); + + if (length <= 0) + { + if (errno != EWOULDBLOCK || length == 0) + { + int err_val = errno; + jerry_debugger_transport_close (); + jerryx_debugger_bt_log_error (err_val); + 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_bt_receive */ + +/** + * Create a bluetooth connection. + * + * @return true if successful, + * false otherwise + */ +bool +jerryx_debugger_bt_create (uint16_t port) /**< listening port */ +{ + struct sockaddr_rc loc_addr; + int server_socket; + char buf[1024] = { 0 }; + socklen_t bluetooth_size = sizeof (struct sockaddr_rc); + + loc_addr.rc_family = AF_BLUETOOTH; + loc_addr.rc_bdaddr = *BDADDR_ANY; + loc_addr.rc_channel = (uint8_t) port; + + if ((server_socket = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) == -1) + { + jerryx_debugger_bt_log_error (errno); + return false; + } + + int opt_value = 1; + + if (setsockopt (server_socket, SOL_SOCKET, SO_REUSEADDR, &opt_value, sizeof (int)) == -1) + { + close (server_socket); + jerryx_debugger_bt_log_error (errno); + return false; + } + + if (bind (server_socket, (struct sockaddr *)&loc_addr, sizeof (loc_addr)) == -1) + { + close (server_socket); + jerryx_debugger_bt_log_error (errno); + return false; + } + + if (listen (server_socket, port) == -1) + { + close (server_socket); + jerryx_debugger_bt_log_error (errno); + return false; + } + + JERRYX_DEBUG_MSG ("Waiting for client connection\n"); + + int bluetooth_socket = accept (server_socket, (struct sockaddr *)&loc_addr, &bluetooth_size); + + close (server_socket); + + if (bluetooth_socket == -1) + { + jerryx_debugger_bt_log_error (errno); + return false; + } + + /* Set non-blocking mode. */ + int socket_flags = fcntl (bluetooth_socket, F_GETFL, 0); + if (socket_flags < 0) + { + close (bluetooth_socket); + return false; + } + + if (fcntl (bluetooth_socket, F_SETFL, socket_flags | O_NONBLOCK) == -1) + { + close (bluetooth_socket); + return false; + } + ba2str (&loc_addr.rc_bdaddr, buf); + JERRYX_DEBUG_MSG ("Connected from: %s\n",buf); + size_t size = sizeof (jerryx_debugger_transport_bluetooth_t); + jerry_debugger_transport_header_t *header_p; + header_p = (jerry_debugger_transport_header_t *) jerry_heap_alloc (size); + + if (!header_p) + { + close (bluetooth_socket); + return false; + } + + header_p->close = jerryx_debugger_bt_close; + header_p->send = jerryx_debugger_bt_send; + header_p->receive = jerryx_debugger_bt_receive; + + ((jerryx_debugger_transport_bluetooth_t *) header_p)->bluetooth_socket = bluetooth_socket; + + jerry_debugger_transport_add (header_p, + 0, + JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE, + 0, + JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE); + + return true; + +} /* jerryx_debugger_bt_create */ + +#else /* !JERRY_BT_DEBUGGER */ +/** + * Dummy function when bluetooth debugger is disabled. + * + * @return false + */ +bool +jerryx_debugger_bt_create (uint16_t port) +{ + JERRYX_ERROR_MSG ("support for Bluetooth debugging is disabled.\n"); + JERRYX_UNUSED (port); + return false; +} /* jerryx_debugger_bt_create */ + +#endif /* JERRY_BT_DEBUGGER */ diff --git a/jerry-ext/include/jerryscript-ext/debugger.h b/jerry-ext/include/jerryscript-ext/debugger.h index dd6cf93aff..523db448e1 100644 --- a/jerry-ext/include/jerryscript-ext/debugger.h +++ b/jerry-ext/include/jerryscript-ext/debugger.h @@ -31,6 +31,8 @@ void jerryx_debugger_after_connect (bool success); */ bool jerryx_debugger_tcp_create (uint16_t port); +bool jerryx_debugger_bt_create (uint16_t port); + /* * Message encoding interfaces. */ diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index b8e5801614..98922085f3 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -316,6 +316,7 @@ typedef enum OPT_SHOW_OP, OPT_SHOW_RE_OP, OPT_DEBUG_SERVER, + OPT_DEBUG_SERVER_TYPE, OPT_DEBUG_PORT, OPT_DEBUGGER_WAIT_SOURCE, OPT_EXEC_SNAP, @@ -325,6 +326,13 @@ typedef enum OPT_NO_PROMPT } main_opt_id_t; +/** + * Definition of jerryx debugger layer create function pointer + */ +typedef bool (*jerryx_debugger_transport_create_t) (uint16_t port); + +static jerryx_debugger_transport_create_t debugger_layer_create_cb = jerryx_debugger_tcp_create; + /** * Command line options */ @@ -344,6 +352,9 @@ static const cli_opt_t main_opts[] = .help = "dump regexp byte-code"), CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "start-debug-server", .help = "start debug server and wait for a connecting client"), + CLI_OPT_DEF (.id = OPT_DEBUG_SERVER_TYPE, .longopt = "debug-server-type", .meta = "TYPE", + .help = "choose a server type (choices : tcp or bluetooth) \ + (default: tcp)"), CLI_OPT_DEF (.id = OPT_DEBUG_PORT, .longopt = "debug-port", .meta = "NUM", .help = "debug server port (default: 5001)"), CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source", @@ -419,15 +430,16 @@ context_alloc (size_t size, static void init_engine (jerry_init_flag_t flags, /**< initialized flags for the engine */ bool debug_server, /**< enable the debugger init or not */ + jerryx_debugger_transport_create_t layer_inited, /**< choose which type or server create */ uint16_t debug_port) /**< the debugger port */ { jerry_init (flags); if (debug_server) { - jerryx_debugger_after_connect (jerryx_debugger_tcp_create (debug_port) - && jerryx_debugger_ws_create ()); + jerryx_debugger_after_connect (layer_inited (debug_port) && jerryx_debugger_ws_create ()); } + register_js_function ("assert", jerryx_handler_assert); register_js_function ("gc", jerryx_handler_gc); register_js_function ("print", jerryx_handler_print); @@ -511,6 +523,20 @@ main (int argc, } break; } + case OPT_DEBUG_SERVER_TYPE: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + const char *temp = cli_consume_string (&cli_state); + if (strcmp (temp, "bluetooth") == 0) + { + debug_port = 1; + debugger_layer_create_cb = jerryx_debugger_bt_create; + } + + } + break; + } case OPT_DEBUG_PORT: { if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) @@ -612,7 +638,7 @@ main (int argc, #endif /* JERRY_ENABLE_EXTERNAL_CONTEXT */ - init_engine (flags, start_debug_server, debug_port); + init_engine (flags, start_debug_server, debugger_layer_create_cb, debug_port); jerry_value_t ret_value = jerry_create_undefined (); @@ -730,7 +756,7 @@ main (int argc, break; } - init_engine (flags, true, debug_port); + init_engine (flags, true, debugger_layer_create_cb, debug_port); ret_value = jerry_create_undefined (); } @@ -774,7 +800,7 @@ main (int argc, jerry_cleanup (); - init_engine (flags, true, debug_port); + init_engine (flags, true, debugger_layer_create_cb, debug_port); ret_value = jerry_create_undefined (); } diff --git a/tools/build.py b/tools/build.py index cc6816d1e3..3b31aef836 100755 --- a/tools/build.py +++ b/tools/build.py @@ -110,6 +110,8 @@ def devhelp(helpstring): help='enable external context (%(choices)s)') coregrp.add_argument('--jerry-debugger', metavar='X', choices=['ON', 'OFF'], type=str.upper, help='enable the jerry debugger (%(choices)s)') + coregrp.add_argument('--jerry-bt-debugger', metavar='X', choices=['ON', 'OFF'], type=str.upper, + help='enable the jerry bluetooth debugger (%(choices)s)') coregrp.add_argument('--js-parser', metavar='X', choices=['ON', 'OFF'], type=str.upper, help='enable js-parser (%(choices)s)') coregrp.add_argument('--line-info', metavar='X', choices=['ON', 'OFF'], type=str.upper, @@ -186,6 +188,7 @@ def build_options_append(cmakeopt, cliarg): build_options_append('FEATURE_ERROR_MESSAGES', arguments.error_messages) build_options_append('FEATURE_EXTERNAL_CONTEXT', arguments.external_context) build_options_append('FEATURE_DEBUGGER', arguments.jerry_debugger) + build_options_append('FEATURE_BT_DEBUGGER', arguments.jerry_bt_debugger) build_options_append('FEATURE_JS_PARSER', arguments.js_parser) build_options_append('FEATURE_LINE_INFO', arguments.line_info) build_options_append('FEATURE_LOGGING', arguments.logging)