mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 10:08:56 +01:00 
			
		
		
		
	Symptom: 20 0x00007ffff756337b in gtk_widget_size_allocate_with_baseline (widget=0xd7a0c0 [DirectWindow], allocation=, baseline=) at gdb.error: value has been optimized out
		
			
				
	
	
		
			360 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import gdb
 | 
						|
import glib_gdb
 | 
						|
import sys
 | 
						|
 | 
						|
if sys.version_info[0] >= 3:
 | 
						|
    long = int
 | 
						|
else:
 | 
						|
    import itertools
 | 
						|
 | 
						|
    map = itertools.imap
 | 
						|
 | 
						|
# FrameDecorator is new in gdb 7.7, so we adapt to its absence.
 | 
						|
try:
 | 
						|
    import gdb.FrameDecorator
 | 
						|
 | 
						|
    HAVE_GDB_FRAMEDECORATOR = True
 | 
						|
    FrameDecorator = gdb.FrameDecorator.FrameDecorator
 | 
						|
except ImportError:
 | 
						|
    HAVE_GDB_FRAMEDECORATOR = False
 | 
						|
 | 
						|
 | 
						|
# 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_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 is not None:
 | 
						|
        return glib_gdb.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 is_g_type_class_instance(val):
 | 
						|
    type = val.type
 | 
						|
    if type.code != gdb.TYPE_CODE_PTR:
 | 
						|
        return False
 | 
						|
    return str(type.target()) == "GTypeClass"
 | 
						|
 | 
						|
 | 
						|
class GTypeHandlePrettyPrinter:
 | 
						|
    "Prints a GType instance"
 | 
						|
 | 
						|
    def __init__(self, val, hint=""):
 | 
						|
        self.val = val
 | 
						|
        self.hint = hint
 | 
						|
 | 
						|
    def to_string(self):
 | 
						|
        typenode = g_type_to_typenode(self.val)
 | 
						|
        if typenode is not None:
 | 
						|
            name = glib_gdb.g_quark_to_string(typenode["qname"])
 | 
						|
            s = ("0x%x [%s%s") % (long(self.val), self.hint, name)
 | 
						|
            for i in range(1, int(typenode["n_supers"])):
 | 
						|
                node = g_type_to_typenode(typenode["supers"][i])
 | 
						|
                if node:
 | 
						|
                    name = glib_gdb.g_quark_to_string(node["qname"])
 | 
						|
                else:
 | 
						|
                    name = "???"
 | 
						|
                s += "/" + name
 | 
						|
            return s + "]"
 | 
						|
        else:
 | 
						|
            return ("0x%x") % (long(self.val))
 | 
						|
 | 
						|
 | 
						|
def pretty_printer_lookup(val):
 | 
						|
    if is_g_type_instance(val):
 | 
						|
        return GTypePrettyPrinter(val)
 | 
						|
    if str(val.type) == "GType":
 | 
						|
        return GTypeHandlePrettyPrinter(val)
 | 
						|
    if is_g_type_class_instance(val):
 | 
						|
        return GTypeHandlePrettyPrinter(val["g_type"], "g_type: ")
 | 
						|
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def get_signal_name(id):
 | 
						|
    if id is None:
 | 
						|
        return None
 | 
						|
    id = long(id)
 | 
						|
    if id == 0:
 | 
						|
        return None
 | 
						|
 | 
						|
    try:
 | 
						|
        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()
 | 
						|
    except gdb.error:
 | 
						|
        return None
 | 
						|
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def frame_name(frame):
 | 
						|
    return str(frame.function())
 | 
						|
 | 
						|
 | 
						|
def frame_var(frame, var):
 | 
						|
    return frame.inferior_frame().read_var(var)
 | 
						|
 | 
						|
 | 
						|
