diff --git a/lix/libexpr/value.cc b/lix/libexpr/value.cc index 5224d2aaa..06cf106fb 100644 --- a/lix/libexpr/value.cc +++ b/lix/libexpr/value.cc @@ -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 }