seukseok의 개인 블로그

FreeRTOS에서 스택 오버플로우가 의심될 때, 제일 먼저 한 3가지

· tech

FreeRTOS 프로젝트에서 랜덤 멈춤 이슈를 스택 오버플로우 관점으로 좁혀가는 방법, configCHECK_FOR_STACK_OVERFLOW 동작 차이, 흔한 링크 에러 해결 포인트를 짧고 실용적으로 정리한다.

결론부터 말하면, 랜덤 멈춤처럼 보이는 FreeRTOS 이슈는 스택부터 의심하는 게 제일 빨랐다.

이번에 겪은 증상은 단순했다. 센서 태스크 + 통신 태스크를 동시에 돌릴 때 가끔 멈추고, 재부팅하면 또 잠깐 정상 동작. 처음엔 CAN 드라이버 타이밍 문제인가 했는데, 실제로는 태스크 스택 여유가 너무 타이트했다.

내가 제일 먼저 확인한 건 딱 세 가지였다.

중요한 포인트 하나: configCHECK_FOR_STACK_OVERFLOW 값이 1일 때와 2 이상일 때 체크 방식이 다르다. FreeRTOS 커널 코드 기준으로는, 2 이상일 때 스택 경계 패턴까지 추가 확인한다.

// FreeRTOSConfig.h
#define configCHECK_FOR_STACK_OVERFLOW 2

그리고 주기적으로 남은 스택을 찍어봤다.

UBaseType_t watermark = uxTaskGetStackHighWaterMark(sensorTaskHandle);
printf("sensor stack watermark: %u words\n", (unsigned)watermark);

여기서 watermark가 20~30 words 수준까지 내려가면, 실험 환경에서는 거의 경고등 켜진 상태로 봤다. ISR이 겹치거나 로그 출력이 늘어나는 순간 바로 터질 수 있어서.

디버깅하다가 많이 만나는 컴파일/링크 이슈도 있었다.

undefined reference to `vApplicationStackOverflowHook`

원인은 보통 두 가지였다.

  1. 훅 함수를 구현 안 했거나 시그니처가 다름
  2. C++ 프로젝트에서 extern "C" 처리를 안 해서 심볼 이름이 바뀜

나는 두 번째 케이스였다.

extern "C" void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    (void)xTask;
    (void)pcTaskName;
    taskDISABLE_INTERRUPTS();
    for(;;) {}
}

여기까지 정리하고 나서 센서 태스크 스택을 512 words에서 768 words로 올렸고, 로그 포맷팅(특히 float printf)을 통신 태스크에서 빼서 멈춤 증상이 사라졌다.

완전한 정답이라기보다, FreeRTOS 멈춤 이슈에서 시행착오를 줄이는 순서에 가깝다.

HardFault가 바로 나는 경우는 MPU 설정이나 스택 가드 옵션 영향도 있어서 보드/툴체인별로 다를 수 있다. 이건 환경차가 커서 확인 필요.

참고로, Linux 쪽 로그 수집 파이프라인을 같이 쓰는 경우에는 SocketCAN처럼 네트워크 스택 기반 인터페이스를 쓰면 관측 포인트를 늘리기 편했다. 임베디드 보드 내부 문제와 호스트 수집 문제를 분리해서 보기 좋다.

참고 자료

정리하면, FreeRTOS 랜덤 멈춤은 큰 이론보다 “스택 숫자”가 먼저였다. 의심되면 바로 watermark부터 찍어보는 걸 추천한다.