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