Handle MAME SIGTERM during source debugging
This commit is contained in:
@@ -222,6 +222,8 @@ pyenv exec python tests/sdbg/run_mame_probe.py --banked --launcher --source-step
|
|||||||
pyenv exec python tests/sdbg/run_mame_probe.py --banked --launcher --step-out
|
pyenv exec python tests/sdbg/run_mame_probe.py --banked --launcher --step-out
|
||||||
pyenv exec python tests/sdbg/run_vscode_dap_probe.py
|
pyenv exec python tests/sdbg/run_vscode_dap_probe.py
|
||||||
pyenv exec python tests/sdbg/run_vscode_dap_probe.py --exit-while-stopped
|
pyenv exec python tests/sdbg/run_vscode_dap_probe.py --exit-while-stopped
|
||||||
|
pyenv exec python tests/sdbg/run_vscode_dap_probe.py --stop-while-stopped
|
||||||
|
pyenv exec python tests/sdbg/run_vscode_dap_probe.py --term-while-stopped
|
||||||
pyenv exec python tests/sdbg/run_vscode_dap_probe.py --waitkey --manual-key
|
pyenv exec python tests/sdbg/run_vscode_dap_probe.py --waitkey --manual-key
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -273,9 +275,16 @@ VS Code запустил сборку SprPoP и остановился в `src/s
|
|||||||
при остановке в `hello/main` подтверждает DAP `terminated` после внезапной
|
при остановке в `hello/main` подтверждает DAP `terminated` после внезапной
|
||||||
потери процесса. Штатный DAP launch в `hello/main` также повторно прошёл
|
потери процесса. Штатный DAP launch в `hello/main` также повторно прошёл
|
||||||
с `sdbg` и опциональным `osx` debugger provider.
|
с `sdbg` и опциональным `osx` debugger provider.
|
||||||
Отдельная проба `SIGTERM` остановленному MAME не дала DAP `terminated` за
|
Первоначально прямой `SIGTERM` остановленному MAME не давал DAP `terminated`:
|
||||||
15 секунд; причина (обработка сигнала самим MAME или путь закрытия сессии)
|
через 12 секунд процесс оставался живым. Причина: SDL3 по умолчанию
|
||||||
ещё не установлена. Это отдельный OS-signal путь, не равный закрытию окна.
|
преобразует `SIGTERM` в `SDL_EVENT_QUIT`, а SDL3 OSD MAME это событие
|
||||||
|
не обрабатывает. Launcher теперь выставляет `SDL_NO_SIGNAL_HANDLERS=1`
|
||||||
|
только для своего MAME, если переменная не задана пользователем. Повторный
|
||||||
|
живой прогон подтвердил `SIGTERM` → DAP `terminated`; DAP `disconnect`
|
||||||
|
(VS Code Stop) завершил MAME и адаптер с provider `sdbg` и `osx`.
|
||||||
|
Отдельный патч MAME для этого
|
||||||
|
не требуется. Закрытие окна по-прежнему проверено только через тот же
|
||||||
|
`schedule_exit()` в исходнике MAME, без автоматического UI-клика.
|
||||||
|
|
||||||
Живой lifecycle-пробник на банковой программе теперь прошёл с `sdbg` и
|
Живой lifecycle-пробник на банковой программе теперь прошёл с `sdbg` и
|
||||||
опциональным `osx` provider. На остановке в `main` Lua вызвал
|
опциональным `osx` provider. На остановке в `main` Lua вызвал
|
||||||
@@ -344,8 +353,8 @@ Crash reports в 19:22/19:23 относились к неподдержанно
|
|||||||
строит проверенное условие через I/O port. После штатных прогонов в 20:43 и
|
строит проверенное условие через I/O port. После штатных прогонов в 20:43 и
|
||||||
позже новых `mame.arm-*.ips` не появилось.
|
позже новых `mame.arm-*.ips` не появилось.
|
||||||
|
|
||||||
Далее по плану: исследовать отдельно OS-signal путь `SIGTERM`, сделать
|
Далее по плану: сделать MCP-адаптер, безопасный restart и расширенные
|
||||||
MCP-адаптер, безопасный restart и расширенные выражения. VSIX уже упакован;
|
выражения. VSIX уже упакован;
|
||||||
для IDE ещё нужны Run-команда, выбор профиля
|
для IDE ещё нужны Run-команда, выбор профиля
|
||||||
сборки/данных и расширенная диагностика assembler/linker.
|
сборки/данных и расширенная диагностика assembler/linker.
|
||||||
Базовый attach уже проверяет принадлежность resident/current-bank к build,
|
Базовый attach уже проверяет принадлежность resident/current-bank к build,
|
||||||
|
|||||||
@@ -684,9 +684,13 @@ exit/reset/reload инвалидируют состояние; потеря кл
|
|||||||
Lua `machine:exit()` и реальный save/load state при остановке в `main`:
|
Lua `machine:exit()` и реальный save/load state при остановке в `main`:
|
||||||
оба завершают DAP, post-load удаляет старые точки. SDL3-событие закрытия
|
оба завершают DAP, post-load удаляет старые точки. SDL3-событие закрытия
|
||||||
окна использует тот же `schedule_exit()`; прямой UI-клик не автоматизировался.
|
окна использует тот же `schedule_exit()`; прямой UI-клик не автоматизировался.
|
||||||
Reset по-прежнему требует безопасного отдельного репро. `SIGTERM`
|
Reset по-прежнему требует безопасного отдельного репро. Для запускаемого
|
||||||
остановленному процессу MAME пока не дал DAP `terminated` за 15 секунд;
|
через toolkit MAME launcher выставляет `SDL_NO_SIGNAL_HANDLERS=1`, если
|
||||||
это отдельный OS-signal путь, не проверка штатного выхода.
|
переменная не задана пользователем: SDL3 иначе превращает `SIGTERM` в
|
||||||
|
`SDL_EVENT_QUIT`, который MAME SDL3 OSD игнорирует. Живые пробы подтвердили
|
||||||
|
прямой `SIGTERM` → DAP `terminated` и DAP `disconnect` → завершение MAME и
|
||||||
|
адаптера при остановке в `main`. Изменение действует только на MAME,
|
||||||
|
запущенный launcher; системный MAME без этого окружения ведёт себя по-прежнему.
|
||||||
|
|
||||||
### Этап 3 — полезная CLI/MCP-отладка и логи
|
### Этап 3 — полезная CLI/MCP-отладка и логи
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,12 @@ Launcher монтирует временную копию CHD как `-hard2`,
|
|||||||
6. Завершите сессию кнопкой Stop. Затем повторите профиль с `osx`: вместе с
|
6. Завершите сессию кнопкой Stop. Затем повторите профиль с `osx`: вместе с
|
||||||
тем же VS Code-сеансом должно открыться штатное Cocoa-окно debugger MAME.
|
тем же VS Code-сеансом должно открыться штатное Cocoa-окно debugger MAME.
|
||||||
|
|
||||||
|
Stop отправляет DAP `disconnect`; launcher завершает запущенный им MAME.
|
||||||
|
Чтобы SDL3 не перехватывал `SIGTERM` в необрабатываемое MAME событие Quit,
|
||||||
|
launcher задаёт этому процессу `SDL_NO_SIGNAL_HANDLERS=1`, если переменная
|
||||||
|
не была задана вручную. Прямой `SIGTERM` и Stop проверены живыми пробниками
|
||||||
|
на остановке в `main`; они не требуют отдельного патча MAME.
|
||||||
|
|
||||||
Команда палитры `Sprinter: Build Active Project` собирает приложение по
|
Команда палитры `Sprinter: Build Active Project` собирает приложение по
|
||||||
Makefile открытого C-файла. Задачи `Sprinter: Build ...` доступны и в
|
Makefile открытого C-файла. Задачи `Sprinter: Build ...` доступны и в
|
||||||
`Tasks: Run Task`. Для launch автоматическая сборка включена по умолчанию,
|
`Tasks: Run Task`. Для launch автоматическая сборка включена по умолчанию,
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ def main():
|
|||||||
parser.add_argument('--launch-path', default=None)
|
parser.add_argument('--launch-path', default=None)
|
||||||
parser.add_argument('--exit-while-stopped', action='store_true',
|
parser.add_argument('--exit-while-stopped', action='store_true',
|
||||||
help='аварийно завершить собственный MAME на main и ждать DAP terminated')
|
help='аварийно завершить собственный MAME на main и ждать DAP terminated')
|
||||||
|
parser.add_argument('--stop-while-stopped', action='store_true',
|
||||||
|
help='послать DAP disconnect на main и проверить завершение MAME')
|
||||||
|
parser.add_argument('--term-while-stopped', action='store_true',
|
||||||
|
help='послать SIGTERM собственному MAME на main и проверить DAP terminated')
|
||||||
options, _ = parser.parse_known_args()
|
options, _ = parser.parse_known_args()
|
||||||
if not PYTHON.is_file():
|
if not PYTHON.is_file():
|
||||||
raise RuntimeError('Нет local pyenv shim: '+str(PYTHON))
|
raise RuntimeError('Нет local pyenv shim: '+str(PYTHON))
|
||||||
@@ -124,6 +128,50 @@ def main():
|
|||||||
frame = frame_response['body']['stackFrames'][0]
|
frame = frame_response['body']['stackFrames'][0]
|
||||||
if frame['name'] != 'main':
|
if frame['name'] != 'main':
|
||||||
raise RuntimeError('Не main: '+str(frame))
|
raise RuntimeError('Не main: '+str(frame))
|
||||||
|
if options.stop_while_stopped:
|
||||||
|
mame_pid = launched['body']['mamePid']
|
||||||
|
send(process, 5, 'disconnect')
|
||||||
|
_, buffer, _ = wait_response(process, buffer, 'disconnect', 10)
|
||||||
|
deadline = time.monotonic()+12
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
try:
|
||||||
|
os.kill(mame_pid, 0)
|
||||||
|
except ProcessLookupError:
|
||||||
|
break
|
||||||
|
time.sleep(.1)
|
||||||
|
else:
|
||||||
|
raise RuntimeError('После DAP disconnect MAME остался запущен')
|
||||||
|
code = process.wait(timeout=3)
|
||||||
|
if code != 0:
|
||||||
|
raise RuntimeError(f'После DAP disconnect адаптер завершился с rc={code}')
|
||||||
|
print(json.dumps({'event': 'vscode_stop_closes_mame', 'frame': frame},
|
||||||
|
ensure_ascii=False), flush=True)
|
||||||
|
return 0
|
||||||
|
if options.term_while_stopped:
|
||||||
|
mame_pid = launched['body']['mamePid']
|
||||||
|
os.kill(mame_pid, signal.SIGTERM)
|
||||||
|
deadline = time.monotonic()+12
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
try:
|
||||||
|
message, buffer = receive(process, buffer, deadline-time.monotonic())
|
||||||
|
except TimeoutError:
|
||||||
|
break
|
||||||
|
if message.get('event') == 'terminated':
|
||||||
|
print(json.dumps({'event': 'sigterm_terminates_dap', 'frame': frame},
|
||||||
|
ensure_ascii=False), flush=True)
|
||||||
|
send(process, 5, 'disconnect')
|
||||||
|
wait_response(process, buffer, 'disconnect', 10)
|
||||||
|
return 0
|
||||||
|
try:
|
||||||
|
os.kill(mame_pid, 0)
|
||||||
|
status = 'alive'
|
||||||
|
except ProcessLookupError:
|
||||||
|
status = 'exited'
|
||||||
|
process_state = subprocess.run(
|
||||||
|
['ps', '-p', str(mame_pid), '-o', 'stat=,ppid=,comm='],
|
||||||
|
capture_output=True, text=True, check=False).stdout.strip()
|
||||||
|
raise RuntimeError('SIGTERM: DAP terminated не получен за 12 с; MAME '+
|
||||||
|
status+'; ps='+repr(process_state))
|
||||||
if options.exit_while_stopped:
|
if options.exit_while_stopped:
|
||||||
os.kill(launched['body']['mamePid'], signal.SIGKILL)
|
os.kill(launched['body']['mamePid'], signal.SIGKILL)
|
||||||
deadline = time.monotonic()+15
|
deadline = time.monotonic()+15
|
||||||
|
|||||||
@@ -222,6 +222,10 @@ def main():
|
|||||||
args.dss_timeout)
|
args.dss_timeout)
|
||||||
session_id=uuid.uuid4().hex
|
session_id=uuid.uuid4().hex
|
||||||
environment=dict(os.environ,SDBG_IPC_DIR=str(ipc),SDBG_SESSION_ID=session_id)
|
environment=dict(os.environ,SDBG_IPC_DIR=str(ipc),SDBG_SESSION_ID=session_id)
|
||||||
|
# SDL3 превращает SIGTERM в SDL_EVENT_QUIT, который MAME SDL3 OSD
|
||||||
|
# не обрабатывает. Оставляем стандартное действие сигнала, чтобы
|
||||||
|
# DAP Stop не ждал принудительного kill остановленного MAME.
|
||||||
|
environment.setdefault('SDL_NO_SIGNAL_HANDLERS','1')
|
||||||
plugin_paths=[str(ROOT/'toolchain/mcp')]
|
plugin_paths=[str(ROOT/'toolchain/mcp')]
|
||||||
installed_plugins=profile.home.parent/'plugins' if profile.home else None
|
installed_plugins=profile.home.parent/'plugins' if profile.home else None
|
||||||
if installed_plugins and installed_plugins.is_dir():
|
if installed_plugins and installed_plugins.is_dir():
|
||||||
|
|||||||
Reference in New Issue
Block a user