Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / data / gdb-pre7.0 / 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 a python-enabled gdb:
8 # http://sourceware.org/gdb/wiki/PythonGdb
9 # Usage:
10 # - copy/symlink this file, plus mono-gdbinit to the directory where the mono 
11 #   executable lives.
12 # - run mono under gdb, or attach to a mono process using gdb
13 # - Type 'xdb' in gdb to load/reload the debugging info emitted by the runtime.
14 # - The debug info is emitted to a file called xdb.s in the current working directory.
15 #   When attaching to a mono process, make sure you are in the same directory.
16 #
17
18 from __future__ import print_function
19 import os
20
21 class StringPrinter:
22     "Print a C# string"
23
24     def __init__(self, val):
25         self.val = val
26
27     def to_string(self):
28         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
29             return "null"
30
31         obj = self.val.cast (gdb.lookup_type ("MonoString").pointer ()).dereference ()
32         len = obj ['length']
33         chars = obj ['chars']
34         i = 0
35         res = ['"']
36         while i < len:
37             val = (chars.cast(gdb.lookup_type ("gint64")) + (i * 2)).cast(gdb.lookup_type ("gunichar2").pointer ()).dereference ()
38             if val >= 256:
39                 c = "\u%X".format (val)
40             else:
41                 c = chr (val)
42             res.append (c)
43             i = i + 1
44         res.append ('"')
45         return ''.join (res)
46
47 def stringify_class_name(ns, name):
48     if ns == "System":
49         if name == "Byte":
50             return "byte"
51         if name == "String":
52             return "string"
53     if ns == "":
54         return name
55     else:
56         return "{}.{}".format (ns, name)
57
58 class ArrayPrinter:
59     "Print a C# array"
60
61     def __init__(self, val, class_ns, class_name):
62         self.val = val
63         self.class_ns = class_ns
64         self.class_name = class_name
65
66     def to_string(self):
67         obj = self.val.cast (gdb.lookup_type ("MonoArray").pointer ()).dereference ()
68         length = obj ['max_length']
69         return "{} [{}]".format (stringify_class_name (self.class_ns, self.class_name [0:len (self.class_name) - 2]), int (length))
70         
71 class ObjectPrinter:
72     "Print a C# object"
73
74     def __init__(self, val):
75         if str(val.type)[-1] == "&":
76             self.val = val.address.cast (gdb.lookup_type ("MonoObject").pointer ())
77         else:
78             self.val = val.cast (gdb.lookup_type ("MonoObject").pointer ())
79
80     class _iterator:
81         def __init__(self,obj):
82             self.obj = obj
83             self.iter = self.obj.type.fields ().__iter__ ()
84             pass
85
86         def __iter__(self):
87             return self
88
89         def next(self):
90             field = self.iter.next ()
91             try:
92                 if str(self.obj [field.name].type) == "object":
93                     # Avoid recursion
94                     return (field.name, self.obj [field.name].cast (gdb.lookup_type ("void").pointer ()))
95                 else:
96                     return (field.name, self.obj [field.name])
97             except:
98                 # Superclass
99                 return (field.name, self.obj.cast (gdb.lookup_type ("{}".format (field.name))))
100
101     def children(self):
102         # FIXME: It would be easier if gdb.Value would support iteration itself
103         # It would also be better if we could return None
104         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
105             return {}.__iter__ ()
106         try:
107             obj = self.val.dereference ()
108             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
109             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
110             if class_name [-2:len(class_name)] == "[]":
111                 return {}.__iter__ ()
112             gdb_type = gdb.lookup_type ("struct {}_{}".format (class_ns.replace (".", "_"), class_name))
113             return self._iterator(obj.cast (gdb_type))
114         except:
115             print (sys.exc_info ()[0])
116             print (sys.exc_info ()[1])
117             return {}.__iter__ ()
118
119     def to_string(self):
120         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
121             return "null"
122         try:
123             obj = self.val.dereference ()
124             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
125             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
126             if class_ns == "System" and class_name == "String":
127                 return StringPrinter (self.val).to_string ()
128             if class_name [-2:len(class_name)] == "[]":
129                 return ArrayPrinter (self.val,class_ns,class_name).to_string ()
130             if class_ns != "":
131                 try:
132                     gdb_type = gdb.lookup_type ("struct {}.{}".format (class_ns, class_name))
133                 except:
134                     # Maybe there is no debug info for that type
135                     return "{}.{}".format (class_ns, class_name)
136                 #return obj.cast (gdb_type)
137                 return "{}.{}".format (class_ns, class_name)
138             return class_name
139         except:
140             print (sys.exc_info ()[0])
141             print (sys.exc_info ()[1])
142             # FIXME: This can happen because we don't have liveness information
143             return self.val.cast (gdb.lookup_type ("guint64"))
144         
145 class MonoMethodPrinter:
146     "Print a MonoMethod structure"
147
148     def __init__(self, val):
149         self.val = val
150
151     def to_string(self):
152         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
153             return "0x0"
154         val = self.val.dereference ()
155         klass = val ["klass"].dereference ()
156         class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
157         return "\"{}:{} ()\"".format (class_name, val ["name"].string ())
158         # This returns more info but requires calling into the inferior
159         #return "\"{}\"".format (gdb.parse_and_eval ("mono_method_full_name ({}, 1)".format (str (int (self.val.cast (gdb.lookup_type ("guint64")))))).string ())
160
161 class MonoClassPrinter:
162     "Print a MonoClass structure"
163
164     def __init__(self, val):
165         self.val = val
166
167     def to_string(self):
168         if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
169             return "0x0"
170         klass = self.val.dereference ()
171         class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
172         return "\"{}\"".format (class_name)
173         # This returns more info but requires calling into the inferior
174         #return "\"{}\"".format (gdb.parse_and_eval ("mono_type_full_name (&((MonoClass*){})->byval_arg)".format (str (int ((self.val).cast (gdb.lookup_type ("guint64")))))))
175
176 def lookup_pretty_printer(val):
177     t = str (val.type)
178     if t == "object":
179         return ObjectPrinter (val)
180     if t[0:5] == "class" and t[-1] == "&":
181         return ObjectPrinter (val)    
182     if t == "string":
183         return StringPrinter (val)
184     if t == "MonoMethod *":
185         return MonoMethodPrinter (val)
186     if t == "MonoClass *":
187         return MonoClassPrinter (val)
188     return None
189
190 def register_csharp_printers(obj):
191     "Register C# pretty-printers with objfile Obj."
192
193     if obj == None:
194         obj = gdb
195
196     obj.pretty_printers.append (lookup_pretty_printer)
197
198 register_csharp_printers (gdb.current_objfile())
199
200 class MonoSupport(object):
201
202     def __init__(self):
203         self.s_size = 0
204
205     def run_hook(self):
206         if os.access ("xdb.s", os.F_OK):
207             os.remove ("xdb.s")
208         gdb.execute ("set environment MONO_XDEBUG gdb")
209         
210     def stop_hook(self):
211         # Called when the program is stopped
212         # Need to recompile+reload the xdb.s file if needed
213         # FIXME: Need to detect half-written files created when the child is
214         # interrupted while executing the xdb.s writing code
215         # FIXME: Handle appdomain unload
216         if os.access ("xdb.s", os.F_OK):
217             new_size = os.stat ("xdb.s").st_size
218             if new_size > self.s_size:
219                 sofile = "xdb.so"
220                 gdb.execute ("shell as -o xdb.o xdb.s && ld -shared -o {} xdb.o".format (sofile))
221                 # FIXME: This prints messages which couldn't be turned off
222                 gdb.execute ("add-symbol-file {} 0".format (sofile))
223                 self.s_size = new_size
224
225 class RunHook (gdb.Command):
226     def __init__ (self):
227         super (RunHook, self).__init__ ("hook-run", gdb.COMMAND_NONE,
228                                         gdb.COMPLETE_COMMAND, pre_hook_of="run")
229
230     def invoke(self, arg, from_tty):
231         mono_support.run_hook ()
232
233 print ("Mono support loaded.")
234
235 mono_support = MonoSupport ()
236
237 # This depends on the changes in gdb-python.diff to work
238 #RunHook ()
239
240 # Register our hooks
241 # This currently cannot be done from python code
242
243 exec_file = gdb.current_objfile ().filename
244 # FIXME: Is there a way to detect symbolic links ?
245 if os.stat (exec_file).st_size != os.lstat (exec_file).st_size:
246     exec_file = os.readlink (exec_file)
247 exec_dir = os.path.dirname (exec_file)
248 gdb.execute ("source {}/{}-gdbinit".format (exec_dir, os.path.basename (exec_file)))