디버그 심볼은 크래시 덤프·프로파일링에 필요하지만, 빌드 설정에 따라 코드 생성이나 크기에 영향을 줄 수 있습니다. 이 챕터에서는 그 경계를 다룹니다.
디버그 심볼이 코드 생성에 미치는 영향
일반적으로 디버그 정보만 추가하는 옵션(-g, -g3 등)은 **생성되는 기계어(코드 생성)**를 바꾸지 않습니다. 컴파일러는 소스 라인·변수·타입 정보를 별도 섹션(.debug_*, DWARF 등)에 넣을 뿐, 최적화된 코드는 -g 없이 빌드한 것과 동일하게 나옵니다. 따라서 릴리즈 최적화(-O2/-O3)와 -g를 함께 써도 실행 성능은 동일하다고 보면 됩니다.
“DWARF is a debugging information file format used by many compilers and debuggers to support source level debugging.” — DWARF Debugging Standard
이처럼 디버그 정보는 실행 코드가 아니라 별도 형식의 메타데이터로 저장되므로, 심볼을 더하거나 빼는 일이 성능이 아니라 파일 크기·분석 가능성에만 영향을 줍니다.
예외적으로 빌드/링크 설정이 잘못되면 성능에 영향을 줄 수 있습니다. 예를 들어 -g를 켰을 때만 특정 최적화가 비활성화되는 레거시 빌드 스크립트가 있거나, 디버그 정보 생성 과정에서 부수적으로 다른 플래그가 바뀌는 경우입니다. Split DWARF(-gsplit-dwarf)는 디버그 정보를 별도 .dwo 파일로 빼서 링크 시간과 실행 파일 크기를 줄이는 데 쓰이며, 코드 생성 자체는 바꾸지 않습니다.
릴리즈 빌드에서 심볼 유지 전략
프로덕션에서 크래시 덤프를 분석하거나 샘플링 프로파일러(perf, VTune 등)로 심볼이 있는 스택을 보고 싶다면, 릴리즈 빌드에서도 디버그 심볼을 포함한 바이너리를 만든 뒤, 필요에 따라 strip으로 분리하는 방식을 씁니다.
- RelWithDebInfo: 최적화(-O2) + 디버그 정보(-g)로 빌드합니다. 실행 파일이 커지지만, 크래시 시 addr2line·gdb로 소스 위치를 볼 수 있고, perf report에서 함수명이 보입니다.
- 심볼만 별도 보관: strip하지 않은 바이너리를 보관해 두거나, debug symbol 패키지를 따로 배포합니다. 배포용 바이너리는 strip해 크기를 줄이고, 크래시 덤프는 심볼이 있는 환경에서 열어 분석합니다.
이렇게 하면 실행 성능은 최적화된 그대로 유지하면서, 사후 분석만을 위해 심볼을 활용할 수 있습니다.
심볼 분리는 objcopy로 표준화되어 있습니다. 심볼을 별도 파일로 뽑고(--only-keep-debug), 배포 바이너리는 strip한 뒤(--strip-debug), 둘을 --add-gnu-debuglink로 연결해 두면 디버거가 strip된 바이너리에서도 별도 심볼 파일을 찾아 씁니다.
| |
배포에는 strip된 app을, 사후 분석에는 app.debug를 심볼 서버에 보관해 두는 패턴이 일반적입니다. 실행 코드는 세 단계 어디서도 바뀌지 않으므로 성능은 동일합니다.
아래 표는 같은 소스를 빌드 타입만 바꿔 만든 산출물의 예시값입니다. 런타임은 -g 유무와 무관하게 동일하고, 차이는 파일 크기에만 나타납니다(절대 크기는 코드 규모·툴체인에 따라 다름).
| 빌드 | 런타임(예시) | 바이너리 크기(예시) | 분석 가능성 |
|---|---|---|---|
-O2 (no -g) | 1.00× | 기준 | 주소만(어려움) |
-O2 -g (RelWithDebInfo) | 1.00× | 2~5× 큼 | 함수명·소스 라인 |
-O2 -g → strip + .debug | 1.00× | 배포본은 기준 수준 | 심볼 파일로 분석 |
Split DWARF로 빌드 시간 절감
Split DWARF(-gsplit-dwarf)는 디버그 정보를 별도 .dwo 파일로 분리해, 링크 시 오브젝트 파일에서 디버그 섹션을 합치는 작업을 생략합니다. 대형 프로젝트에서 링크 시간과 링커 메모리 사용을 줄이는 데 효과적입니다.
| |
링크 시:
| |
gdb나 addr2line 등 디버거는 .dwo 파일을 자동으로 찾아 심볼을 해석합니다. CI에서 빌드 후 .dwo 파일을 보관해 두면 크래시 분석에 사용할 수 있습니다.
perf + 릴리즈 심볼 워크플로우
RelWithDebInfo 빌드(-O2 -g)를 사용하면 perf report에서 함수명과 소스 라인이 보입니다.
| |
심볼이 없는 strip된 바이너리에서는 perf report에 주소(0x401234)만 나와 분석이 어렵습니다. 배포 바이너리는 strip하되, 심볼이 있는 바이너리를 별도 보관하는 것이 이 문제를 해결합니다.
strip / 비strip, FDO·LTO와 디버그 정보
- strip:
strip명령으로 실행 파일에서 디버그 섹션을 제거하면 파일 크기가 줄어듭니다. 실행 코드는 그대로이므로 성능 차이는 없고, 단지 심볼이 없어져 크래시 분석이 어려워질 뿐입니다. - FDO(Feedback-Directed Optimization) / PGO: 프로파일 수집 시에는 보통 -g를 켜서 프로파일러가 소스/라인과 매핑할 수 있게 합니다. 최종 PGO 최적화 빌드에서 -g를 유지해도 생성 코드는 동일하고, 심볼만 추가로 들어갑니다.
- LTO: LTO 빌드에서 -g를 쓰면 링크 시점에 디버그 정보가 합쳐집니다. 일부 툴체인에서는 LTO + 디버그 정보 조합이 링크 시간을 늘리거나 디버그 품질에 영향을 줄 수 있으므로, 사용하는 컴파일러·버전 문서를 확인하는 것이 좋습니다. 성능 자체는 LTO가 주는 이득을 그대로 받습니다.
용어 정리
| 용어 | 설명 |
|---|---|
| RelWithDebInfo | Release with Debug Info; -O2 수준 최적화 + 디버그 심볼(-g). 크래시 분석·perf에 유리 |
| Split DWARF | -gsplit-dwarf; 디버그 정보를 별도 .dwo 파일로 분리해 링크 시간·실행 파일 크기 절감 |
판단 기준: 디버그 정보와 빌드 타입
| 상황 | 권장 | 비권장 |
|---|---|---|
| 릴리즈 + 크래시 분석 | -O2 -g(RelWithDebInfo) 또는 심볼 별도 보관 | 릴리즈에서 -g 생략 후 분석 불가 |
| 성능 측정 | -g 유지해도 코드 생성 동일 | -g가 성능을 낮춘다고 오해 |
| 배포 크기 | strip으로 심볼 제거·별도 패키지 | 심볼 포함 배포(보안·크기) |
자주 하는 실수
- strip/심볼 분리 시 주의: 배포용으로 strip한 바이너리만 남기고 심볼이 있는 복사본을 버리면, 나중에 크래시 덤프를 받아도 소스 위치를 알 수 없다. RelWithDebInfo 빌드나 심볼 패키지는 별도 보관해 두고, 배포용만 strip한다.
- -g가 성능을 낮춘다고 오해: 디버그 정보만 추가하는 -g는 코드 생성에 영향을 주지 않는다. 릴리즈 최적화(-O2/-O3)와 -g를 함께 써도 실행 성능은 동일하다. 성능 측정 시 -g를 빼야 한다고 잘못 알고 있지 않도록 한다.
학습 성과 목표
- 디버그 정보(-g)가 코드 생성을 바꾸지 않음을 설명할 수 있다.
- 릴리즈 빌드에서 심볼 유지(RelWithDebInfo·strip 분리) 전략을 적용할 수 있다.
- strip·FDO·LTO와 디버그 정보의 관계를 설명할 수 있다.
비판적 시각: 한계와 트레이드오프
디버그 정보는 분석 편의를 위한 것이지, 성능을 바꾸지 않는다. 다만 빌드 스크립트가 -g와 다른 플래그를 엮어 두어서, -g를 켰을 때만 최적화가 달라지는 레거시 환경이 있을 수 있다. 그런 경우에는 스크립트를 정리해 “디버그 정보 = 별도 섹션만"이 되도록 하고, 성능에 영향을 주는 요인과 분리하는 것이 좋다.
핵심 요약
| 항목 | 요약 |
|---|---|
| -g와 성능 | 디버그 정보만 추가 시 생성 기계어 동일; 성능 영향 없음 |
| 릴리즈 전략 | RelWithDebInfo 또는 심볼 별도 보관 후 strip |
| LTO+PGO | -g 유지해도 성능 이득 유지; 툴체인별 링크·품질 확인 |
다음 장에서는
C++20 Modules가 빌드 시간과 런타임에 미치는 영향, 도입 단계별 전략을 다룹니다.
→ C++20 Modules (챕터 11)
![Featured image of post [Compiler 03] 디버그 정보와 릴리즈 성능](/post/compiler-optimization/debug-info-and-release-performance/wordcloud_hu_9bb4c4cff1768a61.webp)
![[Compiler 03] 컴파일러 intrinsics 카탈로그](/post/compiler-optimization/compiler-intrinsics-catalog/wordcloud_hu_e930fa57b2241981.webp)
![[Compiler 03] Sanitizer 성능 오버헤드](/post/compiler-optimization/sanitizer-overhead-performance/wordcloud_hu_6031575f5f1179fb.webp)
![[Compiler 03] 디버그 정보와 릴리즈 성능](/post/compiler-optimization/debug-info-and-release-performance/wordcloud_hu_2020e2d649d44375.webp)
![[Compiler 03] C++20 Modules와 빌드·성능](/post/compiler-optimization/cpp20-modules-build-performance/wordcloud_hu_fb6a0090a25cd0b2.webp)
![[Compiler 03] 빌드 병렬화: ccache, distcc, sccache](/post/compiler-optimization/build-parallelization-ccache-distcc-sccache/wordcloud_hu_78f655ff2fcdfcd0.webp)
![[Compiler 03] AutoFDO 워크플로우: 샘플링 기반 프로파일 최적화](/post/compiler-optimization/autofdo-workflow-sampling-based/wordcloud_hu_d305107584078a19.webp)
![[Compiler 03] CPU 기능별 함수 다중 버전](/post/compiler-optimization/function-multiversioning-cpu/wordcloud_hu_f5001b04a04f9a62.webp)
![[Compiler 03] GCC vs Clang vs MSVC 최적화 차이](/post/compiler-optimization/compiler-comparison-gcc-clang-msvc/wordcloud_hu_f9e92dca98d719c3.webp)
![[Compiler 03] Introduction: Low-latency 컴파일러·빌드 최적화](/post/compiler-optimization/getting-started-compiler-build-performance-tuning/wordcloud_hu_54fcb9b6f9bee677.webp)