Отладка Wasm
Учимся находить и чинить ошибки в Wasm-коде.
Отладка Wasm опирается на отладочную информацию (DWARF/source maps), которая связывает байткод с исходным кодом на C/Rust.
Проблема: байткод нечитаем
Когда модуль падает, в стеке вы видите номера функций Wasm, а не строки вашего C или Rust. Чтобы отладка была осмысленной, компилятор может вложить отладочную информацию — карту, связывающую каждую Wasm-инструкцию с местом в исходнике. Браузерные DevTools умеют читать эту карту и показывать ваш исходный C/Rust прямо в отладчике, с точками останова и просмотром переменных.
Отладка в браузере
Современные Chrome DevTools поддерживают формат DWARF — тот же, что используют нативные отладчики. Скомпилировав с флагом отладки, вы получаете возможность ставить брейкпоинты в Rust/C-коде, который реально исполняется как Wasm:
# Emscripten: вложить DWARF-отладку
emcc app.c -g -o app.js
# Rust: собрать в debug-профиле (отладка по умолчанию)
wasm-pack build --dev
Логирование через импорт
Самый простой и универсальный способ — старый добрый «вывод в лог». Поскольку у Wasm нет своего console.log, его импортируют из JS. Тогда из кода на C/Rust можно «печатать» значения для отладки. Смоделируем такое логирование на JS, где функция log играет роль импорта:
// log — импортированная из JS функция логирования для Wasm
function log(value) { console.log("[wasm]", value); }
function process(n) {
log(n); // отладочный вывод "входа"
const result = n * n;
log(result); // отладочный вывод "результата"
return result;
}
process(7);
Вывод:
[wasm] 7 [wasm] 49
Ловушки (traps)
Wasm не «падает молча». При ошибке — выход за границу памяти, деление на ноль, переполнение таблицы, несовпадение сигнатуры в call_indirect — исполнение прерывается ловушкой (trap). В JS это превращается в исключение RuntimeError с сообщением вроде «unreachable» или «out of bounds memory access». Ловушка — ваш друг: она точно говорит, что нарушен инвариант, вместо тихой порчи данных.
| Ловушка | Причина |
| out of bounds | чтение/запись за пределы линейной памяти |
| integer divide by zero | деление целого на ноль |
| indirect call type mismatch | сигнатура в call_indirect не совпала |
| unreachable | достигнута инструкция unreachable (часто паника/assert) |
Как работает под капотом
Отладочная карта (DWARF) не входит в исполнение — это отдельная секция модуля, которую читает отладчик. Поэтому для продакшена её вырезают (wasm-strip), чтобы уменьшить размер, а для отладки оставляют. Ловушки же встроены в саму проверку каждой опасной операции: например, перед каждым обращением к памяти движок проверяет границу, и если нарушена — генерирует trap. Это часть гарантий безопасности Wasm, не опциональная добавка.
Частые ошибки
- Отлаживать release-сборку — без флага отладки нет карты, в стеке только номера функций.
- Игнорировать текст trap — «out of bounds» прямо указывает на выход за память.
- Тащить DWARF в прод — это раздувает модуль; для релиза отладку вырезают.
Итоги
- Отладка опирается на DWARF/source maps — карту байткод ↔ исходник.
- Chrome DevTools умеют ставить брейкпоинты в C/Rust, исполняемом как Wasm.
- Универсальный приём — логирование через импортированную из JS функцию.
- Ошибки превращаются в ловушки (trap) →
RuntimeErrorс понятной причиной.