Tips & Tricks

C++ 디버그를 위한 유용한 팁 7가지

Read this post in other languages:

오늘은 Greg Law와 함께합니다. 이 게스트 블로그 게시물에서 Greg는 C++ 코드 디버그를 위한 유용한 팁을 소개합니다.

Greg Law Greg(@gregthelaw)은 Undo의 공동 창립자 겸 CEO로, 타고 난 프로그래머이지만 소프트웨어와 비즈니스에 모두 관심을 두고 있습니다. Greg은 혁신적인 소프트웨어 기술을 실제 비즈니스에 적용할 때 특히 보람을 느낀다고 합니다. 또한 Greg은 학계와 혁신적인 소프트웨어 스타트업에서의 경력은 20년이 넘습니다.

Brian Kernighan은 “디버그는 처음 프로그램을 작성하는 것보다 2배 어려운 일이라는 걸 누구나 알고 있죠. 프로그래머가 코드를 작성할 때 가능한 한 영리하게 작성한다면, 디버그를 어떻게 할 수 있을까요?라는 유명한 말을 남긴 바 있습니다. 제가 보기에 이 말은 코드를 ‘간단하게’ 작성하라는 의미일 뿐 아니라 디버그가 프로그래밍의 핵심이라는 의미도 내포하고 있습니다. 디버그를 능숙하게 수행하지 않고 훌륭한 프로그래머가 될 수 없습니다. 여러분이 처음 코드를 작성할 때처럼 똑똑하게, 생산적으로 코드를 디버그할 수 있도록 제가 자주 사용하는 C++ 디버그 팁이 도움이 되기를 바랍니다.

CLion에서 디버거 사용해보기

#1 다양한 디버그 도구 활용

다른 모든 조건이 동일하다면, 더 뛰어난 도구를 보유한 개발자가 그렇지 않은 개발자보다 결함을 방지하고 더 빠르게 해결할 수 있을 겁니다. 4가지 디버그 도구의 카테고리는 다음과 같습니다. 프로그래머라면 각 도구의 사용법과 해당 도구가 필요한 상황을 숙지하고 있기를 권장합니다. 여기에는 살펴볼 만한 유료 옵션과 무료 오픈소스 옵션이 포함되어 있습니다.

카테고리 도구의 기능 예시
대화형 디버거 실행을 중지하고 프로그램의 현재 상태를 살펴봅니다. GDB, strace
시간 이동 디버거 실행 시간을 앞뒤로 이동하여 프로그램이 특정 위치까지 어떻게 도달했는지 확인합니다. UDB, rr, WinDbg
동적 코드 검사기 코드를 분석하거나 계측하여 버퍼 오버플로 및 기타 결함을 검사합니다. Valgrind, ASan
정적 코드 검사기 코드를 분석하여 특정 결함의 발생 위험을 판단합니다. Clang Analyzer, Clang-Tidy, Coverity, Cppcheck, IDE에서 기본 제공하는 Linter

저는 Coverity의 Dewang Li와 함께 ACCU 발표에서 카테고리별 도구를 살펴보며 각 도구의 내부 작동 원리를 설명한 바 있습니다.

Anastasia의 CLion 디버거 팁: CLion은 GDB 또는 LLDB 백엔드를 사용한 코드 디버그뿐 아니라 Valgrind 및 Sanitizers 통합을 비롯한 다른 도구와 다양한 정적 코드 분석 옵션을 지원합니다.

#2 조건부 중단점

중단점을 사용하면 코드의 특정 줄이나 함수에서 프로그램 실행을 중단할 수 있습니다. 프로그램이 중단점에 도달하면 사용자가 애플리케이션 상태 검사 또는 조작, 실행 재개 등을 선택할 때까지 실행이 중단됩니다.
저는 더 효율적인 디버그 작업을 위해 조건부 중단점을 자주 사용합니다. 중단점에 도달할 때마다 멈추는 설정은 루프 내에서 중단점이 정의된 경우 지루하게 느껴질 수 있으므로, 조건이 충족되면 실행을 중지하도록 중단점 조건을 정의할 수 있습니다. 예를 들어, 변수 ‘i’가 일반적으로 0인 경우 ‘i’가 0이 아닐 때 실행을 중단하려면 다음과 같이 작성하세요.

(gdb) break my_func if i!=0

