[glib] Initial support for gdb python macros
- From: Alexander Larsson <alexl src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [glib] Initial support for gdb python macros
- Date: Mon, 21 Sep 2009 13:39:45 +0000 (UTC)
commit efe9169234e226f594b4254618f35a139338c35f
Author: Alexander Larsson <alexl redhat com>
Date: Fri Sep 18 17:15:32 2009 +0200
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
glib/Makefile.am | 13 ++-
glib/glib.py | 27 ++++
glib/libglib-gdb.py.in | 10 ++
gobject/Makefile.am | 12 ++-
gobject/gobject.py | 305 ++++++++++++++++++++++++++++++++++++++++++
gobject/libgobject-gdb.py.in | 10 ++
6 files changed, 375 insertions(+), 2 deletions(-)
---
diff --git a/glib/Makefile.am b/glib/Makefile.am
index af07716..52d1c62 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -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)
diff --git a/glib/glib.py b/glib/glib.py
new file mode 100644
index 0000000..b2cccb3
--- /dev/null
+++ b/glib/glib.py
@@ -0,0 +1,27 @@
+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)
diff --git a/glib/libglib-gdb.py.in b/glib/libglib-gdb.py.in
new file mode 100644
index 0000000..78316be
--- /dev/null
+++ b/glib/libglib-gdb.py.in
@@ -0,0 +1,10 @@
+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 ())
diff --git a/gobject/Makefile.am b/gobject/Makefile.am
index ec27aa1..044c413 100644
--- a/gobject/Makefile.am
+++ b/gobject/Makefile.am
@@ -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)
diff --git a/gobject/gobject.py b/gobject/gobject.py
new file mode 100644
index 0000000..b96d150
--- /dev/null
+++ b/gobject/gobject.py
@@ -0,0 +1,305 @@
+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)
diff --git a/gobject/libgobject-gdb.py.in b/gobject/libgobject-gdb.py.in
new file mode 100644
index 0000000..6ebd0dc
--- /dev/null
+++ b/gobject/libgobject-gdb.py.in
@@ -0,0 +1,10 @@
+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 ())
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]