mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-24 14:36:13 +01:00
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
This commit is contained in:
parent
18af48ba9a
commit
efe9169234
@ -70,6 +70,7 @@ EXTRA_DIST += \
|
|||||||
gregex.c \
|
gregex.c \
|
||||||
gregex.h \
|
gregex.h \
|
||||||
win_iconv.c \
|
win_iconv.c \
|
||||||
|
libglib-gdb.py.in \
|
||||||
$(MIRRORING_TAB_SOURCE)
|
$(MIRRORING_TAB_SOURCE)
|
||||||
|
|
||||||
# These may be in the builddir too
|
# 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; \
|
if test -f $$f; then d=.; else d=$(srcdir); fi; \
|
||||||
cp $$d/$$f $(distdir) || exit 1; done
|
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
|
if HAVE_GLIB_RUNTIME_LIBDIR
|
||||||
install-data-hook:
|
|
||||||
mkdir -p $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
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 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
||||||
mv $(DESTDIR)$(libdir)/libglib-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
mv $(DESTDIR)$(libdir)/libglib-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
||||||
|
27
glib/glib.py
Normal file
27
glib/glib.py
Normal file
@ -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)
|
10
glib/libglib-gdb.py.in
Normal file
10
glib/libglib-gdb.py.in
Normal file
@ -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 ())
|
@ -225,6 +225,7 @@ testgobject_LDADD = $(progs_LDADD)
|
|||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
makefile.msc.in \
|
makefile.msc.in \
|
||||||
gobject.rc.in \
|
gobject.rc.in \
|
||||||
|
libgobject-gdb.py.in \
|
||||||
glib-genmarshal.1 \
|
glib-genmarshal.1 \
|
||||||
glib-mkenums.in \
|
glib-mkenums.in \
|
||||||
glib-mkenums.1 \
|
glib-mkenums.1 \
|
||||||
@ -261,8 +262,17 @@ distclean-local:
|
|||||||
rm -f $(BUILT_EXTRA_DIST); \
|
rm -f $(BUILT_EXTRA_DIST); \
|
||||||
fi
|
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
|
if HAVE_GLIB_RUNTIME_LIBDIR
|
||||||
install-data-hook:
|
|
||||||
mkdir -p $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
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 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
||||||
mv $(DESTDIR)$(libdir)/libgobject-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
mv $(DESTDIR)$(libdir)/libgobject-2.0.so.0.$(LT_CURRENT).0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
|
||||||
|
305
gobject/gobject.py
Normal file
305
gobject/gobject.py
Normal file
@ -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)
|
10
gobject/libgobject-gdb.py.in
Normal file
10
gobject/libgobject-gdb.py.in
Normal file
@ -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 ())
|
Loading…
Reference in New Issue
Block a user