Type Here to Get Search Results !

valgrind 기본 툴과 사용법 | 설정법 - memcheck | cachegrind | callgrind | helgrind | massif | thread |usage

이 글에서는 디버깅에 매우 유용한 오픈소스 소프트웨어인 valgrind에서 사용할 수 있는 툴들에 대해서 설명하고, 기본 사용법 그리고 스레드를 위한 지원내용에 대해서 설명합니다. 

  • valgrind란?
  • Support for Threads
  • valgrind의 옵션을 설정하는 방법
valgrind 기본 툴과 사용법 | 설정법 - memcheck | cachegrind | callgrind | helgrind | massif | thread |usage 썸네일

valgrind란?


Valgrind는 메모리 디버깅, 프로파일링 및 성능 분석을 위한 도구 모음입니다. 다양한 도구가 포함되어 있어 다양한 목적에 맞게 사용할 수 있습니다. 여기에서는 Valgrind에서 제공하는 주요 도구에 대해 자세히 설명해드리겠습니다:

Memcheck: 

Memcheck은 Valgrind의 가장 널리 사용되는 도구로, 메모리 오류와 메모리 누수를 검사합니다. Memcheck은 프로그램의 실행을 추적하고, 할당 및 해제된 메모리에 대한 추적 정보를 수집합니다. 이를 통해 잘못된 메모리 액세스(예: 버퍼 오버런, 버퍼 언더런), 미할당된 메모리 액세스, 잘못된 메모리 해제, 메모리 누수 등을 검출합니다.

Cachegrind: 

Cachegrind는 프로그램의 캐시 메모리 사용과 관련된 정보를 제공하는 프로파일링 도구입니다. Cachegrind는 프로그램의 명령어 실행을 추적하고, 명령어 수행에 따른 캐시 메모리 접근 정보를 수집합니다. 이를 통해 캐시 메모리의 부적절한 사용, 캐시 미스 비율, 캐시 성능 저하 등을 분석할 수 있습니다.

Callgrind: 

Callgrind는 프로그램의 함수 호출과 관련된 정보를 제공하는 프로파일링 도구입니다. Callgrind는 프로그램의 실행을 추적하고, 함수 호출 정보, 호출 횟수, 호출 경로 등을 수집합니다. 이를 통해 프로그램의 함수 호출 구조와 성능을 분석할 수 있습니다.

Helgrind: 

Helgrind는 다중 스레드 환경에서 발생할 수 있는 스레드 관련 오류를 검사하는 도구입니다. 경쟁 조건, 데드락, 스레드 간 동기화 문제 등을 식별할 수 있습니다. Helgrind는 프로그램의 스레드 실행을 추적하고, 스레드 간 동기화 동작을 분석하여 오류를 검출합니다.

Massif: 

Massif는 프로그램의 힙 메모리 사용과 관련된 정보를 제공하는 프로파일링 도구입니다. Massif는 프로그램의 실행을 추적하고, 힙 메모리 할당 정보, 사용량, 누적량 등을 수집합니다. 이를 통해 프로그램의 메모리 사용 패턴을 분석하고, 메모리 누수를 식별할 수 있습니다.

DRD:

DRD는 프로그램의 실행을 추적하고, 스레드 간의 메모리 액세스 및 동기화 동작을 분석하여 경쟁 조건을 식별합니다. 이를 통해 잠재적인 경쟁 조건을 검출하고 디버깅할 수 있습니다.


이 외에도 Valgrind에는 더 많은 도구가 있으며, 이들 도구는 각각 특정한 목적에 맞게 사용됩니다. Valgrind는 커맨드 라인에서 실행되며, 프로그램을 실행할 때 해당 도구를 선택하고 옵션을 설정하여 사용할 수 있습니다.


Valgrind는 커맨드 라인 도구로 사용되며, 다양한 옵션을 사용하여 원하는 검사를 수행할 수 있습니다. 일반적으로 다음과 같은 방식으로 사용됩니다

valgrind [options] your_program [program_options]


Support for Threads


스레드 프로그램과 관련하여 가장 중요한 점은 프로그램이 네이티브 스레딩 라이브러리를 사용하지만 Valgrind는 한 번에 하나의 (커널) 스레드만 실행되도록 실행을 직렬화한다는 것입니다. 이 접근 방식은 진정한 멀티스레드 버전의 Valgrind를 구현할 때 발생하는 끔찍한 구현 문제를 피할 수 있지만, 멀티프로세서 또는 멀티코어 머신이 있더라도 스레드 앱이 동시에 두 개 이상의 CPU를 사용하지 않는다는 것을 의미합니다.


Valgrind는 스레드 자체를 스케줄링하지 않습니다. 단지 간단한 잠금 체계를 사용하여 한 번에 하나의 스레드만 실행되도록 할 뿐입니다. 실제 스레드 스케줄링은 OS 커널의 제어하에 있습니다. 그러나 이것이 의미하는 바는 프로그램이 Valgrind에서 실행될 때 정상적으로 실행될 때와 매우 다른 스케줄링이 표시된다는 것입니다. 이는 Valgrind가 스레드를 직렬화하기 때문이기도 하고, 코드가 평소보다 훨씬 느리게 실행되기 때문입니다.


