[glib] Initial support for gdb python macros



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]