class ValueEntry(Gtk.Frame): @require_gui_thread def __init__(self, title, text): Gtk.Frame.__init__(self) self.set_label(title) self.set_label_align(0.0, 0.0) self.box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) self.box.set_margin_bottom(5) self.box.set_margin_left(2) self.text_entry = Gtk.Entry() self.text_entry.set_text(text) self.confirm_button = Gtk.Button(label="Set") self.get_style_context().add_class("value-entry") self.box.pack_start(self.text_entry, False, False, 0) self.box.pack_start(self.confirm_button, False, False, 5) self.add(self.box) self.show_all() self.confirm_button.connect("clicked", lambda btn: self._handle_confirm_click()) self.on_value_entered = EventBroadcaster() @require_gui_thread def set_value(self, value): """ @type value: str """ self.text_entry.set_text(value) def _handle_confirm_click(self): value = self.text_entry.get_text() self.set_value("") self.on_value_entered.notify(value) self.hide()
class AddressInput(Gtk.Box): def __init__(self): Gtk.Box.__init__(self) self.set_orientation(Gtk.Orientation.VERTICAL) self.label_row = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) self.label = Gtk.Label().new("Hex address or expression") self.label_row.pack_start(self.label, False, False, 0) self.input_row = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) self.address_input = Gtk.Entry() self.address_input.set_editable(True) self.confirm_button = Gtk.Button() self.confirm_button.set_label("Load") self.input_row.pack_start(self.address_input, False, False, 0) self.input_row.pack_start(self.confirm_button, False, False, 0) self.pack_start(self.label_row, False, False, 2) self.pack_start(self.input_row, False, False, 0) self.set_margin_left(10) self.on_address_selected = EventBroadcaster() self.confirm_button.connect("clicked", lambda *x: self.on_address_selected.notify( self.address_input.get_text())) def set_enabled(self, value): """ Sets whether the input should be enabled. @type value: bool """ self.confirm_button.set_sensitive(value) self.address_input.set_sensitive(value)
class ProgramRunner(object): def __init__(self, debugger, file, *args): self.debugger = debugger self.file = os.path.abspath(file) self.args = args self.parent_thread = None self.on_signal = EventBroadcaster() self.msg_queue = Queue.Queue() self.child_pid = None self.last_signal = None def run(self): assert not self.parent_thread self.parent_thread = threading.Thread(target=self._start_process) self.parent_thread.start() def exec_step_single(self): self._add_queue_command("step-single") def exec_continue(self): self._add_queue_command("continue") def exec_interrupt(self): try: os.kill(self.child_pid, signal.SIGUSR1) return True except: return False def exec_kill(self): try: os.kill(self.child_pid, signal.SIGKILL) return True except: return False def _add_queue_command(self, cmd): self.msg_queue.put(cmd) def _start_process(self): child_pid = os.fork() if child_pid: self.child_pid = child_pid self._parent() else: self._child() def _child(self): if ptrace.ptrace(ptrace.PTRACE_TRACEME) < 0: raise Exception() os.execl(self.file, self.file, *self.args) exit(0) def _parent(self): try: while True: pid, status = os.waitpid(self.child_pid, os.WNOHANG) if pid != 0: self._handle_child_status(status) try: command = self.msg_queue.get(True, 0.1) self._handle_command(self.child_pid, status, command) except Queue.Empty: pass except: traceback.print_exc() except OSError: self.on_signal.notify(ProcessState.Exited) except ProcessExitException: pass except: traceback.print_exc() self.child_pid = None self.parent_thread = None def _handle_child_status(self, status): if os.WIFSTOPPED(status): self._on_stop(status) elif os.WIFEXITED(status): self.on_signal.notify(ProcessState.Exited, os.WTERMSIG(status), os.WEXITSTATUS(status)) raise ProcessExitException() def _handle_command(self, pid, status, command): if command == "continue": self._do_continue(pid) elif command == "step-single": self._do_step_single(pid) def _on_stop(self, status): self.last_signal = os.WSTOPSIG(status) self.debugger.breakpoint_manager.set_breakpoints(self.child_pid) self.on_signal.notify(ProcessState.Stopped, self.last_signal) def _continue_after_breakpoint(self, exec_continue): pid = self.child_pid regs = ptrace.ptrace_getregs(pid) orig_address = regs.eip - 1 if self.debugger.breakpoint_manager.has_breakpoint_for_address( orig_address): self.debugger.breakpoint_manager.restore_instruction( pid, orig_address) regs.eip -= 1 assert ptrace.ptrace_setregs(pid, regs) self._do_step_single(pid) pid, status = os.waitpid(pid, 0) self.debugger.breakpoint_manager.set_breakpoints(pid) if exec_continue: self.exec_continue() else: self._handle_child_status(status) return True else: return False def _do_continue(self, pid): if self.last_signal == 5: # sigtrap if self._continue_after_breakpoint(True): return ptrace.ptrace(ptrace.PTRACE_CONT, pid) def _do_step_single(self, pid): if self.last_signal == 5: # sigtrap if self._continue_after_breakpoint(False): return ptrace.ptrace(ptrace.PTRACE_SINGLESTEP, pid)
class ToolbarManager(object): def __init__(self, toolbar_builder, debugger): signals = { "toolbar-run": lambda *x: self.run(), "toolbar-continue": lambda *x: self.cont(), "toolbar-stop": lambda *x: self.stop(), "toolbar-pause": lambda *x: self.pause(), "toolbar-step-over": lambda *x: self.step_over(), "toolbar-step-in": lambda *x: self.step_in(), "toolbar-step-out": lambda *x: self.step_out() } toolbar_builder.connect_signals(signals) self.toolbar_builder = toolbar_builder self.toolbar = toolbar_builder.get_object("toolbar") self.debugger = debugger self.debugger.on_process_state_changed.subscribe( self._handle_process_state_change) self.debugger.on_debugger_state_changed.subscribe( self._handle_debugger_state_change) self.grp_halt_control = ["stop", "pause"] self.grp_step = ["continue", "step_over", "step_in", "step_out"] self.on_run_process = EventBroadcaster() @require_gui_thread def _get_items(self): return [ self.toolbar.get_nth_item(i) for i in xrange(0, self.toolbar.get_n_items()) ] def _state_exited(self): self._change_grp_state(self.grp_halt_control, False) self._change_grp_state(self.grp_step, False) self._change_state("run", True) def _state_stopped(self): self._change_grp_state(self.grp_halt_control, False) self._change_grp_state(self.grp_step, True) self._change_state("stop", True) def _state_running(self): self._change_grp_state(self.grp_halt_control, True) self._change_grp_state(self.grp_step, False) self._change_state("run", False) def _handle_process_state_change(self, state, event_data): if state == ProcessState.Exited: self._state_exited() elif state == ProcessState.Stopped: self._state_stopped() elif state == ProcessState.Running: self._state_running() def _handle_debugger_state_change(self, state, old_value): if (state.is_set(DebuggerState.BinaryLoaded) and not state.is_set(DebuggerState.Running)): self._change_state("run", True) else: self._change_state("run", False) def _change_state(self, item_name, sensitive=True): run_on_gui(self._change_state_ui, item_name, sensitive) def _change_grp_state(self, group, sensitive=True): for item in group: self._change_state(item, sensitive) @require_gui_thread def _change_state_ui(self, item_name, sensitive=True): item = self.toolbar_builder.get_object(item_name) item.set_sensitive(sensitive) def run(self): self.on_run_process.notify() def cont(self): self.debugger.exec_continue() def stop(self): self.debugger.quit_program() def pause(self): self.debugger.exec_pause() def step_over(self): self.debugger.exec_step_over() def step_in(self): self.debugger.exec_step_in() def step_out(self): self.debugger.exec_step_out()
class SourceEditor(GtkSource.View): @staticmethod def load_file(path): try: return open(path).read() except: return None def __init__(self, debugger, language="cpp"): """ @type debugger: debugger.mi.MiDebugger @type language: str """ self.buffer = GtkSource.Buffer() super(SourceEditor, self).__init__(buffer=self.get_buffer()) self.debugger = debugger self.start_undoable() self.set_language(language) self.get_buffer().set_highlight_syntax(True) self.get_buffer().set_highlight_matching_brackets(True) self.set_editable(False) self.gutter_renderer = BreakpointRenderer( self, paths.get_resource("img/circle.png"), paths.get_resource("img/arrow.png")) gutter = self.get_gutter(Gtk.TextWindowType.LEFT) gutter.insert(self.gutter_renderer, 0) self.stop_undoable() self.set_show_line_numbers(True) self.set_highlight_current_line(True) self.set_show_right_margin(True) self.set_right_margin_position(80) self.file = None self.on_breakpoint_changed = EventBroadcaster() self.exec_line = None self.bp_lines = set() self.analyser = SourceAnalyzer() self.connect("motion-notify-event", lambda widget, event: self._handle_mouse_move(event)) self.connect("button-press-event", lambda widget, event: self._handle_mouse_click(event)) self.on_symbol_hover = EventBroadcaster() self.debugger.breakpoint_manager.on_breakpoint_changed.subscribe( self._handle_model_breakpoint_change) self.breakpoint_lines = [] def _handle_model_breakpoint_change(self, breakpoint): """ @type breakpoint: debugee.Breakpoint """ self._refresh_breakpoints() def _refresh_breakpoints(self): lines = [] if (self.file and self.debugger.state.is_set(DebuggerState.BinaryLoaded) and self.debugger.process_state != ProcessState.Running): for bp in self.debugger.breakpoint_manager.get_breakpoints(): if os.path.abspath(bp.location) == os.path.abspath(self.file): lines.append(bp.line - 1) self.breakpoint_lines = lines def _handle_mouse_move(self, event): x, y = self.window_to_buffer_coords(Gtk.TextWindowType.TEXT, event.x, event.y) iter = self.get_iter_at_location(x, y) line = iter.get_line() + 1 column = iter.get_line_offset() + 1 symbol = self.analyser.get_symbol_name(line, column) if symbol is not None: self.on_symbol_hover.notify(self, symbol) def _handle_mouse_click(self, event): if (event.type == Gdk.EventType.BUTTON_PRESS and self.get_window(Gtk.TextWindowType.LEFT) == event.window): x, y = self.window_to_buffer_coords(Gtk.TextWindowType.LEFT, event.x, event.y) iter = self.get_iter_at_location(x, y) if self.debugger.state.is_set(DebuggerState.BinaryLoaded): self.toggle_breakpoint(iter.get_line()) def get_buffer(self): return self.buffer def get_file(self): return self.file def get_breakpoint_lines(self): return self.breakpoint_lines @require_gui_thread def toggle_breakpoint(self, line): self.debugger.breakpoint_manager.toggle_breakpoint(self.file, line + 1) self.gutter_renderer.queue_draw() @require_gui_thread def set_language(self, key): manager = GtkSource.LanguageManager() language = manager.get_language(key) self.start_undoable() self.get_buffer().set_language(language) self.stop_undoable() @require_gui_thread def start_undoable(self): self.get_buffer().begin_not_undoable_action() @require_gui_thread def stop_undoable(self): self.get_buffer().end_not_undoable_action() @require_gui_thread def set_content_from_file(self, path): content = SourceEditor.load_file(path) if content: self.file = path self.start_undoable() self.get_buffer().set_text(content) self.stop_undoable() self.analyser.set_file(path) self._refresh_breakpoints() self.gutter_renderer.queue_draw() # show existing breakpoints @require_gui_thread def get_cursor_iter(self): cursor_rectangle = self.get_cursor_locations(None)[0] return self.get_iter_at_location(cursor_rectangle.x, cursor_rectangle.y) @require_gui_thread def get_current_column(self): return self.get_cursor_iter().get_line_offset() @require_gui_thread def get_current_line(self): return self.get_cursor_iter().get_line() @require_gui_thread def get_line_iters(self, line_number): start_line = self.get_buffer().get_iter_at_line(line_number) end_line = start_line.copy() end_line.forward_to_line_end() return (start_line, end_line) @require_gui_thread def set_exec_line(self, line_number): self.unset_exec_line() line_iters = self.get_line_iters(line_number) self.scroll_to_iter(line_iters[0], 0.0, True, 0.5, 0.5) self.exec_line = line_number self.gutter_renderer.queue_draw() @require_gui_thread def unset_exec_line(self): self.exec_line = None self.gutter_renderer.queue_draw() @require_gui_thread def undo(self): if self.get_buffer().can_undo(): self.get_buffer().undo() return True else: return False @require_gui_thread def redo(self): if self.get_buffer().can_redo(): self.get_buffer().redo() return True else: return False
class SourceEditor(GtkSource.View): @staticmethod def load_file(path): try: return open(path).read() except: return None def __init__(self, debugger, language="cpp"): """ @type debugger: debugger.mi.MiDebugger @type language: str """ self.buffer = GtkSource.Buffer() super(SourceEditor, self).__init__(buffer=self.get_buffer()) self.debugger = debugger self.start_undoable() self.set_language(language) self.get_buffer().set_highlight_syntax(True) self.get_buffer().set_highlight_matching_brackets(True) self.set_editable(False) self.gutter_renderer = BreakpointRenderer(self, paths.get_resource( "img/circle.png"), paths.get_resource( "img/arrow.png")) gutter = self.get_gutter(Gtk.TextWindowType.LEFT) gutter.insert(self.gutter_renderer, 0) self.stop_undoable() self.set_show_line_numbers(True) self.set_highlight_current_line(True) self.set_show_right_margin(True) self.set_right_margin_position(80) self.file = None self.on_breakpoint_changed = EventBroadcaster() self.exec_line = None self.bp_lines = set() self.analyser = SourceAnalyzer() self.connect("motion-notify-event", lambda widget, event: self._handle_mouse_move(event)) self.connect("button-press-event", lambda widget, event: self._handle_mouse_click(event)) self.on_symbol_hover = EventBroadcaster() self.debugger.breakpoint_manager.on_breakpoint_changed.subscribe( self._handle_model_breakpoint_change) self.breakpoint_lines = [] def _handle_model_breakpoint_change(self, breakpoint): """ @type breakpoint: debugee.Breakpoint """ self._refresh_breakpoints() def _refresh_breakpoints(self): lines = [] if (self.file and self.debugger.state.is_set(DebuggerState.BinaryLoaded) and self.debugger.process_state != ProcessState.Running): for bp in self.debugger.breakpoint_manager.get_breakpoints(): if os.path.abspath(bp.location) == os.path.abspath(self.file): lines.append(bp.line - 1) self.breakpoint_lines = lines def _handle_mouse_move(self, event): x, y = self.window_to_buffer_coords(Gtk.TextWindowType.TEXT, event.x, event.y) iter = self.get_iter_at_location(x, y) line = iter.get_line() + 1 column = iter.get_line_offset() + 1 symbol = self.analyser.get_symbol_name(line, column) if symbol is not None: self.on_symbol_hover.notify(self, symbol) def _handle_mouse_click(self, event): if (event.type == Gdk.EventType.BUTTON_PRESS and self.get_window(Gtk.TextWindowType.LEFT) == event.window): x, y = self.window_to_buffer_coords(Gtk.TextWindowType.LEFT, event.x, event.y) iter = self.get_iter_at_location(x, y) if self.debugger.state.is_set(DebuggerState.BinaryLoaded): self.toggle_breakpoint(iter.get_line()) def get_buffer(self): return self.buffer def get_file(self): return self.file def get_breakpoint_lines(self): return self.breakpoint_lines @require_gui_thread def toggle_breakpoint(self, line): self.debugger.breakpoint_manager.toggle_breakpoint(self.file, line + 1) self.gutter_renderer.queue_draw() @require_gui_thread def set_language(self, key): manager = GtkSource.LanguageManager() language = manager.get_language(key) self.start_undoable() self.get_buffer().set_language(language) self.stop_undoable() @require_gui_thread def start_undoable(self): self.get_buffer().begin_not_undoable_action() @require_gui_thread def stop_undoable(self): self.get_buffer().end_not_undoable_action() @require_gui_thread def set_content_from_file(self, path): content = SourceEditor.load_file(path) if content: self.file = path self.start_undoable() self.get_buffer().set_text(content) self.stop_undoable() self.analyser.set_file(path) self._refresh_breakpoints() self.gutter_renderer.queue_draw() # show existing breakpoints @require_gui_thread def get_cursor_iter(self): cursor_rectangle = self.get_cursor_locations(None)[0] return self.get_iter_at_location(cursor_rectangle.x, cursor_rectangle.y) @require_gui_thread def get_current_column(self): return self.get_cursor_iter().get_line_offset() @require_gui_thread def get_current_line(self): return self.get_cursor_iter().get_line() @require_gui_thread def get_line_iters(self, line_number): start_line = self.get_buffer().get_iter_at_line(line_number) end_line = start_line.copy() end_line.forward_to_line_end() return (start_line, end_line) @require_gui_thread def set_exec_line(self, line_number): self.unset_exec_line() line_iters = self.get_line_iters(line_number) self.scroll_to_iter(line_iters[0], 0.0, True, 0.5, 0.5) self.exec_line = line_number self.gutter_renderer.queue_draw() @require_gui_thread def unset_exec_line(self): self.exec_line = None self.gutter_renderer.queue_draw() @require_gui_thread def undo(self): if self.get_buffer().can_undo(): self.get_buffer().undo() return True else: return False @require_gui_thread def redo(self): if self.get_buffer().can_redo(): self.get_buffer().redo() return True else: return False
class ToolbarManager(object): def __init__(self, toolbar_builder, debugger): signals = { "toolbar-run": lambda *x: self.run(), "toolbar-continue": lambda *x: self.cont(), "toolbar-stop": lambda *x: self.stop(), "toolbar-pause": lambda *x: self.pause(), "toolbar-step-over": lambda *x: self.step_over(), "toolbar-step-in": lambda *x: self.step_in(), "toolbar-step-out": lambda *x: self.step_out() } toolbar_builder.connect_signals(signals) self.toolbar_builder = toolbar_builder self.toolbar = toolbar_builder.get_object("toolbar") self.debugger = debugger self.debugger.on_process_state_changed.subscribe( self._handle_process_state_change) self.debugger.on_debugger_state_changed.subscribe( self._handle_debugger_state_change) self.grp_halt_control = ["stop", "pause"] self.grp_step = ["continue", "step_over", "step_in", "step_out"] self.on_run_process = EventBroadcaster() @require_gui_thread def _get_items(self): return [self.toolbar.get_nth_item(i) for i in xrange(0, self.toolbar.get_n_items())] def _state_exited(self): self._change_grp_state(self.grp_halt_control, False) self._change_grp_state(self.grp_step, False) self._change_state("run", True) def _state_stopped(self): self._change_grp_state(self.grp_halt_control, False) self._change_grp_state(self.grp_step, True) self._change_state("stop", True) def _state_running(self): self._change_grp_state(self.grp_halt_control, True) self._change_grp_state(self.grp_step, False) self._change_state("run", False) def _handle_process_state_change(self, state, event_data): if state == ProcessState.Exited: self._state_exited() elif state == ProcessState.Stopped: self._state_stopped() elif state == ProcessState.Running: self._state_running() def _handle_debugger_state_change(self, state, old_value): if (state.is_set(DebuggerState.BinaryLoaded) and not state.is_set(DebuggerState.Running)): self._change_state("run", True) else: self._change_state("run", False) def _change_state(self, item_name, sensitive=True): run_on_gui(self._change_state_ui, item_name, sensitive) def _change_grp_state(self, group, sensitive=True): for item in group: self._change_state(item, sensitive) @require_gui_thread def _change_state_ui(self, item_name, sensitive=True): item = self.toolbar_builder.get_object(item_name) item.set_sensitive(sensitive) def run(self): self.on_run_process.notify() def cont(self): self.debugger.exec_continue() def stop(self): self.debugger.quit_program() def pause(self): self.debugger.exec_pause() def step_over(self): self.debugger.exec_step_over() def step_in(self): self.debugger.exec_step_in() def step_out(self): self.debugger.exec_step_out()