Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

355 lines
12 KiB
Python

#
# Author: Zoltan Varga (vargaz@gmail.com)
# License: MIT/X11
#
#
# This is a mono support mode for gdb 7.0 and later
# Usage:
# - copy/symlink this file to the directory where the mono executable lives.
# - run mono under gdb, or attach to a mono process started with --debug=gdb using gdb.
#
from __future__ import print_function
import os
class StringPrinter:
"Print a C# string"
def __init__(self, val):
self.val = val
def to_string(self):
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return "null"
obj = self.val.cast (gdb.lookup_type ("MonoString").pointer ()).dereference ()
len = obj ['length']
chars = obj ['chars']
i = 0
res = ['"']
while i < len:
val = (chars.cast(gdb.lookup_type ("gint64")) + (i * 2)).cast(gdb.lookup_type ("gunichar2").pointer ()).dereference ()
if val >= 256:
c = "\u%X".format (val)
else:
c = chr (val)
res.append (c)
i = i + 1
res.append ('"')
return ''.join (res)
def stringify_class_name(ns, name):
if ns == "System":
if name == "Byte":
return "byte"
if name == "String":
return "string"
if ns == "":
return name
else:
return "%s.%s".format (ns, name)
class ArrayPrinter:
"Print a C# array"
def __init__(self, val, class_ns, class_name):
self.val = val
self.class_ns = class_ns
self.class_name = class_name
def to_string(self):
obj = self.val.cast (gdb.lookup_type ("MonoArray").pointer ()).dereference ()
length = obj ['max_length']
return "%s [%d]".format (stringify_class_name (self.class_ns, self.class_name [0:len(self.class_name) - 2]), int(length))
class ObjectPrinter:
"Print a C# object"
def __init__(self, val):
if str(val.type)[-1] == "&":
self.val = val.address.cast (gdb.lookup_type ("MonoObject").pointer ())
else:
self.val = val.cast (gdb.lookup_type ("MonoObject").pointer ())
class _iterator:
def __init__(self,obj):
self.obj = obj
self.iter = self.obj.type.fields ().__iter__ ()
pass
def __iter__(self):
return self
def next(self):
field = self.iter.next ()
try:
if str(self.obj [field.name].type) == "object":
# Avoid recursion
return (field.name, self.obj [field.name].cast (gdb.lookup_type ("void").pointer ()))
else:
return (field.name, self.obj [field.name])
except:
# Superclass
return (field.name, self.obj.cast (gdb.lookup_type ("%s".format (field.name))))
def children(self):
# FIXME: It would be easier if gdb.Value would support iteration itself
# It would also be better if we could return None
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return {}.__iter__ ()
try:
obj = self.val.dereference ()
class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
if class_name [-2:len(class_name)] == "[]":
return {}.__iter__ ()
try:
gdb_type = gdb.lookup_type ("struct %s_%s".format (class_ns.replace (".", "_"), class_name))
return self._iterator(obj.cast (gdb_type))
except:
return {}.__iter__ ()
except:
print (sys.exc_info ()[0])
print (sys.exc_info ()[1])
return {}.__iter__ ()
def to_string(self):
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return "null"
try:
obj = self.val.dereference ()
class_ns = obj ['vtable'].dereference ()['klass'].dereference ()['name_space'].string ()
class_name = obj ['vtable'].dereference ()['klass'].dereference ()['name'].string ()
if class_ns == "System" and class_name == "String":
return StringPrinter (self.val).to_string ()
if class_name [-2:len(class_name)] == "[]":
return ArrayPrinter (self.val,class_ns,class_name).to_string ()
if class_ns != "":
try:
gdb_type = gdb.lookup_type ("struct %s.%s".format (class_ns, class_name))
except:
# Maybe there is no debug info for that type
return "%s.%s".format (class_ns, class_name)
#return obj.cast (gdb_type)
return "%s.%s".format (class_ns, class_name)
return class_name
except:
print (sys.exc_info ()[0])
print (sys.exc_info ()[1])
# FIXME: This can happen because we don't have liveness information
return self.val.cast (gdb.lookup_type ("guint64"))
class MonoMethodPrinter:
"Print a MonoMethod structure"
def __init__(self, val):
self.val = val
def to_string(self):
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return "0x0"
val = self.val.dereference ()
klass = val ["klass"].dereference ()
class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
return "\"%s:%s ()\"".format (class_name, val ["name"].string ())
# This returns more info but requires calling into the inferior
#return "\"%s\"".format (gdb.parse_and_eval ("mono_method_full_name (%s, 1)".format (str (int (self.val.cast (gdb.lookup_type ("guint64")))))).string ())
class MonoClassPrinter:
"Print a MonoClass structure"
def __init__(self, val):
self.val = val
def to_string_inner(self, add_quotes):
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return "0x0"
klass = self.val.dereference ()
class_name = stringify_class_name (klass ["name_space"].string (), klass ["name"].string ())
if klass ["generic_class"].cast (gdb.lookup_type ("guint64")) != 0:
class_name = "%s<%s>".format (class_name, str (klass ["generic_class"]["context"]["class_inst"]))
if add_quotes:
return "\"%s\"".format (class_name)
else:
return class_name
# This returns more info but requires calling into the inferior
#return "\"%s\"".format (gdb.parse_and_eval ("mono_type_full_name (&((MonoClass*)%s)->byval_arg)".format (str (int ((self.val).cast (gdb.lookup_type ("guint64")))))))
def to_string(self):
try:
return self.to_string_inner (True)
except:
#print (sys.exc_info ()[0])
#print (sys.exc_info ()[1])
return str(self.val.cast (gdb.lookup_type ("gpointer")))
class MonoGenericInstPrinter:
"Print a MonoGenericInst structure"
def __init__(self, val):
self.val = val
def to_string(self):
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return "0x0"
inst = self.val.dereference ()
inst_len = inst ["type_argc"]
inst_args = inst ["type_argv"]
inst_str = ""
for i in range(0, inst_len):
print (inst_args)
type_printer = MonoTypePrinter (inst_args [i])
if i > 0:
inst_str = inst_str + ", "
inst_str = inst_str + type_printer.to_string ()
return inst_str
class MonoGenericClassPrinter:
"Print a MonoGenericClass structure"
def __init__(self, val):
self.val = val
def to_string_inner(self):
gclass = self.val.dereference ()
container_str = str(gclass ["container_class"])
class_inst = gclass ["context"]["class_inst"]
class_inst_str = ""
if int(class_inst.cast (gdb.lookup_type ("guint64"))) != 0:
class_inst_str = str(class_inst)
method_inst = gclass ["context"]["method_inst"]
method_inst_str = ""
if int(method_inst.cast (gdb.lookup_type ("guint64"))) != 0:
method_inst_str = str(method_inst)
return "%s, [%s], [%s]>".format (container_str, class_inst_str, method_inst_str)
def to_string(self):
try:
return self.to_string_inner ()
except:
#print (sys.exc_info ()[0])
#print (sys.exc_info ()[1])
return str(self.val.cast (gdb.lookup_type ("gpointer")))
class MonoTypePrinter:
"Print a MonoType structure"
def __init__(self, val):
self.val = val
def to_string_inner(self, csharp):
try:
t = self.val.dereference ()
kind = str (t ["type"]).replace ("MONO_TYPE_", "").lower ()
info = ""
if kind == "class":
p = MonoClassPrinter(t ["data"]["klass"])
info = p.to_string ()
elif kind == "genericinst":
info = str(t ["data"]["generic_class"])
if info != "":
return "{%s, %s}".format (kind, info)
else:
return "{%s}".format (kind)
except:
#print (sys.exc_info ()[0])
#print (sys.exc_info ()[1])
return str(self.val.cast (gdb.lookup_type ("gpointer")))
def to_string(self):
return self.to_string_inner (False)
class MonoMethodRgctxPrinter:
"Print a MonoMethodRgctx structure"
def __init__(self, val):
self.val = val
def to_string(self):
rgctx = self.val.dereference ()
klass = rgctx ["class_vtable"].dereference () ["klass"]
klass_printer = MonoClassPrinter (klass)
inst = rgctx ["method_inst"].dereference ()
inst_len = inst ["type_argc"]
inst_args = inst ["type_argv"]
inst_str = ""
for i in range(0, inst_len):
print (inst_args)
type_printer = MonoTypePrinter (inst_args [i])
if i > 0:
inst_str = inst_str + ", "
inst_str = inst_str + type_printer.to_string ()
return "MRGCTX[%s, [%s]]".format (klass_printer.to_string(), inst_str)
class MonoVTablePrinter:
"Print a MonoVTable structure"
def __init__(self, val):
self.val = val
def to_string(self):
if int(self.val.cast (gdb.lookup_type ("guint64"))) == 0:
return "0x0"
vtable = self.val.dereference ()
klass = vtable ["klass"]
klass_printer = MonoClassPrinter (klass)
return "vtable(%s)".format (klass_printer.to_string ())
def lookup_pretty_printer(val):
t = str (val.type)
if t == "object":
return ObjectPrinter (val)
if t[0:5] == "class" and t[-1] == "&":
return ObjectPrinter (val)
if t == "string":
return StringPrinter (val)
if t == "MonoString *":
return StringPrinter (val)
if t == "MonoMethod *":
return MonoMethodPrinter (val)
if t == "MonoClass *":
return MonoClassPrinter (val)
if t == "MonoType *":
return MonoTypePrinter (val)
if t == "MonoGenericInst *":
return MonoGenericInstPrinter (val)
if t == "MonoGenericClass *":
return MonoGenericClassPrinter (val)
if t == "MonoMethodRuntimeGenericContext *":
return MonoMethodRgctxPrinter (val)
if t == "MonoVTable *":
return MonoVTablePrinter (val)
return None
def register_csharp_printers(obj):
"Register C# pretty-printers with objfile Obj."
if obj == None:
obj = gdb
obj.pretty_printers.append (lookup_pretty_printer)
# This command will flush the debugging info collected by the runtime
class XdbCommand (gdb.Command):
def __init__ (self):
super (XdbCommand, self).__init__ ("xdb", gdb.COMMAND_NONE,
gdb.COMPLETE_COMMAND)
def invoke(self, arg, from_tty):
gdb.execute ("call mono_xdebug_flush ()")
register_csharp_printers (gdb.current_objfile())
XdbCommand ()
gdb.execute ("set environment MONO_XDEBUG gdb")
print ("Mono support loaded.")