Отладка WebAssembly современными инструментами

Ингвар Степанян
Ingvar Stepanyan

Дорога до сих пор

Год назад Chrome объявил о начальной поддержке встроенной отладки WebAssembly в Chrome DevTools.

Мы продемонстрировали базовую поддержку степпинга и рассказали о возможностях использования информации DWARF вместо исходных карт, которые откроются для нас в будущем:

  • Разрешение имен переменных
  • Симпатичные типы печати
  • Оценка выражений на исходных языках
  • …и многое другое!

Сегодня мы рады продемонстрировать реализацию обещанных функций и прогресс, достигнутый командами Emscripten и Chrome DevTools за этот год, в частности, в приложениях C и C++.

Прежде чем мы начнем, имейте в виду, что это все еще бета-версия нового интерфейса, вам необходимо использовать последнюю версию всех инструментов на свой страх и риск, и если у вас возникнут какие-либо проблемы, сообщите о них по адресу https:/ /issues.chromium.org/issues/new?noWizard=true&template=0&comComponent=1456350 .

Начнем с того же простого примера на C, что и в прошлый раз:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

Чтобы скомпилировать его, мы используем последнюю версию Emscripten и передаем флаг -g , как и в исходном сообщении, для включения отладочной информации:

emcc -g temp.c -o temp.html

Теперь мы можем обслуживать сгенерированную страницу с локального HTTP-сервера (например, с помощью submit ) и открывать ее в последней версии Chrome Canary .

На этот раз нам также понадобится вспомогательное расширение, которое интегрируется с Chrome DevTools и помогает ему разобраться во всей отладочной информации, закодированной в файле WebAssembly. Установите его, перейдя по этой ссылке: goo.gle/wasm-debugging-extension .

Вам также понадобится включить отладку WebAssembly в DevTools Experiments . Откройте Chrome DevTools, щелкните значок шестеренки ( ) в правом верхнем углу панели DevTools, перейдите на панель «Эксперименты» и установите флажок «Отладка WebAssembly: Включить поддержку DWARF» .

Панель «Эксперименты» в настройках DevTools

Когда вы закроете «Настройки» , DevTools предложит перезагрузиться, чтобы применить настройки, так что давайте сделаем именно это. Вот и все, что касается одноразовой установки.

Теперь мы можем вернуться на панель «Источники» , включить «Паузу при исключениях» (значок ⏸), затем установить флажок «Приостановить при обнаружении исключений» и перезагрузить страницу. Вы должны увидеть, что DevTools приостановлены из-за исключения:

Снимок экрана панели «Источники», показывающий, как включить «Паузу при обнаружении исключений».

По умолчанию он останавливается на связующем коде, сгенерированном Emscripten, но справа вы можете увидеть представление стека вызовов , представляющее трассировку стека ошибки, и можете перейти к исходной строке C, которая вызвала abort :

DevTools приостановил работу функции Assert_less и отобразил значения x и y в представлении области действия.

Теперь, если вы посмотрите в представлении «Область» , вы сможете увидеть исходные имена и значения переменных в коде C/C++, и вам больше не придется выяснять, что означают искаженные имена, такие как $localN и как они связаны с исходным кодом, который вы используете. написал.

Это относится не только к примитивным значениям, таким как целые числа, но и к составным типам, таким как структуры, классы, массивы и т. д.!

Богатая поддержка типов

Давайте рассмотрим более сложный пример, чтобы продемонстрировать это. На этот раз мы нарисуем фрактал Мандельброта с помощью следующего кода C++:

#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();
}

Вы можете видеть, что это приложение все еще довольно маленькое — это один файл, содержащий 50 строк кода, — но на этот раз я также использую некоторые внешние API, такие как библиотека SDL для графики, а также комплексные числа из стандартной библиотеки C++.

Я собираюсь скомпилировать его с тем же флагом -g , что и выше, чтобы включить отладочную информацию, а также попрошу Emscripten предоставить библиотеку SDL2 и разрешить использование памяти произвольного размера:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

Когда я посещаю сгенерированную страницу в браузере, я вижу красивую фрактальную форму со случайными цветами:

Демо-страница

Когда я снова открываю DevTools, я вижу исходный файл C++. Однако на этот раз в коде нет ошибки (ух!), поэтому давайте вместо этого установим точку останова в начале нашего кода.

Когда мы снова перезагрузим страницу, отладчик приостановит работу прямо внутри нашего исходного кода C++:

DevTools приостановил вызов SDL_Init.

Мы уже видим все наши переменные справа, но на данный момент инициализированы только width и height , поэтому проверять особо нечего.

Давайте установим еще одну точку останова внутри нашего основного цикла Мандельброта и возобновим выполнение, чтобы пропустить немного вперед.

DevTools приостанавливался внутри вложенных циклов

На данный момент наша palette заполнена случайными цветами, и мы можем расширить как сам массив, так и отдельные структуры SDL_Color и проверить их компоненты, чтобы убедиться, что все выглядит хорошо (например, что «альфа»-канал всегда установите полную непрозрачность). Аналогичным образом мы можем расширить и проверить действительную и мнимую части комплексного числа, хранящегося в center переменной.

Если вы хотите получить доступ к глубоко вложенному свойству, к которому иначе сложно перейти через представление «Область» , вы также можете использовать оценку консоли ! Однако обратите внимание, что более сложные выражения C++ пока не поддерживаются.

