def get_message(self, blocking): """ Receive message. """ # Connection was closed if self.data_buffer is None: return None while True: if len(self.data_buffer) >= 2: if jerry_ord(self.data_buffer[0] ) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT: raise Exception("Unexpected data frame") size = jerry_ord(self.data_buffer[1]) if size == 0 or size >= 126: raise Exception("Unexpected data frame") if len(self.data_buffer) >= size + 2: result = self.data_buffer[2:size + 2] self.data_buffer = self.data_buffer[size + 2:] return result if not blocking and not self.protocol.ready(): return b'' data = self.protocol.receive_data(MAX_BUFFER_SIZE) if not data: self.data_buffer = None return None self.data_buffer += data
def get_message(self, blocking): """ Receive message. """ # Connection was closed if self.data_buffer is None: return None while True: if len(self.data_buffer) >= 1: size = jerry_ord(self.data_buffer[0]) if size == 0: raise Exception("Unexpected data frame") if len(self.data_buffer) >= size + 1: result = self.data_buffer[1:size + 1] self.data_buffer = self.data_buffer[size + 1:] return result if not blocking and not self.protocol.ready(): return b'' received_data = self.protocol.receive_data(MAX_BUFFER_SIZE) if not received_data: return None self.data_buffer += received_data
def _process_scope_variables(self): buff_size = len(self.scope_vars) buff_pos = 0 table = [['name', 'type', 'value']] while buff_pos != buff_size: # Process name name_length = jerry_ord(self.scope_vars[buff_pos]) buff_pos += 1 name = jerry_decode(self.scope_vars[buff_pos:buff_pos + name_length]) buff_pos += name_length # Process type value_type = jerry_ord(self.scope_vars[buff_pos]) buff_pos += 1 value_length = jerry_ord(self.scope_vars[buff_pos]) buff_pos += 1 value = jerry_decode(self.scope_vars[buff_pos:buff_pos + value_length]) buff_pos += value_length if value_type == JERRY_DEBUGGER_VALUE_UNDEFINED: table.append([name, 'undefined', value]) elif value_type == JERRY_DEBUGGER_VALUE_NULL: table.append([name, 'Null', value]) elif value_type == JERRY_DEBUGGER_VALUE_BOOLEAN: table.append([name, 'Boolean', value]) elif value_type == JERRY_DEBUGGER_VALUE_NUMBER: table.append([name, 'Number', value]) elif value_type == JERRY_DEBUGGER_VALUE_STRING: table.append([name, 'String', value]) elif value_type == JERRY_DEBUGGER_VALUE_FUNCTION: table.append([name, 'Function', value]) elif value_type == JERRY_DEBUGGER_VALUE_ARRAY: table.append([name, 'Array', '[' + value + ']']) elif value_type == JERRY_DEBUGGER_VALUE_OBJECT: table.append([name, 'Object', value]) result = self._form_table(table) return result
def _process_scope(self): table = [['level', 'type']] for i_byte, level_byte in enumerate(self.scope_data): i = jerry_ord(i_byte) level = jerry_ord(level_byte) if level == JERRY_DEBUGGER_SCOPE_WITH: table.append([str(i), 'with']) elif level == JERRY_DEBUGGER_SCOPE_GLOBAL: table.append([str(i), 'global']) elif level == JERRY_DEBUGGER_SCOPE_NON_CLOSURE: # Currently it is only marks the catch closure. table.append([str(i), 'catch']) elif level == JERRY_DEBUGGER_SCOPE_LOCAL: table.append([str(i), 'local']) elif level == JERRY_DEBUGGER_SCOPE_CLOSURE: table.append([str(i), 'closure']) else: raise Exception("Unexpected scope chain element") result = self._form_table(table) return result
def _parse_source(self, data): source_code = b"" source_code_name = b"" function_name = b"" stack = [{ "line": 1, "column": 1, "name": b"", "lines": [], "offsets": [] }] new_function_list = {} result = b"" while True: if data is None: return b"Error: connection lost during source code receiving" buffer_type = jerry_ord(data[0]) buffer_size = len(data) - 1 logging.debug("Parser buffer type: %d, message size: %d", buffer_type, buffer_size) if buffer_type == JERRY_DEBUGGER_PARSE_ERROR: logging.error("Syntax error found") return b"" elif buffer_type in [ JERRY_DEBUGGER_SOURCE_CODE, JERRY_DEBUGGER_SOURCE_CODE_END ]: source_code += data[1:] elif buffer_type in [ JERRY_DEBUGGER_SOURCE_CODE_NAME, JERRY_DEBUGGER_SOURCE_CODE_NAME_END ]: source_code_name += data[1:] elif buffer_type in [ JERRY_DEBUGGER_FUNCTION_NAME, JERRY_DEBUGGER_FUNCTION_NAME_END ]: function_name += data[1:] elif buffer_type == JERRY_DEBUGGER_PARSE_FUNCTION: logging.debug("Source name: %s, function name: %s", source_code_name, function_name) position = struct.unpack( self.byte_order + self.idx_format + self.idx_format, data[1:1 + 4 + 4]) stack.append({ "source": source_code, "source_name": source_code_name, "line": position[0], "column": position[1], "name": function_name, "lines": [], "offsets": [] }) function_name = b"" elif buffer_type in [ JERRY_DEBUGGER_BREAKPOINT_LIST, JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST ]: name = "lines" if buffer_type == JERRY_DEBUGGER_BREAKPOINT_OFFSET_LIST: name = "offsets" logging.debug("Breakpoint %s received", name) buffer_pos = 1 while buffer_size > 0: line = struct.unpack(self.byte_order + self.idx_format, data[buffer_pos:buffer_pos + 4]) stack[-1][name].append(line[0]) buffer_pos += 4 buffer_size -= 4 elif buffer_type == JERRY_DEBUGGER_BYTE_CODE_CP: byte_code_cp = struct.unpack(self.byte_order + self.cp_format, data[1:1 + self.cp_size])[0] logging.debug("Byte code cptr received: {0x%x}", byte_code_cp) func_desc = stack.pop() # We know the last item in the list is the general byte code. if not stack: func_desc["source"] = source_code func_desc["source_name"] = source_code_name function = JerryFunction( stack, byte_code_cp, func_desc["source"], func_desc["source_name"], func_desc["line"], func_desc["column"], func_desc["name"], func_desc["lines"], func_desc["offsets"]) new_function_list[byte_code_cp] = function if not stack: logging.debug("Parse completed.") break elif buffer_type == JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP: # Redefined functions are dropped during parsing. byte_code_cp = struct.unpack(self.byte_order + self.cp_format, data[1:1 + self.cp_size])[0] if byte_code_cp in new_function_list: del new_function_list[byte_code_cp] self._send_bytecode_cp(byte_code_cp) else: self._release_function(data) elif buffer_type in [ JERRY_DEBUGGER_OUTPUT_RESULT, JERRY_DEBUGGER_OUTPUT_RESULT_END ]: result += self._process_incoming_text(buffer_type, data) else: logging.error("Parser error!") raise Exception("Unexpected message") data = self.channel.get_message(True) # Copy the ready list to the global storage. self.function_list.update(new_function_list) for function in new_function_list.values(): for line, breakpoint in function.lines.items(): self.line_list.insert(line, breakpoint) # Try to set the pending breakpoints if self.pending_breakpoint_list: logging.debug("Pending breakpoints available") bp_list = self.pending_breakpoint_list for breakpoint_index, breakpoint in list(bp_list.items()): source_lines = 0 for src in new_function_list.values(): if (src.source_name == breakpoint.source_name or src.source_name.endswith(b"/" + breakpoint.source_name) or src.source_name.endswith(b"\\" + breakpoint.source_name)): source_lines = len(src.source) break if breakpoint.line: if breakpoint.line <= source_lines: command = breakpoint.source_name + b":%d" % ( breakpoint.line) set_result = self._set_breakpoint(command, True) if set_result: result += set_result del bp_list[breakpoint_index] elif breakpoint.function: command = breakpoint.function set_result = self._set_breakpoint(command, True) if set_result: result += set_result del bp_list[breakpoint_index] if not bp_list: self._send_parser_config(0) return result logging.debug("No pending breakpoints") return result
def process_messages(self): result = b"" while True: data = self.channel.get_message(False) if not self.non_interactive: if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: sys.stdin.readline() self.stop() if data == b'': action_type = DebuggerAction.PROMPT if self.prompt else DebuggerAction.WAIT return DebuggerAction(action_type, b"") if not data: # Break the while loop if there is no more data. return DebuggerAction(DebuggerAction.END, b"") buffer_type = jerry_ord(data[0]) buffer_size = len(data) - 1 logging.debug("Main buffer type: %d, message size: %d", buffer_type, buffer_size) if buffer_type in [ JERRY_DEBUGGER_PARSE_ERROR, JERRY_DEBUGGER_BYTE_CODE_CP, JERRY_DEBUGGER_PARSE_FUNCTION, JERRY_DEBUGGER_BREAKPOINT_LIST, JERRY_DEBUGGER_SOURCE_CODE, JERRY_DEBUGGER_SOURCE_CODE_END, JERRY_DEBUGGER_SOURCE_CODE_NAME, JERRY_DEBUGGER_SOURCE_CODE_NAME_END, JERRY_DEBUGGER_FUNCTION_NAME, JERRY_DEBUGGER_FUNCTION_NAME_END ]: result = self._parse_source(data) if result: return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type == JERRY_DEBUGGER_WAITING_AFTER_PARSE: self._exec_command(JERRY_DEBUGGER_PARSER_RESUME) elif buffer_type == JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP: self._release_function(data) elif buffer_type in [ JERRY_DEBUGGER_BREAKPOINT_HIT, JERRY_DEBUGGER_EXCEPTION_HIT ]: breakpoint_data = struct.unpack( self.byte_order + self.cp_format + self.idx_format, data[1:]) breakpoint = self._get_breakpoint(breakpoint_data) self.last_breakpoint_hit = breakpoint[0] if buffer_type == JERRY_DEBUGGER_EXCEPTION_HIT: result += b"Exception throw detected (to disable automatic stop type exception 0)\n" if self.exception_string: result += b"Exception hint: %s\n" % ( self.exception_string) self.exception_string = b"" if breakpoint[1]: breakpoint_info = b"at" else: breakpoint_info = b"around" if breakpoint[0].active_index >= 0: breakpoint_info += b" breakpoint:%s%d%s" % ( self.red, breakpoint[0].active_index, self.nocolor) result += b"Stopped %s %s\n" % (breakpoint_info, breakpoint[0]) if self.display > 0: result += self.print_source(self.display, self.src_offset) self.prompt = True return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type == JERRY_DEBUGGER_EXCEPTION_STR: self.exception_string += data[1:] elif buffer_type == JERRY_DEBUGGER_EXCEPTION_STR_END: self.exception_string += data[1:] elif buffer_type == JERRY_DEBUGGER_BACKTRACE_TOTAL: total = struct.unpack(self.byte_order + self.idx_format, data[1:])[0] result += b"Total number of frames: %d\n" % (total) return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type in [ JERRY_DEBUGGER_BACKTRACE, JERRY_DEBUGGER_BACKTRACE_END ]: frame_index = self.frame_index buffer_pos = 1 while buffer_size > 0: breakpoint_data = struct.unpack( self.byte_order + self.cp_format + self.idx_format, data[buffer_pos:buffer_pos + self.cp_size + 4]) breakpoint = self._get_breakpoint(breakpoint_data) result += b"Frame %d: %s\n" % (frame_index, breakpoint[0]) frame_index += 1 buffer_pos += self.cp_size + 4 buffer_size -= self.cp_size + 4 if buffer_type == JERRY_DEBUGGER_BACKTRACE_END: self.prompt = True else: self.frame_index = frame_index return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type in [ JERRY_DEBUGGER_EVAL_RESULT, JERRY_DEBUGGER_EVAL_RESULT_END, JERRY_DEBUGGER_OUTPUT_RESULT, JERRY_DEBUGGER_OUTPUT_RESULT_END ]: result = self._process_incoming_text(buffer_type, data) return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type == JERRY_DEBUGGER_MEMSTATS_RECEIVE: memory_stats = struct.unpack( self.byte_order + self.idx_format * 5, data[1:1 + 4 * 5]) result += b"Allocated bytes: %s\n" % memory_stats[0] result += b"Byte code bytes: %s\n" % memory_stats[1] result += b"String bytes: %s\n" % memory_stats[2] result += b"Object bytes: %s\n" % memory_stats[3] result += b"Property bytes: %s\n" % memory_stats[4] self.prompt = True return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type == JERRY_DEBUGGER_WAIT_FOR_SOURCE: self.send_client_source() elif buffer_type in [ JERRY_DEBUGGER_SCOPE_CHAIN, JERRY_DEBUGGER_SCOPE_CHAIN_END ]: self.scope_data = data[1:] if buffer_type == JERRY_DEBUGGER_SCOPE_CHAIN_END: result = self._process_scope() self.scope_data = b"" self.prompt = True return DebuggerAction(DebuggerAction.TEXT, result) elif buffer_type in [ JERRY_DEBUGGER_SCOPE_VARIABLES, JERRY_DEBUGGER_SCOPE_VARIABLES_END ]: self.scope_vars += data[1:] if buffer_type == JERRY_DEBUGGER_SCOPE_VARIABLES_END: result = self._process_scope_variables() self.scope_vars = b"" self.prompt = True return DebuggerAction(DebuggerAction.TEXT, result) elif JERRY_DEBUGGER_CLOSE_CONNECTION: return DebuggerAction(DebuggerAction.END, b"") else: raise Exception("Unknown message")
def __init__(self, channel): self.prompt = False self.function_list = {} self.source = b'' self.source_name = b'' self.exception_string = b'' self.frame_index = 0 self.scope_vars = b"" self.scope_data = b"" self.client_sources = [] self.last_breakpoint_hit = None self.next_breakpoint_index = 0 self.active_breakpoint_list = {} self.pending_breakpoint_list = {} self.line_list = Multimap() self.display = 0 self.green = b'' self.red = b'' self.yellow = b'' self.green_bg = b'' self.yellow_bg = b'' self.blue = b'' self.nocolor = b'' self.src_offset = 0 self.src_offset_diff = 0 self.non_interactive = False self.current_out = b"" self.current_log = b"" self.channel = channel config_size = 8 # The server will send the configuration message after connection established # type [1] # configuration [1] # version [4] # max_message_size [1] # cpointer_size [1] result = self.channel.connect(config_size) if len(result) != config_size or jerry_ord( result[0]) != JERRY_DEBUGGER_CONFIGURATION: raise Exception("Unexpected configuration") self.little_endian = jerry_ord( result[1]) & JERRY_DEBUGGER_LITTLE_ENDIAN self.max_message_size = jerry_ord(result[6]) self.cp_size = jerry_ord(result[7]) if self.little_endian: self.byte_order = "<" logging.debug("Little-endian machine") else: self.byte_order = ">" logging.debug("Big-endian machine") if self.cp_size == 2: self.cp_format = "H" else: self.cp_format = "I" self.idx_format = "I" self.version = struct.unpack(self.byte_order + self.idx_format, result[2:6])[0] if self.version != JERRY_DEBUGGER_VERSION: raise Exception( "Incorrect debugger version from target: %d expected: %d" % (self.version, JERRY_DEBUGGER_VERSION)) logging.debug("Compressed pointer size: %d", self.cp_size)
def _process_incoming_text(self, buffer_type, data): message = b"" msg_type = buffer_type while True: if buffer_type in [ JERRY_DEBUGGER_EVAL_RESULT_END, JERRY_DEBUGGER_OUTPUT_RESULT_END ]: subtype = jerry_ord(data[-1]) message += data[1:-1] break else: message += data[1:] data = self.channel.get_message(True) buffer_type = jerry_ord(data[0]) # Checks if the next frame would be an invalid data frame. # If it is not the message type, or the end type of it, an exception is thrown. if buffer_type not in [msg_type, msg_type + 1]: raise Exception("Invalid data caught") # Subtypes of output if buffer_type == JERRY_DEBUGGER_OUTPUT_RESULT_END: if subtype == JERRY_DEBUGGER_OUTPUT_OK: log_type = b"%sout:%s " % (self.blue, self.nocolor) message = self.current_out + message lines = message.split(b"\n") self.current_out = lines.pop() return b"".join( [b"%s%s\n" % (log_type, line) for line in lines]) if subtype == JERRY_DEBUGGER_OUTPUT_DEBUG: log_type = b"%slog:%s " % (self.yellow, self.nocolor) message = self.current_log + message lines = message.split(b"\n") self.current_log = lines.pop() return b"".join( [b"%s%s\n" % (log_type, line) for line in lines]) if not message.endswith(b"\n"): message += b"\n" if subtype == JERRY_DEBUGGER_OUTPUT_WARNING: return b"%swarning: %s%s" % (self.yellow, self.nocolor, message) elif subtype == JERRY_DEBUGGER_OUTPUT_ERROR: return b"%serr: %s%s" % (self.red, self.nocolor, message) elif subtype == JERRY_DEBUGGER_OUTPUT_TRACE: return b"%strace: %s%s" % (self.blue, self.nocolor, message) # Subtypes of eval self.prompt = True if not message.endswith(b"\n"): message += b"\n" if subtype == JERRY_DEBUGGER_EVAL_ERROR: return b"Uncaught exception: %s" % (message) return message