Download hdc/debugger.py from AEUPH/hdcstorage: direct link, hf CLI and curl.
- Browser
- Download file 9.14 kB
-
https://huggingface.co/AEUPH/hdcstorage/resolve/main/hdc/debugger.py
- Command line
-
hf download hf://AEUPH/hdcstorage/hdc/debugger.py
-
curl -L -o debugger.py https://huggingface.co/AEUPH/hdcstorage/resolve/main/hdc/debugger.py
9.14 kB
| """The debugger. | |
| A debugger for this machine has an unusual job: the instruction stream is not | |
| readable by construction, so inspection *is* decoding. Every view here goes | |
| through cleanup and carries its confidence, which means a low-confidence | |
| decode is visible as a fact about the program rather than hidden behind a | |
| plausible-looking mnemonic. | |
| What it offers: | |
| * breakpoints by cell index or by label; | |
| * step / step-over, with the PC held at a BREAK trap; | |
| * a trace ring of executed instructions with decode confidence and the | |
| registers each one wrote; | |
| * symbolic register inspection — for each register, the nearest named symbol | |
| and its similarity, which is the HDC equivalent of a type readout; | |
| * raw bit-plane views of registers and storage cells. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from .isa import REG_NAMES, format_operands | |
| from .interpreter import Machine, TraceEntry | |
| class StopReason: | |
| """Why execution stopped.""" | |
| reason: str # 'break' | 'halt' | 'budget' | 'step' | 'error' | |
| pc: int | |
| detail: str = "" | |
| def __str__(self) -> str: | |
| return "%s at pc=%d%s" % (self.reason, self.pc, (" (%s)" % self.detail) if self.detail else "") | |
| class Debugger: | |
| """Interactive control of a :class:`~hdc.interpreter.Machine`.""" | |
| def __init__(self, machine: Machine): | |
| self.m = machine | |
| self.breakpoints: set[int] = set() | |
| self.labels: dict[str, int] = {} | |
| self._trap_pc: int | None = None | |
| # ---------------------------------------------------------- breakpoints | |
| def attach_program(self, program) -> "Debugger": | |
| """Take label -> cell mapping from an assembled program.""" | |
| self.labels = dict(program.labels) | |
| self.breakpoints.clear() | |
| self._trap_pc = None | |
| return self | |
| def add_break(self, where: int | str) -> int: | |
| """Set a breakpoint at a cell index or a label name.""" | |
| pc = self._where(where) | |
| self.breakpoints.add(pc) | |
| return pc | |
| def remove_break(self, where: int | str) -> bool: | |
| pc = self._where(where) | |
| if pc in self.breakpoints: | |
| self.breakpoints.discard(pc) | |
| return True | |
| return False | |
| def _where(self, where: int | str) -> int: | |
| if isinstance(where, int): | |
| return where | |
| if where in self.labels: | |
| return self.labels[where] | |
| if where.startswith("@"): | |
| return int(where[1:]) | |
| raise KeyError("no such label: %r" % (where,)) | |
| def check(self) -> StopReason | None: | |
| """Does the current PC sit on a fresh breakpoint?""" | |
| pc = self.m.pc | |
| if pc == self._trap_pc: | |
| return None | |
| if pc in self.breakpoints: | |
| self._trap_pc = pc | |
| return StopReason("break", pc, "breakpoint") | |
| return None | |
| # -------------------------------------------------------------- running | |
| def step(self) -> TraceEntry | None: | |
| """Execute exactly one instruction, ignoring breakpoints.""" | |
| entry = self.m.step() | |
| if self.m.status == "break": | |
| self._trap_pc = self.m.pc | |
| else: | |
| self._trap_pc = None | |
| return entry | |
| def step_over(self) -> TraceEntry | None: | |
| """Step one instruction; a CALL runs to completion first.""" | |
| entry = self.m.step() | |
| if entry is not None and entry.op == "CALL": | |
| depth = len(self.m.stack) | |
| guard = 0 | |
| while len(self.m.stack) >= depth and not self.m.halted and guard < 100_000: | |
| self.m.step() | |
| guard += 1 | |
| self._trap_pc = None | |
| return entry | |
| def continue_(self, max_steps: int = 100_000) -> StopReason: | |
| """Run until the next breakpoint, HALT, fault or budget.""" | |
| m = self.m | |
| if m.halted: | |
| return StopReason("halt", m.pc, "already halted") | |
| if m.status == "ready": | |
| m.status = "running" | |
| # If we are parked on a trap, allow that cell to execute once. | |
| if m.pc == self._trap_pc: | |
| m.skip_break = True | |
| self._trap_pc = None | |
| while m.steps < max_steps: | |
| stop = self.check() | |
| if stop is not None: | |
| m.status = "break" | |
| return stop | |
| if m.halted: | |
| return StopReason("halt", m.pc) | |
| if m.pc >= m.prog_len: | |
| m.halted = True | |
| m.status = "halted" | |
| return StopReason("halt", m.pc, "past end of program") | |
| before = m.pc | |
| entry = m.step() | |
| if entry is None: | |
| return StopReason("halt", m.pc) | |
| if m.status == "error": | |
| return StopReason("error", m.pc, self.trace_tail(1)) | |
| if entry.op == "BREAK" and m.status == "break": | |
| self._trap_pc = before | |
| return StopReason("break", before, "BREAK trap") | |
| m.status = "budget" | |
| return StopReason("budget", m.pc, "step budget exhausted") | |
| # ---------------------------------------------------------------- views | |
| def trace_tail(self, n: int = 10) -> str: | |
| rows = self.m.trace[-n:] | |
| if not rows: | |
| return "<no steps yet>" | |
| return "\n".join(e.render() for e in rows) | |
| def disasm(self, start: int | None = None, count: int = 12) -> str: | |
| """Disassemble cells, marking the PC and any breakpoints.""" | |
| m = self.m | |
| if start is None: | |
| start = max(0, m.pc - 2) | |
| rows = [] | |
| for pc in range(start, min(start + count, m.prog_len)): | |
| dec = m.decode_at(pc) | |
| marks = [] | |
| if pc == m.pc: | |
| marks.append("=>") | |
| else: | |
| marks.append(" ") | |
| if pc in self.breakpoints: | |
| marks.append("B") | |
| else: | |
| marks.append(" ") | |
| rows.append( | |
| "%s%s %04d %-8s %-26s conf=%.2f" | |
| % ("".join(marks), "", pc, dec.op, format_operands(dec), dec.confidence) | |
| ) | |
| return "\n".join(rows) if rows else "<empty program>" | |
| def regs(self) -> str: | |
| """Registers: integer payload, and nearest named symbol (cleanup).""" | |
| m = self.m | |
| rows = [ | |
| "REG VALUE POP SYMBOL SIM HV(head)" | |
| , | |
| "-" * 78, | |
| ] | |
| for i in range(len(m.regs)): | |
| val = m.get_int(i) | |
| hits = m.storage.nearest_symbol(m.regs[i], k=1) | |
| sym, s = (hits[0] if hits else ("", 0.0)) | |
| if s < 0.15: | |
| # Below the noise band there is no nearest symbol worth | |
| # naming; printing the argmax anyway would imply a match. | |
| sym, s = "", 0.0 | |
| from .hypervec import bits | |
| rows.append( | |
| "%-5s %-10d %-6d %-25s %-5.2f %s" | |
| % ( | |
| REG_NAMES[i], | |
| val, | |
| m.regs[i].bit_count(), | |
| sym[:25] or "-", | |
| s, | |
| bits(m.regs[i], m.d)[:24], | |
| ) | |
| ) | |
| rows.append( | |
| "PC=%d ZF=%d STACK=%d STEPS=%d STATUS=%s" | |
| % (m.pc, int(m.flag_z), len(m.stack), m.steps, m.status) | |
| ) | |
| return "\n".join(rows) | |
| def sim_table(self, regs: list[int] | None = None) -> str: | |
| """Pairwise register similarity — the 'how HDC is my state' view.""" | |
| m = self.m | |
| idx = regs if regs is not None else list(range(len(m.regs))) | |
| head = " " + " ".join("%6s" % REG_NAMES[i] for i in idx) | |
| rows = [head] | |
| for i in idx: | |
| cells = [] | |
| for j in idx: | |
| cells.append("%6.3f" % m.get_sim(i, j)) | |
| rows.append("%5s " % REG_NAMES[i] + " ".join(cells)) | |
| return "\n".join(rows) | |
| def bits(self, reg: int) -> str: | |
| """Bit planes of one register.""" | |
| from .hypervec import bitplanes | |
| return "\n".join(bitplanes(self.m.regs[reg], self.m.d)) | |
| def cell_bits(self, name: str) -> str: | |
| """Bit planes of a storage cell.""" | |
| return "\n".join(self.m.storage.bitplanes(name)) | |
| def summary(self) -> str: | |
| m = self.m | |
| return ( | |
| "status=%-8s pc=%-4d steps=%-5d halted=%-5s zf=%d bps=%s\n%s" | |
| % ( | |
| m.status, | |
| m.pc, | |
| m.steps, | |
| m.halted, | |
| int(m.flag_z), | |
| sorted(self.breakpoints) or "[]", | |
| self.disasm(count=5), | |
| ) | |
| ) | |
| def debug_program(machine: Machine, program, breakpoints: list[int | str] = ()) -> str: | |
| """Convenience: attach, set breaks, run, and return a session transcript.""" | |
| dbg = Debugger(machine).attach_program(program) | |
| for b in breakpoints: | |
| dbg.add_break(b) | |
| lines = [dbg.summary()] | |
| stop = dbg.continue_() | |
| lines.append("stopped: %s" % stop) | |
| lines.append(dbg.regs()) | |
| lines.append("trace:\n" + dbg.trace_tail(8)) | |
| return "\n".join(lines) | |