Панель консоли, показывающая результат `palette[10].r`

Давайте возобновим выполнение несколько раз, и мы увидим, как также меняется внутренний x : либо снова просмотрев представление «Область» , добавив имя переменной в список наблюдения, вычислив его в консоли, либо наведя указатель мыши на переменную в исходный код:

Подсказка над переменной `x` в исходном коде, показывающая ее значение `3`

Отсюда мы можем вводить или обходить операторы C++ и наблюдать, как изменяются и другие переменные:

Всплывающие подсказки и вид «Область», показывающие значения «цвета», «точки» и других переменных.

Хорошо, все это прекрасно работает, когда доступна отладочная информация, но что, если мы хотим отладить код, который был создан без параметров отладки?

Отладка необработанной WebAssembly

Например, мы попросили Emscripten предоставить нам предварительно созданную библиотеку SDL вместо того, чтобы компилировать ее самостоятельно из исходного кода, поэтому — по крайней мере, на данный момент — у отладчика нет возможности найти связанные исходные коды. Давайте снова вмешаемся, чтобы перейти к SDL_RenderDrawColor :

DevTools показывает вид разборки `mandelbrot.wasm`

Мы вернулись к чистому опыту отладки WebAssembly.

Это выглядит немного пугающе, и большинству веб-разработчиков это не то, с чем когда-либо придется иметь дело, но иногда вам может понадобиться отладить библиотеку, созданную без отладочной информации, потому что это сторонняя библиотека, которую вы не можете контролировать. , или потому что вы столкнулись с одной из тех ошибок, которые возникают только в рабочей среде.

Чтобы помочь в таких случаях, мы также внесли некоторые улучшения в базовый процесс отладки.

Прежде всего, если вы раньше использовали необработанную отладку WebAssembly, вы могли заметить, что вся дизассемблирование теперь отображается в одном файле — больше не нужно гадать, какой функции может соответствовать запись в исходном коде wasm-53834e3e/ wasm-53834e3e-7 .

Новая схема генерации имени

Мы также улучшили имена в режиме дизассемблирования. Раньше вы видели только числовые индексы или, в случае функций, вообще не видели имени.

Теперь мы генерируем имена аналогично другим инструментам дизассемблирования, используя подсказки из раздела имени WebAssembly , пути импорта/экспорта и, наконец, если ничего не помогает, генерируем их на основе типа и индекса элемента, например $func123 . На скриншоте выше вы можете видеть, как это уже помогает получить немного более читаемые трассировки стека и дизассемблирование.

Когда информация о типе отсутствует, может быть сложно проверить какие-либо значения, кроме примитивов — например, указатели будут отображаться как обычные целые числа, без возможности узнать, что хранится за ними в памяти.

Проверка памяти

Раньше можно было только развернуть объект памяти WebAssembly, представленный env.memory в представлении «Область» , для поиска отдельных байтов. Это работало в некоторых тривиальных сценариях, но было не особенно удобно расширять и не позволяло интерпретировать данные в форматах, отличных от байтовых значений. Мы также добавили новую функцию, которая поможет в этом: инспектор линейной памяти.

Если вы щелкнете правой кнопкой мыши по env.memory , вы увидите новую опцию под названием «Проверить память» :

Контекстное меню в env.memory на панели «Область», отображающее элемент «Проверить память».

После щелчка откроется инспектор памяти , в котором вы сможете проверить память WebAssembly в шестнадцатеричном и ASCII-представлении, перейти к определенным адресам, а также интерпретировать данные в различных форматах:

Панель инспектора памяти в DevTools, показывающая шестнадцатеричное и ASCII-представление памяти.

Расширенные сценарии и предостережения

Профилирование кода WebAssembly

Когда вы открываете DevTools, код WebAssembly «понижается» до неоптимизированной версии, чтобы обеспечить возможность отладки. Эта версия намного медленнее, а это означает, что вы не можете полагаться на console.time , performance.now и другие методы измерения скорости вашего кода, пока DevTools открыты, поскольку полученные вами цифры не будут отражать реальный мир. производительность вообще.

Вместо этого вам следует использовать панель DevTools Performance , которая запустит код на полной скорости и предоставит вам подробную разбивку времени, затраченного на различные функции:

Панель профилирования, показывающая различные функции Wasm

Альтернативно, вы можете запустить приложение с закрытыми DevTools и открыть их после завершения, чтобы проверить консоль .

В будущем мы будем улучшать сценарии профилирования, но на данный момент это предостережение, о котором следует знать. Если вы хотите узнать больше о сценариях многоуровневого управления WebAssembly, ознакомьтесь с нашей документацией по конвейеру компиляции WebAssembly .

Сборка и отладка на разных машинах (включая Docker/хост)

При сборке в Docker, виртуальной машине или на удаленном сервере сборки вы, скорее всего, столкнетесь с ситуациями, когда пути к исходным файлам, использованным во время сборки, не совпадают с путями в вашей собственной файловой системе, где запущены Chrome DevTools. В этом случае файлы будут отображаться на панели «Источники» , но не смогут загружаться.

Чтобы решить эту проблему, мы реализовали функцию сопоставления путей в опциях расширения C/C++. Вы можете использовать его для переназначения произвольных путей и помощи DevTools в поиске источников.

