2 # Author: Zoltan Varga (vargaz@gmail.com)
7 # This is a mono support mode for a python-enabled gdb:
8 # http://sourceware.org/gdb/wiki/PythonGdb
10 # - copy/symlink this file, plus mono-gdbinit to the directory where the mono
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.
18 from __future__
import print_function
24 def __init__(self
, val
):
28 if int(self
.val
.cast (gdb
.lookup_type ("guint64"))) == 0:
31 obj
= self
.val
.cast (gdb
.lookup_type ("MonoString").pointer ()).dereference ()
37 val
= (chars
.cast(gdb
.lookup_type ("gint64")) + (i
* 2)).cast(gdb
.lookup_type ("gunichar2").pointer ()).dereference ()
39 c
= "\u%X".format (val
)
47 def stringify_class_name(ns
, name
):
56 return "{}.{}".format (ns
, name
)
61 def __init__(self
, val
, class_ns
, class_name
):
63 self
.class_ns
= class_ns
64 self
.class_name
= class_name
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
))
74 def __init__(self
, val
):
75 if str(val
.type)[-1] == "&":
76 self
.val
= val
.address
.cast (gdb
.lookup_type ("MonoObject").pointer ())
78 self
.val
= val
.cast (gdb
.lookup_type ("MonoObject").pointer ())
81 def __init__(self
,obj
):
83 self
.iter = self
.obj
.type.fields ().__iter
__ ()
90 field
= self
.iter.next ()
92 if str(self
.obj
[field
.name
].type) == "object":
94 return (field
.name
, self
.obj
[field
.name
].cast (gdb
.lookup_type ("void").pointer ()))
96 return (field
.name
, self
.obj
[field
.name
])
99 return (field
.name
, self
.obj
.cast (gdb
.lookup_type ("{}".format (field
.name
))))
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
__ ()
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
))
115 print (sys
.exc_info ()[0])
116 print (sys
.exc_info ()[1])
117 return {}.__iter
__ ()
120 if int(self
.val
.cast (gdb
.lookup_type ("guint64"))) == 0:
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 ()
132 gdb_type
= gdb
.lookup_type ("struct {}.{}".format (class_ns
, class_name
))
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
)
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"))
145 class MonoMethodPrinter
:
146 "Print a MonoMethod structure"
148 def __init__(self
, val
):
152 if int(self
.val
.cast (gdb
.lookup_type ("guint64"))) == 0:
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 ())
161 class MonoClassPrinter
:
162 "Print a MonoClass structure"
164 def __init__(self
, val
):
168 if int(self
.val
.cast (gdb
.lookup_type ("guint64"))) == 0:
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")))))))
176 def lookup_pretty_printer(val
):
179 return ObjectPrinter (val
)
180 if t
[0:5] == "class" and t
[-1] == "&":
181 return ObjectPrinter (val
)
183 return StringPrinter (val
)
184 if t
== "MonoMethod *":
185 return MonoMethodPrinter (val
)
186 if t
== "MonoClass *":
187 return MonoClassPrinter (val
)
190 def register_csharp_printers(obj
):
191 "Register C# pretty-printers with objfile Obj."
196 obj
.pretty_printers
.append (lookup_pretty_printer
)
198 register_csharp_printers (gdb
.current_objfile())
200 class MonoSupport(object):
206 if os
.access ("xdb.s", os
.F_OK
):
208 gdb
.execute ("set environment MONO_XDEBUG gdb")
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
:
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
225 class RunHook (gdb
.Command
):
227 super (RunHook
, self
).__init
__ ("hook-run", gdb
.COMMAND_NONE
,
228 gdb
.COMPLETE_COMMAND
, pre_hook_of
="run")
230 def invoke(self
, arg
, from_tty
):
231 mono_support
.run_hook ()
233 print ("Mono support loaded.")
235 mono_support
= MonoSupport ()
237 # This depends on the changes in gdb-python.diff to work
241 # This currently cannot be done from python code
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
)))