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