최신 도구로 WebAssembly 디버깅

Ingvar Stepanyan
Ingvar Stepanyan

지금까지의 여정

1년 전 Chrome에서 초기 지원 계획을 발표했습니다. 을 참조하세요.

기본 단계 지원을 시연하고 향후 소스 맵 대신 DWARF 정보를 사용할 수 있는 기회에 관해 이야기했습니다.

  • 변수 이름 확인
  • pretty print 적용 유형
  • 소스 언어로 표현식 평가
  • 그 외에도 다양한 기능 제공

오늘, 약속한 기능이 실제로 구현되고 있다는 기쁜 소식을 전해 드립니다. Emscripten 및 Chrome DevTools팀이 특히 C 및 C++ 앱에 사용할 수 있습니다.

시작하기 전에 아직 새로운 환경의 베타 버전이므로 모든 도구의 최신 버전을 자체 책임하에 사용해야 하며 문제가 발생하면 https://issues.chromium.org/issues/new?noWizard=true&template=0&component=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

이제 localhost HTTP 서버( (예: serve) 최신 Chrome Canary에서 여세요.

이번에는 Chrome DevTools와 통합되고 WebAssembly 파일에 인코딩된 모든 디버깅 정보를 이해하는 데 도움이 되는 도우미 확장 프로그램도 필요합니다. goo.gle/wasm-debugging-extension 링크로 이동하여 설치하세요.

또한 DevTools에서 WebAssembly 디버깅을 사용 설정하는 것이 좋습니다. 실험. Chrome DevTools를 열고 DevTools 창의 오른쪽 상단에 있는 톱니바퀴() 아이콘을 클릭한 다음 실험 패널로 이동하여 WebAssembly 디버깅: DWARF 지원 사용 설정을 선택합니다.

DevTools 설정의 실험 창

설정을 닫으면 DevTools에서 설정을 적용하기 위해 새로고침하라는 메시지가 표시됩니다. 일회성 혜택 설정할 수 있습니다

이제 Sources(소스) 패널로 돌아가 Pause on(일시중지) on 예외 (⏸ 아이콘)로 표시된 다음 포착된 예외에서 일시중지를 선택합니다. 페이지를 새로고침합니다 예외로 인해 DevTools가 일시중지된 것을 확인할 수 있습니다.

&#39;포착된 예외에서 일시중지&#39;를 사용 설정하는 방법을 보여주는 소스 패널의 스크린샷

기본적으로 Emscripten에서 생성한 글루 코드에서 중지되지만 오른쪽에는 호출의 스택 트레이스를 나타내는 Call Stack 뷰가 호출한 원래 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에 임의 크기의 메모리를 허용합니다.

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

브라우저에서 생성된 페이지를 방문하면 아름다운 프랙털 도형:

데모 페이지

DevTools를 열면 원래 C++ 파일이 다시 표시됩니다. 이 하지만 코드에 오류가 없으므로 코드 시작 부분에 있는 일부 중단점을 대신 표시합니다.

페이지를 다시 새로고침하면 디버거가 C++ 소스:

DevTools가 `SDL_Init` 호출에서 일시중지됨

이미 오른쪽에서 모든 변수를 볼 수 있지만 width만 표시됩니다. height는 현재 초기화되어 있으므로 검사하겠습니다

기본 Mandelbrot 루프 내에 다른 중단점을 설정하고 실행을 재개하여 조금 앞으로 건너뛰어 보겠습니다.

DevTools가 중첩된 루프 내에서 일시중지됨

이 시점에서 palette는 임의의 색상으로 채워졌으며 배열 자체와 개별 SDL_Color 구조를 모두 펼치고 컴포넌트를 검사하여 모든 것이 잘 표시되는지 확인할 수 있습니다(예: '알파' 채널이 항상 전체 불투명도로 설정됨). 마찬가지로 여기서 실제와 center 변수에 저장된 복소수의 허수부입니다.

범위 뷰를 통해 이동하기 어려운 중첩된 속성에 액세스하려면 콘솔 평가도 사용할 수 있습니다. 그러나 더 복잡한 C++ 표현식은 아직 지원되지 않습니다.

`palette[10].r`의 결과를 보여주는 콘솔 패널

실행을 몇 번 재개하면 내부 x도 어떻게 변경되는지 확인할 수 있습니다. 범위 뷰를 다시 확인하거나, 변수 이름을 보기 목록에 추가하거나, 콘솔에서 평가하거나, 소스 코드에서 변수 위로 마우스를 가져가면 됩니다.

소스에서 값이 &#39;3&#39;인 변수 &#39;x&#39; 위의 도움말

여기에서 C++ 문을 스테프인 또는 스테프오버하고 다른 변수도 어떻게 변경되는지 관찰할 수 있습니다.

`color`, `point` 및 기타 변수의 값을 보여주는 도움말 및 범위 뷰

디버그 정보를 사용할 수 있을 때 이 모든 것이 잘 작동하지만 디버깅을 통해 빌드되지 않은 코드를 디버깅하려는 경우에는 어떻게 해야 할까요? 옵션은 무엇인가요?

원시 WebAssembly 디버깅

예를 들어, 저희는 사전 빌드된 SDL 라이브러리를 제공하도록 Emscripten에 적어도 소스에서 직접 컴파일하기 보다는 디버거가 연결된 소스를 찾을 방법이 없습니다. 다시 SDL_RenderDrawColor로 들어가 보겠습니다.

`mandelbrot.wasm`의 분해 뷰를 보여주는 DevTools

이제 원시 WebAssembly 디버깅 환경으로 돌아갑니다.

조금 무섭게 보일 수 있으며 대부분의 웹 개발자가 다룰 필요가 없지만, 디버그 정보 없이 빌드된 라이브러리를 디버그해야 하는 경우가 있습니다. 제어할 수 없는 서드 파티 라이브러리이거나 프로덕션에서만 발생하는 버그 중 하나가 발생했기 때문일 수 있습니다.

이러한 경우를 돕기 위해 기본 디버깅 환경도 일부 개선했습니다.

먼저, 이전에 원시 WebAssembly 디버깅을 사용한 경우 이제 전체 디스어셈블리가 Sources 항목 wasm-53834e3e/ wasm-53834e3e-7가 어느 함수에 해당하는지 더 추측해야 합니다.

새 이름 생성 스키마

분해 뷰의 이름도 개선했습니다. 이전에는 숫자 색인만 표시되거나 함수의 경우 이름이 전혀 표시되지 않았습니다.

이제 WebAssembly 이름 섹션의 힌트, 가져오기/내보내기 경로를 사용하여 다른 디스어셈블리 도구와 마찬가지로 이름을 생성하고, 다른 모든 방법이 실패하면 $func123와 같은 항목의 유형과 색인을 기반으로 이름을 생성합니다. 위의 스크린샷에서 볼 수 있듯이 이렇게 하면 읽기 쉬운 스택 트레이스와 디스어셈블리를 얻는 데 도움이 됩니다.

사용 가능한 유형 정보가 없으면 검사가 어려울 수 있습니다. 프리미티브 외의 모든 값(예: 포인터는 그 뒤에 무엇이 저장되어 있는지 알 수 있는 방법이 없다는 것을 사용할 수 있습니다

메모리 검사

이전에는 범위 뷰에서 env.memory로 표시된 WebAssembly 메모리 객체만 펼쳐 조회할 수 있었습니다. 개별 바이트입니다 이는 일부 사소한 시나리오에서는 작동했지만 확장하기가 특히 편리하지 않았고 바이트 값 이외의 형식으로 데이터를 재해석할 수 없었습니다. 이 문제 해결에 도움이 되는 선형 메모리 검사기라는 새로운 기능도 추가되었습니다.

env.memory를 마우스 오른쪽 버튼으로 클릭하면 이제 새 메모리 검사라는 옵션을 사용합니다.

&#39;메모리 검사&#39;를 보여주는 범위 창의 `env.memory` 컨텍스트 메뉴 항목

클릭하면 메모리 검사기가 표시됩니다. 여기에서 16진수 및 ASCII 뷰로 WebAssembly 메모리를 검사하고, 특정 주소로 이동하고, 다양한 형식으로 데이터를 해석할 수 있습니다.

메모리의 16진수 및 ASCII 뷰를 보여주는 DevTools의 Memory Inspector 창

고급 시나리오 및 주의사항

WebAssembly 코드 프로파일링

DevTools를 열면 디버깅을 사용 설정하기 위해 최적화되지 않은 버전으로 WebAssembly 코드가 '다운스케일링'됩니다. 이 버전은 속도가 훨씬 느리지만 즉, console.time, performance.now에 의존할 수 없습니다. DevTools가 작동하는 동안 코드의 속도를 측정하는 다른 방법도 표시되는 수치는 실제 실적을 나타내지 않기 때문입니다. 전혀 그렇지 않습니다.

대신 DevTools 성능 패널을 사용해야 합니다. 이 패널은 코드를 최대 속도로 실행하고 다양한 함수에 소비된 시간을 자세히 분류하여 보여줍니다.

다양한 Wasm 함수를 보여주는 프로파일링 패널

또는 DevTools를 닫은 상태에서 애플리케이션을 실행할 수 있습니다. 완료되면 열어서 콘솔을 검사합니다.

향후 프로파일링 시나리오가 개선될 예정이지만 지금은 주의해야 할 사항입니다. WebAssembly 및 WebAssembly가 계층화 시나리오는 WebAssembly 컴파일 파이프라인에 대한 문서를 확인하세요.

다른 머신 (Docker / 호스트 포함)에서 빌드 및 디버깅

Docker, 가상 머신 또는 원격 빌드 서버에서 빌드할 때는 소스 파일의 경로가 파일 시스템의 경로와 일치하지 않으면 Chrome DevTools가 실행되고 있는지 확인할 수 있습니다 이 경우 파일이 Sources 패널이 표시되는데 로드되지 않습니다.

이 문제를 해결하기 위해 C/C++ 확장 프로그램 옵션에 경로 매핑 기능을 구현했습니다. 이를 사용하여 임의의 경로를 재매핑하고 DevTools에서 소스를 찾는 데 도움을 줄 수 있습니다.

예를 들어 호스트 머신의 프로젝트가 경로 C:\src\my_project 아래에 있지만 이 경로가 /mnt/c/src/my_project로 표시된 Docker 컨테이너 내에서 빌드된 경우 이러한 경로를 접두사로 지정하여 디버깅 중에 다시 매핑할 수 있습니다.

C/C++ 디버깅 확장 프로그램의 옵션 페이지

일치하는 첫 번째 접두사가 '이깁니다'. 다른 C++ 디버거에 익숙한 경우 이 옵션은 GDB의 set substitute-path 명령어 또는 LLDB의 target.source-map 설정과 유사합니다.

최적화된 빌드 디버깅

다른 언어와 마찬가지로 최적화를 사용 중지하면 디버깅이 가장 잘 작동합니다. 최적화는 함수를 다른 함수에 인라인 처리하거나, 코드 순서를 변경하거나, 코드의 일부를 완전히 삭제할 수 있습니다. 이 모든 작업은 디버거와 사용자를 혼동시킬 수 있습니다.

좀 더 제한된 디버깅 환경에도 괜찮고 좀 더 디버그하는 경우 대부분의 최적화는 (함수 인라인 처리는 예외) 나머지 문제는 향후 해결할 계획이지만 지금은 -O 수준 최적화로 컴파일할 때 -fno-inline를 사용하여 사용 중지하세요. 예를 들면 다음과 같습니다.

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++ 앱을 위한 실용적이고 강력한 디버거가 됩니다. 이제 다양한 기술로 빌드된 앱을 공유된 크로스 플랫폼 웹으로 가져가기가 그 어느 때보다 쉬워졌습니다.

하지만 여정은 아직 끝나지 않았습니다. 앞으로 YouTube에서 준비 중인 작업은 다음과 같습니다.

  • 디버깅 환경의 미흡한 부분을 정리했습니다.
  • 맞춤 유형 형식 지정자에 대한 지원을 추가했습니다.
  • WebAssembly 앱의 프로파일링 개선을 위해 노력하고 있습니다.
  • 사용되지 않는 코드를 더 쉽게 찾을 수 있도록 코드 범위 지원을 추가했습니다.
  • 콘솔 평가에서 표현식 지원을 개선했습니다.
  • 더 많은 언어 지원 추가
  • 그 밖의 다양한 신호

그동안 자체 코드에서 현재 베타를 사용해 보고 발견된 문제를 https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350에 신고해 주세요.

미리보기 채널 다운로드

Chrome Canary, Dev 또는 베타를 기본 개발 브라우저로 사용하는 것이 좋습니다. 이러한 미리보기 채널을 통해 최신 DevTools 기능에 액세스하고, 최첨단 웹 플랫폼 API를 테스트하고, 사용자보다 먼저 사이트에서 문제를 발견할 수 있습니다.

Chrome DevTools팀에 문의하기

다음 옵션을 사용하여 게시물의 새로운 기능 및 변경사항 또는 DevTools와 관련된 기타 사항을 논의하세요.