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