Например, если проект на вашем хост-компьютере находится по пути C:\src\my_project , но был построен внутри контейнера Docker, где этот путь был представлен как /mnt/c/src/my_project , вы можете переназначить его обратно во время отладки. указав эти пути в качестве префиксов:

Страница параметров расширения отладки C/C++.

Первый совпавший префикс «выигрывает». Если вы знакомы с другими отладчиками C++, эта опция аналогична команде set substitute-path в GDB или настройке target.source-map в LLDB.

Отладка оптимизированных сборок

Как и в случае с любыми другими языками, отладка работает лучше всего, если оптимизация отключена. Оптимизации могут встраивать функции друг в друга, переупорядочивать код или вообще удалять части кода — и все это может запутать отладчик и, следовательно, вас как пользователя.

Если вы не возражаете против более ограниченного опыта отладки и по-прежнему хотите отлаживать оптимизированную сборку, то большинство оптимизаций будут работать как положено, за исключением встраивания функций. Мы планируем решить оставшиеся проблемы в будущем, но сейчас используйте -fno-inline , чтобы отключить его при компиляции с любыми оптимизациями уровня -O , например:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

Разделение отладочной информации

В отладочной информации сохраняется множество подробностей о вашем коде, определенных типах, переменных, функциях, областях и расположениях — все, что может быть полезно для отладчика. В результате он часто может быть больше, чем сам код.

Чтобы ускорить загрузку и компиляцию модуля WebAssembly, вы можете выделить эту отладочную информацию в отдельный файл WebAssembly. Чтобы сделать это в Emscripten, передайте флаг -gseparate-dwarf=… с нужным именем файла:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

В этом случае основное приложение будет хранить только имя файла temp.debug.wasm , а вспомогательное расширение сможет найти и загрузить его при открытии DevTools.

В сочетании с оптимизациями, описанными выше, эту функцию можно даже использовать для поставки почти оптимизированных рабочих сборок вашего приложения, а затем их отладки с помощью локального файла. В этом случае нам дополнительно потребуется переопределить сохраненный URL-адрес, чтобы помочь расширению найти дополнительный файл, например:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

Продолжение следует…

Ого, сколько новых функций!

Благодаря всем этим новым интеграциям Chrome DevTools становится жизнеспособным и мощным отладчиком не только для JavaScript, но и для приложений C и C++, благодаря чему становится проще, чем когда-либо, брать приложения, созданные с использованием различных технологий, и переносить их в общий доступ. кроссплатформенный веб.

Однако наше путешествие еще не закончено. Вот некоторые вещи, над которыми мы будем работать дальше:

  • Устранение неровностей в процессе отладки.
  • Добавление поддержки форматировщиков пользовательских типов.
  • Работаем над улучшениями профилирования приложений WebAssembly.
  • Добавление поддержки покрытия кода , чтобы упростить поиск неиспользуемого кода.
  • Улучшение поддержки выражений в консольных вычислениях.
  • Добавление поддержки большего количества языков.
  • … и многое другое!

Тем временем, пожалуйста, помогите нам, опробовав текущую бета-версию на своем собственном коде и сообщая о любых обнаруженных проблемах по адресу https://issues.chromium.org/issues/new?noWizard=true&template=0&comComponent=1456350 .

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра дают вам доступ к новейшим функциям DevTools, тестируют передовые API-интерфейсы веб-платформы и находят проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Связь с командой Chrome DevTools

Используйте следующие параметры, чтобы обсудить новые функции и изменения в публикации или что-либо еще, связанное с DevTools.

,

Ингвар Степанян
Ingvar Stepanyan

Дорога до сих пор

Год назад Chrome объявил о начальной поддержке встроенной отладки WebAssembly в Chrome DevTools.

Мы продемонстрировали базовую поддержку степпинга и рассказали о возможностях использования информации DWARF вместо исходных карт, которые откроются для нас в будущем:

  • Разрешение имен переменных
  • Симпатичные типы печати
  • Оценка выражений на исходных языках
  • …и многое другое!

Сегодня мы рады продемонстрировать реализацию обещанных функций и прогресс, достигнутый командами Emscripten и Chrome DevTools за этот год, в частности, в приложениях C и C++.

Прежде чем мы начнем, имейте в виду, что это все еще бета-версия нового интерфейса, вам необходимо использовать последнюю версию всех инструментов на свой страх и риск, и если у вас возникнут какие-либо проблемы, сообщите о них по адресу https:/ /issues.chromium.org/issues/new?noWizard=true&template=0&comComponent=1456350 .

Начнем с того же простого примера на C, что и в прошлый раз:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

Чтобы скомпилировать его, мы используем последнюю версию Emscripten и передаем флаг -g , как и в исходном сообщении, для включения отладочной информации:

emcc -g temp.c -o temp.html

Теперь мы можем обслуживать сгенерированную страницу с локального HTTP-сервера (например, с помощью submit ) и открывать ее в последней версии Chrome Canary .

На этот раз нам также понадобится вспомогательное расширение, которое интегрируется с Chrome DevTools и помогает ему разобраться во всей отладочной информации, закодированной в файле WebAssembly. Пожалуйста, установите его, перейдя по этой ссылке: goo.gle/wasm-debugging-extension .

