diff --git a/jerry-core/debugger/jerry-debugger.c b/jerry-core/debugger/jerry-debugger.c index dabc82280..3adecdbe8 100644 --- a/jerry-core/debugger/jerry-debugger.c +++ b/jerry-core/debugger/jerry-debugger.c @@ -225,6 +225,14 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec { /* Process the received message. */ + if (recv_buffer_p[0] >= JERRY_DEBUGGER_CONTINUE + && !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_BREAKPOINT_MODE)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Message requires breakpoint mode\n"); + jerry_debugger_close_connection (); + return false; + } + if (*expected_message_type_p != 0) { JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART); @@ -466,10 +474,14 @@ jerry_debugger_breakpoint_hit (void) return; } + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) | JERRY_DEBUGGER_BREAKPOINT_MODE); + while (!jerry_debugger_receive ()) { } + JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) & ~JERRY_DEBUGGER_BREAKPOINT_MODE); + JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; } /* jerry_debugger_breakpoint_hit */ diff --git a/jerry-core/debugger/jerry-debugger.h b/jerry-core/debugger/jerry-debugger.h index f155a0e1b..df6eb272a 100644 --- a/jerry-core/debugger/jerry-debugger.h +++ b/jerry-core/debugger/jerry-debugger.h @@ -43,15 +43,40 @@ #define JERRY_DEBUGGER_SEND_MAX(type) \ ((JERRY_DEBUGGER_MAX_SEND_SIZE - sizeof (jerry_debugger_send_header_t) - 1) / sizeof (type)) +/** + * Debugger operation modes: + * + * The debugger has two operation modes: run mode and breakpoint mode. + * + * In run mode the debugger server accepts only a limited number of message + * types from the debugger client (e.g. stop execution, set breakpoint). + * + * In breakpoint mode the JavaScript execution is stopped at a breakpoint and + * more message types are accepted (e.g. get backtrace, evaluate expression). + * + * Switching between modes: + * + * When the JavaScript execution stops at a breakpoint the server sends a + * JERRY_DEBUGGER_BREAKPOINT_HIT message to the client. The client can only + * issue breakpoint mode commands after this message is received. + * + * Certain breakpoint mode commands (e.g. continue) resumes the JavaScript + * execution and the client must not send any breakpoint mode messages + * until the JERRY_DEBUGGER_BREAKPOINT_HIT is received again. + * + * The debugger server starts in run mode but stops at the first available + * breakpoint. + */ + /** * Debugger option flags. */ typedef enum { JERRY_DEBUGGER_CONNECTED = 1u << 0, /**< debugger is connected */ - JERRY_DEBUGGER_VM_STOP = 1u << 1, /**< stop at the next breakpoint - * regardless it is enabled */ - JERRY_DEBUGGER_VM_IGNORE = 1u << 2, /**< ignore all breakpoints */ + JERRY_DEBUGGER_BREAKPOINT_MODE = 1u << 1, /**< debugger waiting at a breakpoint */ + JERRY_DEBUGGER_VM_STOP = 1u << 2, /**< stop at the next breakpoint regardless it is enabled */ + JERRY_DEBUGGER_VM_IGNORE = 1u << 3, /**< ignore all breakpoints */ } jerry_debugger_flags_t; /** @@ -82,12 +107,18 @@ typedef enum JERRY_DEBUGGER_EVAL_ERROR_END = 20, /**< last part of eval result when an error is occured */ /* Messages sent by the client to server. */ + + /* The following messages are accepted in both run and breakpoint modes. */ JERRY_DEBUGGER_FREE_BYTE_CODE_CP = 1, /**< free byte code compressed pointer */ JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2, /**< update breakpoint status */ JERRY_DEBUGGER_STOP = 3, /**< stop execution */ + /* The following messages are only available in breakpoint + * mode and they switch the engine to run mode. */ JERRY_DEBUGGER_CONTINUE = 4, /**< continue execution */ JERRY_DEBUGGER_STEP = 5, /**< next breakpoint, step into functions */ JERRY_DEBUGGER_NEXT = 6, /**< next breakpoint in the same context */ + /* The following messages are only available in breakpoint + * mode and this mode is kept after the message is processed. */ JERRY_DEBUGGER_GET_BACKTRACE = 7, /**< get backtrace */ JERRY_DEBUGGER_EVAL = 8, /**< first message of evaluating a string */ JERRY_DEBUGGER_EVAL_PART = 9, /**< next message of evaluating a string */ diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 5b89f2c43..d5a17bf0f 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -2321,19 +2321,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); - if (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE) - { - /* Messages are still processed regardless of ignore. */ - if (JERRY_CONTEXT (debugger_message_delay) > 0) - { - JERRY_CONTEXT (debugger_message_delay)--; - continue; - } - - JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; - jerry_debugger_receive (); - continue; - } + JERRY_ASSERT (!(frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE)); frame_ctx_p->byte_code_p = byte_code_start_p; @@ -2351,19 +2339,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); - if (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE) - { - /* Messages are still processed regardless of ignore. */ - if (JERRY_CONTEXT (debugger_message_delay) > 0) - { - JERRY_CONTEXT (debugger_message_delay)--; - continue; - } - - JERRY_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY; - jerry_debugger_receive (); - continue; - } + JERRY_ASSERT (!(frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE)); frame_ctx_p->byte_code_p = byte_code_start_p; diff --git a/jerry-debugger/jerry-client-ws.html b/jerry-debugger/jerry-client-ws.html index 0c58761fd..25f7ec9ca 100644 --- a/jerry-debugger/jerry-client-ws.html +++ b/jerry-debugger/jerry-client-ws.html @@ -894,10 +894,43 @@ function DebuggerClient(address) } } + this.sendResumeExec = function(command) + { + if (!lastBreakpointHit) + { + appendLog("This command is allowed only if JavaScript execution is stopped at a breakpoint."); + return; + } + + encodeMessage("B", [ command ]); + + lastBreakpointHit = null; + } + + this.sendGetBacktrace = function(depth) + { + if (!lastBreakpointHit) + { + appendLog("This command is allowed only if JavaScript execution is stopped at a breakpoint."); + return; + } + + encodeMessage("BI", [ JERRY_DEBUGGER_GET_BACKTRACE, max_depth ]); + + appendLog("Backtrace:"); + } + this.sendEval = function(str) { + if (!lastBreakpointHit) + { + appendLog("This command is allowed only if JavaScript execution is stopped at a breakpoint."); + return; + } + if (str == "") { + appendLog("Argument required"); return; } @@ -1054,23 +1087,24 @@ function debuggerCommand(event) debuggerObj.deleteBreakpoint(args[2]); break; + case "st": case "stop": debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STOP ]); break; case "c": case "continue": - debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_CONTINUE ]); + debuggerObj.sendResumeExec(JERRY_DEBUGGER_CONTINUE); break; case "s": case "step": - debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STEP ]); + debuggerObj.sendResumeExec(JERRY_DEBUGGER_STEP); break; case "n": case "next": - debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_NEXT ]); + debuggerObj.sendResumeExec(JERRY_DEBUGGER_NEXT); break; case "e": @@ -1095,9 +1129,7 @@ function debuggerCommand(event) } } - appendLog("Backtrace:"); - - debuggerObj.encodeMessage("BI", [ JERRY_DEBUGGER_GET_BACKTRACE, max_depth ]); + debuggerObj.sendGetBacktrace(max_depth); break; case "src": diff --git a/jerry-debugger/jerry-client-ws.py b/jerry-debugger/jerry-client-ws.py index 05a25d742..5ea4fa7d8 100755 --- a/jerry-debugger/jerry-client-ws.py +++ b/jerry-debugger/jerry-client-ws.py @@ -68,6 +68,7 @@ def arguments_parse(): parser.add_argument("address", action="store", nargs="?", default="localhost:5001", help="specify a unique network address for 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, help="disable stop when newline is pressed (default: %(default)s)") args = parser.parse_args() @@ -140,7 +141,7 @@ class DebuggerPrompt(Cmd): self.debugger = debugger self.stop = False self.quit = False - self.cont = False + self.cont = True def precmd(self, line): self.stop = False @@ -188,12 +189,14 @@ class DebuggerPrompt(Cmd): def do_step(self, args): """ Next breakpoint, step into functions """ self.exec_command(args, JERRY_DEBUGGER_STEP) + self.cont = True do_s = do_step def do_next(self, args): """ Next breakpoint in the same context """ self.exec_command(args, JERRY_DEBUGGER_NEXT) + self.cont = True do_n = do_next @@ -669,13 +672,15 @@ def main(): debugger = JerryDebugger(args.address) + non_interactive = args.non_interactive + logging.debug("Connected to JerryScript on %d port" % (debugger.port)) prompt = DebuggerPrompt(debugger) prompt.prompt = "(jerry-debugger) " while True: - if prompt.cont: + if not non_interactive and prompt.cont: if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: sys.stdin.readline() prompt.cont = False diff --git a/tools/runners/run-debugger-test.sh b/tools/runners/run-debugger-test.sh index 2fdf3cde8..d5f80a8f3 100755 --- a/tools/runners/run-debugger-test.sh +++ b/tools/runners/run-debugger-test.sh @@ -24,7 +24,7 @@ echo "$START_DEBUG_SERVER" eval "$START_DEBUG_SERVER" sleep 1s -RESULT=$((cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT}) 2>&1) +RESULT=$((cat "${TEST_CASE}.cmd" | ${DEBUGGER_CLIENT} --non-interactive) 2>&1) DIFF=$(diff -u0 ${TEST_CASE}.expected <(echo "$RESULT")) if [ -n "$DIFF" ]