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