Merge pull request #301 from directhex/master
[mono.git] / data / gdb / mono-gdb.py
1 #
2 # Author: Zoltan Varga (vargaz@gmail.com)
3 # License: MIT/X11
4 #
5
6 #
7 # This is a mono support mode for gdb 7.0 and later
8 # Usage:
9 # - copy/symlink this file to the directory where the mono executable lives.
10 # - run mono under gdb, or attach to a mono process started with --debug=gdb using gdb.
11 #
12
13 import os
14
15 class StringPrinter:
16     "Print a C# string"
17
18     def __init__(self, val):
19         self.val = val
20
21     def to_string(self):
22         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
23             return "null"
24
25         obj = self.val.cast (gdb.lookup_type ("MonoString").pointer ()).dereference ()
26         len = obj ['length']
27         chars = obj ['chars']
28         i = 0
29         res = ['"']
30         while i < len:
31             val = (chars.cast(gdb.lookup_type ("gint64")) + (i * 2)).cast(gdb.lookup_type ("gunichar2").pointer ()).dereference ()
32             if val >= 256:
33                 c = "\u%X" % val
34             else:
35                 c = chr (val)
36             res.append (c)
37             i = i + 1
38         res.append ('"')
39         return ''.join (res)
40
41 def stringify_class_name(ns, name):
42     if ns == "System":
43         if name == "Byte":
44             return "byte"
45         if name == "String":
46             return "string"
47     if ns == "":
48         return name
49     else:
50         return "%s.%s" % (ns, name)
51
52 class ArrayPrinter:
53     "Print a C# array"
54
55     def __init__(self, val, class_ns, class_name):
56         self.val = val
57         self.class_ns = class_ns
58         self.class_name = class_name
59
60     def to_string(self):
61         obj = self.val.cast (gdb.lookup_type ("MonoArray").pointer ()).dereference ()
62         length = obj ['max_length']
63         return "%s [%d]" % (stringify_class_name (self.class_ns, self.class_name [0:len(self.class_name) - 2]), int(length))
64         
65 class ObjectPrinter:
66     "Print a C# object"
67
68     def __init__(self, val):
69         if str(val.type)[-1] == "&":
70             self.val = val.address.cast (gdb.lookup_type ("MonoObject").pointer ())
71         else:
72             self.val = val.cast (gdb.lookup_type ("MonoObject").pointer ())
73
74     class _iterator:
75         def __init__(self,obj):
76             self.obj = obj
77             self.iter = self.obj.type.fields ().__iter__ ()
78             pass
79
80         def __iter__(self):
81             return self
82
83         def next(self):
84             field = self.iter.next ()
85             try:
86                 if str(self.obj [field.name].type) == "object":
87                     # Avoid recursion
88                     return (field.name, self.obj [field.name].cast (gdb.lookup_type ("void").pointer ()))
89                 else:
90                     return (field.name, self.obj [field.name])
91             except:
92                 # Superclass
93                 return (field.name, self.obj.cast (gdb.lookup_type ("%s" % (field.name))))
94
95     def children(self):
96         # FIXME: It would be easier if gdb.Value would support iteration itself
97         # It would also be better if we could return None
98         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
99             return {}.__iter__ ()
100         try:
101             obj = self.val.dereference ()
102             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
103             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
104             if class_name [-2:len(class_name)] == "[]":
105                 return {}.__iter__ ()
106             try:
107                 gdb_type = gdb.lookup_type ("struct %s_%s" % (class_ns.replace (".", "_"), class_name))
108                 return self._iterator(obj.cast (gdb_type))
109             except:
110                 return {}.__iter__ ()
111         except:
112             print sys.exc_info ()[0]
113             print sys.exc_info ()[1]
114             return {}.__iter__ ()
115
116     def to_string(self):
117         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
118             return "null"
119         try:
120             obj = self.val.dereference ()
121             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
122             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
123             if class_ns == "System" and class_name == "String":
124                 return StringPrinter (self.val).to_string ()
125             if class_name [-2:len(class_name)] == "[]":
126                 return ArrayPrinter (self.val,class_ns,class_name).to_string ()
127             if class_ns != "":
128                 try:
129                     gdb_type = gdb.lookup_type ("struct %s.%s" % (class_ns, class_name))
130                 except:
131                     # Maybe there is no debug info for that type
132                     return "%s.%s" % (class_ns, class_name)
133                 #return obj.cast (gdb_type)
134                 return "%s.%s" % (class_ns, class_name)
135             return class_name
136         except:
137             print sys.exc_info ()[0]
138             print sys.exc_info ()[1]
139             # FIXME: This can happen because we don't have liveness information
140             return self.val.cast (gdb.lookup_type ("guint64"))
141         
142 class MonoMethodPrinter:
143     "Print a MonoMethod structure"
144
145     def __init__(self, val):
146         self.val = val
147
148     def to_string(self):
149         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
150             return "0x0"
151         val = self.val.dereference ()
152         klass = val ["klass"].dereference ()
153         class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
154         return "\"%s:%s ()\"" % (class_name, val ["name"].string ())
155         # This returns more info but requires calling into the inferior
156         #return "\"%s\"" % (gdb.parse_and_eval ("mono_method_full_name (%s, 1)" % (str (int (self.val.cast (gdb.lookup_type ("guint64")))))).string ())
157
158 class MonoClassPrinter:
159     "Print a MonoClass structure"
160
161     def __init__(self, val):
162         self.val = val
163
164     def to_string_inner(self, add_quotes):
165         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
166             return "0x0"
167         klass = self.val.dereference ()
168         class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
169         if klass ["generic_class"].cast (gdb.lookup_type ("guint64")) != 0:
170             class_name = "%s<%s>" % (class_name, str (klass ["generic_class"]["context"]["class_inst"]))
171         if add_quotes:
172             return "\"%s\"" % (class_name)
173         else:
174             return class_name
175         # This returns more info but requires calling into the inferior
176         #return "\"%s\"" % (gdb.parse_and_eval ("mono_type_full_name (&((MonoClass*)%s)->byval_arg)" % (str (int ((self.val).cast (gdb.lookup_type ("guint64")))))))
177
178     def to_string(self):
179         try:
180             return self.to_string_inner (True)
181         except:
182             #print sys.exc_info ()[0]
183             #print sys.exc_info ()[1]
184             return str(self.val.cast (gdb.lookup_type ("gpointer")))
185
186 class MonoGenericInstPrinter:
187     "Print a MonoGenericInst structure"
188
189     def __init__(self, val):
190         self.val = val
191
192     def to_string(self):
193         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
194             return "0x0"
195         inst = self.val.dereference ()
196         inst_len = inst ["type_argc"]
197         inst_args = inst ["type_argv"]
198         inst_str = ""
199         for i in range(0, inst_len):
200             print inst_args
201             type_printer = MonoTypePrinter (inst_args [i])
202             if i > 0:
203                 inst_str = inst_str + ", "
204             inst_str = inst_str + type_printer.to_string ()
205         return inst_str
206
207 class MonoGenericClassPrinter:
208     "Print a MonoGenericClass structure"
209
210     def __init__(self, val):
211         self.val = val
212
213     def to_string_inner(self):
214         gclass = self.val.dereference ()
215         container_str = str(gclass ["container_class"])
216         class_inst = gclass ["context"]["class_inst"]
217         class_inst_str = ""
218         if int(class_inst.cast (gdb.lookup_type ("guint64"))) != 0:
219             class_inst_str  = str(class_inst)
220         method_inst = gclass ["context"]["method_inst"]
221         method_inst_str = ""
222         if int(method_inst.cast (gdb.lookup_type ("guint64"))) != 0:
223             method_inst_str  = str(method_inst)
224         return "%s, [%s], [%s]>" % (container_str, class_inst_str, method_inst_str)
225
226     def to_string(self):
227         try:
228             return self.to_string_inner ()
229         except:
230             #print sys.exc_info ()[0]
231             #print sys.exc_info ()[1]
232             return str(self.val.cast (gdb.lookup_type ("gpointer")))
233
234 class MonoTypePrinter:
235     "Print a MonoType structure"
236
237     def __init__(self, val):
238         self.val = val
239
240     def to_string_inner(self, csharp):
241         try:
242             t = self.val.dereference ()
243
244             kind = str (t ["type"]).replace ("MONO_TYPE_", "").lower ()
245             info = ""
246
247             if kind == "class":
248                 p = MonoClassPrinter(t ["data"]["klass"])
249                 info = p.to_string ()
250             elif kind == "genericinst":
251                 info = str(t ["data"]["generic_class"])
252
253             if info != "":
254                 return "{%s, %s}" % (kind, info)
255             else:
256                 return "{%s}" % (kind)
257         except:
258             #print sys.exc_info ()[0]
259             #print sys.exc_info ()[1]
260             return str(self.val.cast (gdb.lookup_type ("gpointer")))
261
262     def to_string(self):
263         return self.to_string_inner (False)
264
265 class MonoMethodRgctxPrinter:
266     "Print a MonoMethodRgctx structure"
267
268     def __init__(self, val):
269         self.val = val
270
271     def to_string(self):
272         rgctx = self.val.dereference ()
273         klass = rgctx ["class_vtable"].dereference () ["klass"]
274         klass_printer = MonoClassPrinter (klass)
275         inst = rgctx ["method_inst"].dereference ()
276         inst_len = inst ["type_argc"]
277         inst_args = inst ["type_argv"]
278         inst_str = ""
279         for i in range(0, inst_len):
280             print inst_args
281             type_printer = MonoTypePrinter (inst_args [i])
282             if i > 0:
283                 inst_str = inst_str + ", "
284             inst_str = inst_str + type_printer.to_string ()
285         return "MRGCTX[%s, [%s]]" % (klass_printer.to_string(), inst_str)
286
287 class MonoVTablePrinter:
288     "Print a MonoVTable structure"
289
290     def __init__(self, val):
291         self.val = val
292
293     def to_string(self):
294         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
295             return "0x0"
296         vtable = self.val.dereference ()
297         klass = vtable ["klass"]
298         klass_printer = MonoClassPrinter (klass)
299
300         return "vtable(%s)" % (klass_printer.to_string ())
301
302 def lookup_pretty_printer(val):
303     t = str (val.type)
304     if t == "object":
305         return ObjectPrinter (val)
306     if t[0:5] == "class" and t[-1] == "&":
307         return ObjectPrinter (val)    
308     if t == "string":
309         return StringPrinter (val)
310     if t == "MonoString *":
311         return StringPrinter (val)
312     if t == "MonoMethod *":
313         return MonoMethodPrinter (val)
314     if t == "MonoClass *":
315         return MonoClassPrinter (val)
316     if t == "MonoType *":
317         return MonoTypePrinter (val)
318     if t == "MonoGenericInst *":
319         return MonoGenericInstPrinter (val)
320     if t == "MonoGenericClass *":
321         return MonoGenericClassPrinter (val)
322     if t == "MonoMethodRuntimeGenericContext *":
323         return MonoMethodRgctxPrinter (val)
324     if t == "MonoVTable *":
325         return MonoVTablePrinter (val)
326     return None
327
328 def register_csharp_printers(obj):
329     "Register C# pretty-printers with objfile Obj."
330
331     if obj == None:
332         obj = gdb
333
334     obj.pretty_printers.append (lookup_pretty_printer)
335
336 # This command will flush the debugging info collected by the runtime
337 class XdbCommand (gdb.Command):
338     def __init__ (self):
339         super (XdbCommand, self).__init__ ("xdb", gdb.COMMAND_NONE,
340                                            gdb.COMPLETE_COMMAND)
341
342     def invoke(self, arg, from_tty):
343         gdb.execute ("call mono_xdebug_flush ()")
344
345 register_csharp_printers (gdb.current_objfile())
346
347 XdbCommand ()
348
349 gdb.execute ("set environment MONO_XDEBUG gdb")
350
351 print "Mono support loaded."
352
353