디버그하는 프로그램의 프로그래밍 언어로 거의 모든 조건을 자유롭게 작성할 수 있기에, 조건부 중단점은 매우 강력하고 효율적입니다. 조건에는 함수 호출, 변숫값 또는 GDB 표현식의 결과가 포함될 수 있습니다.
자세한 내용은 조건부 중단점 및 기타 여러 중단점의 유형과 디버그 중 각각의 유형을 활용하는 방법을 설명하는 동영상 튜토리얼에서 확인해 보세요.

Anastasia의 CLion 디버거 팁: CLion에서 중단점 도달 시마다 검사되는 조건을 간편하게 지정할 수 있습니다.

#3 감시점

감시점을 사용하면 중단점과 마찬가지로 실행이 중단됩니다. 단, 중단이 발생할 수 있는 특정 코드 줄을 예측하지 않고도 표현식의 이 변경될 때마다 중단된다는 점에서 차이가 있습니다. 감시점은 동시성 이슈를 디버그할 때 매우 유용합니다. 가령, 어떤 스레드나 프로세스가 공유된 리소스를 변경하는지 알아보려는 경우 활용하면 좋습니다. 표현식 중에는 변수 하나의 값만 있는 단순한 것도, 여러 변수와 연산자가 결합된 복잡한 것도 있습니다. 예시는 다음과 같습니다.

  • 단일 변숫값에 대한 참조.
  • 적절한 데이터 타입으로 형 변환된 주소. 예: *(int *)0x12345678은 지정된 주소에서 4바이트 부분을 감시(정수형 변수가 4바이트라고 가정할 경우).
  • 임의의 복잡한 표현식(예: a*b + c/d). 이러한 표현식에서 프로그램의 네이티브 언어로 유효한 모든 연산자가 사용될 수 있습니다.

다음 블로그 게시물과 동영상에서 다양한 유형의 감시점을 사용하는 방법을 설명합니다.

Anastasia의 CLion 디버거 팁: CLion에서 감시점을 추가하는 방법을 알아보려면 웹 도움말 페이지를 참조하세요.

#4 Python의 사용자 정의 디버그 명령어

프로젝트와 팀에 적합하게 디버거를 맞춤 설정하는 것이 좋습니다. GDB로 Python 사용자 정의 명령어를 생성하여 설정해 보세요. 온갖 종류의 스마트한 설정으로 까다로운 버그를 쉽게 탐지하고 해결할 수 있습니다. 또한 특정 프로젝트나 디버그 요구 사항에 따라 GDB를 다양한 방식으로 사용자 지정할 수 있습니다.
Python을 활용하지 않으면 디버그 속도를 높이고 전반적 삶의 질을 개선할 기회를 놓치는 것입니다! 잠시만 시간을 투자하면 빠르게, 그리고 시간이 갈수록 그 효과를 느끼실 겁니다.
예를 들어, 디버거 출력을 소스 관리 시스템에 체크인하고 팀원에게 공유하는 작업을 자동화해보세요. 다음 블로그 게시물은 GDB와 Python 통합 기능을 활용하여 이를 수행하는 방법을 설명하고 있습니다.

Anastasia의 CLion 디버거 팁: 디버그 세션 중 CLion에서 바로 GDB/LLDB 콘솔에 액세스가 지원된다는 사실을 알고 계셨나요? 해당 탭에서 디버거의 출력/오류 스트림을 표시하고, GDB/LLDB 명령어를 실행할 수 있습니다.

#5 구조체를 깔끔하게 출력

변수, 구조체 및 클래스 출력은 디버그의 중요한 과정입니다. 기본적으로 디버거는 개발자가 쉽게 이해할 수 있는 방식으로 값을 출력하지 않을 수 있습니다.
예를 들어, siginfo_t라는 구조체를 출력하는 경우 print 명령어는 해당 구조체의 모든 데이터를 반환하며, 사용된 공용체의 자세한 내용도 이에 포함됩니다.
결국 지저분하며 가독성이 떨어지게 되죠!
다행히 GDB는 ‘pretty-printer’ 함수로 확장될 수 있습니다. GDB는 값을 출력할 때 해당 값에 pretty-printer가 등록되어 있는지 확인하고, 등록된 경우 pretty-printer를 사용해 값을 출력합니다. 등록되지 않은 경우 일반적인 방식으로 값이 출력됩니다.
pretty-printer 함수는 코딩 작업으로 생성해야 하지만, 컴퓨터 화면을 응시하는 시간을 대폭 단축할 수 있을 겁니다. GDB로 구조체를 깔끔하게 출력하는 간단한 방법을 영상으로 확인해 보세요.