이러한 스케줄링의 차이로 인해 동시성, 크리티컬 레이스, 잠금 또는 이와 유사한 버그가 있는 경우 프로그램이 다르게 동작할 수 있습니다. 이 경우 헬그라인드 및/또는 DRD 도구를 사용하여 버그를 추적하는 것을 고려할 수 있습니다.


리눅스에서는 클론 시스템 호출, futex 등을 직접 사용할 수도 있습니다. 클론은 모든 것이 공유되거나(스레드) 아무것도 공유되지 않는(포크와 같은) 경우 지원되며, 부분 공유는 실패합니다.


스레드는 위에서 언급한 잠금을 보유하고 있을 때만 코드를 실행합니다. 몇 개의 명령어를 실행한 후 실행 중인 스레드는 잠금을 해제합니다. 그러면 실행 준비가 된 모든 스레드가 잠금을 획득하기 위해 경쟁하게 됩니다.


fair-sched 옵션은 스레드 실행을 직렬화하는 데 사용되는 잠금 메커니즘을 제어합니다.


기본 파이프 기반 잠금 메커니즘(--fair-sched=no)은 모든 플랫폼에서 사용할 수 있습니다. 파이프 기반 잠금은 스레드 간의 공정성을 보장하지 않습니다. 다른 스레드가 실행할 준비가 되었더라도 방금 잠금을 해제했던 스레드가 즉시 잠금을 다시 획득할 가능성이 매우 높습니다. 파이프 기반 잠금을 사용하는 경우, 동일한 멀티스레드 애플리케이션을 여러 번 실행하면 스레드 스케줄링이 매우 달라질 수 있습니다.


일부 플랫폼에서는 퓨텍스를 기반으로 하는 대체 잠금 메커니즘을 사용할 수 있습니다. 사용 가능한 경우 --fair-sched=yes 또는 --fair-sched=try로 활성화됩니다. 퓨텍스 기반 잠금은 스레드 간의 공정성(라운드 로빈 스케줄링)을 보장합니다. 여러 스레드가 실행할 준비가 된 경우, 먼저 잠금을 요청한 스레드에 잠금이 부여됩니다. 시스템 호출(예: 읽기 차단 시스템 호출)에서 차단된 스레드는 아직 잠금을 요청하지 않았으며, 이러한 스레드는 시스템 호출이 완료된 후에만 잠금을 요청한다는 점에 유의하세요.


퓨텍스 기반 잠금의 공정성은 멀티스레드 애플리케이션의 다양한 실행에 대한 스레드 스케줄링의 재현성을 향상시킵니다. 이러한 재현성 향상은 헬그라인드나 DRD를 사용할 때 특히 유용합니다.


Valgrind가 스레드 직렬화를 사용한다는 것은 한 번에 하나의 스레드만 실행할 수 있음을 의미합니다. 멀티프로세서/멀티코어 시스템에서 실행 중인 스레드는 OS 커널 스케줄러에 의해 CPU 중 하나에 할당됩니다. 스레드가 잠금을 획득하면 방금 잠금을 해제한 스레드와 동일한 CPU에 스레드가 할당되는 경우가 있습니다. 때로는 스레드가 다른 CPU에 할당되기도 합니다. 파이프 기반 잠금을 사용하는 경우, 방금 잠금을 획득한 스레드는 일반적으로 방금 잠금을 해제한 스레드와 동일한 CPU에서 스케줄링됩니다. 퓨텍스 기반 메커니즘을 사용하면 방금 잠금을 획득한 스레드가 다른 CPU에서 스케줄링되는 경우가 더 많습니다.


Valgrind의 스레드 직렬화 및 OS 커널 스케줄러에 의한 CPU 할당은 많은 최신 CPU에서 사용할 수 있는 CPU 주파수 스케일링과 나쁜 상호작용을 일으킬 수 있습니다. 전력 소비를 줄이기 위해 CPU/코어가 최근에 사용되지 않은 경우 CPU 또는 코어의 주파수가 자동으로 감소합니다. OS 커널이 방금 잠금을 획득한 스레드를 다른 CPU/코어에 할당하는 경우가 많다면, 이 CPU/코어의 주파수가 현재 낮을 가능성이 큽니다. 이 CPU의 주파수는 잠시 후 증가합니다. 그러나 이 시간 동안 실행 중인 (유일한) 스레드는 낮은 주파수로 실행될 것입니다. 이 스레드가 일정 시간 동안 실행되면 잠금이 해제됩니다. 다른 스레드가 이 잠금을 획득하고 그 사이에 클럭 주파수가 감소한 다른 CPU에서 다시 스케줄링될 수 있습니다.