Вам также понадобится включить отладку WebAssembly в DevTools Experiments . Откройте Chrome DevTools, щелкните значок шестеренки ( ) в правом верхнем углу панели DevTools, перейдите на панель «Эксперименты» и установите флажок «Отладка WebAssembly: Включить поддержку DWARF» .

Панель «Эксперименты» в настройках DevTools

Когда вы закроете «Настройки» , DevTools предложит перезагрузиться, чтобы применить настройки, так что давайте сделаем именно это. Вот и все, что касается одноразовой установки.

Теперь мы можем вернуться на панель «Источники» , включить «Паузу при исключениях» (значок ⏸), затем установить флажок «Приостановить при обнаружении исключений» и перезагрузить страницу. Вы должны увидеть, что DevTools приостановлены из-за исключения:

Снимок экрана панели «Источники», показывающий, как включить «Паузу при обнаружении исключений».

По умолчанию он останавливается на связующем коде, сгенерированном Emscripten, но справа вы можете увидеть представление стека вызовов , представляющее трассировку стека ошибки, и можете перейти к исходной строке C, которая вызвала abort :

DevTools приостановил работу функции Assert_less и отобразил значения x и y в представлении области действия.

Теперь, если вы посмотрите в представлении «Область» , вы сможете увидеть исходные имена и значения переменных в коде C/C++, и вам больше не придется выяснять, что означают искаженные имена, такие как $localN и как они связаны с исходным кодом, который вы используете. написал.

Это относится не только к примитивным значениям, таким как целые числа, но и к составным типам, таким как структуры, классы, массивы и т. д.!

Богатая поддержка типов

Давайте рассмотрим более сложный пример, чтобы продемонстрировать это. На этот раз мы нарисуем фрактал Мандельброта с помощью следующего кода C++:

#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();
}

Вы можете видеть, что это приложение все еще довольно маленькое — это один файл, содержащий 50 строк кода, — но на этот раз я также использую некоторые внешние API, такие как библиотека SDL для графики, а также комплексные числа из стандартной библиотеки C++.

Я собираюсь скомпилировать его с тем же флагом -g , что и выше, чтобы включить отладочную информацию, а также попрошу Emscripten предоставить библиотеку SDL2 и разрешить использование памяти произвольного размера:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

Когда я посещаю сгенерированную страницу в браузере, я вижу красивую фрактальную форму со случайными цветами:

Демо-страница

Когда я снова открываю DevTools, я вижу исходный файл C++. Однако на этот раз в коде нет ошибки (ух!), поэтому давайте вместо этого установим точку останова в начале нашего кода.

Когда мы снова перезагрузим страницу, отладчик приостановит работу прямо внутри нашего исходного кода C++:

DevTools приостановил вызов SDL_Init.

Мы уже видим все наши переменные справа, но на данный момент инициализированы только width и height , поэтому проверять особо нечего.

Давайте установим еще одну точку останова внутри нашего основного цикла Мандельброта и возобновим выполнение, чтобы пропустить немного вперед.

DevTools приостанавливался внутри вложенных циклов

На данный момент наша palette заполнена случайными цветами, и мы можем расширить как сам массив, так и отдельные структуры SDL_Color и проверить их компоненты, чтобы убедиться, что все выглядит хорошо (например, что «альфа»-канал всегда установите полную непрозрачность). Аналогичным образом мы можем расширить и проверить действительную и мнимую части комплексного числа, хранящегося в center переменной.

Если вы хотите получить доступ к глубоко вложенному свойству, к которому иначе сложно перейти через представление «Область» , вы также можете использовать оценку консоли ! Однако обратите внимание, что более сложные выражения C++ пока не поддерживаются.

Панель консоли, показывающая результат `palette[10].r`

Давайте возобновим выполнение несколько раз, и мы увидим, как также меняется внутренний x : либо снова просмотрев представление «Область» , добавив имя переменной в список наблюдения, оценив его в консоли, либо наведя указатель мыши на переменную в исходный код:

Подсказка над переменной `x` в исходном коде, показывающая ее значение `3`

Отсюда мы можем вводить или обходить операторы C++ и наблюдать, как изменяются и другие переменные:

Всплывающие подсказки и вид «Область», показывающие значения «цвета», «точки» и других переменных.

Хорошо, все это прекрасно работает, когда доступна отладочная информация, но что, если мы хотим отладить код, который был создан без параметров отладки?

Отладка необработанной WebAssembly

Например, мы попросили Emscripten предоставить нам предварительно созданную библиотеку SDL вместо того, чтобы компилировать ее самостоятельно из исходного кода, поэтому — по крайней мере, на данный момент — у отладчика нет возможности найти связанные исходные коды. Давайте снова вмешаемся, чтобы перейти к SDL_RenderDrawColor :

DevTools показывает вид разборки `mandelbrot.wasm`

Мы вернулись к чистому опыту отладки WebAssembly.

Это выглядит немного пугающе, и большинству веб-разработчиков это не то, с чем когда-либо придется иметь дело, но иногда вам может понадобиться отладить библиотеку, созданную без отладочной информации, потому что это сторонняя библиотека, которую вы не можете контролировать. , или потому что вы столкнулись с одной из тех ошибок, которые возникают только в рабочей среде.

Чтобы помочь в таких случаях, мы также внесли некоторые улучшения в базовый процесс отладки.

