From d3f06a6795a2aa4b66bbe16411e554ccf603ff47 Mon Sep 17 00:00:00 2001 From: ElectronixTM Date: Mon, 31 Mar 2025 03:03:41 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=B5=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=B1=D0=BB=D0=B8=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B4=D0=B5=D0=B1=D0=B0=D0=B3=D0=B3=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сейчас он не запускаестя из-за проблем с модулями --- src/debugger/__init__.py | 0 src/debugger/debugger.py | 170 +++++++++++++++++++++++++++++++++++++++ src/debugger/main.py | 5 ++ 3 files changed, 175 insertions(+) create mode 100644 src/debugger/__init__.py create mode 100644 src/debugger/debugger.py create mode 100644 src/debugger/main.py diff --git a/src/debugger/__init__.py b/src/debugger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/debugger/debugger.py b/src/debugger/debugger.py new file mode 100644 index 0000000..188e14c --- /dev/null +++ b/src/debugger/debugger.py @@ -0,0 +1,170 @@ +""" +Отладчик принимает на вход довольно много всего и пытается вам +хоть как-то помочь +""" +from emulator.vm import VM, VMException, Breakpoint, Condition, WORD_SIZE + +from dataclasses import dataclass +from typing import TypedDict, Callable, cast +import re +import struct + +class DbgInstrDesc(TypedDict): + """ + Описание отдельно взятой инструкции по определенному оффсету + """ + length: int + srcline: int + +class DbgDict(TypedDict): + """ + Урезанная версия полной отладочной информации, предоставляемой + в файле dbg.json. Содержит ту информацию, которую может + предоставить ассемблер + """ + src: str + labels: dict[str, int] + instructions: dict[int, DbgInstrDesc] + +@dataclass +class Debugger: + _vm: VM + _dbg_dict: DbgDict + _callbacks_table: dict[str, Callable[[list[str]], str]] + _breakpoints: set[int] + _source_lines: list[str] + + # will be nessesary in _inspect + UNPACK_SYMBOL_TABLE = { + "b": "b", + "B": "B", + "h": "h", + "H": "H", + "w": "i", + "W": "I" + } + + SIZE_SYMBOL_TABLE = { + "b": 1, + "B": 1, + "h": 2, + "H": 2, + "w": 4, + "W": 4 + } + + def __init__(self, mem: bytearray, dbg_dict: DbgDict): + with open(dbg_dict["src"], 'r') as f: + self._source_lines = f.readlines() + self._vm = VM(mem) + self._dbg_dict = dbg_dict + self._breakpoints = set() + + def __init_callbacks__(self): + self._callbacks_table = { + "step": self._step, + "continue": self._continue, + "breakpoint": self._breakpoint, + "print": self._print, + "run": self._run, + "inspect": self._inspect + } + + def do_command(self, command: list[str]) -> str: + callback_name = command[0] + if not callback_name in self._callbacks_table: + return "Unknown command" + return self._callbacks_table[callback_name](command[1:]) + + def _step(self, args: list[str]) -> str: + try: + self._vm.step() + except VMException as e: + # TODO: тут необходимо выкорчевать из + # исключения текст ошибки + return f"Virtual machine exception: {e.message}" + return "" + + def _continue(self, args: list[str]) -> str: + try: + self._vm.continue_() + except Breakpoint: + return "" + return "Program finished" + + def _breakpoint(self, args: list[str]) -> str: + if len(args) == 0: + addr = self._vm.pc.value + self._breakpoints.add(addr) + src_line = self._dbg_dict["instructions"][addr]["srcline"] + return f"Breakpoint succesfully set on line {src_line}" + desired_addr: int = int(args[0]) + addr = -1 + # находим за линейное время. Плохая практика, но так как + # сходные коды вряд ли будут длиннее 1000 строк - приемлемо + for addr, desc in self._dbg_dict.items(): + desc = cast(DbgInstrDesc, desc) + if not desc["srcline"] == desired_addr: + continue + self._breakpoints.add(int(addr)) + return f"Breakpoint succesfully set on line {desired_addr}" + + return f"Couldn't place breakpoint on src line {desired_addr}" + + def _print(self, args: list[str]) -> str: + to_print = args[0] + if to_print == "pc": + return "pc: " + str(self._vm.pc.value) + elif to_print == "cc": + flags = Condition(self._vm.cc.value) + return ( + f"v: {flags.v}\n" + f"n: {flags.n}\n" + f"z: {flags.z}\n" + ) + + elif re.fullmatch(r'r\d+', to_print): + index = int(to_print[1:]) + return f"{to_print}: {self._vm.registers[index].value}" + + elif to_print in self._dbg_dict["labels"]: + return f'{to_print}: {self._dbg_dict["labels"][to_print]}' + else: + return "Can't print required value" + + def _run(self, args: list[str]) -> str: + try: + self._vm.run() + except Breakpoint: + return "" + return "Program finished" + + def _inspect(self, args: list[str]) -> str: + size_arg = args.pop(0) + amount = int(args.pop(0)) + mem_location = args.pop(0) + offset = -1 + if mem_location in self._dbg_dict["labels"]: + offset = self._dbg_dict["labels"][mem_location] + elif mem_location.isdigit(): + offset = int(mem_location) * WORD_SIZE + elif mem_location.startswith("0x"): + offset = int(mem_location, 16) + else: + return ("You passed wrong offset parameter. It should be either " + "name of some label or explicit decimal or hexdecimal " + "number, in the second case it should start with 0x") + + try: + sym = self.UNPACK_SYMBOL_TABLE[size_arg] + size = self.SIZE_SYMBOL_TABLE[size_arg] + except KeyError: + return ("Size Error. Please select one of the following " + "options as size: b/B - byte, h/H - 2 bytes, " + "w/W - 4 bytes. Big - unsigned, small - signed") + contents = struct.unpack( + sym*amount, + self._vm.mem[offset:offset+size*amount] + ) + return f"{mem_location}:" + " ".join(map(str, contents)) + diff --git a/src/debugger/main.py b/src/debugger/main.py new file mode 100644 index 0000000..1358978 --- /dev/null +++ b/src/debugger/main.py @@ -0,0 +1,5 @@ +def main(): + print("Hello world!") + +if __name__ == "__main__": + main()