Example #1
0
 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)
Example #2
0
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)
Example #3
0
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)
Example #4
0
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()
Example #5
0
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()
Example #6
0
 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)