Прежде всего, если вы раньше использовали необработанную отладку WebAssembly, вы могли заметить, что вся дизассемблирование теперь отображается в одном файле — больше не нужно гадать, какой функции может соответствовать запись в исходном коде wasm-53834e3e/ wasm-53834e3e-7 .

Новая схема генерации имени

Мы также улучшили имена в режиме дизассемблирования. Раньше вы видели только числовые индексы или, в случае функций, вообще не видели имени.

Теперь мы генерируем имена аналогично другим инструментам дизассемблирования, используя подсказки из раздела имени WebAssembly , пути импорта/экспорта и, наконец, если ничего не помогает, генерируем их на основе типа и индекса элемента, например $func123 . На скриншоте выше вы можете видеть, как это уже помогает получить немного более читаемые трассировки стека и дизассемблирование.

Когда информация о типе отсутствует, может быть сложно проверить какие-либо значения, кроме примитивов — например, указатели будут отображаться как обычные целые числа, без возможности узнать, что хранится за ними в памяти.

Проверка памяти

Раньше можно было только развернуть объект памяти WebAssembly, представленный env.memory в представлении «Область» , для поиска отдельных байтов. Это работало в некоторых тривиальных сценариях, но было не особенно удобно расширять и не позволяло интерпретировать данные в форматах, отличных от байтовых значений. Мы также добавили новую функцию, которая поможет в этом: инспектор линейной памяти.

Если вы щелкнете правой кнопкой мыши по env.memory , вы увидите новую опцию под названием «Проверить память» :

Контекстное меню в env.memory на панели «Область» с элементом «Проверка памяти».

После щелчка откроется инспектор памяти , в котором вы сможете проверить память WebAssembly в шестнадцатеричном и ASCII-представлении, перейти к определенным адресам, а также интерпретировать данные в различных форматах:

Панель инспектора памяти в DevTools, показывающая шестнадцатеричное и ASCII-представление памяти.

Расширенные сценарии и предостережения

Профилирование кода WebAssembly

Когда вы открываете DevTools, код WebAssembly «понижается» до неоптимизированной версии, чтобы обеспечить возможность отладки. Эта версия намного медленнее, а это означает, что вы не можете полагаться на console.time , performance.now и другие методы измерения скорости вашего кода, пока DevTools открыты, поскольку полученные вами цифры не будут отражать реальный мир. производительность вообще.

Вместо этого вам следует использовать панель DevTools Performance , которая запустит код на полной скорости и предоставит вам подробную разбивку времени, затраченного на различные функции:

Панель профилирования, показывающая различные функции Wasm

Альтернативно, вы можете запустить приложение с закрытыми DevTools и открыть их после завершения, чтобы проверить консоль .

В будущем мы будем улучшать сценарии профилирования, но на данный момент это предостережение, о котором следует знать. Если вы хотите узнать больше о сценариях многоуровневого управления WebAssembly, ознакомьтесь с нашей документацией по конвейеру компиляции WebAssembly .

Сборка и отладка на разных машинах (включая Docker/хост)

При сборке в Docker, виртуальной машине или на удаленном сервере сборки вы, скорее всего, столкнетесь с ситуациями, когда пути к исходным файлам, использованным во время сборки, не совпадают с путями в вашей собственной файловой системе, где запущены Chrome DevTools. В этом случае файлы будут отображаться на панели «Источники» , но не смогут загружаться.

Чтобы решить эту проблему, мы реализовали функцию сопоставления путей в опциях расширения C/C++. Вы можете использовать его для переназначения произвольных путей и помощи DevTools в поиске источников.

Например, если проект на вашем хост-компьютере находится по пути C:\src\my_project , но был построен внутри контейнера Docker, где этот путь был представлен как /mnt/c/src/my_project , вы можете переназначить его обратно во время отладки. указав эти пути в качестве префиксов:

Страница параметров расширения отладки C/C++.

Первый совпавший префикс «выигрывает». Если вы знакомы с другими отладчиками C++, эта опция аналогична команде set substitute-path в GDB или настройке target.source-map в LLDB.

Отладка оптимизированных сборок

Как и в случае с любыми другими языками, отладка работает лучше всего, если оптимизация отключена. Оптимизации могут встраивать функции друг в друга, переупорядочивать код или вообще удалять части кода — и все это может запутать отладчик и, следовательно, вас как пользователя.

Если вы не возражаете против более ограниченного опыта отладки и по-прежнему хотите отлаживать оптимизированную сборку, то большинство оптимизаций будут работать как положено, за исключением встраивания функций. Мы планируем решить оставшиеся проблемы в будущем, но сейчас используйте -fno-inline , чтобы отключить его при компиляции с любыми оптимизациями уровня -O , например:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

Разделение отладочной информации

В отладочной информации сохраняется множество подробностей о вашем коде, определенных типах, переменных, функциях, областях и расположениях — все, что может быть полезно для отладчика. В результате он часто может быть больше, чем сам код.

Чтобы ускорить загрузку и компиляцию модуля WebAssembly, вы можете выделить эту отладочную информацию в отдельный файл WebAssembly. Чтобы сделать это в Emscripten, передайте флаг -gseparate-dwarf=… с нужным именем файла:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

В этом случае основное приложение будет хранить только имя файла temp.debug.wasm , а вспомогательное расширение сможет найти и загрузить его при открытии DevTools.

