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도 알아 두면 다양한 디버깅에 도움을 받을 수 있습니다. 


감사합니다.

매거진의 이전글 마법의 도구 strace

매거진 선택

키워드 선택 0 / 3 0
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari