gst: add some gdb python macros

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
This commit is contained in:
Michael Olbrich 2018-09-26 13:33:31 +02:00 committed by Thibault Saunier
parent 1fda8c3bcf
commit bc621cc335
7 changed files with 226 additions and 0 deletions

View 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

View file

@ -1,4 +1,5 @@
gst-plugin-scanner
gst-completion-helper
gst-ptp-helper
libgstreamer-gdb.py
*.o

View 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

View file

@ -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

116
libs/gst/helpers/gst_gdb.py Normal file
View file

@ -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)

View file

@ -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 ())

View 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)