Introducing debugger modes (run, breakpoint). (#1645)
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 zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
@@ -225,6 +225,14 @@ jerry_debugger_process_message (uint8_t *recv_buffer_p, /**< pointer the the rec
|
|||||||
{
|
{
|
||||||
/* Process the received message. */
|
/* 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)
|
if (*expected_message_type_p != 0)
|
||||||
{
|
{
|
||||||
JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART);
|
JERRY_ASSERT (*expected_message_type_p == JERRY_DEBUGGER_EVAL_PART);
|
||||||
@@ -466,10 +474,14 @@ jerry_debugger_breakpoint_hit (void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JERRY_CONTEXT (debugger_flags) = (uint8_t) (JERRY_CONTEXT (debugger_flags) | JERRY_DEBUGGER_BREAKPOINT_MODE);
|
||||||
|
|
||||||
while (!jerry_debugger_receive ())
|
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_CONTEXT (debugger_message_delay) = JERRY_DEBUGGER_MESSAGE_FREQUENCY;
|
||||||
} /* jerry_debugger_breakpoint_hit */
|
} /* jerry_debugger_breakpoint_hit */
|
||||||
|
|
||||||
|
|||||||
@@ -43,15 +43,40 @@
|
|||||||
#define JERRY_DEBUGGER_SEND_MAX(type) \
|
#define JERRY_DEBUGGER_SEND_MAX(type) \
|
||||||
((JERRY_DEBUGGER_MAX_SEND_SIZE - sizeof (jerry_debugger_send_header_t) - 1) / sizeof (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.
|
* Debugger option flags.
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
JERRY_DEBUGGER_CONNECTED = 1u << 0, /**< debugger is connected */
|
JERRY_DEBUGGER_CONNECTED = 1u << 0, /**< debugger is connected */
|
||||||
JERRY_DEBUGGER_VM_STOP = 1u << 1, /**< stop at the next breakpoint
|
JERRY_DEBUGGER_BREAKPOINT_MODE = 1u << 1, /**< debugger waiting at a breakpoint */
|
||||||
* regardless it is enabled */
|
JERRY_DEBUGGER_VM_STOP = 1u << 2, /**< stop at the next breakpoint regardless it is enabled */
|
||||||
JERRY_DEBUGGER_VM_IGNORE = 1u << 2, /**< ignore all breakpoints */
|
JERRY_DEBUGGER_VM_IGNORE = 1u << 3, /**< ignore all breakpoints */
|
||||||
} jerry_debugger_flags_t;
|
} 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 */
|
JERRY_DEBUGGER_EVAL_ERROR_END = 20, /**< last part of eval result when an error is occured */
|
||||||
|
|
||||||
/* Messages sent by the client to server. */
|
/* 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_FREE_BYTE_CODE_CP = 1, /**< free byte code compressed pointer */
|
||||||
JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2, /**< update breakpoint status */
|
JERRY_DEBUGGER_UPDATE_BREAKPOINT = 2, /**< update breakpoint status */
|
||||||
JERRY_DEBUGGER_STOP = 3, /**< stop execution */
|
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_CONTINUE = 4, /**< continue execution */
|
||||||
JERRY_DEBUGGER_STEP = 5, /**< next breakpoint, step into functions */
|
JERRY_DEBUGGER_STEP = 5, /**< next breakpoint, step into functions */
|
||||||
JERRY_DEBUGGER_NEXT = 6, /**< next breakpoint in the same context */
|
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_GET_BACKTRACE = 7, /**< get backtrace */
|
||||||
JERRY_DEBUGGER_EVAL = 8, /**< first message of evaluating a string */
|
JERRY_DEBUGGER_EVAL = 8, /**< first message of evaluating a string */
|
||||||
JERRY_DEBUGGER_EVAL_PART = 9, /**< next message of evaluating a string */
|
JERRY_DEBUGGER_EVAL_PART = 9, /**< next message of evaluating a string */
|
||||||
|
|||||||
+2
-26
@@ -2321,19 +2321,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
|||||||
|
|
||||||
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
|
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
|
||||||
|
|
||||||
if (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE)
|
JERRY_ASSERT (!(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_ctx_p->byte_code_p = byte_code_start_p;
|
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);
|
JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED);
|
||||||
|
|
||||||
if (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE)
|
JERRY_ASSERT (!(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_ctx_p->byte_code_p = byte_code_start_p;
|
frame_ctx_p->byte_code_p = byte_code_start_p;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
this.sendEval = function(str)
|
||||||
{
|
{
|
||||||
|
if (!lastBreakpointHit)
|
||||||
|
{
|
||||||
|
appendLog("This command is allowed only if JavaScript execution is stopped at a breakpoint.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (str == "")
|
if (str == "")
|
||||||
{
|
{
|
||||||
|
appendLog("Argument required");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1054,23 +1087,24 @@ function debuggerCommand(event)
|
|||||||
debuggerObj.deleteBreakpoint(args[2]);
|
debuggerObj.deleteBreakpoint(args[2]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "st":
|
||||||
case "stop":
|
case "stop":
|
||||||
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STOP ]);
|
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STOP ]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "c":
|
case "c":
|
||||||
case "continue":
|
case "continue":
|
||||||
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_CONTINUE ]);
|
debuggerObj.sendResumeExec(JERRY_DEBUGGER_CONTINUE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "s":
|
case "s":
|
||||||
case "step":
|
case "step":
|
||||||
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_STEP ]);
|
debuggerObj.sendResumeExec(JERRY_DEBUGGER_STEP);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "n":
|
case "n":
|
||||||
case "next":
|
case "next":
|
||||||
debuggerObj.encodeMessage("B", [ JERRY_DEBUGGER_NEXT ]);
|
debuggerObj.sendResumeExec(JERRY_DEBUGGER_NEXT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "e":
|
case "e":
|
||||||
@@ -1095,9 +1129,7 @@ function debuggerCommand(event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLog("Backtrace:");
|
debuggerObj.sendGetBacktrace(max_depth);
|
||||||
|
|
||||||
debuggerObj.encodeMessage("BI", [ JERRY_DEBUGGER_GET_BACKTRACE, max_depth ]);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "src":
|
case "src":
|
||||||
|
|||||||
@@ -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("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("-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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ class DebuggerPrompt(Cmd):
|
|||||||
self.debugger = debugger
|
self.debugger = debugger
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.quit = False
|
self.quit = False
|
||||||
self.cont = False
|
self.cont = True
|
||||||
|
|
||||||
def precmd(self, line):
|
def precmd(self, line):
|
||||||
self.stop = False
|
self.stop = False
|
||||||
@@ -188,12 +189,14 @@ class DebuggerPrompt(Cmd):
|
|||||||
def do_step(self, args):
|
def do_step(self, args):
|
||||||
""" Next breakpoint, step into functions """
|
""" Next breakpoint, step into functions """
|
||||||
self.exec_command(args, JERRY_DEBUGGER_STEP)
|
self.exec_command(args, JERRY_DEBUGGER_STEP)
|
||||||
|
self.cont = True
|
||||||
|
|
||||||
do_s = do_step
|
do_s = do_step
|
||||||
|
|
||||||
def do_next(self, args):
|
def do_next(self, args):
|
||||||
""" Next breakpoint in the same context """
|
""" Next breakpoint in the same context """
|
||||||
self.exec_command(args, JERRY_DEBUGGER_NEXT)
|
self.exec_command(args, JERRY_DEBUGGER_NEXT)
|
||||||
|
self.cont = True
|
||||||
|
|
||||||
do_n = do_next
|
do_n = do_next
|
||||||
|
|
||||||
@@ -669,13 +672,15 @@ def main():
|
|||||||
|
|
||||||
debugger = JerryDebugger(args.address)
|
debugger = JerryDebugger(args.address)
|
||||||
|
|
||||||
|
non_interactive = args.non_interactive
|
||||||
|
|
||||||
logging.debug("Connected to JerryScript on %d port" % (debugger.port))
|
logging.debug("Connected to JerryScript on %d port" % (debugger.port))
|
||||||
|
|
||||||
prompt = DebuggerPrompt(debugger)
|
prompt = DebuggerPrompt(debugger)
|
||||||
prompt.prompt = "(jerry-debugger) "
|
prompt.prompt = "(jerry-debugger) "
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if prompt.cont:
|
if not non_interactive and prompt.cont:
|
||||||
if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
|
if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
|
||||||
sys.stdin.readline()
|
sys.stdin.readline()
|
||||||
prompt.cont = False
|
prompt.cont = False
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ echo "$START_DEBUG_SERVER"
|
|||||||
eval "$START_DEBUG_SERVER"
|
eval "$START_DEBUG_SERVER"
|
||||||
sleep 1s
|
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"))
|
DIFF=$(diff -u0 ${TEST_CASE}.expected <(echo "$RESULT"))
|
||||||
|
|
||||||
if [ -n "$DIFF" ]
|
if [ -n "$DIFF" ]
|
||||||
|
|||||||
Reference in New Issue
Block a user