Anastasia의 CLion 디버거 팁: GDB에 사용자 지정 pretty-printer를 추가하면 CLion에서 디폴트 값으로 사용됩니다.

#6 시간 이동 디버그

많은 경우 개발자는 프로그램이 수행할 것으로 예상된 것이 아닌, 실제로 수행한 것을 알아야 합니다. 그렇기에 일반적으로 디버그 작업 시에는 버그를 여러 번 복제하고, 정확한 원인을 찾을 때까지 수많은 정보를 계속 들여다보는 과정이 수반됩니다.
시간 이동 디버그 기능을 활용하면 모든 추정과 시행착오를 제거할 수 있습니다. 방금 전의 상황을 디버거에서 바로 알려줄 수 있기 때문이죠.
GDB와 같은 무료 디버거는 시간 이동 디버그 기능을 기본으로 제공합니다. 상당히 우수한 기능이지만 성능이 (아주 크게) 떨어질 수 있습니다. 시간 이동 기능을 중심으로 제작된 유료 디버거인 UDB는 훨씬 빠른 시간 이동 디버그 성능을 제공합니다.
프로세스는 단계별/계속 실행이 시간상 역방향으로 이루어진다는 점을 제외하면 일반 디버그와 동일합니다. 중단점과 감시점은 반대로 작동하므로 프로그램에서 특정 변수가 변경되는 이전의 지점으로 바로 이동하는 데 도움이 될 수 있습니다. 리버스 감시점은 매우 강력합니다. 개발자가 몇 달, 심지어 몇 년까지도 발견하지 못했던 버그가 리버스 감시점을 사용해 불과 몇 시간 이내에 해결된 사례도 꽤 있습니다.
다음 영상에서 GDB로 리버스 디버그를 수행하는 방법을 설명합니다. 프로그램에서 오류를 추적하는 방법을 역순의 단계별로 살펴볼 수 있으니, 직접 확인해 보세요.

Anastasia의 CLion 디버거 팁: Undo는 UDB 시간 이동 디버거용 CLion 플러그인을 개발했습니다. 자세한 내용은 Undo 팀과 함께 작성한 공동 게시물을 참조하세요.

#7 find 명령어로 바이트 시퀀스 검색

디버그 중 프로그램의 메모리 공간에서 특정 바이트 시퀀스를 검색해야 하거나, 특정 객체에 대한 모든 포인터를 표시해야 하는 경우가 있습니다. 바이트 시퀀스에 해당하는 메모리의 모든 8바이트는 식별하려는 주소입니다.

GDB에서 find 명령어는 프로그램을 위한 다른 유형의 검사를 제공합니다. 모든 검색 값은 프로그램의 프로그래밍 언어로 해석됩니다. 예를 들어, hello.c의 소스 언어는 C/C++입니다. 따라서 ‘Hello, world!’라는 문자열을 검색하면 후행하는 '\0'이 포함됩니다.

또한 GDB는 프로그램 프로세스의 메모리 매핑에 대한 정보를 제공하여 프로그램 메모리의 특정 세그먼트에서 집중적으로 검색할 수 있습니다. 검색된 각 일치 항목의 주소와 개수가 반환됩니다.

추가 C++ 디버그 팁 살펴보기

지금까지 C++ 디버그 생산성 개선을 위한 7가지 팁을 알려 드렸습니다. 평소 디버그 습관과 루틴의 작은 변화가 큰 차이를 불러올 수 있으므로 이 팁이 도움이 되기를 바랍니다. 여러분의 팁도 공유해 주세요! 궁금하신 점이 있으시면 Twitter 또는 LinkedIn을 통해 문의주세요.
다음은 디버그 습관 개선에 도움이 되는 리소스입니다.

다음은 JetBrains에서 제공하는 CLion 관련 추가 리소스입니다.

유용한 팁을 공유해준 Greg에게 감사를 전합니다!

독자 여러분이 자주 활용하는 디버거 관련 팁은 무엇인가요? 댓글에 공유해 주세요!

게시물 원문 작성자

image description