gst: add some gdb python macros
authorMichael Olbrich <m.olbrich@pengutronix.de>
Wed, 26 Sep 2018 11:33:31 +0000 (13:33 +0200)
committerThibault Saunier <tsaunier@gnome.org>
Thu, 8 Nov 2018 12:36:23 +0000 (12:36 +0000)
This adds gdb pretty printer for some GStreamer types.
For GstObject pointers the type and name is added, e.g.
"0x5555557e4110 [GstDecodeBin|decodebin0]".
For GstMiniObject pointers the object type is added, e.g.
"0x7fffe001fc50 [GstBuffer]".
For GstClockTime and GstClockTimeDiff the time is also printed in human
readable form, e.g. "150116219955 [+0:02:30.116219955]".

Fixes #320

configure.ac
libs/gst/helpers/.gitignore
libs/gst/helpers/Makefile.am
libs/gst/helpers/glib_gobject_helper.py [new file with mode: 0644]
libs/gst/helpers/gst_gdb.py [new file with mode: 0644]
libs/gst/helpers/libgstreamer-gdb.py.in [new file with mode: 0644]
libs/gst/helpers/meson.build

index 93a6755..2bb7430 100644 (file)
@@ -1083,6 +1083,7 @@ data/bash-completion/helpers/gst
 gst/Makefile
 gst/gstconfig.h
 gst/gstversion.h
+libs/gst/helpers/libgstreamer-gdb.py
 gst/parse/Makefile
 gst/printf/Makefile
 libs/Makefile
index 2a86569..87c36bf 100644 (file)
@@ -1,4 +1,5 @@
 gst-plugin-scanner
 gst-completion-helper
 gst-ptp-helper
+libgstreamer-gdb.py
 *.o
index d72a652..f76644a 100644 (file)
@@ -31,3 +31,10 @@ endif
 endif
 
 EXTRA_DIST = ptp_helper_post_install.sh
