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