Commit efe91692 authored by Alexander Larsson's avatar Alexander Larsson

Initial support for gdb python macros

This includes support for gobject pointer pretty printing and
signal frame compression in backtraces.

https://bugzilla.gnome.org/show_bug.cgi?id=595619
parent 18af48ba
......@@ -70,6 +70,7 @@ EXTRA_DIST += \
gregex.c \
gregex.h \
win_iconv.c \
libglib-gdb.py.in \
$(MIRRORING_TAB_SOURCE)
# These may be in the builddir too
......@@ -365,8 +366,18 @@ dist-hook: $(BUILT_EXTRA_DIST)
if test -f $$f; then d=.; else d=$(srcdir); fi; \
cp $$d/$$f $(distdir) || exit 1; done
# install gdb scripts
gdbdir = $(datadir)/glib-2.0/gdb
gdb_SCRIPTS = glib.py
libglib-gdb.py: libglib-gdb.py.in
sed -e "s|\@datadir\@|$(datadir)|" libglib-gdb.py.in > libglib-gdb.py
install-data-hook: libglib-gdb.py
mkdir -p $(DESTDIR)$(datadir)/gdb/auto-load${libdir}
$(INSTALL) libglib-gdb.py $(DESTDIR)$(datadir)/gdb/auto-load${libdir}/libglib-2.0.so.0.$(LT_CURRENT).0-gdb.py
if HAVE_GLIB_RUNTIME_LIBDIR
install-data-hook:
mkdir -p $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
mv $(DESTDIR)$(libdir)/libglib-2.0.so.0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
mv $(DESTDIR)$(libdir)/libglib-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
......
import gdb
# This is not quite right, as local vars may override symname
def read_global_var (symname):
return gdb.selected_frame().read_var(symname)
def g_quark_to_string (quark):
if quark == None:
return None
quark = long(quark)
if quark == 0:
return None
val = read_global_var ("g_quarks")
max_q = long(read_global_var ("g_quark_seq_id"))
if quark < max_q:
return val[quark].string()
return None
def pretty_printer_lookup (val):
# None yet, want things like hash table and list
return None
def register (obj):
if obj == None:
obj = gdb
obj.pretty_printers.append(pretty_printer_lookup)
import sys
import gdb
# Update module path.
dir = '@datadir@/glib-2.0/gdb'
if not dir in sys.path:
sys.path.insert(0, dir)
from glib import register
register (gdb.current_objfile ())
......@@ -225,6 +225,7 @@ testgobject_LDADD = $(progs_LDADD)
EXTRA_DIST += \
makefile.msc.in \
gobject.rc.in \
libgobject-gdb.py.in \
glib-genmarshal.1 \
glib-mkenums.in \
glib-mkenums.1 \
......@@ -261,8 +262,17 @@ distclean-local:
rm -f $(BUILT_EXTRA_DIST); \
fi
# install gdb scripts
gdbdir = $(datadir)/glib-2.0/gdb
gdb_SCRIPTS = gobject.py
libgobject-gdb.py: libgobject-gdb.py.in
sed -e "s|\@datadir\@|$(datadir)|" libgobject-gdb.py.in > libgobject-gdb.py
install-data-hook: libgobject-gdb.py
mkdir -p $(DESTDIR)$(datadir)/gdb/auto-load${libdir}
$(INSTALL) libgobject-gdb.py $(DESTDIR)$(datadir)/gdb/auto-load${libdir}/libgobject-2.0.so.0.$(LT_CURRENT).0-gdb.py
if HAVE_GLIB_RUNTIME_LIBDIR
install-data-hook:
mkdir -p $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
mv $(DESTDIR)$(libdir)/libgobject-2.0.so.0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
mv $(DESTDIR)$(libdir)/libgobject-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
......
import gdb
import glib
import gdb.backtrace
import gdb.command.backtrace
# This is not quite right, as local vars may override symname
def read_global_var (symname):
return gdb.selected_frame().read_var(symname)
def g_type_to_name (gtype):
def lookup_fundamental_type (typenode):
if typenode == 0:
return None
val = read_global_var ("static_fundamental_type_nodes")
if val == None:
return None
return val[typenode >> 2].address()
gtype = long(gtype)
typenode = gtype - gtype % 4
if typenode > (255 << 2):
typenode = gdb.Value(typenode).cast (gdb.lookup_type("TypeNode").pointer())
else:
typenode = lookup_fundamental_type (typenode)
if typenode != None:
return glib.g_quark_to_string (typenode["qname"])
return None
def is_g_type_instance (val):
def is_g_type_instance_helper (type):
if str(type) == "GTypeInstance":
return True
while type.code == gdb.TYPE_CODE_TYPEDEF:
type = type.target()
if type.code != gdb.TYPE_CODE_STRUCT:
return False
fields = type.fields()
if len (fields) < 1:
return False
first_field = fields[0]
return is_g_type_instance_helper(first_field.type)
type = val.type
if type.code != gdb.TYPE_CODE_PTR:
return False
type = type.target()
return is_g_type_instance_helper (type)
def g_type_name_from_instance (instance):
if long(instance) != 0:
try:
inst = instance.cast (gdb.lookup_type("GTypeInstance").pointer())
klass = inst["g_class"]
gtype = klass["g_type"]
name = g_type_to_name (gtype)
return name
except RuntimeError:
pass
return None
class GTypePrettyPrinter:
"Prints a GType instance pointer"
def __init__ (self, val):
self.val = val
def to_string (self):
name = g_type_name_from_instance (self.val)
if name:
return ("0x%x [%s]")% (long(self.val), name)
return ("0x%x") % (long(self.val))
def pretty_printer_lookup (val):
if is_g_type_instance (val):
return GTypePrettyPrinter (val)
return None
def get_signal_name (id):
if id == None:
return None
id = long(id)
if id == 0:
return None
val = read_global_var ("g_signal_nodes")
max_s = read_global_var ("g_n_signal_nodes")
max_s = long(max_s)
if id < max_s:
return val[id]["name"].string()
return None
class GFrameWrapper:
def __init__ (self, frame):
self.frame = frame;
def name (self):
name = self.frame.name()
if name and name.startswith("IA__"):
return name[4:]
return name
def __getattr__ (self, name):
return getattr (self.frame, name)
# Monkey patch FrameWrapper to avoid IA__ in symbol names
old__init__ = gdb.command.backtrace.FrameWrapper.__init__
def monkey_patched_init(self, frame):
name = frame.name()
if name and name.startswith("IA__"):
frame = GFrameWrapper(frame)
old__init__(self,frame)
gdb.command.backtrace.FrameWrapper.__init__ = monkey_patched_init
class DummyFrame:
def __init__ (self, frame):
self.frame = frame
def name (self):
return "signal-emission-dummy"
def describe (self, stream, full):
stream.write (" <...>\n")
def __getattr__ (self, name):
return getattr (self.frame, name)
class SignalFrame:
def __init__ (self, frames):
self.frame = frames[-1]
self.frames = frames;
def name (self):
return "signal-emission"
def read_var (self, frame, name, array = None):
try:
v = frame.read_var (name)
if v == None or v.is_optimized_out:
return None
if array != None:
array.append (v)
return v
except ValueError:
return None
def read_object (self, frame, name, array = None):
try:
v = frame.read_var (name)
if v == None or v.is_optimized_out:
return None
v = v.cast (gdb.lookup_type("GObject").pointer())
# Ensure this is a somewhat correct object pointer
if v != None and g_type_name_from_instance (v):
if array != None:
array.append (v)
return v
return None
except ValueError:
return None
def append (self, array, obj):
if obj != None:
array.append (obj)
def or_join_array (self, array):
if len(array) == 0:
return "???"
v = {}
for i in range(len(array)):
v[str(array[i])] = 1
array = v.keys()
s = array[0]
for i in range(1, len(array)):
s = s + " or %s"%array[i]
return s
def describe (self, stream, full):
instances = []
signals = []
for frame in self.frames:
name = frame.name()
if name == "signal_emit_unlocked_R":
self.read_object (frame, "instance", instances)
node = self.read_var (frame, "node")
if node:
signal = node["name"].string()
detail = self.read_var (frame, "detail")
detail = glib.g_quark_to_string (detail)
if detail != None:
signal = signal + ":" + detail
self.append (signals, signal)
if name == "g_signal_emitv":
instance_and_params = self.read_var (frame, "instance_and_params")
if instance_and_params:
instance = instance_and_params[0]["v_pointer"].cast (gdb.Type("GObject").pointer())
self.append (instances, instance)
id = self.read_var (frame, "signal_id")
signal = get_signal_name (id)
if signal:
detail = self.read_var (frame, "detail")
detail = glib.g_quark_to_string (detail)
if detail != None:
signal = signal + ":" + detail
self.append (signals, signal)
if name == "g_signal_emit_valist" or name == "g_signal_emit":
self.read_object (frame, "instance", instances)
id = self.read_var (frame, "signal_id")
signal = get_signal_name (id)
if signal:
detail = self.read_var (frame, "detail")
detail = glib.g_quark_to_string (detail)
if detail != None:
signal = signal + ":" + detail
self.append (signals, signal)
if name == "g_signal_emit_by_name":
self.read_object (frame, "instance", instances)
self.read_var (frame, "detailed_signal", signals)
break
instance = self.or_join_array (instances)
signal = self.or_join_array (signals)
stream.write (" <emit signal %s on instance %s>\n" % (signal, instance))
def __getattr__ (self, name):
return getattr (self.frame, name)
class GFrameFilter:
def __init__ (self, iter):
self.queue = []
self.iter = iter
def __iter__ (self):
return self
def fill (self):
while len(self.queue) <= 6:
try:
f = self.iter.next ()
self.queue.append (f)
except StopIteration:
return
def find_signal_emission (self):
for i in range (min (len(self.queue), 3)):
if self.queue[i].name() == "signal_emit_unlocked_R":
return i
return -1
def next (self):
# Ensure we have enough frames for a full signal emission
self.fill()
# Are we at the end?
if len(self.queue) == 0:
raise StopIteration
emission = self.find_signal_emission ()
if emission > 0:
start = emission
while True:
if start == 0:
break
prev_name = self.queue[start-1].name()
if prev_name.find("_marshal_") or prev_name == "g_closure_invoke":
start = start - 1
else:
break
end = emission + 1
while end < len(self.queue):
if self.queue[end].name() in ["g_signal_emitv",
"g_signal_emit_valist",
"g_signal_emit",
"g_signal_emit_by_name"]:
end = end + 1
else:
break
signal_frames = self.queue[start:end]
new_frames = []
for i in range(len(signal_frames)-1):
new_frames.append(DummyFrame(signal_frames[i]))
new_frames.append(SignalFrame(signal_frames))
self.queue[start:end] = new_frames
return self.queue.pop(0)
def register (obj):
if obj == None:
obj = gdb
gdb.backtrace.push_frame_filter (GFrameFilter)
obj.pretty_printers.append(pretty_printer_lookup)
import sys
import gdb
# Update module path.
dir = '@datadir@/glib-2.0/gdb'
if not dir in sys.path:
sys.path.insert(0, dir)
from gobject import register
register (gdb.current_objfile ())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment