На Chrome Dev Summit 2020 мы впервые продемонстрировали поддержку отладки Chrome для приложений WebAssembly в Интернете. С тех пор команда вложила много энергии в расширение возможностей разработчиков для больших и даже огромных приложений. В этом посте мы покажем вам ручки, которые мы добавили (или заставили работать) в различных инструментах и как их использовать!
Масштабируемая отладка
Давайте продолжим с того места, на котором мы остановились в нашем посте 2020 года. Вот пример, который мы тогда рассматривали:
#include <SDL2/SDL.h>
#include <complex>
int main() {
// Init SDL.
int width = 600, height = 600;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window;
SDL_Renderer* renderer;
SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
&renderer);
// Generate a palette with random colors.
enum { MAX_ITER_COUNT = 256 };
SDL_Color palette[MAX_ITER_COUNT];
srand(time(0));
for (int i = 0; i < MAX_ITER_COUNT; ++i) {
palette[i] = {
.r = (uint8_t)rand(),
.g = (uint8_t)rand(),
.b = (uint8_t)rand(),
.a = 255,
};
}
// Calculate and draw the Mandelbrot set.
std::complex<double> center(0.5, 0.5);
double scale = 4.0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::complex<double> point((double)x / width, (double)y / height);
std::complex<double> c = (point - center) * scale;
std::complex<double> z(0, 0);
int i = 0;
for (; i < MAX_ITER_COUNT - 1; i++) {
z = z * z + c;
if (abs(z) > 2.0)
break;
}
SDL_Color color = palette[i];
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderDrawPoint(renderer, x, y);
}
}
// Render everything we've drawn to the canvas.
SDL_RenderPresent(renderer);
// SDL_Quit();
}
Это все еще довольно небольшой пример, и вы, скорее всего, не увидите никаких реальных проблем, которые можно увидеть в действительно большом приложении, но мы все равно можем показать вам, каковы новые функции. Это быстро и легко настроить и попробовать самому!
В последнем посте мы обсуждали, как скомпилировать и отладить этот пример. Давайте сделаем это еще раз, но давайте также взглянем на //производительность// :
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH
Эта команда создает двоичный файл Wasm размером 3 МБ. И основная часть этой информации, как и следовало ожидать, — это отладочная информация. Вы можете проверить это с помощью инструмента llvm-objdump
[1], например:
$ llvm-objdump -h mandelbrot.wasm
mandelbrot.wasm: file format wasm
Sections:
Idx Name Size VMA Type
0 TYPE 0000026f 00000000
1 IMPORT 00001f03 00000000
2 FUNCTION 0000043e 00000000
3 TABLE 00000007 00000000
4 MEMORY 00000007 00000000
5 GLOBAL 00000021 00000000
6 EXPORT 0000014a 00000000
7 ELEM 00000457 00000000
8 CODE 0009308a 00000000 TEXT
9 DATA 0000e4cc 00000000 DATA
10 name 00007e58 00000000
11 .debug_info 000bb1c9 00000000
12 .debug_loc 0009b407 00000000
13 .debug_ranges 0000ad90 00000000
14 .debug_abbrev 000136e8 00000000
15 .debug_line 000bb3ab 00000000
16 .debug_str 000209bd 00000000
Этот вывод показывает нам все разделы, которые есть в сгенерированном файле Wasm, большинство из них являются стандартными разделами WebAssembly, но есть также несколько пользовательских разделов, имена которых начинаются с .debug_
. Вот где двоичный файл содержит нашу отладочную информацию! Если мы сложим все размеры, мы увидим, что отладочная информация составляет примерно 2,3 МБ из нашего файла размером 3 МБ. Если мы также time
команды emcc
, мы увидим, что на нашей машине ее выполнение заняло примерно 1,5 секунды. Эти цифры представляют собой небольшой базовый показатель, но они настолько малы, что, вероятно, никто на них не обратит внимания. Однако в реальных приложениях двоичный файл отладки может легко достигать размера в ГБ, а его сборка может занять несколько минут!
Пропуск Бинариена
При создании приложения Wasm с помощью Emscripten одним из последних этапов сборки является запуск оптимизатора Binaryen . Binaryen — это набор инструментов компилятора, который оптимизирует и легализует двоичные файлы, подобные WebAssembly. Использование Binaryen в составе сборки обходится довольно дорого, но требуется только при определенных условиях. Для отладочных сборок мы можем значительно ускорить время сборки, если избежим необходимости в проходах Binaryen. Наиболее распространенный проход Binaryen предназначен для легализации сигнатур функций, включающих 64-битные целочисленные значения. Выбрав интеграцию WebAssembly BigInt с использованием -sWASM_BIGINT
мы можем избежать этого.
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
На всякий случай мы добавили флаг -sERROR_ON_WASM_CHANGES_AFTER_LINK
. Это помогает обнаружить, когда Binaryen запущен, и неожиданно перезаписать двоичный файл. Таким образом, мы можем быть уверены, что остаемся на быстром пути.
Несмотря на то, что наш пример довольно небольшой, мы все равно можем увидеть эффект от пропуска Binaryen! По time
эта команда выполняется чуть меньше 1 с, то есть на полсекунды быстрее, чем раньше!
Расширенные настройки
Пропуск сканирования входного файла
Обычно при связывании проекта Emscripten emcc
сканирует все входные объектные файлы и библиотеки. Это делается для того, чтобы реализовать точные зависимости между функциями библиотеки JavaScript и собственными символами в вашей программе. Для более крупных проектов дополнительное сканирование входных файлов (с использованием llvm-nm
) может значительно увеличить время компоновки.
Вместо этого можно запустить параметр -sREVERSE_DEPS=all
, который указывает emcc
включить все возможные собственные зависимости функций JavaScript. Это требует небольшого размера кода, но может ускорить время компоновки и может быть полезно для отладочных сборок.
Для такого маленького проекта, как наш пример, это не имеет особого значения, но если в вашем проекте сотни или даже тысячи объектных файлов, это может значительно улучшить время компоновки.
Удаление раздела «имя»
В крупных проектах, особенно в тех, в которых часто используются шаблоны C++, раздел «имя» WebAssembly может быть очень большим. В нашем примере это лишь небольшая часть общего размера файла (см. вывод llvm-objdump
выше), но в некоторых случаях он может быть очень значительным. Если раздел «имя» вашего приложения очень велик и отладочной информации достаточно для ваших нужд отладки, может быть полезно удалить раздел «имя»:
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
Это удалит раздел «имя» WebAssembly, сохранив при этом разделы отладки DWARF.
Отладка деления
Двоичные файлы с большим количеством отладочных данных увеличивают не только время сборки, но и время отладки. Отладчику необходимо загрузить данные и построить для них индекс, чтобы он мог быстро отвечать на запросы, например «Какой тип локальной переменной x?».
Разделение отладки позволяет нам разделить отладочную информацию для двоичного файла на две части: одна, которая остается в двоичном файле, и одна, которая содержится в отдельном, так называемом объектном файле DWARF ( .dwo
). Его можно включить, передав флаг -gsplit-dwarf
в Emscripten:
$ emcc -sUSE_SDL=2 -g -gsplit-dwarf -gdwarf-5 -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Ниже мы показываем различные команды и файлы, генерируемые при компиляции без отладочных данных, с отладочными данными и, наконец, как с отладочными данными, так и с отладочным разделением.
При разделении данных DWARF часть отладочных данных сохраняется вместе с двоичными файлами, тогда как большая часть помещается в файл mandelbrot.dwo
(как показано выше).
Для mandelbrot
у нас есть только один исходный файл, но обычно проекты больше этого и включают более одного файла. Отладка деления генерирует файл .dwo
для каждого из них. Чтобы текущая бета-версия отладчика (0.1.6.1615) могла загружать эту разделенную отладочную информацию, нам нужно объединить все это в так называемый пакет DWARF ( .dwp
), например:
$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp
Преимущество сборки пакета DWARF из отдельных объектов заключается в том, что вам нужно обслуживать только один дополнительный файл! В настоящее время мы работаем над загрузкой всех отдельных объектов в будущем выпуске.
Что с DWARF 5?
Возможно, вы заметили, что в приведенную выше команду emcc
мы добавили еще один флаг -gdwarf-5
. Включение версии 5 символов DWARF, которая в настоящее время не используется по умолчанию, — это еще один трюк, который поможет нам быстрее начать отладку. При этом в основном двоичном файле сохраняется определенная информация, которая в версии 4 по умолчанию не учитывается. В частности, мы можем определить полный набор исходных файлов только из основного двоичного файла. Это позволяет отладчику выполнять базовые действия, такие как отображение полного дерева исходного кода и установка точек останова, без загрузки и анализа полных данных символов. Это значительно ускоряет отладку с помощью символов разделения, поэтому мы всегда используем флаги командной строки -gsplit-dwarf
и -gdwarf-5
вместе!
Благодаря формату отладки DWARF5 мы также получаем доступ к еще одной полезной функции. Он вводит индекс имени в отладочные данные, который будет сгенерирован при передаче флага -gpubnames
:
$ emcc -sUSE_SDL=2 -g -gdwarf-5 -gsplit-dwarf -gpubnames -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Во время сеанса отладки поиск символов часто происходит путем поиска объекта по имени, например, при поиске переменной или типа. Индекс имени ускоряет этот поиск, указывая непосредственно на единицу компиляции, которая определяет это имя. Без индекса имени потребовался бы исчерпывающий поиск по всем отладочным данным, чтобы найти правильную единицу компиляции, определяющую искомую именованную сущность.
Для любопытных: просмотр отладочных данных
Вы можете использовать llvm-dwarfdump
, чтобы просмотреть данные DWARF. Давайте попробуем:
llvm-dwarfdump mandelbrot.wasm
Это дает нам обзор «модулей компиляции» (грубо говоря, исходных файлов), для которых у нас есть отладочная информация. В этом примере у нас есть только отладочная информация для mandelbrot.cc
. Общая информация сообщит нам, что у нас есть скелет модуля , что просто означает, что у нас есть неполные данные в этом файле, и что существует отдельный файл .dwo
, который содержит оставшуюся отладочную информацию:
Вы также можете просмотреть другие таблицы в этом файле, например, таблицу строк, которая показывает сопоставление байт-кода Wasm со строками C++ (попробуйте использовать llvm-dwarfdump -debug-line
).
Мы также можем просмотреть отладочную информацию, содержащуюся в отдельном файле .dwo
:
llvm-dwarfdump mandelbrot.dwo
TL;DR: В чем преимущество использования отладочного деления?
Разделение отладочной информации дает несколько преимуществ при работе с большими приложениями:
Более быстрое связывание: компоновщику больше не нужно анализировать всю отладочную информацию . Линкерам обычно необходимо проанализировать все данные DWARF, находящиеся в двоичном формате. Выделяя большие части отладочной информации в отдельные файлы, компоновщики работают с меньшими двоичными файлами, что приводит к сокращению времени компоновки (особенно актуально для больших приложений).
Ускоренная отладка. Отладчик может пропустить анализ дополнительных символов в файлах
.dwo
/.dwp
для поиска некоторых символов . Для некоторых поисков (например, запросов на сопоставление строк файлов wasm-to-C++) нам не нужно просматривать дополнительные отладочные данные. Это экономит нам время, поскольку нет необходимости загружать и анализировать дополнительные данные отладки.
1 : Если в вашей системе нет последней версии llvm-objdump
и вы используете emsdk
, вы можете найти ее в каталоге emsdk/upstream/bin
.
Загрузите предварительный просмотр каналов
Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра предоставляют вам доступ к новейшим функциям DevTools, позволяют тестировать передовые API-интерфейсы веб-платформы и помогают находить проблемы на вашем сайте раньше, чем это сделают ваши пользователи!
Свяжитесь с командой Chrome DevTools
Используйте следующие параметры, чтобы обсудить новые функции, обновления или что-либо еще, связанное с DevTools.
- Отправляйте нам отзывы и запросы на добавление новых функций на crbug.com .
- Сообщите о проблеме DevTools, используя Дополнительные параметры > Справка > Сообщить о проблеме DevTools в DevTools.
- Напишите в Твиттере @ChromeDevTools .
- Оставляйте комментарии к видеороликам YouTube «Что нового в DevTools» или «Советы разработчика» на YouTube .