퓨텍스 기반 잠금으로 인해 스레드가 CPU/코어를 더 자주 변경하게 됩니다. 따라서 CPU 주파수 스케일링이 활성화된 경우 퓨텍스 기반 잠금으로 인해 Valgrind에서 실행되는 멀티스레드 앱의 성능이 크게 저하될 수 있습니다. CPU 주파수 스케일링이 비활성화된 머신에서 실행할 때와 비교하여 최대 50%의 성능 저하가 관찰되었습니다. 파이프 기반 잠금 잠금 체계도 CPU 주파수 스케일링과 심하게 상호작용하여 10~20% 범위의 성능 손실이 관찰되었습니다.


이러한 성능 저하를 방지하려면 모든 CPU/코어가 항상 최대 클럭 속도로 실행되도록 커널에 지정해야 합니다. Linux 배포에 따라 그래픽 인터페이스를 사용하거나 cpufreq-selector 또는 cpufreq-set과 같은 명령줄을 사용하여 CPU 주파수 스케일링을 제어할 수 있습니다.


이러한 문제를 피하는 또 다른 방법은 taskset 명령을 사용하여 OS 스케줄러에 특정 (고정) CPU에 Valgrind 프로세스를 연결하도록 지시하는 것입니다. 이렇게 하면 프로그램의 모든 스레드가 수행해야 할 작업이 있는 한 선택한 CPU가 최대 주파수 설정 아래로 떨어지지 않도록 보장할 수 있습니다.


Valgrind의 DRD(Dynamic Race Detector)는 다중 스레드 환경에서 발생할 수 있는 경쟁 조건(race condition)을 검사하는 툴입니다. 경쟁 조건은 둘 이상의 스레드가 동시에 공유된 자원에 접근하고 수정하는 상황에서 발생할 수 있습니다. 이러한 상황에서 스레드 간의 실행 순서나 타이밍에 따라 예상치 못한 결과가 발생할 수 있습니다.


DRD는 Valgrind의 독립된 서브툴로서, 메모리 오류와 메모리 누수를 검사하는 Memcheck 도구와는 달리 경쟁 조건을 검사합니다. DRD는 프로그램의 실행을 추적하고, 스레드 간의 메모리 액세스 및 동기화 동작을 분석하여 경쟁 조건을 식별합니다. 이를 통해 잠재적인 경쟁 조건을 검출하고 디버깅할 수 있습니다.


DRD는 스레드 간의 경쟁 조건을 식별하기 위해 다양한 기법을 사용합니다. 예를 들어, 메모리의 읽기/쓰기 동작을 추적하고 스레드 간에 공유된 데이터에 대한 액세스를 모니터링합니다. 또한, 잠금(locking) 및 동기화 동작을 분석하여 스레드 간의 동기화 문제를 식별합니다.


DRD는 경쟁 조건을 발견하면 해당 경쟁 조건이 발생한 위치와 스레드 간의 상호작용 정보를 제공합니다. 이를 통해 개발자는 경쟁 조건을 수정하고 스레드 동기화를 올바르게 구현하여 프로그램의 정확성과 안정성을 향상시킬 수 있습니다.

DRD는 Valgrind의 다른 도구와 마찬가지로 커맨드 라인에서 실행되며, 프로그램을 실행할 때 DRD를 선택하고 옵션을 설정하여 사용할 수 있습니다.

기본 옵션 설정법(Setting Default Options)


Valgrind는 다음과 같은 순서로 설정을 읽습니다. 

  1. ~/.valgrindrc 파일
  2.  $VALGRIND_OPTS 환경변수
  3. ./.valgrindrc 파일

이러한 옵션은 명령줄 옵션보다 먼저 지정된 순서로 처리됩니다. 나중에 처리된 옵션은 이전에 처리된 옵션보다 우선합니다. 예를 들어, ./.valgrindrc에 있는 옵션이 ~/.valgrindrc에 있는 옵션보다 우선합니다.

그러므로, 명령줄 옵션(커맨드라인 옵션)을 지정하면 해당 지정 옵션이 우선하여 적용됩니다.

./.valgrindrc 파일이 일반 파일이 아니거나, 모든 사용자에게 쓰기 가능한 파일이거나, 현재 사용자가 소유하지 않은 경우 무시된다는 점에 유의하세요. 
이는 ./.valgrindrc에 잠재적으로 유해하거나 로컬 공격자가 사용자 계정으로 코드를 실행하는 데 사용할 수 있는 옵션이 포함될 수 있기 때문입니다.

VALGRIND_OPTS 또는 .valgrindrc 파일에 넣는 모든 도구별 옵션은 도구 이름과 콜론이 앞에 와야 합니다. 예를 들어, 멤체크가 항상 누출 검사를 수행하도록 하려면 ~/.valgrindrc에 다음 항목을 넣으면 됩니다:
--memcheck:leak-check=yes --max-threads=2048

memcheck 이외의 다른 도구를 실행하면 무시됩니다. memcheck: 부분이 없으면 --leak-check=yes를 이해하지 못하는 다른 도구를 선택하면 문제가 발생할 수 있습니다.


참고: valgrind user manual

https://valgrind.org/docs/manual/manual.html