Sprinter: добавить отладку C-исходников и интеграцию VS Code
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
# Source-debug Sprinter в VS Code
|
||||
|
||||
Статус: MVP для разработки самого отладчика. Поддержаны автоматический launch,
|
||||
ручной attach, точки по строкам и функциям, logpoints, один C-frame,
|
||||
регистры, простые global/static, continue/pause, instruction step и шаги
|
||||
по исходнику F10/F11/Shift+F11.
|
||||
|
||||
> [!WARNING]
|
||||
> **Windows пока не поддерживает полный цикл отладки.** Значение
|
||||
> `debugger: "windows"` включает только штатное окно debugger MAME.
|
||||
> Текущие launcher, owner lock и DAP/session transport используют Unix
|
||||
> shell, `fcntl` и Unix domain sockets. Поэтому native Windows launch/attach
|
||||
> из VS Code пока не считается рабочим. Поддержка потребует отдельного
|
||||
> Windows transport и end-to-end проверки.
|
||||
|
||||
## Какие расширения нужны
|
||||
|
||||
Для build/run/debug используется собственное расширение этого проекта —
|
||||
`toolchain/vscode-sprinter-debug`. Только оно знает формат source-debug
|
||||
пакета, загрузку приложения через DSS, банки Sprinter и DAP-сессию MAME.
|
||||
|
||||
Microsoft C/C++ или clangd можно поставить дополнительно ради completion,
|
||||
переходов по исходникам и подсветки. Оба анализируют код как близкий к
|
||||
обычному C и не являются точной моделью SDCC: параметры `__naked`, ABI и
|
||||
часть target-заголовков потребуют отдельных defines/configuration. Ошибки
|
||||
реальной сборки всегда определяет `sprinter-cc`.
|
||||
|
||||
## Подготовка программы
|
||||
|
||||
Соберите приложение с полной картой:
|
||||
|
||||
```sh
|
||||
pyenv exec make -C tests/hello SRC_DEBUG=1
|
||||
```
|
||||
|
||||
Рядом с EXE появится `.sprinter-cc-hello/manifest.json`. При изменении C,
|
||||
заголовка или опций `make` пересоберёт пакет по хэшу содержимого.
|
||||
|
||||
## Загрузка development-расширения
|
||||
|
||||
Из корня репозитория откройте VS Code с распакованным расширением:
|
||||
|
||||
```sh
|
||||
code --extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" "$PWD"
|
||||
```
|
||||
|
||||
Если команда `code` не добавлена в `PATH`, на macOS этого проекта доступен
|
||||
полный путь:
|
||||
|
||||
```sh
|
||||
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \
|
||||
--new-window \
|
||||
--extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" \
|
||||
"$PWD"
|
||||
```
|
||||
|
||||
Расширение в режиме `auto` использует `~/.pyenv/shims/python` при наличии
|
||||
local `.python-version`; для внешнего workspace ищет установленный
|
||||
`~/.pyenv/versions/3.12*/bin/python`, затем `.venv/bin/python` и известные
|
||||
абсолютные пути Python 3.12. Local-версия передаётся задаче сборки через
|
||||
`PYENV_VERSION`, даже если приложение лежит вне workspace. Это не зависит от `PATH` процесса VS Code,
|
||||
который мог быть открыт из Dock. Выбор можно переопределить абсолютным путём
|
||||
в `sprinterDebugger.pythonCommand`; дополнительные аргументы задаются через
|
||||
`sprinterDebugger.pythonArguments`.
|
||||
|
||||
## Автоматический launch
|
||||
|
||||
Добавьте в локальный `.vscode/launch.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "sprinter-mame",
|
||||
"request": "launch",
|
||||
"name": "Sprinter MAME: hello",
|
||||
"build": "${workspaceFolder}/tests/hello/.sprinter-cc-hello"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Launcher создаёт временные floppy/HDD/state-каталоги и видимое окно MAME.
|
||||
В отдельный `cfg/sprinter.cfg` он записывает только включение обеих клавиатур
|
||||
Sprinter: `:` и `:kbd:ms_naturl`. MAME по умолчанию активирует лишь первую,
|
||||
поэтому без этой настройки физические клавиши не доходят до DSS `getchar()`.
|
||||
Действующая пользовательская конфигурация MAME при этом не копируется.
|
||||
Он читает символы текстового экрана из VRAM, находит пустой prompt `X:…>` и
|
||||
требует, чтобы тот оставался готовым 0,25 секунды. Только затем ставится
|
||||
service-точка `main` и вводится `a:\\HELLO.EXE`. После совпадения PC и
|
||||
сигнатуры кода запускается session server, DAP принимает редакторские точки,
|
||||
а VS Code показывает остановку на entry. Завершение debug session останавливает
|
||||
только созданные ей MAME/server.
|
||||
|
||||
`dssTimeout` задаёт предельное время ожидания prompt (30 эмулируемых секунд).
|
||||
`launchAt` можно задать как необязательную нижнюю границу времени запуска;
|
||||
наличие prompt всё равно обязательно. Перед вводом сохраняется диагностический
|
||||
снимок DSS.
|
||||
|
||||
Дополнительные файлы на floppy задаются массивом `data`, путь к другому MAME —
|
||||
полем `mame`.
|
||||
|
||||
## Ручная проверка VS Code
|
||||
|
||||
В репозитории локально подготовлены два профиля `.vscode/launch.json`:
|
||||
`Sprinter: hello (VS Code)` и `Sprinter: hello (VS Code + MAME debugger)`.
|
||||
Файл `.vscode` намеренно игнорируется Git и не содержит личных путей.
|
||||
|
||||
1. Соберите `hello` командой из раздела «Подготовка программы» и запустите
|
||||
Extension Development Host одной из команд выше.
|
||||
2. Откройте `tests/hello/hello.c` и поставьте breakpoint на строке 31,
|
||||
вызове `puts`.
|
||||
3. В Run and Debug выберите `Sprinter: hello (VS Code)` и нажмите F5.
|
||||
Расширение сначала выполнит `make SRC_DEBUG=1` в `tests/hello` как задачу
|
||||
Sprinter Build. После успешной сборки должно появиться окно Sprinter;
|
||||
launcher дождётся prompt DSS, введёт
|
||||
`A:\\HELLO.EXE` и VS Code остановится в `main`.
|
||||
4. Проверьте Call Stack, scope Registers и Debug Console. После Continue
|
||||
должна сработать подтверждённая точка строки 31 по адресу `0x824b`.
|
||||
5. На остановке проверьте F11 и F10. Курсор должен переходить только после
|
||||
фактической остановки CPU, а не сразу после отправки команды. На строке 62
|
||||
(`getchar`) нажмите F10, щёлкните окно Sprinter MAME и нажмите латинскую `x`:
|
||||
выполнение должно перейти на строку 63. Пока программа ждёт клавишу,
|
||||
кнопка Pause в VS Code должна останавливать CPU.
|
||||
6. Завершите сессию кнопкой Stop. Затем повторите профиль с `osx`: вместе с
|
||||
тем же VS Code-сеансом должно открыться штатное Cocoa-окно debugger MAME.
|
||||
|
||||
Команда палитры `Sprinter: Build Active Project` собирает приложение по
|
||||
Makefile открытого C-файла. Задачи `Sprinter: Build ...` доступны и в
|
||||
`Tasks: Run Task`. Для launch автоматическая сборка включена по умолчанию,
|
||||
если из `build` можно найти Makefile с `app.mk`; нестандартный каталог задаётся
|
||||
полем `project`, а уже собранный пакет запускается с `"autoBuild": false`.
|
||||
Ошибка SDCC с файлом и строкой видна в Problems; ненулевой код задачи
|
||||
останавливает F5 до запуска MAME. Явный `preLaunchTask` использует стандартное
|
||||
поведение VS Code и отключает автоматическую сборку расширения.
|
||||
|
||||
Если F5 сообщает, что тип `sprinter-mame` неизвестен, окно запущено без
|
||||
`--extensionDevelopmentPath`. Сообщение `Couldn't find a debug adapter
|
||||
descriptor` означает, что extension manifest загружен, но расширение не
|
||||
активировалось; после изменения `package.json` выполните `Developer: Reload
|
||||
Window`. Канал Output → `Sprinter MAME Debug` показывает выбранные Python и
|
||||
DAP. При раннем завершении MAME ответ launch включает последние строки
|
||||
MAME log. Если сборочный пакет устарел, адаптер должен отказать до запуска
|
||||
программы и показать причину, а не использовать старую карту.
|
||||
|
||||
Логи из исходника без вывода в DSS задаются в C через `<sdbg.h>`;
|
||||
полный синтаксис и таблица поддержанных типов — в
|
||||
[руководстве по SDBG_LOG](sdbg-log-macros.md):
|
||||
|
||||
```c
|
||||
#include <sdbg.h>
|
||||
|
||||
volatile int total;
|
||||
|
||||
/* tag уникален в этом .c; total читает debugger, не приложение. */
|
||||
SDBG_LOG(after_increment, "total={total}");
|
||||
```
|
||||
|
||||
При F5 debug-сборка привяжет нулевой asm-якорь к адресу и session server
|
||||
поставит logpoint после загрузки DSS. Попадание напишет `total=...` в
|
||||
Debug Console VS Code и в debugger console MAME, затем продолжит выполнение.
|
||||
В обычной сборке макрос пуст. Поддерживаются только global/static переменные
|
||||
доказанного типа, литералы и `{name}`; локальные, выражения и `%d` пока нет.
|
||||
`SDBG_LOGIF(tag, flag, "...")` выводит сообщение, если поддержанная
|
||||
global/static `flag` ненулевая. Аргументы не исполняются на Sprinter, поэтому
|
||||
выражения с побочными эффектами не имеют здесь смысла. В режиме `sdbg`
|
||||
журнал MAME существует, но его штатное окно не открывается; для видимого
|
||||
окна выберите `osx` ниже. Горячий logpoint может заметно замедлить эмуляцию:
|
||||
CPU останавливается на каждом попадании. Если DAP не успевает забрать
|
||||
события из ограниченного журнала, Debug Console сообщает число пропусков.
|
||||
Текст в VS Code сохраняется точно; MAME console заменяет двойные кавычки
|
||||
апострофами и управляющие символы пробелами из-за синтаксиса `printf`.
|
||||
|
||||
## Родной debugger MAME вместе с VS Code
|
||||
|
||||
По умолчанию используется project backend `sdbg`: видимо окно Sprinter,
|
||||
а отдельное Cocoa-окно debugger не создаётся. Для регистров, дизассемблера,
|
||||
памяти и console штатного MAME добавьте в launch-конфигурацию:
|
||||
|
||||
```json
|
||||
"debugger": "osx"
|
||||
```
|
||||
|
||||
Bridge и DAP продолжают работать параллельно. Ручные `go`, изменение точек и
|
||||
reset в native console обходят модель состояния VS Code; reset пока нельзя
|
||||
использовать из-за lifecycle-crash MAME 0.287.
|
||||
|
||||
Для MAME без project patch выберите штатный provider:
|
||||
|
||||
| Платформа/сборка | `debugger` |
|
||||
|---|---|
|
||||
| macOS | `osx` |
|
||||
| Windows native | `windows` |
|
||||
| Linux с Qt debugger | `qt` |
|
||||
| Linux/SDL без Qt | `imgui` |
|
||||
| Автоматический выбор доступного provider | `auto` |
|
||||
|
||||
`imgui` использует основное графическое окно MAME; launcher уже запускает его
|
||||
с `-video soft -window`. `none` немедленно продолжает остановленный CPU, а
|
||||
`gdbstub` ждёт протокол GDB, поэтому они не подходят для sdbgbridge.
|
||||
|
||||
Текущая host-часть sdbg использует `fcntl` и Unix domain sockets. Она работает
|
||||
на macOS/Linux. Для native Windows нужны отдельные реализации owner lock,
|
||||
локального RPC и launcher; выбор `windows` решает только сторону MAME и не
|
||||
обеспечивает работу VS Code-интеграции.
|
||||
|
||||
Backend `sdbg` входит как воспроизводимый patch к MAME 0.287. Для локального
|
||||
checkout достаточно:
|
||||
|
||||
```sh
|
||||
make mame-sdbg
|
||||
```
|
||||
|
||||
Команда идемпотентно применяет
|
||||
`toolchain/mame-patches/0001-sdbg-debugger-backend.patch`, инкрементально
|
||||
собирает MAME и устанавливает `mame/v306/mame.arm`.
|
||||
|
||||
## Logpoints
|
||||
|
||||
Обычная команда VS Code **Add Logpoint…** работает без пересборки. В сообщении
|
||||
разрешены литералы и простые подстановки:
|
||||
|
||||
```text
|
||||
frame={frame_counter} state={state}
|
||||
```
|
||||
|
||||
Подстановка принимает только имя поддержанной переменной. Выражения,
|
||||
format specifier и conversion отклоняются. Если logpoint и обычная точка
|
||||
разрешились в один адрес, сообщение печатается, а CPU остаётся остановленным.
|
||||
|
||||
## Шаги по исходнику
|
||||
|
||||
F11 выполняет Z80-инструкции до следующей отличающейся C-позиции. F10 на
|
||||
каждом машинном шаге использует MAME `over`, поэтому банковский вызов проходит
|
||||
целиком. Shift+F11 сначала делает MAME `out`, затем проходит служебный bank
|
||||
trampoline до первой C-позиции вызывающей функции. Instruction granularity
|
||||
остаётся доступна для точного одиночного шага.
|
||||
|
||||
Автомат ограничен 512 машинными операциями. Команда шага отвечает VS Code
|
||||
сразу; пока `getchar()` или другой вызов ждёт внешнего ввода, MAME продолжает
|
||||
работать, а пользователь может нажать клавишу в его окне либо выполнить Pause
|
||||
в VS Code. Время ожидания ввода не ограничивается таймаутом исходного шага.
|
||||
При достижении предела CPU остаётся остановленным, а Debug Console получает
|
||||
сообщение.
|
||||
Пользовательская точка внутри шага имеет приоритет; logpoint печатается и шаг
|
||||
продолжается с прежней семантикой.
|
||||
|
||||
## Ручной attach
|
||||
|
||||
Для общего MAME между CLI и VS Code сначала запустите `sdbg_server.py`, затем:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "sprinter-mame",
|
||||
"request": "attach",
|
||||
"name": "Sprinter MAME: Attach",
|
||||
"socket": "/tmp/sprinter-sdbg.sock"
|
||||
}
|
||||
```
|
||||
|
||||
Команды server и диагностического CLI приведены в
|
||||
[mame-source-debug-status.md](mame-source-debug-status.md).
|
||||
|
||||
## Ограничения MVP
|
||||
|
||||
- Reset/restart отключён после найденного crash MAME 0.287 на старом Lua
|
||||
callback. Session завершается штатным terminate.
|
||||
- Локальные, backtrace, setVariable, watchpoints и чтение неотображённого
|
||||
bank-data ещё не реализованы.
|
||||
- Если одна инструкция имеет несколько C-маркеров, frame помечается
|
||||
`[ambiguous]`; адаптер не выбирает строку молча.
|
||||
Reference in New Issue
Block a user