최신 도구로 WebAssembly 디버깅

Ingvar Stepanyan
Ingvar Stepanyan

지금까지의 도로

1년 전 Chrome은 Chrome DevTools에서 네이티브 WebAssembly 디버깅을 초기 지원한다고 발표했습니다.

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

  • 변수 이름 확인
  • 프리티 프린팅 유형
  • 소스 언어로 표현식 평가
  • 그 외에도 다양한 기능 제공

오늘, 약속한 기능이 실제로 구현되고 있다는 기쁜 소식을 전해 드립니다. 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

이제 로컬호스트 HTTP 서버(예: serve 사용)에서 생성된 페이지를 게재하고 최신 Chrome Canary에서 열 수 있습니다.

이번에는 Chrome과 통합되는 도우미 확장 프로그램도 필요합니다. 모든 디버깅 정보를 이해하고 프로그래밍 방식으로 인코딩합니다. 이 링크: goo.gle/wasm-debugging-extension

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

DevTools 설정의 실험 창

Settings를 닫으면 DevTools에서 자동으로 새로고침하도록 제안합니다. 설정을 적용하겠습니다. 그렇게 하겠습니다 일회성 혜택 설정할 수 있습니다

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

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

기본적으로 Emscripten에서 생성한 글루 코드에서 중지되지만 오른쪽에는 호출의 스택 트레이스를 나타내는 Call Stack 뷰가 호출한 원래 C 라인으로 이동할 수 있으며 abort:

DevTools가 `assert_less` 함수에서 일시중지하고 범위 뷰에 `x` 및 `y` 값을 표시하는 경우

이제 범위 보기를 보면 원래 이름을 확인할 수 있습니다. 변수 값을 포함하므로 더 이상 일일이 $localN와 같이 손상된 이름의 의미와 소스 코드를 가져옵니다.

이는 정수와 같은 원시 값뿐만 아니라 복합 값에도 적용됩니다. 구조, 클래스, 배열 등과 같은 유형도 있습니다.

리치 유형 지원

이를 보여주기 위해 좀 더 복잡한 예를 살펴보겠습니다. 이 1을 시작해서 만델브로 프랙탈을 다음 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++ 소스:

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

이미 오른쪽에 모든 변수가 표시되지만 현재 widthheight만 초기화되므로 검사할 것이 많지 않습니다.

기본 망델브로 루프 내에 다른 중단점을 설정하고 조금 앞으로 건너뛰도록 할 수 있습니다.

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

이 시점에서 palette가 임의의 색상으로 채워졌습니다. 그리고 배열 자체뿐만 아니라 개별 배열도 확장할 수 있습니다. SDL_Color 구조를 만들고 구성요소를 검사하여 아무 문제가 없어 보입니다. 예를 들어 '알파' 채널은 항상 전체 불투명도) 마찬가지로 center 변수에 저장된 복소수의 실수와 허수 부분을 펼치고 확인할 수 있습니다.

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

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

실행을 몇 번 재개해 보겠습니다. 그러면 내부 x가 어떻게 작동하는지 확인할 수 있습니다. 범위 뷰를 다시 보고 감시 목록에 변수 이름을 추가하거나 콘솔에서 변수를 평가하거나 소스 코드에서 변수 위로 마우스를 가져갑니다.

값 `3` 을 표시하는 소스의 `x` 변수에 대한 도움말

여기에서부터 C++ 명령문을 Stepin 또는 Step Over를 통해 수행할 수 있으며, 다른 변수도 변경됩니다.

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

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

원시 WebAssembly 디버깅

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

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

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

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

Google은 이러한 경우를 돕기 위해 기본 살펴봤습니다

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

새 이름 생성 스키마

분해 뷰의 이름도 개선했습니다. 이전에 본 숫자 색인만 사용하거나 함수의 경우 이름을 전혀 사용하지 않습니다.

이제 다른 분해 도구와 비슷한 이름을 생성합니다. WebAssembly 이름 섹션의 힌트 사용 가져오기/내보내기 경로를 확인하고, 마지막으로, 다른 모든 방법이 실패할 경우 항목 유형 및 색인(예: $func123)에 따라 쿼리합니다. 다음을 수행할 수 있습니다. 위 스크린샷에서 볼 수 있듯이 이 방법은 더 읽기 쉬운 스택 트레이스 및 디스어셈블리를 지원합니다.

사용 가능한 유형 정보가 없으면 기본 유형 외의 값을 검사하기 어려울 수 있습니다. 예를 들어 포인터는 메모리에 저장된 내용을 알 수 있는 방법이 없이 일반 정수로 표시됩니다.

메모리 검사

이전에는 범위 뷰에서 env.memory로 표시된 WebAssembly 메모리 객체만 펼쳐 조회할 수 있었습니다. 개별 바이트입니다 이것은 몇 가지 사소한 시나리오에서는 효과적이었지만 확장할 때 특히 편리하고 데이터를 해석할 수 없음 사용하지 못할 수 있습니다. 새 기능이 추가되어 선형 메모리 검사기도 사용할 수 있습니다.

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

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

클릭하면 Memory Inspector가 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가 실행되고 있는지 확인할 수 있습니다 이 경우 파일이 소스 패널에 표시되지만 로드되지 않습니다.

이 문제를 해결하기 위해 다음 경로에 경로 매핑 기능을 구현했습니다. C/C++ 확장 옵션입니다. 이를 사용하여 임의의 경로를 재매핑하고 DevTools가 소스를 찾을 수 있게 도와줍니다.

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

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

일치하는 첫 번째 접두사가 '낙찰'됩니다. 다른 C++ 프로그래밍 언어나 디버거의 경우 이 옵션은 set substitute-path 명령어와 비슷합니다. GDB에서 또는 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에서 준비 중인 작업은 다음과 같습니다.

  • 디버깅 환경에서 거친 가장자리를 정리했습니다.
  • 맞춤 유형 형식 지정자에 대한 지원을 추가했습니다.
  • Ad Exchange의 프로파일링하는 방법을 설명합니다.
  • 쉽게 찾을 수 있도록 코드 범위 지원 추가 있습니다.
  • 콘솔 평가에서 표현식에 대한 지원을 개선합니다.
  • 더 많은 언어가 지원됩니다.
  • 그 밖의 다양한 신호

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

미리보기 채널 다운로드

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

Chrome DevTools 팀에 문의하기

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

  • crbug.com을 통해 제안이나 의견을 보내주세요.
  • DevTools에서 옵션 더보기   더보기   > 도움말 > DevTools 문제 신고를 사용하여 DevTools 문제를 신고합니다.
  • @ChromeDevTools에서 트윗하세요.
  • DevTools의 새로운 기능 YouTube 동영상 또는 DevTools 도움말 YouTube 동영상에 의견을 남겨주세요.