libexpr: add a gdb script with value pretty printers
Change-Id: Ia4a6feb194c73dddf468c78287962713bef582f7
This commit is contained in:
@@ -81,4 +81,147 @@ void Value::mkPath(const SourcePath & path)
|
||||
*this = Value(NewValueAs::path, path);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
[[gnu::section(".debug_gdb_scripts"), gnu::used]]
|
||||
static const char printer_script[] =
|
||||
"\4"
|
||||
R"(lix-value-printer
|
||||
class ValuePrinter(gdb.ValuePrinter):
|
||||
def __init__(self, val):
|
||||
self._val = val
|
||||
self._t_thunk = gdb.lookup_type('nix::Value::Thunk').pointer()
|
||||
self._t_app = gdb.lookup_type('nix::Value::App').pointer()
|
||||
self._t_int = gdb.lookup_type('intptr_t')
|
||||
self._t_string = gdb.lookup_type('nix::Value::String').pointer()
|
||||
self._t_attrs = gdb.lookup_type('nix::Bindings').pointer()
|
||||
self._t_list = gdb.lookup_type('nix::Value::List').pointer()
|
||||
self._t_aux = gdb.lookup_type('nix::Value::Acb').pointer()
|
||||
|
||||
def _addr(self, t = None):
|
||||
addr = self._val['raw'] & ~self._val['TAG_MASK']
|
||||
return addr.cast(t).referenced_value() if t else addr
|
||||
|
||||
def _tag(self):
|
||||
return self._val['raw'] & self._val['TAG_MASK']
|
||||
def _v_int(self):
|
||||
return self._val['raw'].cast(self._t_int) >> self._val['TAG_BITS']
|
||||
|
||||
def to_string(self):
|
||||
if self._tag() == 7:
|
||||
return self._addr(self._t_aux)
|
||||
elif not self.children():
|
||||
return f'undecoded {self._val['raw'].format_string(format='x')}'
|
||||
return None
|
||||
|
||||
def children(self):
|
||||
match self._tag():
|
||||
case 0:
|
||||
return [('thunk', self._addr(self._t_thunk))]
|
||||
case 1:
|
||||
return [('app', self._addr(self._t_app))]
|
||||
case 2:
|
||||
return [('int', self._v_int())]
|
||||
case 3:
|
||||
return [('bool', (self._val['raw'] >> self._val['TAG_BITS']) != 0)]
|
||||
case 4:
|
||||
return [('string', self._addr(self._t_string))]
|
||||
case 5:
|
||||
return [('attrs', self._addr(self._t_attrs))]
|
||||
case 6:
|
||||
return [('list', self._addr(self._t_list))]
|
||||
case _:
|
||||
return []
|
||||
|
||||
class ThunkPrinter(gdb.ValuePrinter):
|
||||
def __init__(self, val):
|
||||
self._val = val
|
||||
self._t_value = gdb.lookup_type('nix::Value')
|
||||
self._t_env = gdb.lookup_type('nix::Env').pointer()
|
||||
|
||||
def children(self):
|
||||
if self._val['expr'] == 0:
|
||||
return [('result', self._val['_result'].cast(self._t_value))]
|
||||
else:
|
||||
return [
|
||||
('env', self._val['_env'].cast(self._t_env).referenced_value()),
|
||||
('expr', self._val['expr'].referenced_value()),
|
||||
]
|
||||
|
||||
class AppPrinter(gdb.ValuePrinter):
|
||||
def __init__(self, val):
|
||||
self._val = val
|
||||
self._t_value = gdb.lookup_type('nix::Value')
|
||||
|
||||
def children(self):
|
||||
if self._val['_n'] + 1 == 0:
|
||||
return [('result', self._val['_left'].cast(self._t_value))]
|
||||
else:
|
||||
n = int(self._val['_n'])
|
||||
arr = self._t_value.array(0, n - 1)
|
||||
return [
|
||||
('fn', self._val['_left'].cast(self._t_value)),
|
||||
('n', n),
|
||||
('args', self._val['_args'][0].cast(arr)),
|
||||
]
|
||||
|
||||
class AcbPrinter(gdb.ValuePrinter):
|
||||
def __init__(self, val):
|
||||
self._val = val
|
||||
self._t_external = gdb.lookup_type('nix::Value::External').pointer()
|
||||
self._t_float = gdb.lookup_type('nix::Value::Float').pointer()
|
||||
self._t_primop = gdb.lookup_type('nix::Value::PrimOp')
|
||||
self._t_primopdetails = gdb.lookup_type('nix::PrimOpDetails')
|
||||
self._t_lambda = gdb.lookup_type('nix::Value::Lambda').pointer()
|
||||
self._t_int = gdb.lookup_type('nix::Value::Int').pointer()
|
||||
|
||||
def _addr(self, t = None):
|
||||
addr = self._val['raw'] & ~self._val['TAG_MASK']
|
||||
return addr.cast(t).referenced_value() if t else addr
|
||||
|
||||
def _tag(self):
|
||||
return self._val['raw'] & self._val['TAG_MASK']
|
||||
|
||||
def to_string(self):
|
||||
match self._tag():
|
||||
case 2:
|
||||
return 'null'
|
||||
case 3:
|
||||
op = self._val.cast(self._t_primop).cast(self._t_primopdetails)
|
||||
return f'primop {op['name']}'
|
||||
case _:
|
||||
return None
|
||||
|
||||
def children(self):
|
||||
match self._tag():
|
||||
case 0:
|
||||
return [('external', self._addr(self._t_external))]
|
||||
case 1:
|
||||
return [('float', self._addr(self._t_float)['value'])]
|
||||
case 4:
|
||||
return [('lambda', self._addr(self._t_lambda))]
|
||||
case 5:
|
||||
return [('int', self._addr(self._t_int)['value'])]
|
||||
case _:
|
||||
return []
|
||||
|
||||
def value_lookup_function(val):
|
||||
lookup_tag = val.type.tag
|
||||
if lookup_tag is None:
|
||||
return None
|
||||
if lookup_tag == 'nix::Value':
|
||||
return ValuePrinter(val)
|
||||
elif lookup_tag == 'nix::Value::Thunk':
|
||||
return ThunkPrinter(val)
|
||||
elif lookup_tag == 'nix::Value::App':
|
||||
return AppPrinter(val)
|
||||
elif lookup_tag == 'nix::Value::Acb':
|
||||
return AcbPrinter(val)
|
||||
return None
|
||||
|
||||
def register_printers(objfile):
|
||||
objfile.pretty_printers.append(value_lookup_function)
|
||||
|
||||
register_printers(gdb.current_objfile())
|
||||
)";
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user