Joo-Kwang Park

Joo-Kwang Park

Developer

© 2021

Dark Mode

valgrind 간단 사용법

C언어로 프로그래밍을 하다보면 다양한 문제에 봉착할 수 있다. 문제는 크게 두 가지로 정의할 수 있는데, 첫 번째는 문법으로 오류와 두 번째는 논리적인 오류이다. 최근의 컴파일러가 발달하면서 C언어 문법에 대한 구문 분석기가 매우 정확해지고 수정을 위한 제안을 아주 잘해주고 있다. 이는 llvm 툴이 더욱 자세하게 보여주고 있다. 이와 같은 이유로 문법으로 인한 오류는 너무 찾기 쉬워졌다. 코딩하면서 가장 큰 문제는 논리적인 오류이다. 컴파일과 오브젝트 파일에 대한 링크는 잘 되지만, 프로그램을 실행만 하면 오류가 나는 부분이다. 특히, 메모리 관리를 제대로 하지 못하면 “Segmentation Fault” 오류를 계속 볼 수 있을 것이다. 뿐만 아니라, Multi Threading 프로그래밍을 하다보면 공유 자원에 대한 동기화를 진행할 때 Mutex 또는 Semaphore를 사용할 수가 있다. 동기화를 진행하다보면 lock을 했지만, unlock을 하지 않아서 deadlock에 봉착할 수 있거나, lock이 되지 않은 상태에서 unlock을 하는 등 의도하지 않은 코딩을 할 수가 있다. 이러한 문제를 명령어 단위로 분석해주는 도구인 valgrind를 간단하게 소개하려고 한다.

valgrind는 GPL 시스템을 위한 디버깅 또는 프로파일 프로그램이다. valgrind를 통해서 메모리 관리에 대한 문제를 쉽게 찾아낼 수 있으며, thread에 관련된 버그를 찾아낼 수 있다. 이를 통해서 보다 안전하고 빠르게 프로그램을 만들 수 있도록 도와주는 도구이다. 본 글에서는 간단하게 메모리 관리에 대한 오류를 어떻게 찾는지와 thread 버그를 어떻게 보여주는지를 관찰하려고 한다.

1. valgrind 설치

본 글에서 valgrind를 설치하고 테스트한 환경은 CentOS 7.6 이다. 다음과 같은 명령어로 간단하게 설치가 가능하다.

$ sudo yum install valgrind 

2. 메모리 오류 찾기

메모리 오류를 찾기위한 테스트를 위하여 다음과 같은 간단한 C 코드를 작성하였다.

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
	char *tmp = NULL;
	int i = 0;

	tmp = (char*)malloc(24);
	
	for (i = 0 ; i <= 25 ; i++) {
		tmp[i] = 'a'+i;
	}

	
	return 0;
}

위 코드는 의심의 여지가 없이 매우 간단한 코드이며, 두 가지의 문제점을 내포하고 있다. valgrind 결과를 확인하기 전에 한번 어떤 문제가 있는지 확인해보자.

코드를 보면 char 형 변수에 총 24바이트의 크기를 동적 메모리 할당하는 것을 확인할 수 있다. 반복문을 돌리면서 char 형 변수에 ‘a’+i 값을 집어 넣는데, 이를 0~25번 총 25번 반복문을 수행하는 것을 볼 수 있다. 그리고 프로그램을 종료한다.

이제 valgrind 결과를 가지고 문제점을 파악해보도록 해보자. 다음은 valgrind의 결과이다.

[jkpark@node1 Valgrind]$ valgrind ./mem_leak
==26441== Memcheck, a memory error detector
==26441== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26441== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==26441== Command: ./leak
==26441== 
malloc을 통해서 동적 메모리 할당한 사이즈보다 더 큰 블록에 메모리 write가 되었다는 것을 보여주고 있다. 친절하게도 얼마나 메모리가 할당되어 있고, 얼마나 잘못된 write를 하는지 보여주고 있다.
==26441== Invalid write of size 1
==26441==    at 0x40055E: main (in /home/jkpark/Valgrind/leak)
==26441==  Address 0x5203058 is 0 bytes after a block of size 24 alloc'd
==26441==    at 0x4C29BC3: malloc (vg_replace_malloc.c:299)
==26441==    by 0x40053D: main (in /home/jkpark/Valgrind/leak)
==26441== 
==26441== 
Heap 사용량에 대해서 요약한 결과이다. 하나의 블록에 24바이트가 1개 블록이 할당 되었지만, 0개의 free가 있다는 것을 볼 수 있다. 이를 통해서 메모리 누수가 있는 것을 확인할 수 있다.
==26441== HEAP SUMMARY:
==26441==     in use at exit: 24 bytes in 1 blocks
==26441==   total heap usage: 1 allocs, 0 frees, 24 bytes allocated
==26441== 
친절하게도 아래와 같이 메모리 누수에 대한 내용을 확인할 수 있다. 
==26441== LEAK SUMMARY:
==26441==    definitely lost: 24 bytes in 1 blocks
==26441==    indirectly lost: 0 bytes in 0 blocks
==26441==      possibly lost: 0 bytes in 0 blocks
==26441==    still reachable: 0 bytes in 0 blocks
==26441==         suppressed: 0 bytes in 0 blocks
==26441== Rerun with --leak-check=full to see details of leaked memory
==26441== 
==26441== For counts of detected and suppressed errors, rerun with: -v
==26441== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)

이와 같이 valgrind 도구를 사용하면 메모리 누수를 쉽게 찾을 수 있다. 더 이상 malloc과 free를 일일이 비교해가면서 디버깅 할 필요가 사라지게 된다. 또한, 잘못된 메모리를 참조하여 Segmentation Fault의 위험을 감소시킬 수 있다.

Comments