mono-sgen-gdb.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #
  2. # Author: Zoltan Varga (vargaz@gmail.com)
  3. # License: MIT/X11
  4. #
  5. #
  6. # This is a mono support mode for gdb 7.0 and later
  7. # Usage:
  8. # - copy/symlink this file to the directory where the mono executable lives.
  9. # - run mono under gdb, or attach to a mono process started with --debug=gdb using gdb.
  10. #
  11. from __future__ import print_function
  12. import os
  13. class StringPrinter:
  14. "Print a C# string"
  15. def __init__(self, val):
  16. self.val = val
  17. def to_string(self):
  18. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  19. return "null"
  20. obj = self.val.cast (gdb.lookup_type ("MonoString").pointer ()).dereference ()
  21. len = obj ['length']
  22. chars = obj ['chars']
  23. i = 0
  24. res = ['"']
  25. while i < len:
  26. val = (chars.cast(gdb.lookup_type ("gint64")) + (i * 2)).cast(gdb.lookup_type ("gunichar2").pointer ()).dereference ()
  27. if val >= 256:
  28. c = unichr (val)
  29. else:
  30. c = chr (val)
  31. res.append (c)
  32. i = i + 1
  33. res.append ('"')
  34. return ''.join (res)
  35. def stringify_class_name(ns, name):
  36. if ns == "System":
  37. if name == "Byte":
  38. return "byte"
  39. if name == "String":
  40. return "string"
  41. if ns == "":
  42. return name
  43. else:
  44. return "{0}.{1}".format (ns, name)
  45. class ArrayPrinter:
  46. "Print a C# array"
  47. def __init__(self, val, class_ns, class_name):
  48. self.val = val
  49. self.class_ns = class_ns
  50. self.class_name = class_name
  51. def to_string(self):
  52. obj = self.val.cast (gdb.lookup_type ("MonoArray").pointer ()).dereference ()
  53. length = obj ['max_length']
  54. return "{0} [{1}]".format (stringify_class_name (self.class_ns, self.class_name [0:len(self.class_name) - 2]), int(length))
  55. class ObjectPrinter:
  56. "Print a C# object"
  57. def __init__(self, val):
  58. if str(val.type)[-1] == "&":
  59. self.val = val.address.cast (gdb.lookup_type ("MonoObject").pointer ())
  60. else:
  61. self.val = val.cast (gdb.lookup_type ("MonoObject").pointer ())
  62. class _iterator:
  63. def __init__(self,obj):
  64. self.obj = obj
  65. self.iter = self.obj.type.fields ().__iter__ ()
  66. pass
  67. def __iter__(self):
  68. return self
  69. def next(self):
  70. field = self.iter.next ()
  71. try:
  72. if str(self.obj [field.name].type) == "object":
  73. # Avoid recursion
  74. return (field.name, self.obj [field.name].cast (gdb.lookup_type ("void").pointer ()))
  75. else:
  76. return (field.name, self.obj [field.name])
  77. except:
  78. # Superclass
  79. return (field.name, self.obj.cast (gdb.lookup_type ("{0}".format (field.name))))
  80. def children(self):
  81. # FIXME: It would be easier if gdb.Value would support iteration itself
  82. # It would also be better if we could return None
  83. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  84. return {}.__iter__ ()
  85. try:
  86. obj = self.val.dereference ()
  87. class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
  88. class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
  89. if class_name [-2:len(class_name)] == "[]":
  90. return {}.__iter__ ()
  91. try:
  92. gdb_type = gdb.lookup_type ("struct {0}_{1}".format (class_ns.replace (".", "_"), class_name))
  93. return self._iterator(obj.cast (gdb_type))
  94. except:
  95. return {}.__iter__ ()
  96. except:
  97. print (sys.exc_info ()[0])
  98. print (sys.exc_info ()[1])
  99. return {}.__iter__ ()
  100. def to_string(self):
  101. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  102. return "null"
  103. try:
  104. obj = self.val.dereference ()
  105. class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
  106. class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
  107. if class_ns == "System" and class_name == "String":
  108. return StringPrinter (self.val).to_string ()
  109. if class_name [-2:len(class_name)] == "[]":
  110. return ArrayPrinter (self.val,class_ns,class_name).to_string ()
  111. if class_ns != "":
  112. try:
  113. gdb_type = gdb.lookup_type ("struct {0}.{1}".format (class_ns, class_name))
  114. except:
  115. # Maybe there is no debug info for that type
  116. return "{0}.{1}".format (class_ns, class_name)
  117. #return obj.cast (gdb_type)
  118. return "{0}.{1}".format (class_ns, class_name)
  119. return class_name
  120. except:
  121. print (sys.exc_info ()[0])
  122. print (sys.exc_info ()[1])
  123. # FIXME: This can happen because we don't have liveness information
  124. return self.val.cast (gdb.lookup_type ("guint64"))
  125. class MonoMethodPrinter:
  126. "Print a MonoMethod structure"
  127. def __init__(self, val):
  128. self.val = val
  129. def to_string(self):
  130. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  131. return "0x0"
  132. val = self.val.dereference ()
  133. klass = val ["klass"].dereference ()
  134. class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
  135. return "\"{0}:{1} ()\"".format (class_name, val ["name"].string ())
  136. # This returns more info but requires calling into the inferior
  137. #return "\"{0}\"".format (gdb.parse_and_eval ("mono_method_full_name ({0}, 1)".format (str (int (self.val.cast (gdb.lookup_type ("guint64")))))).string ())
  138. class MonoClassPrinter:
  139. "Print a MonoClass structure"
  140. def __init__(self, val):
  141. self.val = val
  142. def to_string_inner(self, add_quotes):
  143. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  144. return "0x0"
  145. klass = self.val.dereference ()
  146. class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
  147. if add_quotes:
  148. return "\"{0}\"".format (class_name)
  149. else:
  150. return class_name
  151. # This returns more info but requires calling into the inferior
  152. #return "\"{0}\"".format (gdb.parse_and_eval ("mono_type_full_name (&((MonoClass*){0})->byval_arg)".format (str (int ((self.val).cast (gdb.lookup_type ("guint64")))))))
  153. def to_string(self):
  154. try:
  155. return self.to_string_inner (True)
  156. except:
  157. #print (sys.exc_info ()[0])
  158. #print (sys.exc_info ()[1])
  159. return str(self.val.cast (gdb.lookup_type ("gpointer")))
  160. class MonoGenericInstPrinter:
  161. "Print a MonoGenericInst structure"
  162. def __init__(self, val):
  163. self.val = val
  164. def to_string(self):
  165. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  166. return "0x0"
  167. inst = self.val.dereference ()
  168. inst_len = inst ["type_argc"]
  169. inst_args = inst ["type_argv"]
  170. inst_str = ""
  171. for i in range(0, inst_len):
  172. # print (inst_args)
  173. type_printer = MonoTypePrinter (inst_args [i])
  174. if i > 0:
  175. inst_str = inst_str + ", "
  176. inst_str = inst_str + type_printer.to_string ()
  177. return inst_str
  178. class MonoGenericClassPrinter:
  179. "Print a MonoGenericClass structure"
  180. def __init__(self, val):
  181. self.val = val
  182. def to_string_inner(self):
  183. gclass = self.val.dereference ()
  184. container_str = str(gclass ["container_class"])
  185. class_inst = gclass ["context"]["class_inst"]
  186. class_inst_str = ""
  187. if int(class_inst.cast (gdb.lookup_type ("guint64"))) != 0:
  188. class_inst_str = str(class_inst)
  189. method_inst = gclass ["context"]["method_inst"]
  190. method_inst_str = ""
  191. if int(method_inst.cast (gdb.lookup_type ("guint64"))) != 0:
  192. method_inst_str = str(method_inst)
  193. return "{0}, [{1}], [{2}]>".format (container_str, class_inst_str, method_inst_str)
  194. def to_string(self):
  195. try:
  196. return self.to_string_inner ()
  197. except:
  198. #print (sys.exc_info ()[0])
  199. #print (sys.exc_info ()[1])
  200. return str(self.val.cast (gdb.lookup_type ("gpointer")))
  201. class MonoTypePrinter:
  202. "Print a MonoType structure"
  203. def __init__(self, val):
  204. self.val = val
  205. def to_string_inner(self, csharp):
  206. try:
  207. t = self.val.referenced_value ()
  208. kind = str (t ["type"]).replace ("MONO_TYPE_", "").lower ()
  209. info = ""
  210. if kind == "class":
  211. p = MonoClassPrinter(t ["data"]["klass"])
  212. info = p.to_string ()
  213. elif kind == "genericinst":
  214. info = str(t ["data"]["generic_class"])
  215. if info != "":
  216. return "{{{0}, {1}}}".format (kind, info)
  217. else:
  218. return "{{{0}}}".format (kind)
  219. except:
  220. #print (sys.exc_info ()[0])
  221. #print (sys.exc_info ()[1])
  222. return str(self.val.cast (gdb.lookup_type ("gpointer")))
  223. def to_string(self):
  224. return self.to_string_inner (False)
  225. class MonoMethodRgctxPrinter:
  226. "Print a MonoMethodRgctx structure"
  227. def __init__(self, val):
  228. self.val = val
  229. def to_string(self):
  230. rgctx = self.val.dereference ()
  231. klass = rgctx ["class_vtable"].dereference () ["klass"]
  232. klass_printer = MonoClassPrinter (klass)
  233. inst = rgctx ["method_inst"].dereference ()
  234. inst_len = inst ["type_argc"]
  235. inst_args = inst ["type_argv"]
  236. inst_str = ""
  237. for i in range(0, inst_len):
  238. # print (inst_args)
  239. type_printer = MonoTypePrinter (inst_args [i])
  240. if i > 0:
  241. inst_str = inst_str + ", "
  242. inst_str = inst_str + type_printer.to_string ()
  243. return "MRGCTX[{0}, [{1}]]".format (klass_printer.to_string(), inst_str)
  244. class MonoVTablePrinter:
  245. "Print a MonoVTable structure"
  246. def __init__(self, val):
  247. self.val = val
  248. def to_string(self):
  249. if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
  250. return "0x0"
  251. vtable = self.val.dereference ()
  252. klass = vtable ["klass"]
  253. klass_printer = MonoClassPrinter (klass)
  254. return "vtable({0})".format (klass_printer.to_string ())
  255. def lookup_pretty_printer(val):
  256. t = str (val.type)
  257. if t == "object":
  258. return ObjectPrinter (val)
  259. if t[0:5] == "class" and t[-1] == "&":
  260. return ObjectPrinter (val)
  261. if t == "string":
  262. return StringPrinter (val)
  263. if t == "MonoString *":
  264. return StringPrinter (val)
  265. if t == "MonoMethod *":
  266. return MonoMethodPrinter (val)
  267. if t == "MonoClass *":
  268. return MonoClassPrinter (val)
  269. if t == "MonoType *":
  270. return MonoTypePrinter (val)
  271. if t == "MonoGenericInst *":
  272. return MonoGenericInstPrinter (val)
  273. if t == "MonoGenericClass *":
  274. return MonoGenericClassPrinter (val)
  275. if t == "MonoMethodRuntimeGenericContext *":
  276. return MonoMethodRgctxPrinter (val)
  277. if t == "MonoVTable *":
  278. return MonoVTablePrinter (val)
  279. return None
  280. def register_csharp_printers(obj):
  281. "Register C# pretty-printers with objfile Obj."
  282. if obj == None:
  283. obj = gdb
  284. obj.pretty_printers.append (lookup_pretty_printer)
  285. # This command will flush the debugging info collected by the runtime
  286. class XdbCommand (gdb.Command):
  287. def __init__ (self):
  288. super (XdbCommand, self).__init__ ("xdb", gdb.COMMAND_NONE,
  289. gdb.COMPLETE_COMMAND)
  290. def invoke(self, arg, from_tty):
  291. gdb.execute ("call mono_xdebug_flush ()")
  292. register_csharp_printers (gdb.current_objfile())
  293. XdbCommand ()
  294. gdb.execute ("set environment MONO_XDEBUG gdb")
  295. print ("Mono support loaded.")