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