지금까지의 여정
1년 전 Chrome은 Chrome DevTools에서 네이티브 WebAssembly 디버깅을 초기 지원한다고 발표했습니다.
기본 단계 지원을 시연하고 향후 소스 맵 대신 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
이제 로컬호스트 HTTP 서버 (예: serve 사용)에서 생성된 페이지를 게재하고 최신 Chrome Canary에서 열 수 있습니다.
이번에는 Chrome DevTools와 통합되고 WebAssembly 파일에 인코딩된 모든 디버깅 정보를 이해하는 데 도움이 되는 도우미 확장 프로그램도 필요합니다. goo.gle/wasm-debugging-extension 링크로 이동하여 설치하세요.
DevTools Experiments에서 WebAssembly 디버깅도 사용 설정해야 합니다. Chrome DevTools를 열고 DevTools 창의 오른쪽 상단에 있는 톱니바퀴 (⚙) 아이콘을 클릭한 다음 실험 패널로 이동하여 WebAssembly 디버깅: DWARF 지원 사용 설정을 선택합니다.
설정을 닫으면 DevTools에서 설정을 적용하기 위해 새로고침하라는 메시지가 표시됩니다. 일회성 설정은 여기까지입니다.
이제 소스 패널로 돌아가 예외 발생 시 일시중지 (⏸ 아이콘)를 사용 설정한 다음 포착된 예외 발생 시 일시중지를 선택하고 페이지를 새로고침합니다. 예외에서 DevTools가 일시중지된 것을 볼 수 있습니다.
기본적으로 Emscripten에서 생성된 글루 코드에서 중지되지만 오른쪽에는 오류의 스택 트레이스를 나타내는 호출 스택 뷰가 표시되며 abort
를 호출한 원래 C 라인으로 이동할 수 있습니다.
이제 범위 뷰를 보면 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줄이 포함된 단일 파일입니다. 하지만 이번에는 그래픽용 SDL 라이브러리와 C++ 표준 라이브러리의 복소수와 같은 일부 외부 API도 사용하고 있습니다.
디버그 정보를 포함하도록 위와 동일한 -g
플래그로 컴파일하고 Emscripten에 SDL2 라이브러리를 제공하고 임의 크기의 메모리를 허용하도록 요청합니다.
emcc -g mandelbrot.cc -o mandelbrot.html \ -s USE_SDL=2 \ -s ALLOW_MEMORY_GROWTH=1
브라우저에서 생성된 페이지를 방문하면 무작위 색상이 포함된 아름다운 프랙탈 도형이 표시됩니다.
DevTools를 열면 원래 C++ 파일이 다시 표시됩니다. 하지만 이번에는 코드에 오류가 없으므로 (휴!) 대신 코드 시작 부분에 몇 가지 중단점을 설정해 보겠습니다.
페이지를 다시 새로고침하면 디버거가 C++ 소스 내부에서 바로 일시중지됩니다.
이미 오른쪽에 모든 변수가 표시되지만 현재 width
및 height
만 초기화되므로 검사할 것이 많지 않습니다.
기본 Mandelbrot 루프 내에 다른 중단점을 설정하고 실행을 재개하여 조금 앞으로 건너뛰어 보겠습니다.
이 시점에서 palette
는 임의의 색상으로 채워졌으며 배열 자체와 개별 SDL_Color
구조를 모두 펼치고 컴포넌트를 검사하여 모든 것이 잘 표시되는지 확인할 수 있습니다 (예: '알파' 채널이 항상 전체 불투명도로 설정됨). 마찬가지로 center
변수에 저장된 복소수의 실수와 허수부를 확장하고 확인할 수 있습니다.
범위 뷰를 통해 이동하기 어려운 중첩된 속성에 액세스하려면 콘솔 평가도 사용할 수 있습니다. 그러나 더 복잡한 C++ 표현식은 아직 지원되지 않습니다.
실행을 몇 번 재개하면 내부 x
도 어떻게 변경되는지 확인할 수 있습니다. 범위 뷰를 다시 확인하거나, 변수 이름을 보기 목록에 추가하거나, 콘솔에서 평가하거나, 소스 코드에서 변수 위로 마우스를 가져가면 됩니다.
여기에서 C++ 문을 스테프인 또는 스테프오버하고 다른 변수도 어떻게 변경되는지 관찰할 수 있습니다.
디버그 정보를 사용할 수 있는 경우에는 이 모든 것이 잘 작동하지만 디버깅 옵션으로 빌드되지 않은 코드를 디버그하려면 어떻게 해야 하나요?
원시 WebAssembly 디버깅
예를 들어 Emscripten에 소스에서 직접 컴파일하는 대신 미리 빌드된 SDL 라이브러리를 제공해 달라고 요청했으므로 적어도 현재로서는 디버거가 연결된 소스를 찾을 방법이 없습니다.
다시 단계를 거쳐 SDL_RenderDrawColor
로 이동해 보겠습니다.
원시 WebAssembly 디버깅 환경으로 돌아갑니다.
조금 무섭게 보일 수 있으며 대부분의 웹 개발자가 다룰 필요가 없지만, 디버그 정보 없이 빌드된 라이브러리를 디버그해야 하는 경우가 있습니다. 제어할 수 없는 서드 파티 라이브러리이거나 프로덕션에서만 발생하는 버그 중 하나가 발생했기 때문일 수 있습니다.
이러한 경우를 지원하기 위해 기본 디버깅 환경도 일부 개선했습니다.
먼저 이전에 원시 WebAssembly 디버깅을 사용했다면 이제 전체 디스어셈블리가 단일 파일에 표시되는 것을 볼 수 있습니다. 더 이상 소스 항목 wasm-53834e3e/
wasm-53834e3e-7
가 어떤 함수에 해당할지 추측할 필요가 없습니다.
새 이름 생성 스키마
디스어셈블리 뷰의 이름도 개선되었습니다. 이전에는 숫자 색인만 표시되거나 함수의 경우 이름이 전혀 표시되지 않았습니다.
이제 WebAssembly 이름 섹션의 힌트, 가져오기/내보내기 경로를 사용하여 다른 디스어셈블리 도구와 마찬가지로 이름을 생성하고, 다른 모든 방법이 실패하면 $func123
와 같은 항목의 유형과 색인을 기반으로 이름을 생성합니다. 위의 스크린샷에서 볼 수 있듯이 이렇게 하면 읽기 쉬운 스택 트레이스와 디스어셈블리를 얻는 데 도움이 됩니다.
사용 가능한 유형 정보가 없으면 기본 유형 외의 값을 검사하기 어려울 수 있습니다. 예를 들어 포인터는 메모리에 저장된 내용을 알 수 있는 방법이 없이 일반 정수로 표시됩니다.
메모리 검사
이전에는 범위 뷰에서 env.memory
로 표시되는 WebAssembly 메모리 객체만 확장하여 개별 바이트를 조회할 수 있었습니다. 이는 일부 사소한 시나리오에서는 작동했지만 확장하기가 특히 편리하지 않았으며 바이트 값 이외의 형식으로 데이터를 재해석할 수 없었습니다. 이 문제 해결에 도움이 되는 선형 메모리 검사기라는 새로운 기능도 추가되었습니다.
env.memory
를 마우스 오른쪽 버튼으로 클릭하면 메모리 검사라는 새 옵션이 표시됩니다.
클릭하면 메모리 검사기가 표시됩니다. 여기에서 16진수 및 ASCII 뷰로 WebAssembly 메모리를 검사하고, 특정 주소로 이동하고, 다양한 형식으로 데이터를 해석할 수 있습니다.
고급 시나리오 및 주의사항
WebAssembly 코드 프로파일링
DevTools를 열면 디버깅을 사용 설정하기 위해 최적화되지 않은 버전으로 WebAssembly 코드가 '다운스케일링'됩니다. 이 버전은 훨씬 느리므로 DevTools가 열려 있는 동안 console.time
, performance.now
및 기타 코드 속도 측정 방법을 사용할 수 없습니다. 표시되는 숫자가 실제 성능을 전혀 나타내지 않기 때문입니다.
대신 DevTools 성능 패널을 사용해야 합니다. 이 패널은 코드를 최대 속도로 실행하고 다양한 함수에 소비된 시간을 자세히 분류하여 보여줍니다.
또는 DevTools를 닫은 상태에서 애플리케이션을 실행하고 완료되면 DevTools를 열어 콘솔을 검사할 수도 있습니다.
향후 프로파일링 시나리오가 개선될 예정이지만 지금은 주의해야 할 사항입니다. WebAssembly 계층화 시나리오에 대해 자세히 알아보려면 WebAssembly 컴파일 파이프라인에 관한 문서를 확인하세요.
Docker / 호스트를 포함한 여러 머신에서 빌드 및 디버그
Docker, 가상 머신 또는 원격 빌드 서버에서 빌드할 때는 빌드 중에 사용되는 소스 파일의 경로가 Chrome DevTools가 실행 중인 자체 파일 시스템의 경로와 일치하지 않는 경우가 발생할 수 있습니다. 이 경우 파일이 소스 패널에 표시되지만 로드되지 않습니다.
이 문제를 해결하기 위해 C/C++ 확장 프로그램 옵션에 경로 매핑 기능을 구현했습니다. 이를 사용하여 임의의 경로를 재매핑하고 DevTools에서 소스를 찾는 데 도움을 줄 수 있습니다.
예를 들어 호스트 머신의 프로젝트가 경로 C:\src\my_project
아래에 있지만 Docker 컨테이너 내에서 빌드되었으며 이 경로가 /mnt/c/src/my_project
로 표시된 경우 이러한 경로를 접두사로 지정하여 디버깅 중에 다시 매핑할 수 있습니다.
일치하는 첫 번째 접두사가 '낙찰'됩니다. 다른 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, 개발자 또는 베타를 기본 개발 브라우저로 사용하는 것이 좋습니다. 이러한 미리보기 채널을 사용하면 최신 DevTools 기능에 액세스하고, 최신 웹 플랫폼 API를 테스트하고, 사용자가 발견하기 전에 사이트에서 문제를 찾을 수 있습니다.
Chrome DevTools팀에 문의하기
다음 옵션을 사용하여 DevTools와 관련된 새로운 기능, 업데이트 또는 기타 사항을 논의하세요.
- crbug.com에서 의견 및 기능 요청을 제출하세요.
- DevTools에서 옵션 더보기 > 도움말 > DevTools 문제 신고를 사용하여 DevTools 문제를 신고합니다.
- @ChromeDevTools에 트윗하세요.
- DevTools의 새로운 기능 YouTube 동영상 또는 DevTools 도움말 YouTube 동영상에 댓글을 남겨주세요.