Chrome 92에서는 선형 메모리 버퍼를 검사하는 도구인 메모리 검사기를 도입했습니다. 이 도움말에서는 C/C++ 디버깅을 위한 검사기를 개선한 방법과 그 과정에서 발생한 기술적 문제를 설명합니다.
C/C++ 디버깅 및 메모리 검사기를 처음 사용하는 경우 다음과 같은 관련 블로그 게시물을 참고하세요.
- 심층 메모리 디버깅에 관심이 있으신가요? 메모리 검사기 소개를 참고하세요.
- 전체 C/C++ 디버깅 도구 모음을 소개받고 싶으신가요? 최신 도구로 WASM 디버깅 및 더 빠르게 WebAssembly 디버깅을 참고하세요.
소개
메모리 검사기는 선형 메모리 버퍼에 더 강력한 디버깅 옵션을 제공합니다. C/C++의 경우 WebAssembly 메모리에서 C/C++ 메모리 객체를 검사할 수 있습니다.
주변 WebAssembly 메모리에서 객체의 바이트를 인식하는 것이 골칫거리였습니다. 객체의 크기를 알고 객체의 시작 부분부터 바이트를 계산해야 합니다. 아래 스크린샷에서는 10개 요소 int32
배열의 첫 번째 바이트가 선택되어 있지만 배열에 속한 다른 바이트는 바로 알 수 없습니다. 객체에 속한 모든 바이트를 즉시 인식할 수 있다면 얼마나 좋을까요?
메모리 검사기에서 객체 강조표시
Chrome 107부터 메모리 검사기가 C/C++ 메모리 객체의 모든 바이트를 강조 표시합니다. 이렇게 하면 주변 메모리와 구분할 수 있습니다.
아래 동영상에서 Memory Inspector의 작동 모습을 확인하세요. 메모리 검사기에서 배열 x
를 표시하면 메모리 뷰어에 강조 표시된 메모리가 바로 위에 있는 새 칩과 함께 표시됩니다. 이 칩은 강조 표시된 메모리의 이름과 유형을 알려줍니다. 칩을 클릭하여 객체의 메모리로 이동합니다. 칩 위로 마우스를 가져가면 X 표시 아이콘이 나타납니다. 이 아이콘을 클릭하면 강조표시가 삭제됩니다.
검사하는 객체 외부의 바이트를 선택하면 방해가 되지 않도록 강조 표시가 흐리게 표시됩니다. 다시 초점을 맞추려면 객체의 바이트 또는 칩을 다시 클릭합니다.
객체 강조 표시는 배열에만 국한되지 않습니다. 구조체, 객체, 포인터도 검사할 수 있습니다. 이번 변경으로 C/C++ 앱의 메모리를 그 어느 때보다 쉽게 탐색할 수 있습니다.
사용해 보고 싶으신가요? 다음을 수행해야 합니다.
- Chrome 107 이상을 사용합니다.
- C/C++ DWARF 확장 프로그램을 설치합니다.
- DevTools > 설정 > 실험 > WebAssemble 디버깅: DWARF 지원 사용 설정에서 DWARF 디버깅을 사용 설정합니다.
- 이 데모 페이지를 엽니다.
- 페이지의 안내를 따릅니다.
디버깅 예시
이 섹션에서는 C/C++ 디버깅에 Memory Inspector를 사용하는 방법을 설명하는 장난감 버그를 살펴보겠습니다. 아래 코드 샘플에서는 프로그래머가 정수 배열을 만들고 포인터 산술을 사용하여 마지막 요소를 선택하기로 결정합니다. 안타깝게도 프로그래머가 포인터 계산을 잘못하여 프로그램이 마지막 요소를 출력하는 대신 무의미한 값을 출력합니다.
#include <iostream>
int main()
{
int numbers[] = {1, 2, 3, 4};
int *ptr = numbers;
int arraySize = sizeof(numbers)/sizeof(int);
int* lastNumber = ptr + arraySize; // Can you notice the bug here?
std::cout <<../ *lastNumber <<../ '\n';
return 0;
}
프로그래머가 Memory Inspector를 사용하여 문제를 디버그합니다. 이 데모를 따라해 보세요. 먼저 Memory Inspector에서 배열을 검사하고 numbers
배열에 예상대로 정수 1
, 2
, 3
, 4
만 포함되어 있는지 확인합니다.
그런 다음 범위 창에서 lastNumber
변수를 표시하고 포인터가 배열 외부의 정수를 가리키는 것을 확인합니다. 이 정보를 바탕으로 프로그래머는 8번 줄에서 포인터 오프셋을 잘못 계산했음을 깨닫습니다. ptr + arraySize - 1
이어야 합니다.
이는 예시일 뿐이지만 객체 강조 표시가 메모리 객체의 크기와 위치를 효과적으로 전달하는 방법을 보여줍니다. 이를 통해 C/C++ 앱의 메모리 내에서 어떤 일이 일어나고 있는지 더 잘 이해할 수 있습니다.
DevTools에서 강조 표시할 항목을 파악하는 방법
이 섹션에서는 C/C++ 디버깅을 지원하는 도구 생태계를 살펴봅니다. 특히 DevTools, V8, C/C++ DWARF 확장 프로그램, Emscripten을 사용하여 Chrome에서 C/C++ 디버깅을 실행하는 방법을 알아봅니다.
DevTools에서 C/C++ 디버깅의 모든 기능을 사용하려면 다음 두 가지가 필요합니다.
- Chrome에 설치된 C/C++ DWARF 확장 프로그램
- 이 블로그 게시물의 안내에 따라 최신 Emscripten 컴파일러를 사용하여 WebAssembly로 컴파일된 C/C++ 소스 파일
이유가 무엇인가요? Chrome의 JavaScript 및 WebAssembly 엔진인 V8은 C 또는 C++를 실행하는 방법을 모릅니다. C/C++에서 WebAssembly로 컴파일하는 컴파일러인 Emscripten을 사용하면 C 또는 C++로 빌드된 앱을 WebAssembly로 컴파일하고 브라우저에서 실행할 수 있습니다.
컴파일 중에 emscripten은 DWARF 디버그 데이터를 바이너리에 삽입합니다. 대략적으로 이 데이터는 확장 프로그램이 C/C++ 변수에 해당하는 WebAssembly 변수 등을 파악하는 데 도움이 됩니다. 이렇게 하면 V8에서 실제로 WebAssembly를 실행 중이더라도 DevTools가 C++ 변수를 표시할 수 있습니다. 궁금하다면 DWARF 디버그 데이터의 예를 보려면 이 블로그 게시물을 확인하세요.
lastNumber
를 표시하면 실제로 어떻게 되나요? 메모리 아이콘을 클릭하는 즉시 DevTools에서 검사할 변수를 확인합니다. 그런 다음 lastNumber
의 데이터 유형 및 위치에 대한 확장 프로그램을 쿼리합니다. 확장 프로그램이 이 정보로 응답하는 즉시 메모리 검사기는 관련 메모리 슬라이스를 표시할 수 있으며, 유형을 알면 객체의 크기도 표시할 수 있습니다.
앞의 예에서 lastNumber
를 보면 lastNumber: int *
를 검사했지만 메모리 검사기의 칩에는 *lastNumber: int
라고 표시됩니다. 왜 그런 거죠? 검사기는 C++ 스타일 포인터 역참조를 사용하여 표시되는 객체의 유형을 나타냅니다. 포인터를 검사하면 검사기에 포인터가 가리키는 대상이 표시됩니다.
디버거 단계에서 강조 표시 유지
메모리 검사기에서 객체를 표시하고 디버거로 단계를 진행하면 검사기에서 여전히 적용 가능하다고 판단되는 경우 강조 표시를 유지합니다. 처음에는 로드맵에 이 기능이 포함되어 있지 않았지만, 디버깅 환경이 저하된다는 사실을 곧바로 깨달았습니다. 아래 동영상과 같이 모든 단계 후에 배열을 다시 검사해야 한다고 가정해 보겠습니다.
디버거가 새 중단점에 도달하면 Memory Inspector는 다시 V8 및 이전 강조 표시와 연결된 변수의 확장 프로그램을 쿼리합니다. 그런 다음 객체의 위치와 유형을 비교합니다. 일치하면 강조 표시가 유지됩니다. 위 동영상에는 x
배열에 쓰는 for 루프가 있습니다. 이러한 작업은 배열의 유형이나 위치를 변경하지 않으므로 강조 표시된 상태로 유지됩니다.
포인터에 어떤 영향을 미치는지 궁금할 수 있습니다. 강조 표시된 포인터가 있고 다른 객체에 다시 할당하면 강조 표시된 객체의 이전 위치와 새 위치가 달라 강조 표시가 사라집니다. 새로 가리키는 객체는 WebAssembly 메모리의 어디에나 있을 수 있으며 이전 메모리 위치와 거의 관련이 없을 수 있으므로 새 메모리 위치로 이동하는 것보다 강조 표시를 삭제하는 것이 더 명확합니다. Scope 창에서 메모리 아이콘을 클릭하여 포인터를 다시 강조 표시할 수 있습니다.
결론
이 도움말에서는 C/C++ 디버깅을 위한 메모리 검사기의 개선사항을 설명했습니다. 이 새로운 기능을 통해 C/C++ 앱의 메모리 디버깅이 간소화되기를 바랍니다. 더 나은 기능을 제안하고 싶다면 버그를 신고해 주세요.
다음 단계
자세한 내용은 다음을 참고하세요.