ca0d19e294f29dd5893e0c345114a5ef4c15fe01
[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             gdb_type = gdb.lookup_type ("struct %s_%s" % (class_ns.replace (".", "_"), class_name))
107             return self._iterator(obj.cast (gdb_type))
108         except:
109             print sys.exc_info ()[0]
110             print sys.exc_info ()[1]
111             return {}.__iter__ ()
112
113     def to_string(self):
114         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
115             return "null"
116         try:
117             obj = self.val.dereference ()
118             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
119             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
120             if class_ns == "System" and class_name == "String":
121                 return StringPrinter (self.val).to_string ()
122             if class_name [-2:len(class_name)] == "[]":
123                 return ArrayPrinter (self.val,class_ns,class_name).to_string ()
124             if class_ns != "":
125                 try:
126                     gdb_type = gdb.lookup_type ("struct %s.%s" % (class_ns, class_name))
127                 except:
128                     # Maybe there is no debug info for that type
129                     return "%s.%s" % (class_ns, class_name)
130                 #return obj.cast (gdb_type)
131                 return "%s.%s" % (class_ns, class_name)
132             return class_name
133         except:
134             print sys.exc_info ()[0]
135             print sys.exc_info ()[1]
136             # FIXME: This can happen because we don't have liveness information
137             return self.val.cast (gdb.lookup_type ("guint64"))
138         
139 class MonoMethodPrinter:
140     "Print a MonoMethod structure"
141
142     def __init__(self, val):
143         self.val = val
144
145     def to_string(self):
146         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
147             return "0x0"
148         val = self.val.dereference ()
149         klass = val ["klass"].dereference ()
150         class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
151         return "\"%s:%s ()\"" % (class_name, val ["name"].string ())
152         # This returns more info but requires calling into the inferior
153         #return "\"%s\"" % (gdb.parse_and_eval ("mono_method_full_name (%s, 1)" % (str (int (self.val.cast (gdb.lookup_type ("guint64")))))).string ())
154
155 class MonoClassPrinter:
156     "Print a MonoClass structure"
157
158     def __init__(self, val):
159         self.val = val
160
161     def to_string(self):
162         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
163             return "0x0"
164         klass = self.val.dereference ()
165         class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
166         return "\"%s\"" % (class_name)
167         # This returns more info but requires calling into the inferior
168         #return "\"%s\"" % (gdb.parse_and_eval ("mono_type_full_name (&((MonoClass*)%s)->byval_arg)" % (str (int ((self.val).cast (gdb.lookup_type ("guint64")))))))
169
170 def lookup_pretty_printer(val):
171     t = str (val.type)
172     if t == "object":
173         return ObjectPrinter (val)
174     if t[0:5] == "class" and t[-1] == "&":
175         return ObjectPrinter (val)    
176     if t == "string":
177         return StringPrinter (val)
178     if t == "MonoMethod *":
179         return MonoMethodPrinter (val)
180     if t == "MonoClass *":
181         return MonoClassPrinter (val)
182     return None
183
184 def register_csharp_printers(obj):
185     "Register C# pretty-printers with objfile Obj."
186
187     if obj == None:
188         obj = gdb
189
190     obj.pretty_printers.append (lookup_pretty_printer)
191
192 # This command will flush the debugging info collected by the runtime
193 class XdbCommand (gdb.Command):
194     def __init__ (self):
195         super (XdbCommand, self).__init__ ("xdb", gdb.COMMAND_NONE,
196                                            gdb.COMPLETE_COMMAND)
197
198     def invoke(self, arg, from_tty):
199         gdb.execute ("call mono_xdebug_flush ()")
200
201 register_csharp_printers (gdb.current_objfile())
202
203 XdbCommand ()
204
205 gdb.execute ("set environment MONO_XDEBUG gdb")
206
207 print "Mono support loaded."
208
209