class SignalFrame(FrameDecorator):
 | 
						|
    def __init__(self, frames):
 | 
						|
        FrameDecorator.__init__(self, frames[-1])
 | 
						|
        self.frame = frames[-1]
 | 
						|
        self.frames = frames
 | 
						|
 | 
						|
    def name(self):
 | 
						|
        return "signal-emission"
 | 
						|
 | 
						|
    def read_var(self, frame, name, array=None):
 | 
						|
        try:
 | 
						|
            v = frame_var(frame, name)
 | 
						|
            if v is None or v.is_optimized_out:
 | 
						|
                return None
 | 
						|
            if array is not None:
 | 
						|
                array.append(v)
 | 
						|
            return v
 | 
						|
        except ValueError:
 | 
						|
            return None
 | 
						|
 | 
						|
    def read_object(self, frame, name, array=None):
 | 
						|
        try:
 | 
						|
            v = frame_var(frame, name)
 | 
						|
            if v is 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 is not None and g_type_name_from_instance(v):
 | 
						|
                if array is not None:
 | 
						|
                    array.append(v)
 | 
						|
                return v
 | 
						|
            return None
 | 
						|
        except ValueError:
 | 
						|
            return None
 | 
						|
 | 
						|
    def append(self, array, obj):
 | 
						|
        if obj is not None:
 | 
						|
            array.append(obj)
 | 
						|
 | 
						|
    def or_join_array(self, array):
 | 
						|
        if len(array) == 0:
 | 
						|
            return "???"
 | 
						|
        else:
 | 
						|
            return " or ".join(set(map(str, array)))
 | 
						|
 | 
						|
    def get_detailed_signal_from_frame(self, frame, signal):
 | 
						|
        detail = self.read_var(frame, "detail")
 | 
						|
        detail = glib_gdb.g_quark_to_string(detail)
 | 
						|
        if detail is not None:
 | 
						|
            return signal + ":" + detail
 | 
						|
        else:
 | 
						|
            return signal
 | 
						|
 | 
						|
    def function(self):
 | 
						|
        instances = []
 | 
						|
        signals = []
 | 
						|
 | 
						|
        for frame in self.frames:
 | 
						|
            name = frame_name(frame)
 | 
						|
            if name == "signal_emit_unlocked_R":
 | 
						|
                self.read_object(frame, "instance", instances)
 | 
						|
                node = self.read_var(frame, "node")
 | 
						|
                if node:
 | 
						|
                    signal = node["name"].string()
 | 
						|
                    signal = self.get_detailed_signal_from_frame(frame, signal)
 | 
						|
                    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:
 | 
						|
                    signal = self.get_detailed_signal_from_frame(frame, signal)
 | 
						|
                    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:
 | 
						|
                    signal = self.get_detailed_signal_from_frame(frame, signal)
 | 
						|
                    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)
 | 
						|
 | 
						|
        return "<emit signal '%s' on instance %s>" % (signal, instance)
 | 
						|
 | 
						|
    def elided(self):
 | 
						|
        return self.frames[0:-1]
 | 
						|
 | 
						|
    def describe(self, stream, full):
 | 
						|
        stream.write(" " + self.function() + "\n")
 | 
						|
 | 
						|
 | 
						|
class GFrameDecorator:
 | 
						|
    def __init__(self, iter):
 | 
						|
        self.queue = []
 | 
						|
        self.iter = iter
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return self
 | 
						|
 | 
						|
    def fill(self):
 | 
						|
        while len(self.queue) <= 8:
 | 
						|
            try:
 | 
						|
                f = next(self.iter)
 | 
						|
                self.queue.append(f)
 | 
						|
            except StopIteration:
 | 
						|
                return
 | 
						|
 | 
						|
    def find_signal_emission(self):
 | 
						|
        for i in range(min(len(self.queue), 3)):
 | 
						|
            name = frame_name(self.queue[i])
 | 
						|
            if name == "signal_emit_unlocked_R" or name == "_g_closure_invoke_va":
 | 
						|
                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 = frame_name(self.queue[start - 1])
 | 
						|
                if prev_name.find("_marshal") >= 0 or prev_name == "g_closure_invoke":
 | 
						|
                    start = start - 1
 | 
						|
                else:
 | 
						|
                    break
 | 
						|
            end = emission + 1
 | 
						|
            while end < len(self.queue):
 | 
						|
                if frame_name(self.queue[end]) in [
 | 
						|
                    "g_signal_emitv",
 | 
						|
                    "g_signal_emit_valist",
 | 
						|
                    "g_signal_emit",
 | 
						|
                    "g_signal_emit_by_name",
 | 
						|
                    "signal_emitv_unlocked",
 | 
						|
                    "signal_emit_valist_unlocked",
 | 
						|
                ]:
 | 
						|
                    end = end + 1
 | 
						|
                else:
 | 
						|
                    break
 | 
						|
 | 
						|
            signal_frames = self.queue[start:end]
 | 
						|
            new_frames = [SignalFrame(signal_frames)]
 | 
						|
            self.queue[start:end] = new_frames
 | 
						|
 | 
						|
        return self.queue.pop(0)
 | 
						|
 | 
						|
    def __next__(self):
 | 
						|
        return self.next()
 | 
						|
 | 
						|
 | 
						|
class GFrameFilter(object):
 | 
						|
    name = "glib"
 | 
						|
    enabled = True
 | 
						|
    priority = 100
 | 
						|
 | 
						|
    def filter(self, iterator):
 | 
						|
        return GFrameDecorator(iterator)
 | 
						|
 | 
						|
 | 
						|
def register(obj):
 | 
						|
    if obj is None:
 | 
						|
        obj = gdb
 | 
						|
 | 
						|
    if HAVE_GDB_FRAMEDECORATOR:
 | 
						|
        filter = GFrameFilter()
 | 
						|
        obj.frame_filters[filter.name] = filter
 | 
						|
    obj.pretty_printers.append(pretty_printer_lookup)
 |