def test_can_be_paused(self): state = mock.Mock(spec=interfaces.UIState) ps = PersistentUIState(state) self.assertEqual(ps.paused(), False) ps.pause_requested() self.assertEqual(ps.paused(), True) self.assertEqual(ps.should_quit(), False)
class TerminalUI: def __init__(self, command_sink: CommandSink, ui_state: UIState, input_func: Callable[[str], str]) -> None: self.command_sink = command_sink self.state = PersistentUIState(ui_state) self.input_func = input_func def run_until_stopped(self) -> None: self.state.pause_requested() while self.state.paused() and not self.state.should_quit(): cmd = self.input_func('wl debug $ ') self.command_sink.process_command(cmd)
class TerminalUI: def __init__(self, command_sink, ui_state, input_func): assert isinstance(command_sink, interfaces.CommandSink) assert isinstance(ui_state, interfaces.UIState) assert callable(input_func) self.command_sink = command_sink self.state = PersistentUIState(ui_state) self.input_func = input_func def run_until_stopped(self): self.state.pause_requested() while self.state.paused() and not self.state.should_quit(): cmd = self.input_func('wl debug $ ') self.command_sink.process_command(cmd)
class Plugin: '''A GDB plugin (should only be instantiated when inside GDB)''' def __init__(self, out, connection_id_sink, command_sink, ui_state): assert isinstance(out, output.Output) assert isinstance(connection_id_sink, interfaces.ConnectionIDSink) assert isinstance(command_sink, interfaces.CommandSink) assert isinstance(ui_state, interfaces.UIState) self.out = out self.connection_id_sink = connection_id_sink self.command_sink = command_sink self.state = PersistentUIState(ui_state) # maps connection ids to thread numbers self.connection_threads = {} self.catch = False # catch exceptions instead of letting them kill the plugin # Show full error messages in the case of a crash gdb.execute('set python print-stack full') if not self.out.show_unprocessed: # Suppress GDB output gdb.execute('set inferior-tty /dev/null') try: # GDB will automatically load the symbols when needed, but if we do it first we get to detect problems libwayland_symbols.verify() except RuntimeError as e: self.out.warn('Loading libwayland symbols failed: ' + str(e)) self.out.warn( 'libwayland debug symbols were not found, so Wayland messages may not be detected in GDB mode' ) self.out.warn( 'See https://github.com/wmww/wayland-debug/blob/master/libwayland_debug_symbols.md for more information' ) WlConnectionCreateBreakpoint(self) WlConnectionDestroyBreakpoint(self) WlClosureCallBreakpoint(self, 'wl_closure_invoke', extract.received_message) WlClosureCallBreakpoint(self, 'wl_closure_dispatch', extract.received_message) WlClosureCallBreakpoint(self, 'wl_closure_send', extract.sent_message) WlClosureCallBreakpoint(self, 'wl_closure_queue', extract.sent_message) WlCommand(self, 'w') WlCommand(self, 'wl') WlCommand(self, 'wayland') for command in command_sink.toplevel_commands(): WlSubcommand(self, command) logger.info('Breakpoints: ' + repr(gdb.breakpoints())) def open_connection(self, connection_id, is_server): try: self.connection_threads[connection_id] = gdb.selected_thread( ).global_num self.connection_id_sink.open_connection(time_now(), connection_id, is_server) except Exception as e: if not self.catch: raise self.out.error( repr(e) + ' raised closing connection ' + str(connection_id)) def close_connection(self, connection_id): try: self.connection_id_sink.close_connection(time_now(), connection_id) except Exception as e: if not self.catch: raise self.out.error( repr(e) + ' raised closing connection ' + str(connection_id)) def process_message(self, message_extractor): if self.state.paused(): self.state.resume_requested() try: connection_id, message = message_extractor() current_thread_num = gdb.selected_thread().global_num connection_thread_num = self.connection_threads.get(connection_id) if connection_thread_num != current_thread_num: self.out.warn('Got message ' + str(message) + ' on thread ' + str(current_thread_num) + ' instead of connection\'s main thread ' + str(connection_thread_num)) except Exception as e: if not self.catch: raise self.out.error(repr(e) + ' raised extracting message from GDB') try: self.connection_id_sink.message(connection_id, message) except Exception as e: if not self.catch: raise self.out.error( repr(e) + ' raised processing message ' + str(message)) def invoke_command(self, command): try: self.state.pause_requested() self.command_sink.process_command(command) if self.state.should_quit(): gdb.execute('quit') elif not self.state.paused(): gdb.execute('continue') except Exception as e: if not self.catch: raise self.out.error( repr(e) + ' raised invoking command `' + command + '`') def paused(self): return self.state.paused()
class Plugin: '''A GDB plugin (should only be instantiated when inside GDB)''' def __init__(self, out: Output, connection_id_sink: ConnectionIDSink, command_sink: CommandSink, ui_state: UIState) -> None: self.out = out self.connection_id_sink = connection_id_sink self.command_sink = command_sink self.state = PersistentUIState(ui_state) # maps connection ids to thread numbers self.connection_threads: Dict[str, int] = {} # Show full error messages in the case of a crash gdb.execute('set python print-stack full') if not self.out.show_unprocessed: # Suppress GDB output gdb.execute('set inferior-tty /dev/null') try: # GDB will automatically load the symbols when needed, but if we do it first we get to detect problems libwayland_symbols.verify() except RuntimeError as e: self.out.warn('Loading libwayland symbols failed: ' + str(e)) self.out.warn( 'libwayland debug symbols were not found, so Wayland messages may not be detected in GDB mode' ) self.out.warn( 'See https://github.com/wmww/wayland-debug/blob/master/libwayland_debug_symbols.md for more information' ) #WlConnectionCreateBreakpoint(self) WlConnectionDestroyBreakpoint(self) WlClosureCallBreakpoint(self, 'wl_closure_invoke', extract.received_message) WlClosureCallBreakpoint(self, 'wl_closure_dispatch', extract.received_message) WlClosureCallBreakpoint(self, 'serialize_closure', extract.sent_message) WlCommand(self, 'w') WlCommand(self, 'wl') WlCommand(self, 'wayland') for command in command_sink.toplevel_commands(): WlSubcommand(self, command) logging.info('Breakpoints: ' + repr(gdb.breakpoints())) def open_connection(self, connection_id: str, is_server: Optional[bool]) -> None: self.connection_threads[connection_id] = gdb.selected_thread( ).global_num self.connection_id_sink.open_connection(time_now(), connection_id, is_server) def close_connection(self, connection_id: str) -> None: del self.connection_threads[connection_id] self.connection_id_sink.close_connection(time_now(), connection_id) def process_message(self, connection_id: str, message: wl.Message) -> None: if self.state.paused(): self.state.resume_requested() current_thread_num = gdb.selected_thread().global_num connection_thread_num = self.connection_threads.get(connection_id) if connection_thread_num is None: is_server = None if message.name == 'get_registry': is_server = not message.sent self.open_connection(connection_id, is_server) elif connection_thread_num != current_thread_num: self.out.warn('Got message ' + str(message) + ' on thread ' + str(current_thread_num) + ' instead of connection\'s main thread ' + str(connection_thread_num)) self.connection_id_sink.message(connection_id, message) def invoke_command(self, command: str) -> None: self.state.pause_requested() self.command_sink.process_command(command) if self.state.should_quit(): gdb.execute('quit') elif not self.state.paused(): gdb.execute('continue') def paused(self) -> bool: return self.state.paused()
def test_starts_out_correctly(self): state = mock.Mock(spec=interfaces.UIState) ps = PersistentUIState(state) self.assertEqual(ps.paused(), False) self.assertEqual(ps.should_quit(), False)