mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Processing VSCode watches skips waitForResolvingPendingBreakpoints (previously having multiple watches in VSC would call this when processing the first watch, which would process pending messages while trying to wait for break point changes while in eval mode (not in waiting mode) and cause and Invalid message error and the closure of the debgger connection when hitting the second watch message) Allow '*.mjs' files in the python debugger Add watches to the python debugger, add test Signed-off-by: Máté Tokodi mate.tokodi@szteszoftver.hu
324 lines
9.3 KiB
Python
Executable file
324 lines
9.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# Copyright 2015-present Samsung Electronics Co., Ltd.
|
|
#
|
|
# 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.
|
|
|
|
from __future__ import print_function
|
|
from cmd import Cmd
|
|
from pprint import pprint
|
|
import math
|
|
import socket
|
|
import sys
|
|
import logging
|
|
import time
|
|
import debugger_core
|
|
|
|
from debugger_websocket import WebSocket
|
|
from debugger_tcp import TcpSocket
|
|
|
|
def write(string):
|
|
print(string, end='')
|
|
|
|
class DebuggerPrompt(Cmd):
|
|
# pylint: disable=too-many-instance-attributes,too-many-arguments
|
|
def __init__(self, debugger):
|
|
Cmd.__init__(self)
|
|
self.debugger = debugger
|
|
self.stop = False
|
|
self.quit = False
|
|
|
|
def precmd(self, line):
|
|
self.stop = False
|
|
if self.debugger.non_interactive:
|
|
print("%s" % line)
|
|
return line
|
|
|
|
def postcmd(self, stop, line):
|
|
return self.stop
|
|
|
|
def do_quit(self, _):
|
|
""" Exit debugger """
|
|
self.debugger.quit()
|
|
self.quit = True
|
|
self.stop = True
|
|
|
|
def do_EOF(self, _):
|
|
""" Exit Escargot debugger """
|
|
print("Unexpected end of input. Connection closed.")
|
|
self.debugger.quit()
|
|
self.quit = True
|
|
self.stop = True
|
|
|
|
def do_display(self, args):
|
|
""" Toggle source code display after breakpoints """
|
|
if args:
|
|
line_num = src_check_args(args)
|
|
if line_num >= 0:
|
|
self.debugger.display = line_num
|
|
else:
|
|
print("Non-negative integer number expected, 0 turns off this function")
|
|
|
|
def do_watch(self, args):
|
|
""" Add value to watched values display after breakpoints """
|
|
if not args:
|
|
write("Error: Argument expected\n")
|
|
else:
|
|
write(self.debugger.set_watch(args))
|
|
do_w = do_watch
|
|
|
|
def do_print_watches(self, _):
|
|
self.debugger.print_watches()
|
|
do_pw = do_print_watches
|
|
|
|
def do_watches(self, _):
|
|
self.debugger.list_watches()
|
|
do_list_watches = do_watches
|
|
do_lw = do_watches
|
|
|
|
def do_break(self, args):
|
|
""" Insert breakpoints on the given lines or functions """
|
|
if not args:
|
|
write("Error: Argument expected\n")
|
|
else:
|
|
write(self.debugger.set_break(args))
|
|
do_b = do_break
|
|
|
|
def do_list(self, _):
|
|
""" Lists the available breakpoints """
|
|
write(self.debugger.breakpoint_list())
|
|
|
|
def do_delete(self, args):
|
|
""" Delete the given breakpoint, use 'delete all|active|pending' to clear all the given breakpoints """
|
|
write(self.debugger.delete(args))
|
|
|
|
def do_delete_watch(self, args):
|
|
""" Delete the given watch """
|
|
write(self.debugger.delete_watch(args))
|
|
|
|
def do_continue(self, _):
|
|
""" Continue execution """
|
|
self.debugger.do_continue()
|
|
self.stop = True
|
|
if not self.debugger.non_interactive:
|
|
print("Press enter to stop JavaScript execution.")
|
|
do_c = do_continue
|
|
|
|
def do_step(self, _):
|
|
""" Next breakpoint, step into functions """
|
|
self.debugger.step()
|
|
self.stop = True
|
|
do_s = do_step
|
|
|
|
def do_next(self, args):
|
|
""" Next breakpoint in the same context """
|
|
self.stop = True
|
|
if not args:
|
|
args = 0
|
|
self.debugger.next()
|
|
return
|
|
|
|
try:
|
|
args = int(args)
|
|
if args <= 0:
|
|
raise ValueError(args)
|
|
|
|
while args > 0:
|
|
self.debugger.next()
|
|
time.sleep(0.1)
|
|
|
|
while True:
|
|
result = self.debugger.process_messages()
|
|
res_type = result.get_type()
|
|
|
|
if res_type == result.END:
|
|
self.quit = True
|
|
return
|
|
elif res_type == result.TEXT:
|
|
write(result.get_text())
|
|
elif res_type == result.PROMPT:
|
|
break
|
|
|
|
args -= 1
|
|
except ValueError as val_errno:
|
|
print("Error: expected a positive integer: %s" % val_errno)
|
|
do_n = do_next
|
|
|
|
def do_finish(self, _):
|
|
""" Continue running until the current function returns """
|
|
self.debugger.finish()
|
|
self.stop = True
|
|
do_f = do_finish
|
|
|
|
def do_src(self, args):
|
|
""" Get current source code """
|
|
if args:
|
|
line_num = src_check_args(args)
|
|
if line_num >= 0:
|
|
write(self.debugger.print_source(line_num, 0))
|
|
else:
|
|
write(self.debugger.print_source(0, 0))
|
|
do_source = do_src
|
|
|
|
def do_scroll(self, _):
|
|
""" Scroll the source up or down """
|
|
while True:
|
|
key = sys.stdin.readline()
|
|
if key == 'w\n':
|
|
_scroll_direction(self.debugger, "up")
|
|
elif key == 's\n':
|
|
_scroll_direction(self.debugger, "down")
|
|
elif key == 'q\n':
|
|
break
|
|
else:
|
|
print("Invalid key")
|
|
|
|
def do_eval(self, args):
|
|
""" Evaluate JavaScript source code """
|
|
self.debugger.eval(args)
|
|
self.stop = True
|
|
do_e = do_eval
|
|
do_p = do_eval
|
|
do_print = do_eval
|
|
|
|
def do_backtrace(self, args):
|
|
""" Get backtrace data from debugger """
|
|
write(self.debugger.backtrace(args))
|
|
self.stop = True
|
|
do_bt = do_backtrace
|
|
|
|
def do_scope(self, args):
|
|
""" Get lexical environment chain """
|
|
self.debugger.scope_chain(args)
|
|
self.stop = True
|
|
|
|
def do_variables(self, args):
|
|
""" Get scope variables """
|
|
write(self.debugger.scope_variables(args))
|
|
self.stop = True
|
|
|
|
def do_object(self, args):
|
|
""" Get object by index """
|
|
if not args:
|
|
write("Error: Argument expected")
|
|
else:
|
|
write(self.debugger.object(args))
|
|
self.stop = True
|
|
|
|
def do_dump(self, args):
|
|
""" Dump all of the debugger data """
|
|
if args:
|
|
print("Error: No argument expected")
|
|
else:
|
|
pprint(self.debugger.function_list)
|
|
|
|
|
|
def _scroll_direction(debugger, direction):
|
|
""" Helper function for do_scroll """
|
|
debugger.src_offset_diff = int(max(math.floor(debugger.display / 3), 1))
|
|
if direction == "up":
|
|
debugger.src_offset -= debugger.src_offset_diff
|
|
else:
|
|
debugger.src_offset += debugger.src_offset_diff
|
|
print(debugger.print_source(debugger.display, debugger.src_offset)['value'])
|
|
|
|
|
|
def src_check_args(args):
|
|
try:
|
|
line_num = int(args)
|
|
if line_num < 0:
|
|
print("Error: Non-negative integer number expected")
|
|
return -1
|
|
|
|
return line_num
|
|
except ValueError as val_errno:
|
|
print("Error: Non-negative integer number expected: %s" % (val_errno))
|
|
return -1
|
|
|
|
|
|
# pylint: disable=too-many-branches,too-many-locals,too-many-statements,redefined-variable-type
|
|
def main():
|
|
args = debugger_core.arguments_parse()
|
|
|
|
tranport = TcpSocket(args.address)
|
|
protocol = WebSocket(tranport)
|
|
debugger = debugger_core.Debugger(protocol)
|
|
|
|
debugger.non_interactive = args.non_interactive
|
|
|
|
logging.debug("Connected to Escargot")
|
|
|
|
prompt = DebuggerPrompt(debugger)
|
|
prompt.prompt = "(escargot-debugger) "
|
|
|
|
if args.color:
|
|
debugger.set_colors()
|
|
|
|
if args.display:
|
|
debugger.display = args.display
|
|
prompt.do_display(args.display)
|
|
else:
|
|
prompt.stop = False
|
|
|
|
if args.client_source:
|
|
debugger.store_client_sources(args.client_source)
|
|
|
|
if args.wait_before_exit:
|
|
debugger.set_wait_exit(args.wait_before_exit)
|
|
|
|
if args.command:
|
|
commands = args.command[0].split(";")
|
|
i = 0
|
|
while i < len(commands):
|
|
result = debugger.process_messages()
|
|
res_type = result.get_type()
|
|
|
|
if res_type == result.END:
|
|
return
|
|
elif res_type == result.PROMPT:
|
|
write(commands[i] + "\n")
|
|
prompt.onecmd(commands[i] + "\n")
|
|
i += 1
|
|
elif res_type == result.TEXT:
|
|
write(result.get_text())
|
|
continue
|
|
|
|
while True:
|
|
if prompt.quit:
|
|
break
|
|
|
|
result = debugger.process_messages()
|
|
res_type = result.get_type()
|
|
|
|
if res_type == result.END:
|
|
break
|
|
elif res_type == result.PROMPT:
|
|
prompt.cmdloop()
|
|
elif res_type == result.TEXT:
|
|
write(result.get_text())
|
|
continue
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
sys.exit("Connection closed.")
|
|
except socket.error as error_msg:
|
|
ERRNO = error_msg.errno
|
|
MSG = str(error_msg)
|
|
if ERRNO == 111:
|
|
sys.exit("Failed to connect to the Escargot debugger.")
|
|
elif ERRNO == 32 or ERRNO == 104:
|
|
sys.exit("Connection closed.")
|
|
else:
|
|
sys.exit("Failed to connect to the Escargot debugger.\nError: %s" % (MSG))
|