Модель DAP¶
Как представлен и как ведётся Debug Adapter Protocol. Крейта два:
dap-protocol для типов и dap-client для самого разговора.
Bugsaur ориентируется на DAP 1.71.
dap-protocol: типы генерируются¶
Типы протокола генерируются из схемы DAP генератором
crates/dap-protocol/generator, а не пишутся руками.
Это осознанный размен. Протокол большой и меняется на той стороне; копия, поддерживаемая вручную, расходится с ним, а расхождение проявляется как поле, которое молча никогда не приходит. С генерируемыми типами изменение схемы становится ошибкой компиляции.
Round-trip-тесты (crates/dap-protocol/tests/roundtrip.rs) проверяют, что
сериализация и разбор сообщения дают то же самое значение, — а это именно то
свойство, которое важно, когда на другой стороне программа, написанная не вами.
dap-client: два транспорта¶
| Транспорт | Кто использует | Как работает |
|---|---|---|
| stdio | debugpy, php |
Bugsaur запускает адаптер и говорит через его stdin / stdout |
| TCP | dlv, codelldb |
Bugsaur выбирает свободный порт, передаёт его через tcp_argument и подключается |
Кадрирование у обоих одинаковое: заголовки Content-Length, за ними JSON-тело.
Реализовано в codec.rs и протестировано на битом и разрезанном вводе.
Разрезанный ввод важнее, чем кажется: граница чтения TCP может прийтись куда угодно, в том числе на середину заголовка, а кодек, считающий одно чтение одним сообщением, ломается только под нагрузкой.
Управление запросами¶
request.rs и менеджер запросов сопоставляют ответы с породившими их запросами
по порядковым номерам протокола.
Три вещи, которые он обязан делать правильно.
Опоздавшие ответы. Ответ может прийти, когда состояния, к которому он относится, уже нет. Актуальность решает поколение остановки, и неактуальный ответ отбрасывается.
Долгие запросы. Стартовый handshake — launch / attach и
configurationDone — ждут 120 секунд по умолчанию. Часть адаптеров компилирует
внутри launch, а часть отвечает на configurationDone только после запуска
отлаживаемого процесса. Каждые 30 секунд ожидание попадает в лог вместе с pid
процесса, который держит ответ: молчащий отладчик неотличим от зависшего.
Смерть адаптера. Адаптер, вышедший посреди разговора, обязан аккуратно уронить сессию, а не оставить запросы висеть навсегда.
Чего Bugsaur не делает¶
Не интерпретирует тела запросов. launch_arguments из профиля уходит
адаптеру как есть, перебивая сгенерированное ключ за ключом. Ни project-config,
ни лаунчер не знают, что эти ключи означают: это знание принадлежит адаптеру.
Не заводит частных случаев под адаптеры в общем коде. Там, где адаптеру нужно особое обращение, оно живёт в каталоге адаптеров как данные. Это намеренный контраст с реализациями, где, например, разэкранирование питоновских строк лежит прямо в общем пути переменных.
Не логирует тела запросов. Значения env в лог не попадают никогда. Это
инвариант: трассировку запросов нельзя расширять, не вычистив их предварительно,
— поэтому в сообщении о долгом запросе есть только mode и program.
Тесты без адаптера¶
fake-dap-adapter — управляемый сценарием адаптер для тестов: он отвечает
заранее заготовленными ответами, поэтому обработку протокола, редукцию состояния
и интерфейс можно тестировать без установленных языковых тулчейнов и без сети.
Настоящие адаптеры покрыты ignored-тестами, которые запускаются явно:
Связанные решения¶
| ADR | Тема |
|---|---|
| ADR-001 | слой протокола и генерируемые типы |
| ADR-002 | транспорты |
| ADR-003 | модель состояния и устаревшие ответы |
| ADR-006 | дерево переменных: ленивость, пагинация, кэш |
| ADR-008 | транспорт PHP DBGp |