lean4-htt/bin/lean-gdb.py
Sebastian Ullrich 80c6edc620 chore(bin/lean-gdb): add lean::object pretty printer
Not quite sure yet if this helps or just confuses gdb/CLion on invalid pointers
2019-02-07 13:02:48 +01:00

304 lines
9 KiB
Python

# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Microsoft Corporation. All rights reserved.
# Released under Apache 2.0 license as described in the file LICENSE.
#
# Authors: Sebastian Ullrich, Gabriel Ebner
#
import gdb
import gdb.printing
def is_scalar(o):
return o.cast(gdb.lookup_type('uintptr_t')) & 1 == 1
def box(n):
return gdb.Value(n << 1 | 1).cast(gdb.lookup_type('lean::object').pointer())
def unbox(o):
return o.cast(gdb.lookup_type('uintptr_t')) >> 1
def to_cnstr(o):
return o.cast(gdb.lookup_type('lean::constructor_object').pointer())
def get_cnstr_tag(o):
return to_cnstr(o).dereference()['m_tag']
char_p = gdb.lookup_type('char').pointer()
def get_cnstr_obj_arg(o, i):
return (o.cast(char_p) + gdb.lookup_type('lean::constructor_object').sizeof).cast(gdb.lookup_type('lean::object').pointer().pointer())[i]
def get_closure_arg(o, i):
return (o.cast(char_p) + gdb.lookup_type('lean::closure_object').sizeof).cast(gdb.lookup_type('lean::object').pointer().pointer())[i]
def get_c_str(o):
return (o.cast(char_p) + gdb.lookup_type('lean::string_object').sizeof)
def get_rc(o):
return (o.cast(gdb.lookup_type('uintptr_t').pointer()) - 1).dereference()
class LeanObjectPrinter:
"""Print a lean::object object."""
kinds = [
('constructor', ['m_tag', 'm_scalar_size']),
('closure', ['m_arity', 'm_fun']),
('array', []),
('sarray', []),
('parray_root', []),
('parray_set', []),
('parray_push', []),
('parray_pop', []),
('string', ['m_size', 'm_capacity', 'm_length']),
('mpz', ['m_value']),
('thunk', ['m_value', 'm_closure']),
('external', []),
]
def __init__(self, val):
self.val = val.address
if not is_scalar(self.val):
self.kind = int(self.val.dereference()['m_kind'])
def to_string(self):
if is_scalar(self.val):
return unbox(self.val)
else:
return "{} (RC {})".format(LeanObjectPrinter.kinds[self.kind][0], get_rc(self.val))
def children(self):
if is_scalar(self.val):
return
yield ('RC', get_rc(self.val))
if self.kind >= len(LeanObjectPrinter.kinds):
return
typ, fields = LeanObjectPrinter.kinds[self.kind]
if fields:
val = self.val.cast(gdb.lookup_type("lean::" + typ + "_object").pointer()).dereference()
for f in fields:
yield (f, val[f])
if typ == 'constructor':
for i in range(val['m_num_objs']):
yield ('', get_cnstr_obj_arg(self.val, i))
elif typ == 'closure':
for i in range(val['m_num_fixed']):
yield ('', get_closure_arg(self.val, i))
class LeanOptionalPrinter:
"""Print a lean::optional object."""
def __init__(self, val):
self.val = val
def get_val(self):
if hasattr(self.val, 'm_some'):
if not self.val['m_some']:
return None
elif not self.val['m_value'].cast(gdb.lookup_type('char').pointer()):
return None
return self.val['m_value']
def children(self):
val = self.get_val()
return [('', val)] if val else []
def to_string(self):
return str(self.val.type)
class LeanNamePrinter:
"""Print a lean::name object."""
def __init__(self, val):
self.val = val
def to_string(self):
def rec(o):
prefix = get_cnstr_obj_arg(o, 0)
part = get_cnstr_obj_arg(o, 1)
s = ("'%s'" % get_c_str(part).string()) if get_cnstr_tag(o) == 1 else str(unbox(part))
if not is_scalar(prefix):
return "%s.%s" % (rec(prefix), s)
else:
return s
if is_scalar(self.val['m_obj']):
return ""
else:
return rec(self.val['m_obj'])
class LeanExprPrinter:
"""Print a lean::expr object."""
expr_kinds = [
('bvar', [('idx', 'nat')]),
('fvar', ['name']),
('mvar', ['name', ('type', 'expr')]),
('sort', ['level']),
('const', ['name', ('levels', 'list_ref<lean::level>')]),
('app', [('fn', 'expr'), ('arg', 'expr')]),
('lam', ['name', ('type', 'expr'), ('body', 'expr')]),
('pi', ['name', ('type', 'expr'), ('body', 'expr')]),
('let', ['name', ('type', 'expr'), ('val', 'expr'), ('body', 'expr')]),
('lit', ['literal']),
('mdata', ['kvmap', 'expr']),
('proj', ['name', 'nat', 'expr']),
]
def __init__(self, val):
self.val = val
def to_string(self):
return 'lean::expr[{}]'.format(LeanExprPrinter.expr_kinds[get_cnstr_tag(self.val['m_obj'])][0])
def children(self):
_, fields = LeanExprPrinter.expr_kinds[get_cnstr_tag(self.val['m_obj'])]
for i, f in enumerate(fields):
if isinstance(f, tuple):
name, typ = f
else:
name, typ = f, f
yield (name, get_cnstr_obj_arg(self.val['m_obj'], i).cast(gdb.lookup_type('lean::' + typ)))
class LeanLevelPrinter:
"""Print a lean::level object."""
level_kinds = [
('lean::level_cell', 'zero', []),
('lean::level_succ', 'succ', ['m_l']),
('lean::level_max_core', 'max', ['m_lhs', 'm_rhs']),
('lean::level_max_core', 'imax', ['m_lhs', 'm_rhs']),
('lean::level_param_core', 'param', ['m_id']),
('lean::level_param_core', 'meta', ['m_id']),
]
def __init__(self, val):
self.val = val
def to_string(self):
kind = int(self.val['m_obj']['m_kind'])
(subtype, name, fields) = LeanLevelPrinter.level_kinds[kind]
subtype = gdb.lookup_type(subtype)
val = self.val['m_obj'].cast(subtype.pointer()).dereference()
return "({})".format(" ".join([name] + [str(val[field]) for field in fields]))
class LeanListPrinter:
"""Print a lean::list object."""
def __init__(self, val):
self.val = val
def children(self):
l = self.val
i = 0
while l['m_obj']:
cell = l['m_obj'].dereference()
yield ('[%s]' % i, cell['m_head'])
l = cell['m_tail']
i += 1
def to_string(self):
return str(self.val.type)
def display_hint(self):
return 'array'
class LeanBufferPrinter:
"""Print a lean::buffer object."""
def __init__(self, val):
self.val = val
def children(self):
p = self.val['m_buffer']
for i in range(int(self.val['m_pos'])):
yield ('[%s]' % i, p.dereference())
p += 1
def to_string(self):
return str(self.val.type)
def display_hint(self):
return 'array'
class LeanListRefPrinter:
"""Print a lean::list_ref object."""
def __init__(self, val):
self.val = val
def children(self):
l = self.val['m_obj']
i = 0
while not is_scalar(l):
yield ('[%s]' % i, get_cnstr_obj_arg(l, 0).cast(self.val.type.template_argument(0)))
l = get_cnstr_obj_arg(l, 1)
i += 1
def to_string(self):
return str(self.val.type)
def display_hint(self):
return 'array'
class LeanRBTreePrinter:
"""Print a lean::rb_tree object."""
def __init__(self, val):
self.val = val
def children(self):
def rec(node):
if node['m_ptr']:
cell = node['m_ptr'].dereference()
for i in rec(cell['m_left']): yield i
yield ('', cell['m_value'])
for i in rec(cell['m_right']): yield i
return rec(self.val['m_root'])
def to_string(self):
return 'lean::rb_tree' # full type is way too verbose
def display_hint(self):
return 'array'
class LeanRBMapPrinter:
"""Print a lean::rb_map object."""
def __init__(self, val):
self.val = val
def children(self):
for (_, child) in LeanRBTreePrinter(self.val['m_map']).children():
yield ('', child['first'])
yield ('', child['second'])
def to_string(self):
return 'lean::rb_map' # full type is way too verbose
def display_hint(self):
return 'map'
def build_pretty_printer():
pp = gdb.printing.RegexpCollectionPrettyPrinter("lean")
pp.add_printer('object', '^lean::object$', LeanObjectPrinter)
pp.add_printer('optional', '^lean::optional', LeanOptionalPrinter)
pp.add_printer('name', '^lean::name$', LeanNamePrinter)
pp.add_printer('expr', '^lean::expr$', LeanExprPrinter)
#pp.add_printer('level', '^lean::level$', LeanLevelPrinter)
#pp.add_printer('list', '^lean::list', LeanListPrinter)
pp.add_printer('buffer', '^lean::buffer', LeanBufferPrinter)
pp.add_printer('list_ref', '^lean::list_ref', LeanListRefPrinter)
pp.add_printer('rb_tree', '^lean::rb_tree', LeanRBTreePrinter)
pp.add_printer('rb_map', '^lean::rb_map', LeanRBMapPrinter)
return pp
def register():
gdb.printing.register_pretty_printer(
gdb.current_objfile(),
build_pretty_printer(),
replace=True)
register()