brunch

You can make anything
by writing

C.S.Lewis

by 강진우 Sep 29. 2015

gdb를 이용해 메모리 덤프 뜨기

Linux Performance

이번 글에서는 gdb를 이용해 메모리 덤프를 생성하는 방법을  살펴보겠습니다. 사실 많이 활용되지는 않지만, 메모리 관련된 이슈가 있을 때 사용하면 꽤 많은 도움을 받을 수 있기 때문에 알고 계시면 언젠가(?)는 도움이 될 겁니다. ^^


언제 메모리 덤프가 필요할까?


여러 가지 경우가 있겠지만 메모리 덤프를 가장 필요로 하는 순간은 역시 프로세스의 메모리 릭이 의심될 때 입니다. 프로세스는 Linux로부터 사용할 메모리 영역을 요청하게 되는데요, 이 때 요청하고 사용한 메모리 영역을 제대로 반환하지 않아서 계속 쌓이게 되면 메모리 릭이 발생하게 됩니다. 그리고 이런 메모리 릭 현상은 주로 Heap 영역에서 발생합니다.

process memory map (http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory/)
Heap 영역 : 프로세스가 데이터의 저장, 가공을 하기 위해 사용하는 메모리 공간으로  malloc과 같은 시스템 콜을 통해 커널로부터  할당받은 메모리 영역

애플리케이션에서 메모리 릭이 의심될 경우, 우선 gdb를 이용해서 메모리 덤프를 만든 후 어떤 데이터들이 해제되지 않고 쌓이고 있는지를 확인하면 어느 부분의 소스 코드에서 문제가 발생하는지를 파악하는데 도움이 됩니다.


gdb 메모리 덤프 실전


그럼 간단하게 메모리 릭을 발생시키는 프로그램을 하나 만들어서  테스트해 보겠습니다.

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define MEGABYTE 1024*1024

int main() {
    struct timeval tv;
    char *current_data;    

    while (1) {                
        gettimeofday(&tv, NULL);
        current_data = (char *) malloc(MEGABYTE);
        sprintf(current_data, "%d", tv.tv_usec);                
        printf("current_data = %s\n", current_data);
        sleep(1);
    }       

    exit(0);
}

malloc을 통해 1MB 의 메모리 영역을  할당받고 특정 값을 해당 영역에 기록하는  코드입니다. free가 없기 때문에 메모리 릭이 발생하게 됩니다.  실행시키고 난 후 pmap 명령을 사용하면 계속해서 메모리 영역이 늘어나는 것을 볼 수 있습니다.

메모리 영역이 늘어나고 있다.

그럼, gdb를 이용해서 메모리 덤프를 떠 보겠습니다. 덤프 뜰 메모리 주소를 확인해 보겠습니다. 

cat /proc/<pid>/smaps을 통해 확인해 보겠습니다. smaps를  살펴보다 보면 아래와 같이 비정상적으로 크기가 커진 Heap 영역을 볼 수 있습니다.

smaps를 통해 살펴본 영역

바로 저 부분이 gdb를 이용해서 메모리 덤프를 뜰  영역입니다. gdb -p <pid> 명령으로 접속한 후에 아래와 같이 명령을  입력합니다.

dump memory /root/memory_dump 0x7fbbc689a000 0x7fbbc77ac000 
gdb 화면

gdb를 빠져 나와서 /root/에 가보면  memory_dump라는 파일이 생성된 것을 볼 수 있습니다. strings 명령으로 어떤 내용들이 써져 있는지 확인해 보겠습니다.

strings 결과

malloc으로  할당받고 sprintf로 해당 메모리 영역에 기록한 값들이 지워지지 않고 계속 append 형식으로 붙어 있는 것을 볼 수 있습니다. (제 환경에서는 맨 처음 tv_usec 값을 찍은 게 1206 이었습니다.)

즉, free를 통해서 명시적으로 해제하지 않았기 때문에 할당된 메모리 영역은 계속 늘어나고 기록된 값들도 지워지지 않고 유지되는 것을 볼 수 있습니다.

실제 애플리케이션에서도 gdb를 이용해서 메모리 덤프를 뜨게 되면 어떤 데이터가 남아서 메모리 릭을 만들어 내는지 살펴볼 수 있습니다.


Case study


실제 모 회사에서 만든 하드웨어 모니터링용 툴이 메모리 릭을 발생하는 이슈가 있었습니다. 특정 메모리 영역이 비정상적으로 컸습니다. 

그 때 당시의 Heap 영역의 사이즈

대상 영역을 gdb로 덤프를 떠서 확인해 보자 아래와 같은 내용이  확인되었습니다.

INFO   | jvm 1    | 2014/12/11 01:30:34 | 01:30:34  INFO[WrapperListener_start_runner]execute command: "./libs/native/MegaCli64" -AdpAllInfo -aALLINFO   | jvm 1    | 2014/12/11 01:30:34 | 01:30:34  INFO[WrapperListener_start_runner]retry count: 3 and isSuccess: trueINFO   | jvm 1    | 2014/12/11 01:30:34 | 01:30:34  INFO[WrapperListener_start_runner]Status Exit Code :OKINFO   | jvm 1    | 2014/12/11 01:30:34 | 01:30:34  INFO[WrapperListener_start_runner]Status Summary :Exit Code: 0x00INFO   | jvm 1    | 2014/12/11 01:30:34 | 01:30:34  INFO[WrapperListener_start_runner]execute command: "./libs/native/MegaCli64" -AdpBbuCmd -aALL

날짜를 보시면 아시겠지만 2014년 12월 11일부터의 로그가 계속 남아 있었습니다. 즉, 해당 프로세스가 로그 파일에 로그를 기록하는 과정에서 메모리 할당을 하고 완료된 후 제대로 반환하지 않아서 메모리 릭이 발생한 것을 확인할 수 있었습니다.


gdb를 통해 메모리 덤프를 남긴다고 해서 모든 문제를 해결할 수 있는 것은 아니지만, 메모리 릭이 발생할 경우 어떤 로직에 의해서 릭이 발생하게 되는지를 빠르게 판단할 수 있어서 많은 도움이 됩니다. 

남아 있는 값들을 확인해 보면 로직을 추정할 수 있기  때문입니다.

strace 만큼은 아니지만 gdb도 알아 두면 다양한 디버깅에 도움을 받을 수 있습니다. 


감사합니다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari