Skip to content

Commit

Permalink
Introducing debugger modes (run, breakpoint). (#1645)
Browse files Browse the repository at this point in the history
This makes the code more robust and error prone, since certain
messages cannot be received in certain situations, e.g. an eval
command during garbage collection.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
  • Loading branch information
zherczeg authored Mar 7, 2017
1 parent c6f22a9 commit a20b9df
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 38 deletions.
12 changes: 12 additions & 0 deletions jerry-core/debugger/jerry-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 */

Expand Down
37 changes: 34 additions & 3 deletions jerry-core/debugger/jerry-debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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 */
Expand Down
28 changes: 2 additions & 26 deletions jerry-core/vm/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;

Expand Down
44 changes: 38 additions & 6 deletions jerry-debugger/jerry-client-ws.html
Original file line number Diff line number Diff line change
Expand Up @@ -894,10 +894,43 @@ <h2>JerryScript HTML (WebSocket) Debugger Client</h2>
}
}

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;
}

Expand Down Expand Up @@ -1054,23 +1087,24 @@ <h2>JerryScript HTML (WebSocket) Debugger Client</h2>
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":
Expand All @@ -1095,9 +1129,7 @@ <h2>JerryScript HTML (WebSocket) Debugger Client</h2>
}
}

appendLog("Backtrace:");

debuggerObj.encodeMessage("BI", [ JERRY_DEBUGGER_GET_BACKTRACE, max_depth ]);
debuggerObj.sendGetBacktrace(max_depth);
break;

case "src":
Expand Down
9 changes: 7 additions & 2 deletions jerry-debugger/jerry-client-ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -140,7 +141,7 @@ def __init__(self, debugger):
self.debugger = debugger
self.stop = False
self.quit = False
self.cont = False
self.cont = True

def precmd(self, line):
self.stop = False
Expand Down Expand Up @@ -188,12 +189,14 @@ def do_continue(self, args):
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

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tools/runners/run-debugger-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
Expand Down

0 comments on commit a20b9df

Please sign in to comment.