+
+# install gdb scripts
+gdbdir = $(datadir)/gstreamer-@GST_API_VERSION@/gdb
+dist_gdb_DATA = gst_gdb.py glib_gobject_helper.py
+
+install-data-hook:
+       $(INSTALL) -D $(builddir)/libgstreamer-gdb.py $(DESTDIR)$(datadir)/gdb/auto-load$(libdir)/libgstreamer-@GST_API_VERSION@.so.0.$(GST_CURRENT).$(GST_REVISION)-gdb.py
diff --git a/libs/gst/helpers/glib_gobject_helper.py b/libs/gst/helpers/glib_gobject_helper.py
new file mode 100644 (file)
index 0000000..5b08c38
--- /dev/null
@@ -0,0 +1,70 @@
+##
+## imported from glib: glib/glib_gdb.py
+##
+import gdb
+import sys
+
+if sys.version_info[0] >= 3:
+    long = int
+
+# 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 is None:
+        return None
+    quark = long(quark)
+    if quark == 0:
+        return None
+    try:
+        val = read_global_var ("quarks")
+        max_q = long(read_global_var ("quark_seq_id"))
+    except:
+        try:
+            val = read_global_var ("g_quarks")
+            max_q = long(read_global_var ("g_quark_seq_id"))
+        except:
+            return None;
+    if quark < max_q:
+        return val[quark].string()
+    return None
+
+##
+## imported from glib: gobject/gobject_gdb.py
+##
+
+def g_type_to_typenode (gtype):
+    def lookup_fundamental_type (typenode):
+        if typenode == 0:
+            return None
+        val = read_global_var ("static_fundamental_type_nodes")
+        if val is 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)
+    return typenode
+
+def g_type_to_name (gtype):
+    typenode = g_type_to_typenode(gtype)
+    if typenode != None:
+        return g_quark_to_string (typenode["qname"])
+    return None
+
+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
diff --git a/libs/gst/helpers/gst_gdb.py b/libs/gst/helpers/gst_gdb.py
new file mode 100644 (file)
index 0000000..fc3b48e
--- /dev/null
@@ -0,0 +1,116 @@
+import gdb
+import sys
+import re
+
+from glib_gobject_helper import g_type_to_name, g_type_name_from_instance
+
+if sys.version_info[0] >= 3:
+    long = int
+
+def is_gst_type (val, klass):
+    def _is_gst_type (type):
+        if str(type) == klass:
+            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_gst_type (first_field.type)
+
+
+    type = val.type
+    if type.code != gdb.TYPE_CODE_PTR:
+        return False
+    type = type.target()
+    return _is_gst_type (type)
+
+class GstMiniObjectPrettyPrinter:
+    "Prints a GstMiniObject instance pointer"
+
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        try:
+            inst = self.val.cast (gdb.lookup_type("GstMiniObject").pointer())
+            gtype = inst["type"]
+            name = g_type_to_name (gtype)
+            return "0x%x [%s]" % (long(self.val), name)
+        except RuntimeError:
+            return  "0x%x" % long(self.val)
+
+class GstObjectPrettyPrinter:
+    "Prints a GstObject instance"
+
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        try:
+            name = g_type_name_from_instance (self.val)
+            if not name:
+                name = str(self.val.type.target())
+            if long(self.val) != 0:
+                inst = self.val.cast (gdb.lookup_type("GstObject").pointer())
+                inst_name = inst["name"].string()
+                if inst_name:
+                    name += "|" + inst_name
+            return  ("0x%x [%s]") % (long(self.val), name)
+        except RuntimeError:
+            return  "0x%x" % long(self.val)
+
+class GstClockTimePrinter:
+    "Prints a GstClockTime / GstClockTimeDiff"
+
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        GST_SECOND = 1000000000
+        GST_CLOCK_TIME_NONE = 2**64-1
+        GST_CLOCK_STIME_NONE = -2**63
+        n = int(self.val)
+        prefix = ""
+        invalid = False
+        if str(self.val.type) == "GstClockTimeDiff":
+            if n == GST_CLOCK_STIME_NONE:
+                invalid = True
+            prefix = "+" if n >= 0 else "-"
+            n = abs(n)
+        else:
+            if n == GST_CLOCK_TIME_NONE:
+                invalid = True
+
+        if invalid:
+            return str(n) + " [99:99:99.999999999]"
+
+        return str(n) + " [%s%u:%02u:%02u.%09u]" % ( prefix,
+             n / (GST_SECOND * 60 * 60),
+            (n / (GST_SECOND * 60)) % 60,
+            (n / GST_SECOND) % 60,
+             n % GST_SECOND )
+
+def gst_pretty_printer_lookup (val):
+    if is_gst_type (val, "GstMiniObject"):
+        return GstMiniObjectPrettyPrinter (val)
+    if is_gst_type (val, "GstObject"):
+        return GstObjectPrettyPrinter (val)
+    if str(val.type) == "GstClockTime" or str(val.type) == "GstClockTimeDiff":
+        return GstClockTimePrinter (val)
+    return None
+
+def register (obj):
+    if obj == None:
+        obj = gdb
+
+    # Make sure this is always used befor the glib lookup function.
+    # Otherwise the gobject pretty printer is used for GstObjects
+    obj.pretty_printers.insert(0, gst_pretty_printer_lookup)
diff --git a/libs/gst/helpers/libgstreamer-gdb.py.in b/libs/gst/helpers/libgstreamer-gdb.py.in
new file mode 100644 (file)
index 0000000..2cf6542
--- /dev/null
@@ -0,0 +1,10 @@
+import sys
+import gdb
+
+# Update module path.
+dir_ = '@DATADIR@/gstreamer-@GST_API_VERSION@/gdb'
+if not dir_ in sys.path:
+    sys.path.insert(0, dir_)
+
+from gst_gdb import register
+register (gdb.current_objfile ())
index 891ea55..747cc9e 100644 (file)
@@ -124,3 +124,24 @@ if have_ptp
       helpers_install_dir, with_ptp_helper_permissions,
       setcap.found() ? setcap.path() : '')
 endif
+
+install_data(['gst_gdb.py', 'glib_gobject_helper.py'],
+  install_dir : join_paths(get_option('datadir'), 'glib-2.0', 'gdb'))
+
+gdbconf = configuration_data()
+gdbconf.set('GST_API_VERSION', apiversion)
+gdbconf.set('DATADIR', '@0@/@1@'.format(get_option('prefix'), get_option('datadir')))
+
+if host_system != 'windows'
+  # XXX: We add a leading './' because prefix is an absolute path and we
+  # need it to be a relative path so that join_paths appends it to the end.
+  gdb_install_dir = join_paths(get_option('datadir'), 'gdb', 'auto-load', './' + get_option('prefix'), get_option('libdir'))
+else
+  # FIXME: Cannot install on Windows because the path will contain a drive
+  # letter and colons are not allowed in paths.
+  gdb_install_dir = disabler()
+endif
+configure_file(input : 'libgstreamer-gdb.py.in',
+  output : 'libgstreamer-@0@.so.@1@-gdb.py'.format(apiversion, libversion),
+  install_dir : gdb_install_dir,
+  configuration : gdbconf)