В сочетании с оптимизациями, описанными выше, эту функцию можно даже использовать для поставки почти оптимизированных рабочих сборок вашего приложения, а затем их отладки с помощью локального файла. В этом случае нам дополнительно потребуется переопределить сохраненный URL-адрес, чтобы помочь расширению найти дополнительный файл, например:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

Продолжение следует…

Ого, это было много новых функций!

Благодаря всем этим новым интеграциям Chrome DevTools становится жизнеспособным и мощным отладчиком не только для JavaScript, но и для приложений C и C++, благодаря чему становится проще, чем когда-либо, брать приложения, созданные с использованием различных технологий, и переносить их в общий доступ. кроссплатформенный веб.

Однако наше путешествие еще не закончено. Вот некоторые вещи, над которыми мы будем работать дальше:

  • Устранение острых углов в процессе отладки.
  • Добавление поддержки форматировщиков пользовательских типов.
  • Работаем над улучшениями профилирования приложений WebAssembly.
  • Добавление поддержки покрытия кода , чтобы упростить поиск неиспользуемого кода.
  • Улучшение поддержки выражений в консольных вычислениях.
  • Добавление поддержки большего количества языков.
  • … и многое другое!

Тем временем, пожалуйста, помогите нам, опробовав текущую бета-версию на своем собственном коде и сообщая о любых обнаруженных проблемах по адресу https://issues.chromium.org/issues/new?noWizard=true&template=0&comComponent=1456350 .

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра дают вам доступ к новейшим функциям DevTools, тестируют передовые API-интерфейсы веб-платформы и находят проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Связь с командой Chrome DevTools

Используйте следующие параметры, чтобы обсудить новые функции и изменения в публикации или что-либо еще, связанное с DevTools.

,

Ингвар Степанян
Ingvar Stepanyan

Дорога до сих пор

Год назад Chrome объявил о начальной поддержке встроенной отладки WebAssembly в Chrome DevTools.

Мы продемонстрировали базовую поддержку степпинга и рассказали о возможностях использования информации DWARF вместо исходных карт, которые откроются для нас в будущем:

  • Разрешение имен переменных
  • Симпатичные типы печати
  • Оценка выражений на исходных языках
  • …и многое другое!

Сегодня мы рады продемонстрировать реализацию обещанных функций и прогресс, достигнутый командами Emscripten и Chrome DevTools за этот год, в частности, в приложениях C и C++.

Прежде чем мы начнем, имейте в виду, что это все еще бета-версия нового интерфейса, вам необходимо использовать последнюю версию всех инструментов на свой страх и риск, и если у вас возникнут какие-либо проблемы, сообщите о них по адресу https:/ /issues.chromium.org/issues/new?noWizard=true&template=0&comComponent=1456350 .

Начнем с того же простого примера на C, что и в прошлый раз:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

Чтобы скомпилировать его, мы используем последнюю версию Emscripten и передаем флаг -g , как и в исходном посте, для включения отладочной информации:

emcc -g temp.c -o temp.html

Теперь мы можем обслуживать сгенерированную страницу с HTTP-сервера локального хоста (например, с помощью submit ) и открывать ее в последней версии Chrome Canary .

На этот раз нам также понадобится вспомогательное расширение, которое интегрируется с Chrome DevTools и помогает ему разобраться во всей отладочной информации, закодированной в файле WebAssembly. Пожалуйста, установите его, перейдя по этой ссылке: goo.gle/wasm-debugging-extension .

Вам также понадобится включить отладку WebAssembly в DevTools Experiments . Откройте Chrome DevTools, щелкните значок шестеренки ( ) в правом верхнем углу панели DevTools, перейдите на панель «Эксперименты» и установите флажок «Отладка WebAssembly: Включить поддержку DWARF» .

Панель «Эксперименты» в настройках DevTools

Когда вы закроете «Настройки» , DevTools предложит перезагрузиться, чтобы применить настройки, так что давайте сделаем именно это. Вот и все, что касается одноразовой установки.

Теперь мы можем вернуться на панель «Источники» , включить «Паузу при исключениях» (значок ⏸), затем установить флажок «Приостановить при обнаружении исключений» и перезагрузить страницу. Вы должны увидеть, что DevTools приостановлены из-за исключения:

Снимок экрана панели «Источники», показывающий, как включить «Паузу при обнаружении исключений».

По умолчанию он останавливается на связующем коде, сгенерированном Emscripten, но справа вы можете увидеть представление стека вызовов , представляющее трассировку стека ошибки, и можете перейти к исходной строке C, которая вызвала abort :

DevTools приостановил работу функции Assert_less и отобразил значения x и y в представлении области действия.

Теперь, если вы посмотрите в представлении «Область» , вы сможете увидеть исходные имена и значения переменных в коде C/C++, и вам больше не придется выяснять, что означают искаженные имена, такие как $localN и как они связаны с исходным кодом, который вы используете. написал.

Это относится не только к примитивным значениям, таким как целые числа, но и к составным типам, таким как структуры, классы, массивы и т. д.!

Богатая поддержка типов

Давайте рассмотрим более сложный пример, чтобы продемонстрировать это. На этот раз мы нарисуем фрактал Мандельброта с помощью следующего кода C++:

#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();
}

Вы можете видеть, что это приложение все еще довольно маленькое — это один файл, содержащий 50 строк кода, — но на этот раз я также использую некоторые внешние API, такие как библиотека SDL для графики, а также комплексные числа из стандартной библиотеки C++.

Я собираюсь скомпилировать его с тем же флагом -g , что и выше, чтобы включить отладочную информацию, а также попрошу Emscripten предоставить библиотеку SDL2 и разрешить использование памяти произвольного размера:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

Когда я посещаю сгенерированную страницу в браузере, я вижу красивую фрактальную форму со случайными цветами:

Демо-страница

Когда я снова открываю DevTools, я вижу исходный файл C++. На этот раз, однако, у нас нет ошибки в коде (вот!), Так что давайте установим некоторую точку останова в начале нашего кода.

Когда мы снова перезагружаем страницу, отладчик сделает паузу прямо внутри нашего источника C ++:

Devtools приостановился на вызове `sdl_init`

Мы уже можем видеть все наши переменные справа, но в данный момент инициализируются только width и height , поэтому нечего осмотреть.

Давайте установим еще одну точку перерыва в нашу главную петлю Мандельброта, и возобновим выполнение, чтобы немного пропустить вперед.

Devtools остановился в вложенных петлях

На данный момент наша palette была заполнена некоторыми случайными цветами, и мы можем расширить как сам массив, так и отдельные структуры SDL_Color и осмотреть их компоненты, чтобы убедиться, что все выглядит хорошо (например, что «альфа» канал всегда является каналом. установить на полную непрозрачность). Точно так же мы можем расширить и проверить реальные и воображаемые части комплексного числа, хранящихся в center переменной.

Если вы хотите получить доступ к глубоко вложенной собственности, к которому в противном случае трудно перемещаться с помощью вида по объему , вы также можете использовать оценку консоли ! Тем не менее, обратите внимание, что более сложные выражения C ++ еще не поддерживаются.

