1#!/usr/bin/env python 2 3# Copyright JS Foundation and other contributors, http://js.foundation 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from __future__ import print_function 18from cmd import Cmd 19from pprint import pprint 20import math 21import socket 22import sys 23import logging 24import time 25import jerry_client_main 26 27from jerry_client_websocket import WebSocket 28from jerry_client_rawpacket import RawPacket 29from jerry_client_tcp import Socket 30 31def write(string): 32 print(string, end='') 33 34class DebuggerPrompt(Cmd): 35 # pylint: disable=too-many-instance-attributes,too-many-arguments 36 def __init__(self, debugger): 37 Cmd.__init__(self) 38 self.debugger = debugger 39 self.stop = False 40 self.quit = False 41 42 def precmd(self, line): 43 self.stop = False 44 if self.debugger.non_interactive: 45 print("%s" % line) 46 return line 47 48 def postcmd(self, stop, line): 49 return self.stop 50 51 def do_quit(self, _): 52 """ Exit JerryScript debugger """ 53 self.debugger.quit() 54 self.quit = True 55 self.stop = True 56 57 def do_display(self, args): 58 """ Toggle source code display after breakpoints """ 59 if args: 60 line_num = src_check_args(args) 61 if line_num >= 0: 62 self.debugger.display = line_num 63 else: 64 print("Non-negative integer number expected, 0 turns off this function") 65 66 def do_break(self, args): 67 """ Insert breakpoints on the given lines or functions """ 68 write(self.debugger.set_break(args)) 69 do_b = do_break 70 71 def do_list(self, _): 72 """ Lists the available breakpoints """ 73 write(self.debugger.breakpoint_list()) 74 75 def do_delete(self, args): 76 """ Delete the given breakpoint, use 'delete all|active|pending' to clear all the given breakpoints """ 77 write(self.debugger.delete(args)) 78 79 def do_exception(self, args): 80 """ Config the exception handler module """ 81 write(self.debugger.exception(args)) 82 83 def do_next(self, args): 84 """ Next breakpoint in the same code block """ 85 self.stop = True 86 if not args: 87 args = 0 88 self.debugger.next() 89 return 90 91 try: 92 args = int(args) 93 if args <= 0: 94 raise ValueError(args) 95 96 while args > 0: 97 self.debugger.next() 98 time.sleep(0.1) 99 100 while True: 101 result = self.debugger.process_messages() 102 res_type = result.get_type() 103 104 if res_type == result.END: 105 self.quit = True 106 return 107 elif res_type == result.TEXT: 108 write(result.get_text()) 109 elif res_type == result.PROMPT: 110 break 111 112 args -= 1 113 except ValueError as val_errno: 114 print("Error: expected a positive integer: %s" % val_errno) 115 do_n = do_next 116 117 def do_step(self, _): 118 """ Next breakpoint, step into functions """ 119 self.debugger.step() 120 self.stop = True 121 do_s = do_step 122 123 def do_continue(self, _): 124 """ Continue execution """ 125 self.debugger.do_continue() 126 self.stop = True 127 if not self.debugger.non_interactive: 128 print("Press enter to stop JavaScript execution.") 129 do_c = do_continue 130 131 def do_finish(self, _): 132 """ Continue running until the current function returns """ 133 self.debugger.finish() 134 self.stop = True 135 do_f = do_finish 136 137 def do_backtrace(self, args): 138 """ Get backtrace data from debugger """ 139 write(self.debugger.backtrace(args)) 140 self.stop = True 141 do_bt = do_backtrace 142 143 def do_src(self, args): 144 """ Get current source code """ 145 if args: 146 line_num = src_check_args(args) 147 if line_num >= 0: 148 write(self.debugger.print_source(line_num, 0)) 149 else: 150 write(self.debugger.print_source(0, 0)) 151 do_source = do_src 152 153 def do_scroll(self, _): 154 """ Scroll the source up or down """ 155 while True: 156 key = sys.stdin.readline() 157 if key == 'w\n': 158 _scroll_direction(self.debugger, "up") 159 elif key == 's\n': 160 _scroll_direction(self.debugger, "down") 161 elif key == 'q\n': 162 break 163 else: 164 print("Invalid key") 165 166 def do_eval(self, args): 167 """ Evaluate JavaScript source code """ 168 self.debugger.eval(args) 169 self.stop = True 170 do_e = do_eval 171 172 def do_eval_at(self, args): 173 """ Evaluate JavaScript source code at a scope chain level """ 174 175 code = '' 176 index = 0 177 try: 178 args = args.split(" ", 1) 179 180 index = int(args[0]) 181 182 if len(args) == 2: 183 code = args[1] 184 185 if index < 0 or index > 65535: 186 raise ValueError("Invalid scope chain index: %d (must be between 0 and 65535)" % index) 187 188 except ValueError as val_errno: 189 print("Error: %s" % (val_errno)) 190 return 191 192 self.debugger.eval_at(code, index) 193 self.stop = True 194 195 def do_throw(self, args): 196 """ Throw an exception """ 197 self.debugger.throw(args) 198 self.stop = True 199 200 def do_abort(self, args): 201 """ Throw an exception which cannot be caught """ 202 self.debugger.abort(args) 203 self.stop = True 204 205 def do_restart(self, _): 206 """ Restart the engine's debug session """ 207 self.debugger.restart() 208 self.stop = True 209 do_res = do_restart 210 211 def do_scope(self, _): 212 """ Get lexical environment chain """ 213 self.debugger.scope_chain() 214 self.stop = True 215 216 def do_variables(self, args): 217 """ Get scope variables from debugger """ 218 write(self.debugger.scope_variables(args)) 219 self.stop = True 220 221 def do_memstats(self, _): 222 """ Memory statistics """ 223 self.debugger.memstats() 224 self.stop = True 225 do_ms = do_memstats 226 227 def do_dump(self, args): 228 """ Dump all of the debugger data """ 229 if args: 230 print("Error: No argument expected") 231 else: 232 pprint(self.debugger.function_list) 233 234 # pylint: disable=invalid-name 235 def do_EOF(self, _): 236 """ Exit JerryScript debugger """ 237 print("Unexpected end of input. Connection closed.") 238 self.debugger.quit() 239 self.quit = True 240 self.stop = True 241 242def _scroll_direction(debugger, direction): 243 """ Helper function for do_scroll """ 244 debugger.src_offset_diff = int(max(math.floor(debugger.display / 3), 1)) 245 if direction == "up": 246 debugger.src_offset -= debugger.src_offset_diff 247 else: 248 debugger.src_offset += debugger.src_offset_diff 249 print(debugger.print_source(debugger.display, debugger.src_offset)['value']) 250 251def src_check_args(args): 252 try: 253 line_num = int(args) 254 if line_num < 0: 255 print("Error: Non-negative integer number expected") 256 return -1 257 258 return line_num 259 except ValueError as val_errno: 260 print("Error: Non-negative integer number expected: %s" % (val_errno)) 261 return -1 262 263# pylint: disable=too-many-branches,too-many-locals,too-many-statements 264def main(): 265 args = jerry_client_main.arguments_parse() 266 267 channel = None 268 protocol = None 269 270 if args.protocol == "tcp": 271 address = None 272 if ":" not in args.address: 273 address = (args.address, 5001) # use default port 274 else: 275 host, port = args.address.split(":") 276 address = (host, int(port)) 277 278 protocol = Socket(address) 279 elif args.protocol == "serial": 280 from jerry_client_serial import Serial 281 protocol = Serial(args.serial_config) 282 else: 283 print("Unsupported transmission protocol") 284 return -1 285 286 if args.channel == "websocket": 287 channel = WebSocket(protocol=protocol) 288 elif args.channel == "rawpacket": 289 channel = RawPacket(protocol=protocol) 290 else: 291 print("Unsupported communication channel") 292 return -1 293 294 debugger = jerry_client_main.JerryDebugger(channel) 295 debugger.non_interactive = args.non_interactive 296 297 logging.debug("Connected to JerryScript") 298 299 prompt = DebuggerPrompt(debugger) 300 prompt.prompt = "(jerry-debugger) " 301 302 if args.color: 303 debugger.set_colors() 304 305 if args.display: 306 debugger.display = args.display 307 prompt.do_display(args.display) 308 else: 309 prompt.stop = False 310 311 if args.exception is not None: 312 prompt.do_exception(str(args.exception)) 313 314 if args.client_source: 315 debugger.store_client_sources(args.client_source) 316 317 while True: 318 if prompt.quit: 319 break 320 321 result = debugger.process_messages() 322 res_type = result.get_type() 323 324 if res_type == result.END: 325 break 326 elif res_type == result.PROMPT: 327 prompt.cmdloop() 328 elif res_type == result.TEXT: 329 write(result.get_text()) 330 continue 331 332if __name__ == "__main__": 333 try: 334 main() 335 except socket.error as error_msg: 336 ERRNO = error_msg.errno 337 MSG = str(error_msg) 338 if ERRNO == 111: 339 sys.exit("Failed to connect to the JerryScript debugger.") 340 elif ERRNO == 32 or ERRNO == 104: 341 sys.exit("Connection closed.") 342 else: 343 sys.exit("Failed to connect to the JerryScript debugger.\nError: %s" % (MSG)) 344