Add a more functional (i.e. fewer-stubs) implementation of System.Data.Linq.
[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 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 import os
19
20 class StringPrinter:
21     "Print a C# string"
22
23     def __init__(self, val):
24         self.val = val
25
26     def to_string(self):
27         if int(self.val.cast (gdb.Type ("guint64"))) == 0:
28             return "null"
29
30         obj = self.val.cast (gdb.Type ("MonoString").pointer ()).dereference ()
31         len = obj ['length']
32         chars = obj ['chars']
33         i = 0
34         res = ['"']
35         while i < len:
36             c = chr ((chars.cast(gdb.Type ("gint64")) + (i * 2)).cast(gdb.Type ("gunichar2").pointer ()).dereference ())
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     return "%s.%s" % (ns, name)
49
50 class ArrayPrinter:
51     "Print a C# array"
52
53     def __init__(self, val, class_ns, class_name):
54         self.val = val
55         self.class_ns = class_ns
56         self.class_name = class_name
57
58     def to_string(self):
59         obj = self.val.cast (gdb.Type ("MonoArray").pointer ()).dereference ()
60         length = obj ['max_length']
61         return "%s [%d]" % (stringify_class_name (self.class_ns, self.class_name [0:len(self.class_name) - 2]), int(length))
62         
63 class ObjectPrinter:
64     "Print a C# object"
65
66     def __init__(self, val):
67         self.val = val
68
69     class _iterator:
70         def __init__(self,obj):
71             self.obj = obj
72             self.iter = self.obj.type ().fields ().__iter__ ()
73             pass
74
75         def __iter__(self):
76             return self
77
78         def next(self):
79             field = self.iter.next ()
80             return (field.name, self.obj [field.name])
81
82     def children(self):
83         # FIXME: It would be easier if gdb.Value would support iteration itself
84         # It would also be better if we could return None
85         if int(self.val.cast (gdb.Type ("guint64"))) == 0:
86             return {}.__iter__ ()
87         try:
88             obj = self.val.cast (gdb.Type ("MonoObject").pointer ()).dereference ()
89             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
90             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
91             gdb_type = gdb.Type ("struct %s.%s" % (class_ns, class_name))
92             return self._iterator(obj.cast (gdb_type))
93         except:
94             return {}.__iter__ ()
95
96     def to_string(self):
97         if int(self.val.cast (gdb.Type ("guint64"))) == 0:
98             return "null"
99         try:
100             obj = self.val.cast (gdb.Type ("MonoObject").pointer ()).dereference ()
101             class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
102             class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
103             if class_ns == "System" and class_name == "String":
104                 return StringPrinter (self.val).to_string ()
105             if class_name [-2:len(class_name)] == "[]":
106                 return ArrayPrinter (self.val,class_ns,class_name).to_string ()
107             if class_ns != "":
108                 try:
109                     gdb_type = gdb.Type ("struct %s.%s" % (class_ns, class_name))
110                 except:
111                     # Maybe there is no debug info for that type
112                     return "%s.%s" % (class_ns, class_name)
113                 #return obj.cast (gdb_type)
114                 return "%s.%s" % (class_ns, class_name)
115             return class_name
116         except:
117             print sys.exc_info ()[0]
118             print sys.exc_info ()[1]
119             # FIXME: This can happen because we don't have liveness information
120             return self.val.cast (gdb.Type ("guint64"))
121
122 def register_csharp_printers(obj):
123     "Register C# pretty-printers with objfile Obj."
124
125     if obj == None:
126         obj = gdb
127
128     obj.pretty_printers['object'] = ObjectPrinter
129     obj.pretty_printers['string'] = StringPrinter
130
131 register_csharp_printers (gdb.current_objfile())
132
133 class MonoSupport(object):
134
135     def __init__(self):
136         self.s_size = 0
137
138     def run_hook(self):
139         if os.access ("xdb.s", os.F_OK):
140             os.remove ("xdb.s")
141         gdb.execute ("set environment MONO_XDEBUG 1")
142         
143     def stop_hook(self):
144         # Called when the program is stopped
145         # Need to recompile+reload the xdb.s file if needed
146         # FIXME: Need to detect half-written files created when the child is
147         # interrupted while executing the xdb.s writing code
148         # FIXME: Handle appdomain unload
149         if os.access ("xdb.s", os.F_OK):
150             new_size = os.stat ("xdb.s").st_size
151             if new_size > self.s_size:
152                 sofile = "xdb.so"
153                 gdb.execute ("shell as -o xdb.o xdb.s && ld -shared -o %s xdb.o" % sofile)
154                 # FIXME: This prints messages which couldn't be turned off
155                 gdb.execute ("add-symbol-file %s 0" % sofile)
156                 self.s_size = new_size
157
158 class RunHook (gdb.Command):
159     def __init__ (self):
160         super (RunHook, self).__init__ ("hook-run", gdb.COMMAND_NONE,
161                                         gdb.COMPLETE_COMMAND, pre_hook_of="run")
162
163     def invoke(self, arg, from_tty):
164         mono_support.run_hook ()
165
166 print "Mono support loaded."
167
168 mono_support = MonoSupport ()
169
170 # This depends on the changes in gdb-python.diff to work
171 #RunHook ()
172
173 # Register our hooks
174 # This currently cannot be done from python code
175
176 exec_file = gdb.current_objfile ().filename
177 # FIXME: Is there a way to detect symbolic links ?
178 if os.stat (exec_file).st_size != os.lstat (exec_file).st_size:
179     exec_file = os.readlink (exec_file)
180 exec_dir = os.path.dirname (exec_file)
181 gdb.execute ("source %s/%s-gdbinit" % (exec_dir, os.path.basename (exec_file)))