Консольная панель, показывающая результат «Палитра [10] .r`

Давайте возобновим выполнение несколько раз, и мы увидим, как Inner x меняется так же хорошо, снова посмотрев в представление области , добавив имя переменной в список просмотра, оценивая его в консоли или парят над переменной в исходный код:

Подсказка на переменной `x` в источнике, показывающая его значение` 3`

Отсюда мы можем перейти или шагнуть операторы C ++ и наблюдать, как меняются другие переменные:

Подсказки и представление о прицеле, показывающие значения `color`,` point` и других переменных

Итак, все это прекрасно работает, когда доступна информация отладки, но что, если мы хотим отладить код, который не был создан с вариантами отладки?

Необработанная отладка Webassembly

Например, мы попросили Emscripten предоставить нам предварительную библиотеку SDL, вместо того, чтобы собирать ее сами из источника, по крайней мере, в настоящее время нет возможности, чтобы отладчик мог найти связанные источники. Давайте снова вступим в SDL_RenderDrawColor :

Devtools, показывающие разборку вид `mandelbrot.wasm`

Мы вернулись к опыту отладки Webassembly.

Теперь это выглядит немного страшно, и не то, с чем когда -либо нужно иметь дело с чем -то, но иногда вы можете отладить библиотеку, построенную без отладки информации, потому что это 3 частичная библиотека, вы не контролируете или потому, что вы сталкиваетесь с одной из тех ошибок, которые возникают только при производстве.

Чтобы помочь в этих случаях, мы также сделали некоторые улучшения в основном опыте отладки.

Прежде всего, если вы использовали необработанную отладку webassembly, вы можете заметить, что вся разборка теперь показана в одном файле-нет больше догадков, какая функция, входящая в источники wasm-53834e3e/ wasm-53834e3e-7 возможно, соответствует.

Схема генерации новых имен

Мы также улучшили имена в представлении разборки. Ранее вы увидели только числовые индексы, или, в случае функций, без имени вообще.

Теперь мы генерируем имена, аналогичные другими инструментами разборки, используя подсказки из раздела имени Webassembly , путей импорта/экспорта и, наконец, если все остальное не удается, генерируя их на основе типа и индекса элемента, например, $func123 . Вы можете увидеть, как на скриншоте выше это уже помогает получить немного более читаемые стектрассы и разборку.

Когда информация о типах не будет, может быть трудно осмотреть любые значения, помимо примитивам, для примера, указатели будут отображаться как обычные целые числа, без возможности узнать, что хранится за ними в памяти.

Инспекция памяти

Ранее вы могли только расширить объект памяти Webassembly, представленное env.memory в визуальном виде по объему , чтобы найти отдельные байты. Это сработало в некоторых тривиальных сценариях, но не было особенно удобно расширяться и не позволило переосмыслить данные в форматах, отличных от байтовых значений. Мы также добавили новую функцию, чтобы помочь с этим: линейный инспектор памяти.

Если вы щелкните правой кнопкой мыши на Memory env.memory .

Контекстное меню на `env.memory` на панели областей, показывающая элемент« осмотреть память »

После нажатия, он заставит инспектора памяти , в котором вы можете осмотреть память Webassembly в шестнадцатеричных представлениях и ASCII, перейдите к конкретным адресам, а также интерпретируют данные в разных форматах:

Панель инспектора памяти в Devtools, показывающих виды HEX и ASCII памяти

Продвинутые сценарии и предостережения

Профилирование кода webassembly

Когда вы открываете Devtools, код Webassembly «усажен» в нептимизированную версию для обеспечения отладки. Эта версия намного медленнее, а это значит, что вы не можете полагаться на console.time , performance.now Теперь и другие методы измерения скорости вашего кода, в то время как DevTools открыты, так как цифры, которые вы получаете, не будут представлять реального мира производительность вообще.

Вместо этого вам следует использовать панель производительности DevTools, которая будет запускать код на полной скорости и предоставить вам подробный разбив времени, проведенного в разных функциях:

Панель профилирования, показывающая различные функции WASM

В качестве альтернативы, вы можете запустить свое приложение с закрытыми Devtools и открыть их после завершения, чтобы осмотреть консоль .

Мы будем улучшать сценарии профилирования в будущем, но сейчас это предостережение, о котором можно знать. Если вы хотите узнать больше о сценариях Webassembly, ознакомьтесь с нашими документами на конвейере компиляции Webassembly .

Строительство и отладка на разных машинах (включая Docker / Host)

При строительстве в Docker, Virtual Machine или на сервере удаленной сборки вы, вероятно, столкнетесь с ситуациями, когда пути к исходным файлам, используемым во время сборки, не соответствуют пути в вашей собственной файловой системе, где работают Chrome Devtools. В этом случае файлы будут отображаться на панели источников , но не загружаются.

Чтобы решить эту проблему, мы внедрили функциональность отображения пути в параметрах расширения C/C ++. Вы можете использовать его, чтобы перенаправить произвольные пути и помочь Devtools найти источники.

Например, если проект на вашем хост -машине находится под пути C:\src\my_project , но был построен в контейнере Docker, где этот путь был представлен как /mnt/c/src/my_project , вы можете пережить его во время отладки указав эти пути в качестве префиксов:

Страница параметров расширения отладки C/C ++

Первый сопоставленный префикс "выигрывает". Если вы знакомы с другими отлаживаниями C ++, эта опция аналогична команде set substitute-path в GDB или настройке target.source-map в LLDB.

Отладка оптимизированных сборки

Как и в случае с любыми другими языками, отладка лучше всего работает, если оптимизация отключена. Оптимизация может внедрить функции одного в другое, код повторного заказа или полностью удалить части кода, и у всего этого есть шанс запутать отладчика и, следовательно, вы как пользователь.

Если вы не возражаете против более ограниченного опыта отладки и все еще хотите отладить оптимизированную сборку, то большинство оптимизаций будут работать так же, как и ожидалось, за исключением функций, внедряющих. Мы планируем решить оставшиеся проблемы в будущем, но пока, пожалуйста, используйте -fno-inline , чтобы отключить их при компиляции с любыми оптимизациями уровня -O , например:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

Разделение информации отладки

Информация отладки сохраняет множество подробностей о вашем коде, определенных типах, переменных, функциях, областях и местах-все, которые могут быть полезны для отладчика. В результате он часто может быть больше, чем сам код.

Чтобы ускорить загрузку и компиляцию модуля Webassembly, вы можете разделить эту информацию отладки на отдельный файл webassembly. Чтобы сделать это в Emscripten, пропустите -gseparate-dwarf=… флаг с желаемым именем файла:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

В этом случае в основном приложении будет храниться только имя файла temp.debug.wasm , а расширение Helper сможет найти и загружать его при открытии Devtools.

В сочетании с оптимизациями, как описано выше, эта функция может быть даже использована для доставки почти оптимизированных производственных сборок вашего приложения, а затем отлаживать их с помощью локального побочного файла. В этом случае нам также необходимо переопределить хранимый URL, чтобы помочь расширению найти побочный файл, например:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

Продолжение следует…

Вот, это было много новых функций!

Со всеми этими новыми интеграциями Chrome Devtools становится жизнеспособным, мощным отладчиком не только для JavaScript, но и для приложений C и C ++, что облегчает приложения, встроенные в различные технологии и приводя их к общим, Кроссплатформенная сеть.

Однако наше путешествие еще не закончилось. Некоторые вещи, над которыми мы будем работать с этого момента:

  • Очистка грубых краев в опыте отладки.
  • Добавление поддержки для пользовательских формах типов.
  • Работа над улучшениями в профилировании для приложений для Webassembly.
  • Добавление поддержки для покрытия кода , чтобы облегчить поиск неиспользованного кода.
  • Улучшение поддержки выражений в оценке консоли.
  • Добавление поддержки для большего количества языков.
  • ... и больше!

Между тем, пожалуйста, помогите нам, попробовав текущую бета -версию в вашем собственном коде и сообщив о любых найденных вопросах для https://issues.chromium.org/issues/new?nowizard=true&template=0&component=1456350 .

Скачать каналы предварительного просмотра

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера разработки по умолчанию. Эти каналы предварительного просмотра дают вам доступ к последним функциям Devtools, тестируют передовые API веб-платформы и найдите проблемы на вашем сайте, прежде чем ваши пользователи сделают!

Связаться с командой Chrome Devtools

Используйте следующие варианты, чтобы обсудить новые функции и изменения в сообщении или что -либо еще, связанное с DevTools.

  • Отправьте предложение или отзыв нам через crbug.com .
  • Сообщите о проблеме Devtools, используя больше вариантовБолее > Помощь > Сообщить о вопросах DevTools в DevTools.
  • Твит на @chromedevtools .
  • Оставьте комментарии к нашим новому в Devtools YouTube Videos или Devtools Советы на YouTube .