난 경고했다. 죽을 수도 있다고...

dwarf-han·2020년 9월 6일
0

삽질 스토리

목록 보기
1/1
post-thumbnail

내 경고를 무시하지마..

오늘 나를 5시간의 삽질러 몰고간 컴파일러 경고를 소개하고자 한다. 프로그램 최종 테스트 중 JNI와 연동된 모듈 중 하나가 돌연사하는 상황이 일어났다. 그것은 Segmentation fault (core dumped)

C언어를 해본 사람이라면 대략 어떤 경유로 프로그램이 죽었는지 알 수있다.

  • NULL checking
  • 잘못된 케스팅
  • 메모리 할당 되지 않는 변수 초기화 등등..

대부분 Segmentation fault (core dumped) 은 메모리 혹은 포인터의 사용과 관련된 경우가 많다. 오늘의 버그도 하나의 실수이겠거니 하고 개발환경에서 디버깅을 하는데 싸한 느낌이왔는데 바로 배포 환경에서만 프로그램이 죽는 것이다.

첫 번째 삽질 - 어디에서 죽었능고?

가장먼저 최근 변경된 코드를 뒤지기 시작했다. 변경 이력을 보여 관련된 로그를 추가하고 테스트를 여러번 돌리던 중 특정 함수 라인에서 프로그램이 돌연사하는 것을 알게되었다.

int main() {
    TimeStamp *timestamp = (TimeStamp *) malloc(sizeof(timestamp));
    struct tm *lt, tm_buf;
    time_t tt;
    int ret = 0;

    tt = time(NULL);
    lt = gmtime_r(&tt, &tm_buf);

    if (lt == NULL)
        ret = -1;
    else {
    	// int a = b + 1900;
        timestamp->year = lt->tm_year + 1900; // 사망하셨습니다.

    return 0;
}

슬프게도 사망 라인은 개발 환경에서 디버깅이 더럽게 잘되는 상황을 맞이하게 된다. 심지서 사망라인을 풀어쓰자면 `int a = b + 1900; 그럼 코드가 잘못되었다는 것은 아닐테니 환경을 의심했다.

두 번째 삽질 - 환경이 다른가?

그렇다. 해당 프로젝트는 거지같은 보안 라이브러리 정책으로 인해 소스코드를 직접 업로드하여 컴파일->링킹->빌드 과정을 거쳐야만 했다. 조사 결과 다음의 차이가 있었다.

  • GCC 버전이 다름
  • Linux 커널 버전이 다름
  • 개발 환경은 Ubuntu 배포 환경은 RedHat

공교롭게도 이 두 번째 의심이 나를 5시간이라는 지옥 불구덩이 속으로 몰아넣었다. GCC버전 OS버전으 맞추기 위해 이것 저것 알아보고 찾아보는 헛 발질을 계속하게 되었다. 그러던 우연히 컴파일 도중 내 눈에 들어온 경고 알람이 있으니...

문제의 해결 - 그러니까 너 이 함수 제대로 선언한거 맞냐고!!

main.c:19:10: warning: implicit declaration of function ‘gmtime_r’; did you mean ‘gmtime’? [-Wimplicit-function-declaration]

GCC 컴파일러는 이미 나에게 충분한 경고를 때려 넣고 있었다. 해당 경고를 해석하자면....
"gmtime_r 혹시 gmtime 쓰려는거 아니니? 뭐 일단 비슷한거 같으니.. 프로그램은 실행 가능해~"

순간 당황했다. 해당 함수의 선언을 따라가다 보니 해답을 얻을 수 있었다.

/* Return the `struct tm' representation of *TIMER
   in Universal Coordinated Time (aka Greenwich Mean Time).  */
extern struct tm *gmtime (const time_t *__timer) __THROW;

#ifdef __USE_POSIX
/* Return the `struct tm' representation of *TIMER in UTC,
   using *TP to store the result.  */
extern struct tm *gmtime_r (const time_t *__restrict __timer,
			    struct tm *__restrict __tp) __THROW;
#endif	/* POSIX */

내가 사용하는 gmtime_r 전처리 문에 의하여 감싸져 있다. 저것은 컴파일시 옵션(__USE_POSIX)에 따라 해당 코드를 추가 할 것인지, 안하고 빌드 할 것인지에 대한 것이다. 배포 환경을 확인 해보니 GCC 컴파일 옵션 (-std=c99)이 적용되어 있었다. 결론적으로 나는 없는 함수를 불러내어 실행하려 했던 것으로 그 결과 프로그램이 죽을 수 밖에 없는 것이었다.

깨달음의 시간

이번 5시간의 여정은 컴파일 경고의 무시로 부터 시작되었다. 타입의 캐스팅이 조금만 달라고 컴파일을 안해주는 JAVA와 다르게 C언어는 없어도 해주는 경우가 있다. 그 경고가 implicit declaration 같은 경고이다. 마지막으로 옵션에 대한 정리를 하고 마무리 하고자 한다...

  • c99 : C 표준 라이브러리 버전
  • gnu99 : C 표준 라이브러리를 포함하여 Unix 확장 라이브러리를 포함시킨 버전

아 JAVA하고 싶다.

profile
dwarf-han

0개의 댓글