C/C++ 정적 분석 도구, Cppcheck

🧐 | 2022-02-22

안녕하세요, 넷마블 TPM실 기술분석팀 조윤성입니다.

C나 C++로 서버를 개발하거나 라이브 운영을 하다 보면 다양한 크래시와 마주합니다. 여러 원인이 있겠지만, 그중에서도 ‘null’ 이슈는 흔한 농담이 될 만큼 자주 마주합니다. ‘null’은 무조건 발생하는 크래시기 때문에, 다른 이슈와 비교하면 뇌리에 박혀버릴 정도로 우리를 괴롭힙니다.

실제 크래시 현황에서 ‘null’이 유발하는 크래시가 어느 정도인지 보기 위해, 정돈이 거의 되지 않은 개발 초기 프로젝트 몇 가지를 집계해봤습니다.

중복 원인 제거 전후로 발생 횟수 점유율이 매우 줄어들긴 합니다만, 많은 부분을 점유하고 있다는 사실은 변하지 않았습니다. (중복 원인이 될 만큼 많은 양이라는 뜻이 되기도 합니다.)

크래시를 줄이는 아이디어

이슈를 발견한 후에 바로바로 수정하면서 크래시에 대응하는 방법도 있습니다. 하지만 개발 초기가 아니라 개발 후기 또는 라이브 서버였다면 어떨까요? 서비스 장애가 빈번히 발생해 사용자에게 치명적인 경험을 줄 수도 있습니다. 그래서, 크래시를 미리 확인할 방법을 마련해 이슈에 선제대응할 방법을 찾아야 합니다.

이상적으로는 주기적으로 실행하는 코드 리뷰와 전수조사로 해소할 수 있습니다. 하지만 현실에서 제한된 리소스로 일정에 맞춰서 철저하게 실행하려면, 개발자의 피로도와 검수 정확성 사이에서 타협할 수밖에 없습니다.

앞서 살펴봤던 ‘null’ 크래시만이라도 확실히 감지할 수 있다면, 적당한 타협과 편의에 걸칠 수 있지 않을까요? 그래서 ‘null’ 이슈만이라도 확실하게 제거할 수 있도록, 소스를 정적 분석할 외부 도구인 Cppcheck를 살펴보기로 했습니다.

정적 분석 도구, Cppcheck

Cppcheck에 대한 상세한 소개는 아래 레퍼런스 링크를 참고해주세요.

Cppcheck
공식 홈페이지: https://cppcheck.sourceforge.io/
공식 매뉴얼: http://cppcheck.sourceforge.net/manual.pdf
깃허브 링크: https://github.com/danmar/cppcheck
라이선스: GNU

주요 기능

Cppcheck는 크로스 플랫폼(윈도우, 데비안, 페도라, 맥 등)을 지원하며, 개발IDE에 플러그인으로 연동할 수 있습니다. 2021년 8월 27일 기준으로 C++ 20버전까지 문법 검사를 할 수 있었습니다.

  • null 의심 탐지
  • rvalue reference
  • 배열 overrun
  • 클래스 검사 (미사용 함수, 변수 초기화 등)
  • 메모리릭(메모리 누수)
  • 자원 누수(파일 or device handle 등)

Cppcheck 설치와 실행

Cppcheck는 간단히 설치할 수 있도록 윈도우용 설치 패키지를 제공합니다. Cppcheck 홈페이지에서 윈도우용 설치 패키지를 다운로드하거나, 각자 개발 환경에 맞는 패키지를 찾으면 됩니다.

설치 과정을 마치면 설치 프로그램 리스트에서 Cppcheck를 찾아 실행하면 됩니다.

주요 검사 타입

Cppcheck를 실행하고 ‘보기’ 메뉴를 누르면, Cppcheck가 검사하는 주요 타입을 볼 수 있습니다.

Errors

‘Errors’는 안정성 위험도가 높은 사항입니다. 버퍼 오버플로(Buffer overflow), 버퍼 오버런(Buffer overrun), 메모리릭(Memory leak), null 등이 여기에 속합니다.

Warnings

‘Warnings’는 null 의심처럼, 특정 조건에서 크래시가 날 수 있는 사항입니다.

Style warnings

‘Style warnings’는 C++ 관용 표현식에 맞지 않는 사항입니다. 관용 표현식이므로 꼭 수정하지 않아도, 에러(Errors)가 되지는 않습니다.

그 외

