C/C++용 디버그 도구

이 기사는 ISer Advent Calendar 2020의 일부입니다.
https://adventar.org/calendars/4946
C/C++ 디버깅에 유용한 소프트웨어와 기능을 소개하고 싶습니다.

Mac의 GCC 및 Clang


Mac로 Xcode를 입력하면 Clang이 설치되며, 왜 gcc 명령으로 Clang을 시작하는지 알 수 없습니다.이것은
gcc --version
면 확인할 수 있어요.이 상태에서 GCC 가입
brew install gcc
그래도 되지만 gcc 명령은 Clang을 시작합니다.이 상태에서 GCC를 사용할 때는 gcc-10 명령을 사용합니다.GCC가 시작되었는지 확인하기
gcc-10 --version
하면 됩니다.그러나 GCC의 버전이 업그레이드될 때 gcc-11, gcc-12...필요했어

정적 분석


소스 코드의 해석에는 동적 분석과 정적 분석이 있다.동적 분석은 실제 실행 프로그램이 진행하는 분석이고 정적 분석은 실행 프로그램이 진행하지 않는 분석이다.나는 동적 분석이 평소에 printf 디버깅과 디버깅으로 이루어진다고 생각하기 때문에 여기서 정적 분석을 소개한다.새 소프트웨어를 추가하지 않더라도 GCC 및 Clang은 정적 해결 기능을 제공합니다.GCC에서 -fanalyzer, Clang에 --analyze를 더하여 컴파일하면 정적 해석이 진행됩니다.또 몇 개의 프로그램을 사용해 시도했지만 GCC와 Clang의 정적 해석 기능은 다른 것 같다.
https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Static-Analyzer-Options.html
https://clang-analyzer.llvm.org/

정적 분석의 예


다음 코드를 정적 분석해 보세요.
sa.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    srand((unsigned)time(NULL));
    int array_size;
    scanf("%d", &array_size);
    int *array = malloc(sizeof(int) * array_size);
    for (int i = 0; i < array_size; i++) {
        array[i] = rand();
    }
    for (int i = 0; i < array_size; i++) {
        printf("%d\n", array[i]);
    }
    return 0;
}
GCC에서
gcc-10 sa.c -fanalyzer
로 번역하다.
Clang에서
gcc sa.c --analyze
로 번역하다.
GCC의 경우 경고를 보내지 않지만, Clang의 경우 다음과 같은 경고를 보냅니다.
sa.c:17:12: warning: Potential leak of memory pointed to by 'array'
    return 0;
           ^
1 warning generated.

Xcode


Xcode에서도 정적 구문 분석 기능을 사용할 수 있으며 Clang과 같을 수 있습니다.Product->Analyze 키를 누르면 간단합니다.GUI로 조작할 수 있어 매우 편리하다.
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/debugging_with_xcode/chapters/static_analyzer.html

Sanitizers


Sanitizers라는 편리한 기능의 집합이 있는데 GCC와 Clang에서 사용할 수 있다.
https://github.com/google/sanitizers

AddressSanitizer


AddressSanitizer는 Sanitizers 중 하나로, C/C++ 메모리 관련 오류를 감지할 수 있습니다.GCC나 Clang에 - fsanitize=address를 추가하여 컴파일하면 AddresssSanitizer가 실행 파일을 생성합니다.또한 -fno-omit-frame-pointer를 더하면 창고 추적을 개선할 수 있다.
https://github.com/google/sanitizers/wiki/AddressSanitizer
https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Instrumentation-Options.html
https://clang.llvm.org/docs/AddressSanitizer.html

AddressSanitizer의 예


as.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    srand((unsigned)time(NULL));
    size_t array_size;
    scanf("%zu", &array_size);
    int *array = malloc(sizeof(int) * array_size);
    for (size_t i = 0; i < array_size; i++) {
        array[i] = rand();
    }
    for (size_t i = array_size - 1; i >= 0; i--) {
        printf("%d\n", array[i]);
    }
    free(array);
    return 0;
}
GCC에서
gcc-10 as.c -fsanitize=address -fno-omit-frame-pointer
로 번역하다.
Clang에서
gcc as.c -fsanitize=address -fno-omit-frame-pointer
로 번역하다.
그리고 생성된 실행 파일을 실행하면 Heap-buffer-overflow 오류가 발생합니다.

UndefinedBehaviorSanitizer


UndefinedBehaviorSanitizer는 Sanitizers 중 하나로 C/C++의 다양한 정의되지 않은 동작을 감지할 수 있습니다.기본적으로 GCC나 Clang에서 -fsanitize = undefined를 사용하는 것은 유효하지만 일부 기능은 별도로 지정하지 않으면 무효입니다.
https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Instrumentation-Options.html
https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html

Unndefined Behavior Santizer의 예


us.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ARRAY_SIZE 10

int main(void)
{
    int array[ARRAY_SIZE];
    srand((unsigned)time(NULL));
    for (size_t i = 0; i < ARRAY_SIZE; i++) {
        array[i] = rand();
    }
    for (size_t i = ARRAY_SIZE - 1; i >= 0; i--) {
        printf("%d\n", array[i]);
    }
    return 0;
}
이 경우 GCC에서 오류가 감지되지 않습니다.
Clang에서
gcc us.c -fsanitize=undefined -fsanitize=bounds
를 컴파일하여 생성된 실행 파일을 실행하면 (일반적인 방법으로)
us.c:15:24: runtime error: index 18446744073709551615 out of bounds for type 'int [10]'
오류가 발생했습니다.여기에서 - fsanitize=bounds는 "진열 범위가 정적 결정될 때의 범위 외 접근 검사"옵션입니다.

좋은 웹페이지 즐겨찾기