Files
gdb-gui/gui/source.py
Tom Tromey b73212ab30 add thread enforcement decorators
This adds a couple of decorators that can be used to
mark functions as needing to be run in the gdb thread
or in the gtk thread.

This found at least one bug.
2013-11-03 19:48:27 -07:00

195 lines
6.2 KiB
Python

# Copyright (C) 2012, 2013 Tom Tromey <tom@tromey.com>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Source view.
import gdb
from gui.invoker import Invoker
from gui.toplevel import Toplevel
import gui.startup
from gui.startup import in_gdb_thread, in_gtk_thread
import gui.toplevel
import gui.events
from gi.repository import Gtk, GtkSource, GObject, Gdk
class BufferManager:
def __init__(self):
self.buffers = {}
def release_buffer(self, buff):
# FIXME: we should be smart about buffer caching.
pass
def get_buffer(self, filename):
if filename in self.buffers:
return self.buffers[filename]
buff = GtkSource.Buffer()
buff.begin_not_undoable_action()
try:
contents = open(filename).read()
except:
return None
buff.set_text(contents)
buff.end_not_undoable_action()
buff.set_modified(False)
buff.filename = filename
self.buffers[filename] = buff
return buff
buffer_manager = BufferManager()
# Return (FILE, LINE) for the selected frame, or, if there is no
# frame, for "main". If there is no symbol file, return (None, None)
# instead.
@in_gdb_thread
def get_current_location():
try:
frame = gdb.selected_frame()
sal = frame.find_sal()
filename = sal.symtab.fullname()
lineno = sal.line
except gdb.error:
# FIXME: should use the static location as set by list etc.
# No frame - try 'main'.
try:
frame = None
sym = gdb.lookup_global_symbol('main')
lineno = sym.line
filename = sym.symtab.fullname()
except gdb.error:
# Perhaps no symbol file.
return (None, None, None)
except AttributeError:
return (None, None, None)
return (frame, filename, lineno)
class LRUHandler:
def __init__(self):
self.windows = []
@in_gdb_thread
def on_event(self, *args):
(frame, filename, lineno) = get_current_location()
if filename is not None:
gui.startup.send_to_gtk(lambda: self.show_source(frame,
filename,
lineno))
@in_gdb_thread
def _connect_events(self):
# FIXME - we need an event for "selected frame changed".
# ... and thread-changed
# really just pre-prompt would be good enough
gdb.events.stop.connect(self.on_event)
gui.events.frame_changed.connect(self.on_event)
@in_gdb_thread
def _disconnect_events(self):
gdb.events.stop.disconnect(self.on_event)
gui.event.frame_changed.disconnect(self.on_event)
@in_gtk_thread
def pick_window(self, frame):
# If a window is showing FRAME, use it.
# Otherwise, if a window has no frame, use that.
# Otherwise, use the first window.
no_frame = None
for w in self.windows:
# Perhaps this is technically not ok.
# We should document thread-safety a bit better.
# Or just fix it up.
if frame == w.frame:
return w
if w.frame is None and no_frame is None:
no_frame = w
if no_frame is not None:
return no_frame
return self.windows[0]
@in_gtk_thread
def show_source(self, frame, srcfile, srcline):
w = self.pick_window(frame)
# LRU policy.
self.windows.remove(w)
self.windows.append(w)
w.frame = frame
w.show_source(srcfile, srcline)
@in_gtk_thread
def remove(self, window):
self.windows.remove(window)
if len(self.windows) == 0:
gui.startup.send_to_gtk(self._disconnect_events)
@in_gtk_thread
def add(self, window):
self.windows.insert(0, window)
if len(self.windows) == 1:
gdb.post_event(self._connect_events)
# Show something.
gdb.post_event(lambda: self.on_event(None))
lru_handler = LRUHandler()
class SourceWindow(Toplevel):
def __init__(self):
super(SourceWindow, self).__init__()
self.frame = None
self.do_step = Invoker("step")
self.do_next = Invoker("next")
self.do_continue = Invoker("continue")
self.do_finish = Invoker("finish")
self.do_stop = Invoker("interrupt")
builder = gui.startup.create_builder('sourcewindow.xml')
builder.connect_signals(self)
self.window = builder.get_object("sourcewindow")
self.view = builder.get_object("view")
lru_handler.add(self)
self.window.show()
def deleted(self, widget, event):
lru_handler.remove(self)
def line_mark_activated(self, view, textiter, event):
if event.type != Gdk.EventType.BUTTON_PRESS:
return
if event.button.get_button()[1] != 1:
return
fun = Invoker("break %s:%d" % (self.view.get_buffer().filename,
textiter.get_line() + 1))
fun()
def _do_scroll(self, buff, srcline):
buff.place_cursor(buff.get_iter_at_line(srcline))
self.view.scroll_mark_onscreen(buff.get_insert())
return False
def show_source(self, srcfile, srcline):
buff = buffer_manager.get_buffer(srcfile)
if buff is not None:
old_buffer = self.view.get_buffer()
self.view.set_buffer(buff)
buffer_manager.release_buffer(old_buffer)
GObject.idle_add(self._do_scroll, buff, srcline - 1)
# self.view.scroll_to_iter(buff.get_iter_at_line(srcline), 0.0)