‘Portability’는 멀티 플랫폼 컴파일에 관련된 사항으로 보입니다. (추후 사용상 활용할 수 있는 특징을 찾는다면 내용을 업데이트하겠습니다.) ‘Performance warnings’는 성능 관련 우려로 예상하는 사항입니다. 개발 경험을 누적해 디텍팅 하는 것으로 보였습니다. 마지막으로, ‘Information’은 위에 속하지 않은 기타 정보입니다.

정적 분석 실행

Cppcheck를 활용해 정적 분석을 하는 방법은 크게 2가지로 나눌 수 있습니다. 설치한 툴 그대로 쓰거나, 개발IDE에 플러그인 형태로 쓰는 방법입니다.

제공 툴 활용

Cppcheck를 실행하고 ‘Analyze’ 메뉴에서 ‘파일(단일)’이나 ‘디렉터리(소스)’를 선택하면 해당 항목을 그대로 분석합니다.

비주얼 스튜디오의 외부 도구

Cppcheck는 비주얼 스튜디오에서 외부 도구로 연결할 수 있습니다.

비주얼 스튜디오에 외부 도구 창에서 ‘추가’를 누르면 메뉴 내용을 추가할 수 있습니다. ‘추가’를 눌러 Cppcheck를 인자로 추가해야 합니다. ‘제목’에는 도구에 출력할 메뉴명을 입력하고, ‘명령’에는 Cppcheck를 설치한 경로를 지정합니다.

‘인수’에는 Cppcheck의 인자 값을 입력해야 합니다. 이때 넣을 주요 인자 값은 아래를 참고해주세요. 인자 값 입력이 끝나면 ‘출력 창 사용’에 체크를 하고 ‘확인’을 누릅니다.

  • –enable
    검색 범위를 지정하는 인자입니다. error, warning, style, performance, portability, information, all을 넣을 수 있습니다. 가급적 warning으로 설정하는 것이 좋습니다.
  • –suppress
    탐지하지 말아야 하는 기능을 명시하는 인자입니다.
  • –std
    언어 버전을 지정하는 인자입니다. 툴 실행 시,’ Analyze → C/C++ standard’에 나오는 범위가 지원 범위입니다.
  • –template vs
    비주얼 스튜디오를 사용함을 명시하는 인자입니다.
  • 최후미에는 소스 경로를 인자로 넣습니다.

이번 예시에서 설정한 최종 인자 값은 아래와 같습니다.

--enable=warning --suppress=missingIncludeSystem --std=c++17 --template vs $(ProjectDir)

이제 비주얼 스튜디오의 ‘도구’ 메뉴에 Cppcheck(제목으로 설정한 이름) 항목이 생겼습니다. Cppcheck를 클릭하면 출력창에서 분석 결과를 볼 수 있습니다.

솔루션 단위가 아닌 선택 프로젝트 단위로 분석하려면 인자를 적용할 때 맨 뒤 인자를 조절해 소스 범위를 지정하는 것이 좋습니다.

권장 및 주의사항

Cppcheck를 직접 사용하기보다는, 출력창을 보고 바로 디버그할 수 있도록 비주얼 스튜디오에 연결해 사용하는 것이 좋습니다. 툴을 직접 사용할 때에는 프로젝트 설정을 별도로 해야 하는 번거로움도 생깁니다.

출력창에 나온 이슈 항목 중에서 ‘error(⛔)’로 탐지된 메시지는 최대한 수정하고 ‘warning(❗)’으로 탐지된 메시지 중에서 null 관련 항목은 가급적 수정한다면, null로 발생하는 크래시를 최대한 막을 수 있습니다.

배열을 포인터로 변환해 접근하는 경우, 버퍼 오버런(Buffer overrun)을 탐지하지 못합니다. 배열 사용 시, ‘size’처럼 인자로 넘기는 습관을 지니는 것이 좋습니다. 그 외 코딩 스타일에 따라 탐지되지 않는 것도 있으니 주의하셔야 합니다.

null 잡을 최소 저지선, Cppcheck

여기까지 정적 분석을 위해 Cppcheck를 사용하는 방법을 간단히 살펴봤습니다. 앞서서도 이야기했듯이 디버그를 하면서 error 메시지와 warning 메시지 중에서는 null 관련 항목은 모두 수정하시기를 추천합니다. 정적 분석 툴을 찾기 시작했던 목표가 null 크래시 최소화였던 만큼, 이번 기회에 null은 꼭 잡고 가셨으면 좋겠습니다.

무엇보다도 꼭 유념하셔야 하는 키워드는 ‘Cppcheck는 만능 툴이 아니다’입니다. Cppcheck는 최소 저지선이라는 점을 잊지 말고, 힘내시기 바랍니다.