Serial.println("여기 옴") 만으로 디버깅하던 시절.

이제 진짜 디버거 쓰자.


디버깅이란

  • 브레이크포인트: 특정 줄에서 멈춤
  • 스텝 실행: 한 줄씩 실행
  • 변수 확인: 현재 값 보기
  • 콜스택: 어떤 함수에서 왔는지

필요한 것

1. 디버그 프로브

프로브지원 MCU가격
ST-Link V2STM32$5~15
J-Link대부분$300+ (EDU $60)
ESP-ProgESP32$15
CMSIS-DAPARM Cortex$10~30

ST-Link V2 클론이 제일 저렴.

2. 연결

SWD 연결 (STM32):

프로브보드
SWDIOSWDIO (PA13)
SWCLKSWCLK (PA14)
GNDGND
3.3V3.3V (선택)

[env:bluepill]
platform = ststm32
board = bluepill_f103c8
framework = arduino

; 업로드도 ST-Link로
upload_protocol = stlink

; 디버그 설정
debug_tool = stlink
debug_build_flags = -O0 -g    ; 최적화 끄고 디버그 심볼 추가

중요: 최적화 끄기

debug_build_flags = -O0 -g
  • -O0: 최적화 없음 (코드 순서 유지)
  • -g: 디버그 심볼 포함

최적화 켜면 변수가 사라지거나 줄이 뒤섞임.


설정 (ESP32)

[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino

debug_tool = esp-prog         ; 또는 esp-builtin
debug_init_break = tbreak app_main

ESP32는 JTAG 필요. ESP-Prog 또는 FT2232H 기반 프로브.


디버깅 시작

방법 1: 상태바

하단 상태바 → 🐞 (벌레 아이콘) 클릭.

방법 2: 단축키

F5

방법 3: 명령 팔레트

Ctrl + Shift + PPlatformIO: Start Debugging


디버그 화면

디버깅 시작하면 VSCode 레이아웃 변경:

왼쪽 패널

  • VARIABLES: 현재 변수 값
  • WATCH: 감시할 표현식
  • CALL STACK: 함수 호출 스택
  • BREAKPOINTS: 브레이크포인트 목록

상단 툴바

버튼기능단축키
▶️Continue (계속)F5
⏭️Step Over (다음 줄)F10
⬇️Step Into (함수 안으로)F11
⬆️Step Out (함수 밖으로)Shift+F11
🔄RestartCtrl+Shift+F5
⏹️StopShift+F5

브레이크포인트

설정

코드 줄 번호 왼쪽 클릭 → 빨간 점.

void loop() {
  int value = analogRead(A0);   // ← 여기 브레이크포인트
  Serial.println(value);
  delay(1000);
}

조건부 브레이크포인트

빨간 점 우클릭 → “Edit Breakpoint”

조건: value > 500

value가 500 초과일 때만 멈춤.

로그포인트

멈추지 않고 로그만 출력:

빨간 점 우클릭 → “Edit Breakpoint” → “Log Message”

Value is {value}

변수 확인

VARIABLES 패널

현재 스코프의 모든 변수 표시.

Locals
  value: 512
  i: 5
  buffer: char[32] "Hello"

WATCH 패널

특정 표현식 감시:

buffer[0]       → 'H'
value * 2       → 1024
millis() / 1000 → 45

호버

코드에서 변수 위에 마우스 올리면 값 표시.


메모리 확인

레지스터

VARIABLES → Registers 섹션.

r0: 0x00000200
r1: 0x20001234
pc: 0x08001234
sp: 0x20010000

메모리 뷰

명령 팔레트 → “Debug: Memory”

특정 주소 영역 16진수로 보기.


하드폴트 디버깅

STM32에서 HardFault 나면:

  1. 디버거로 실행
  2. HardFault에서 멈춤
  3. Call Stack 확인
  4. 어디서 문제 났는지 추적
Call Stack:
  HardFault_Handler
  BusFault_Handler  
  myFunction         ← 여기서 문제
  main

주의사항

1. 디버그 빌드 크기

디버그 심볼 포함하면 바이너리 커짐.

Release: 50KB
Debug:   150KB

Flash 작은 MCU에선 안 들어갈 수 있음.

2. 타이밍

브레이크포인트에서 멈추면 시간 흐름 멈춤.

타이밍 크리티컬한 코드 (통신 프로토콜 등) 디버깅 어려움.

3. 인터럽트

인터럽트 핸들러에서 멈추면 시스템 먹통 될 수 있음.


1. printf 디버깅 병행

#ifdef DEBUG_MODE
  Serial.printf("Value: %d at line %d\n", value, __LINE__);
#endif

2. assert 활용

#include <assert.h>

void process(int* ptr) {
  assert(ptr != NULL);  // NULL이면 여기서 멈춤
  *ptr = 100;
}

3. 조건부 컴파일

#ifdef DEBUG
  // 디버깅용 코드
#endif

다음 글에서 유닛 테스트.

#13 - 유닛 테스트