Merge branch 'master' of github.com:mono/mono
[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 add_quotes:
170             return "\"%s\"" % (class_name)
171         else:
172             return class_name
173         # This returns more info but requires calling into the inferior
174         #return "\"%s\"" % (gdb.parse_and_eval ("mono_type_full_name (&((MonoClass*)%s)->byval_arg)" % (str (int ((self.val).cast (gdb.lookup_type ("guint64")))))))
175
176     def to_string(self):
177         try:
178             return self.to_string_inner (True)
179         except:
180             #print sys.exc_info ()[0]
181             #print sys.exc_info ()[1]
182             return str(self.val.cast (gdb.lookup_type ("gpointer")))
183
184 class MonoGenericInstPrinter:
185     "Print a MonoGenericInst structure"
186
187     def __init__(self, val):
188         self.val = val
189
190     def to_string(self):
191         inst = self.val.dereference ()
192         inst_len = inst ["type_argc"]
193         inst_args = inst ["type_argv"]
194         inst_str = ""
195         for i in range(0, inst_len):
196             print inst_args
197             type_printer = MonoTypePrinter (inst_args [i])
198             if i > 0:
199                 inst_str = inst_str + ", "
200             inst_str = inst_str + type_printer.to_string ()
201         return inst_str
202
203 class MonoGenericClassPrinter:
204     "Print a MonoGenericClass structure"
205
206     def __init__(self, val):
207         self.val = val
208
209     def to_string_inner(self):
210         gclass = self.val.dereference ()
211         container_str = str(gclass ["container_class"])
212         class_inst = gclass ["context"]["class_inst"]
213         class_inst_str = ""
214         if int(class_inst.cast (gdb.lookup_type ("guint64"))) != 0:
215             class_inst_str  = str(class_inst)
216         method_inst = gclass ["context"]["method_inst"]
217         method_inst_str = ""
218         if int(method_inst.cast (gdb.lookup_type ("guint64"))) != 0:
219             method_inst_str  = str(method_inst)
220         return "%s, [%s], [%s]>" % (container_str, class_inst_str, method_inst_str)
221
222     def to_string(self):
223         try:
224             return self.to_string_inner ()
225         except:
226             #print sys.exc_info ()[0]
227             #print sys.exc_info ()[1]
228             return str(self.val.cast (gdb.lookup_type ("gpointer")))
229
230 class MonoTypePrinter:
231     "Print a MonoType structure"
232
233     def __init__(self, val):
234         self.val = val
235
236     def to_string_inner(self, csharp):
237         try:
238             t = self.val.dereference ()
239
240             kind = str (t ["type"]).replace ("MONO_TYPE_", "").lower ()
241             info = ""
242
243             if kind == "class":
244                 p = MonoClassPrinter(t ["data"]["klass"])
245                 info = p.to_string ()
246             elif kind == "genericinst":
247                 info = str(t ["data"]["generic_class"])
248
249             if info != "":
250                 return "{%s, %s}" % (kind, info)
251             else:
252                 return "{%s}" % (kind)
253         except:
254             #print sys.exc_info ()[0]
255             #print sys.exc_info ()[1]
256             return str(self.val.cast (gdb.lookup_type ("gpointer")))
257
258     def to_string(self):
259         return self.to_string_inner (False)
260
261 class MonoMethodRgctxPrinter:
262     "Print a MonoMethodRgctx structure"
263
264     def __init__(self, val):
265         self.val = val
266
267     def to_string(self):
268         rgctx = self.val.dereference ()
269         klass = rgctx ["class_vtable"].dereference () ["klass"]
270         klass_printer = MonoClassPrinter (klass)
271         inst = rgctx ["method_inst"].dereference ()
272         inst_len = inst ["type_argc"]
273         inst_args = inst ["type_argv"]
274         inst_str = ""
275         for i in range(0, inst_len):
276             print inst_args
277             type_printer = MonoTypePrinter (inst_args [i])
278             if i > 0:
279                 inst_str = inst_str + ", "
280             inst_str = inst_str + type_printer.to_string ()
281         return "MRGCTX[%s, [%s]]" % (klass_printer.to_string(), inst_str)
282
283 def lookup_pretty_printer(val):
284     t = str (val.type)
285     if t == "object":
286         return ObjectPrinter (val)
287     if t[0:5] == "class" and t[-1] == "&":
288         return ObjectPrinter (val)    
289     if t == "string":
290         return StringPrinter (val)
291     if t == "MonoMethod *":
292         return MonoMethodPrinter (val)
293     if t == "MonoClass *":
294         return MonoClassPrinter (val)
295     if t == "MonoType *":
296         return MonoTypePrinter (val)
297     if t == "MonoGenericInst *":
298         return MonoGenericInstPrinter (val)
299     if t == "MonoGenericClass *":
300         return MonoGenericClassPrinter (val)
301     if t == "MonoMethodRuntimeGenericContext *":
302         return MonoMethodRgctxPrinter (val)
303     return None
304
305 def register_csharp_printers(obj):
306     "Register C# pretty-printers with objfile Obj."
307
308     if obj == None:
309         obj = gdb
310
311     obj.pretty_printers.append (lookup_pretty_printer)
312
313 # This command will flush the debugging info collected by the runtime
314 class XdbCommand (gdb.Command):
315     def __init__ (self):
316         super (XdbCommand, self).__init__ ("xdb", gdb.COMMAND_NONE,
317                                            gdb.COMPLETE_COMMAND)
318
319     def invoke(self, arg, from_tty):
320         gdb.execute ("call mono_xdebug_flush ()")
321
322 register_csharp_printers (gdb.current_objfile())
323
324 XdbCommand ()
325
326 gdb.execute ("set environment MONO_XDEBUG gdb")
327
328 print "Mono support loaded."
329
330