• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import gdb
2import glib_gdb
3import sys
4
5if sys.version_info[0] >= 3:
6    long = int
7else:
8    import itertools
9
10    map = itertools.imap
11
12# FrameDecorator is new in gdb 7.7, so we adapt to its absence.
13try:
14    import gdb.FrameDecorator
15
16    HAVE_GDB_FRAMEDECORATOR = True
17    FrameDecorator = gdb.FrameDecorator.FrameDecorator
18except ImportError:
19    HAVE_GDB_FRAMEDECORATOR = False
20
21
22# This is not quite right, as local vars may override symname
23def read_global_var(symname):
24    return gdb.selected_frame().read_var(symname)
25
26
27def g_type_to_typenode(gtype):
28    def lookup_fundamental_type(typenode):
29        if typenode == 0:
30            return None
31        val = read_global_var("static_fundamental_type_nodes")
32        if val is None:
33            return None
34        return val[typenode >> 2].address
35
36    gtype = long(gtype)
37    typenode = gtype - gtype % 4
38    if typenode > (255 << 2):
39        typenode = gdb.Value(typenode).cast(gdb.lookup_type("TypeNode").pointer())
40    else:
41        typenode = lookup_fundamental_type(typenode)
42    return typenode
43
44
45def g_type_to_name(gtype):
46    typenode = g_type_to_typenode(gtype)
47    if typenode is not None:
48        return glib_gdb.g_quark_to_string(typenode["qname"])
49    return None
50
51
52def is_g_type_instance(val):
53    def is_g_type_instance_helper(type):
54        if str(type) == "GTypeInstance":
55            return True
56
57        while type.code == gdb.TYPE_CODE_TYPEDEF:
58            type = type.target()
59
60        if type.code != gdb.TYPE_CODE_STRUCT:
61            return False
62
63        fields = type.fields()
64        if len(fields) < 1:
65            return False
66
67        first_field = fields[0]
68        return is_g_type_instance_helper(first_field.type)
69
70    type = val.type
71    if type.code != gdb.TYPE_CODE_PTR:
72        return False
73    type = type.target()
74    return is_g_type_instance_helper(type)
75
76
77def g_type_name_from_instance(instance):
78    if long(instance) != 0:
79        try:
80            inst = instance.cast(gdb.lookup_type("GTypeInstance").pointer())
81            klass = inst["g_class"]
82            gtype = klass["g_type"]
83            name = g_type_to_name(gtype)
84            return name
85        except RuntimeError:
86            pass
87    return None
88
89
90class GTypePrettyPrinter:
91    "Prints a GType instance pointer"
92
93    def __init__(self, val):
94        self.val = val
95
96    def to_string(self):
97        name = g_type_name_from_instance(self.val)
98        if name:
99            return ("0x%x [%s]") % (long(self.val), name)
100        return ("0x%x") % (long(self.val))
101
102
103def is_g_type_class_instance(val):
104    type = val.type
105    if type.code != gdb.TYPE_CODE_PTR:
106        return False
107    return str(type.target()) == "GTypeClass"
108
109
110class GTypeHandlePrettyPrinter:
111    "Prints a GType instance"
112
113    def __init__(self, val, hint=""):
114        self.val = val
115        self.hint = hint
116
117    def to_string(self):
118        typenode = g_type_to_typenode(self.val)
119        if typenode is not None:
120            name = glib_gdb.g_quark_to_string(typenode["qname"])
121            s = ("0x%x [%s%s") % (long(self.val), self.hint, name)
122            for i in range(1, int(typenode["n_supers"])):
123                node = g_type_to_typenode(typenode["supers"][i])
124                if node:
125                    name = glib_gdb.g_quark_to_string(node["qname"])
126                else:
127                    name = "???"
128                s += "/" + name
129            return s + "]"
130        else:
131            return ("0x%x") % (long(self.val))
132
133
134def pretty_printer_lookup(val):
135    if is_g_type_instance(val):
136        return GTypePrettyPrinter(val)
137    if str(val.type) == "GType":
138        return GTypeHandlePrettyPrinter(val)
139    if is_g_type_class_instance(val):
140        return GTypeHandlePrettyPrinter(val["g_type"], "g_type: ")
141
142    return None
143
144
145def get_signal_name(id):
146    if id is None:
147        return None
148    id = long(id)
149    if id == 0:
150        return None
151    val = read_global_var("g_signal_nodes")
152    max_s = read_global_var("g_n_signal_nodes")
153    max_s = long(max_s)
154    if id < max_s:
155        return val[id]["name"].string()
156    return None
157
158
159def frame_name(frame):
160    return str(frame.function())
161
162
163def frame_var(frame, var):
164    return frame.inferior_frame().read_var(var)
165
166
167class SignalFrame(FrameDecorator):
168    def __init__(self, frames):
169        FrameDecorator.__init__(self, frames[-1])
170        self.frame = frames[-1]
171        self.frames = frames
172
173    def name(self):
174        return "signal-emission"
175
176    def read_var(self, frame, name, array=None):
177        try:
178            v = frame_var(frame, name)
179            if v is None or v.is_optimized_out:
180                return None
181            if array is not None:
182                array.append(v)
183            return v
184        except ValueError:
185            return None
186
187    def read_object(self, frame, name, array=None):
188        try:
189            v = frame_var(frame, name)
190            if v is None or v.is_optimized_out:
191                return None
192            v = v.cast(gdb.lookup_type("GObject").pointer())
193            # Ensure this is a somewhat correct object pointer
194            if v is not None and g_type_name_from_instance(v):
195                if array is not None:
196                    array.append(v)
197                return v
198            return None
199        except ValueError:
200            return None
201
202    def append(self, array, obj):
203        if obj is not None:
204            array.append(obj)
205
206    def or_join_array(self, array):
207        if len(array) == 0:
208            return "???"
209        else:
210            return " or ".join(set(map(str, array)))
211
212    def get_detailed_signal_from_frame(self, frame, signal):
213        detail = self.read_var(frame, "detail")
214        detail = glib_gdb.g_quark_to_string(detail)
215        if detail is not None:
216            return signal + ":" + detail
217        else:
218            return detail
219
220    def function(self):
221        instances = []
222        signals = []
223
224        for frame in self.frames:
225            name = frame_name(frame)
226            if name == "signal_emit_unlocked_R":
227                self.read_object(frame, "instance", instances)
228                node = self.read_var(frame, "node")
229                if node:
230                    signal = node["name"].string()
231                    signal = self.get_detailed_signal_from_frame(frame, signal)
232                    self.append(signals, signal)
233
234            if name == "g_signal_emitv":
235                instance_and_params = self.read_var(frame, "instance_and_params")
236                if instance_and_params:
237                    instance = instance_and_params[0]["v_pointer"].cast(
238                        gdb.Type("GObject").pointer()
239                    )
240                    self.append(instances, instance)
241                id = self.read_var(frame, "signal_id")
242                signal = get_signal_name(id)
243                if signal:
244                    signal = self.get_detailed_signal_from_frame(frame, signal)
245                    self.append(signals, signal)
246
247            if name == "g_signal_emit_valist" or name == "g_signal_emit":
248                self.read_object(frame, "instance", instances)
249                id = self.read_var(frame, "signal_id")
250                signal = get_signal_name(id)
251                if signal:
252                    signal = self.get_detailed_signal_from_frame(frame, signal)
253                    self.append(signals, signal)
254
255            if name == "g_signal_emit_by_name":
256                self.read_object(frame, "instance", instances)
257                self.read_var(frame, "detailed_signal", signals)
258                break
259
260        instance = self.or_join_array(instances)
261        signal = self.or_join_array(signals)
262
263        return "<emit signal %s on instance %s>" % (signal, instance)
264
265    def elided(self):
266        return self.frames[0:-1]
267
268    def describe(self, stream, full):
269        stream.write(" " + self.function() + "\n")
270
271
272class GFrameDecorator:
273    def __init__(self, iter):
274        self.queue = []
275        self.iter = iter
276
277    def __iter__(self):
278        return self
279
280    def fill(self):
281        while len(self.queue) <= 8:
282            try:
283                f = next(self.iter)
284                self.queue.append(f)
285            except StopIteration:
286                return
287
288    def find_signal_emission(self):
289        for i in range(min(len(self.queue), 3)):
290            if frame_name(self.queue[i]) == "signal_emit_unlocked_R":
291                return i
292        return -1
293
294    def next(self):
295        # Ensure we have enough frames for a full signal emission
296        self.fill()
297
298        # Are we at the end?
299        if len(self.queue) == 0:
300            raise StopIteration
301
302        emission = self.find_signal_emission()
303        if emission > 0:
304            start = emission
305            while True:
306                if start == 0:
307                    break
308                prev_name = frame_name(self.queue[start - 1])
309                if prev_name.find("_marshal_") >= 0 or prev_name == "g_closure_invoke":
310                    start = start - 1
311                else:
312                    break
313            end = emission + 1
314            while end < len(self.queue):
315                if frame_name(self.queue[end]) in [
316                    "g_signal_emitv",
317                    "g_signal_emit_valist",
318                    "g_signal_emit",
319                    "g_signal_emit_by_name",
320                    "_g_closure_invoke_va",
321                ]:
322                    end = end + 1
323                else:
324                    break
325
326            signal_frames = self.queue[start:end]
327            new_frames = [SignalFrame(signal_frames)]
328            self.queue[start:end] = new_frames
329
330        return self.queue.pop(0)
331
332    def __next__(self):
333        return self.next()
334
335
336class GFrameFilter(object):
337    name = "glib"
338    enabled = True
339    priority = 100
340
341    def filter(self, iterator):
342        return GFrameDecorator(iterator)
343
344
345def register(obj):
346    if obj is None:
347        obj = gdb
348
349    if HAVE_GDB_FRAMEDECORATOR:
350        filter = GFrameFilter()
351        obj.frame_filters[filter.name] = filter
352    obj.pretty_printers.append(pretty_printer_lookup)
353