TL;DR

  • C++26은 플랫폼별 중단점 구현을 표준화한 <debugging> 헤더를 제공하며, 세 함수는 모두 noexcept와 프리스탠딩(freestanding) 환경을 지원함.
  • std::breakpoint()는 디버거가 연결된 경우 실행을 멈추고 디버거에 제어를 넘기며, 디버거가 없을 때의 동작은 구현 정의임.
  • std::is_debugger_present()는 호출할 때마다 디버거 연결 여부를 확인하며, 프리스탠딩 환경의 플랫폼별 구현을 위해 재정의할 수 있음.
  • std::breakpoint_if_debugging()은 디버거가 연결된 경우에만 중단점을 설정하는 편의 함수임.
  • 디버거 감지 함수는 진단 출력, 타임아웃, 메모리 할당자, 최적화 설정 등 중단점 외의 디버깅 동작에도 활용할 수 있음.

디버거 중단점 구현의 표준화

  • C++에서 프로그램 코드로 중단점을 설정하려면 컴파일러와 플랫폼에 따라 __debugbreak(), __builtin_debugtrap(), x86 어셈블리의 int3, raise(SIGTRAP) 등을 조건부로 사용하는 방식이 일반적임.
  • 주요 C++ 프로젝트는 각자 이 기능을 구현해 왔으며, Boost.Test의 debugger_break(), Catch2의 CATCH_BREAK_INTO_DEBUGGER, Dear ImGui의 IM_DEBUG_BREAK(), Unreal의 UE_DEBUG_BREAK가 그 사례임.
  • 도구 구현자에게는 플랫폼별 지식이 잘 알려져 있었지만, 표준화되어 있지는 않았음.
  • Rene Ferdinand Rivera Morell의 제안인 P2546R5와 동반 제안 P2810R4를 통해 C++26에 <debugging> 헤더와 세 함수가 추가됨.

세 함수

  • <debugging> 헤더는 std::breakpoint(), std::is_debugger_present(), std::breakpoint_if_debugging()을 선언함.
  • 세 함수는 모두 noexcept이며 프리스탠딩 환경에서도 제공됨.
  • 디버깅은 런타임 작업이므로 세 함수 중 constexpr 함수는 없음.

`std::breakpoint()`

  • 조건 없이 중단점을 설정함. 디버거가 연결되어 있으면 실행이 멈추고 제어가 디버거로 넘어감.
  • x86에서는 보통 단일 int3 명령어로 컴파일됨.
  • 디버거가 없을 때의 동작은 구현 정의임. 대부분의 플랫폼에서는 SIGTRAP 같은 시그널을 프로세스에 보내며, 해당 시그널을 처리하지 않으면 프로세스가 종료됨.

`std::is_debugger_present()`

  • 프로그램이 현재 디버거에 의해 추적되고 있는지 bool 값으로 반환함.
  • 호출할 때마다 즉시 확인하며 결과를 캐시하지 않음. 실행 중 언제든 디버거가 연결되거나 분리될 수 있기 때문임.
  • 내부 구현은 플랫폼에 따라 다름. Windows 구현은 ::IsDebuggerPresent()를 호출할 수 있으며, Linux의 GCC libstdc++는 /proc/self/status를 읽고 TracerPid 필드를 확인함.
  • 표준은 특정 확인 방법을 요구하지 않으며, 의미는 구현 정의이고 의도된 동작은 비규범적 주석에 설명됨.
  • 이 함수는 operator new와 operator delete처럼 재정의할 수 있음. 예를 들어 플랫폼이 제공하는 하드웨어 디버그 플래그를 반환하도록 자체 구현을 정의할 수 있음.
  • P2810R4는 프리스탠딩 환경 문제를 해결하기 위해 재정의 가능성을 도입함. 베어메탈 플랫폼에는 디버거를 감지하는 운영체제 수준의 방법이 없을 수 있음.
  • 재정의가 불가능하면 프리스탠딩 환경에서 함수를 제외해 곳곳에 #ifdef를 요구하거나, 쓸모없는 기본값을 반환해야 하는 문제가 생김. 재정의 가능성은 플랫폼이 제공하는 수단에 함수를 연결할 수 있게 함.

`std::breakpoint_if_debugging()`

  • 가장 간편하게 사용할 수 있는 함수로, std::is_debugger_present()가 참이면 std::breakpoint()를 호출하는 것과 같음.
  • 디버거가 연결되어 있으면 중단점에 도달하고, 연결되어 있지 않으면 아무 동작도 하지 않음.
  • Catch2, Boost.Test를 비롯한 프레임워크가 수동으로 구현해 온 패턴임.

중단점 외의 활용

  • std::is_debugger_present()를 별도로 제공하므로 조건부 중단점 외에도 다양한 디버깅 동작에 활용할 수 있음.
  • 디버깅 중 추가 진단 출력을 생성함.
  • 단계별 실행 중 발생할 수 있는 타임아웃을 비활성화함.
  • 디버거 친화적인 메모리 할당자로 전환함.
  • 디버깅을 어렵게 만드는 최적화를 건너뜀.
  • 관심사를 분리한 설계임. breakpoint()와 is_debugger_present()는 서로 독립적인 구성 요소이며, breakpoint_if_debugging()은 두 함수를 조합한 편의 함수임.

결론

  • <debugging>은 플랫폼별 구현이 잘 알려져 있고 주요 프로젝트가 이미 자체 버전을 보유한 기능을 세 함수로 표준화한 C++26 추가 사항임.
  • 가장 유용한 표준 라이브러리 추가 기능은 때로 이처럼 단순한 기능임.