진행중인 프로젝트에서 Libevent 를 사용중인데.. valgrind 메모리 누수 확인시 자꾸 메모리 누수가 발생하는 것을 확인했다.

어디가 문제일까… 기능상으로는 문제가 없었다.
malloc/calloc 할당한 부분도 문제가 없었다.
valgrind 가 지목한 문제 발생한 부분은 event_new() 부분이었다.

문제 발생 원인은 잘못된 libevent 사용에 있었다.
나는 다음과 같이 프로그램을 개발했었다.

/*
* main.c
*
* Copyright 2014 Sungtae Kim <pchero21@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <event2/event.h>

typedef struct _wctx
{
struct event_base*  w_base;     // main base
} wctx;

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);

int main(int argc, char **argv)
{

wctx* work = calloc(1, sizeof(wctx));

work->w_base = event_base_new();
struct event* main_listen = event_new(work->w_base, STDIN_FILENO, EV_READ, cb_main_listen, work);
event_add(main_listen, NULL);

event_base_dispatch(work->w_base);

printf(“System terminate…n”);
event_free(main_listen);
event_base_free(work->w_base);

return 0;
}

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
wctx* work = (wctx*)arg;
struct event* w_event = evtimer_new(work->w_base, cb_process, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
evtimer_add(w_event, &tVal);
}

static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Process!!n”);
wctx* work = (wctx*)arg;
struct event* w_event = evtimer_new(work->w_base, cb_process_detail, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
evtimer_add(w_event, &tVal);

}

static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Detail Process!!n”);
}

 

아래는 valgrind 로 메모리 누수를 확인한 내용이다.

pchero@MyGalaxy:~/workspace/Study/Program/LibEvent/memory_leak$ echo “Hello” | valgrind –leak-check=full ./main
==2222== Memcheck, a memory error detector
==2222== Copyright (C) 2002-2012, and GNU GPL’d, by Julian Seward et al.
==2222== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2222== Command: ./main
==2222==
Process!!
Detail Process!!
System terminate…
==2222==
==2222== HEAP SUMMARY:
==2222==     in use at exit: 280 bytes in 3 blocks
==2222==   total heap usage: 12 allocs, 9 frees, 1,848 bytes allocated
==2222==
==2222== 136 bytes in 1 blocks are definitely lost in loss record 2 of 3
==2222==    at 0x4C2A2DB: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2222==    by 0x4E43A46: event_new (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x400A16: cb_process (main.c:73)
==2222==    by 0x4E41F93: event_base_loop (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x400933: main (main.c:49)
==2222==
==2222== 144 (136 direct, 8 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==2222==    at 0x4C2A2DB: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2222==    by 0x4E43A46: event_new (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x4009A1: cb_main_listen (main.c:61)
==2222==    by 0x4E41F93: event_base_loop (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x400933: main (main.c:49)
==2222==
==2222== LEAK SUMMARY:
==2222==    definitely lost: 272 bytes in 2 blocks
==2222==    indirectly lost: 8 bytes in 1 blocks
==2222==      possibly lost: 0 bytes in 0 blocks
==2222==    still reachable: 0 bytes in 0 blocks
==2222==         suppressed: 0 bytes in 0 blocks
==2222==
==2222== For counts of detected and suppressed errors, rerun with: -v
==2222== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)

메모리 누수가 확인되는 지점이 두 곳이 보인다. 모두 event_new 를 했던 곳이다. 내용인즉, event_new() 를 통해 생성한 event를 따로 정상 삭제하지 않았다는 내용이다.
나는 timer 에 등록할 경우, 시간이 되어 함수가 작동할 경우, libevent 에서 자동으로 메모리를 해제해 주는 줄 알았다.
하지만 아니었다…

결국, 소스를 다음과 같이 수정하였다.

/*
* main.c
*
* Copyright 2014 Sungtae Kim <pchero21@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <event2/event.h>

typedef struct _wctx
{
struct event_base*  w_base;     // main base
struct event*       w_event;    // work event
} wctx;

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);

int main(int argc, char **argv)
{

wctx* work = calloc(1, sizeof(wctx));

work->w_base = event_base_new();
struct event* main_listen = event_new(work->w_base, STDIN_FILENO, EV_READ, cb_main_listen, work);
event_add(main_listen, NULL);

event_base_dispatch(work->w_base);

printf(“System terminate…n”);
event_free(main_listen);
event_base_free(work->w_base);
free(work);

return 0;
}

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
wctx* work = (wctx*)arg;
//    struct event* w_event = evtimer_new(work->w_base, cb_process, work);
work->w_event = evtimer_new(work->w_base, cb_process, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
//    evtimer_add(w_event, &tVal);
evtimer_add(work->w_event, &tVal);
}

static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Process!!n”);
wctx* work = (wctx*)arg;
//    struct event* w_event = evtimer_new(work->w_base, cb_process_detail, work);
evtimer_assign(work->w_event, work->w_base, cb_process_detail, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
//    evtimer_add(w_event, &tVal);
evtimer_add(work->w_event, &tVal);
}

static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Detail Process!!n”);
wctx* work = (wctx*)arg;
event_free(work->w_event);
}

소스 수정 후, 다시 valgrind 를 돌려보았다.

pchero@MyGalaxy:~/workspace/Study/Program/LibEvent/memory_leak$ echo “Hello” | valgrind –leak-check=full ./main
==2440== Memcheck, a memory error detector
==2440== Copyright (C) 2002-2012, and GNU GPL’d, by Julian Seward et al.
==2440== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2440== Command: ./main
==2440==
Process!!
Detail Process!!
System terminate…
==2440==
==2440== HEAP SUMMARY:
==2440==     in use at exit: 0 bytes in 0 blocks
==2440==   total heap usage: 11 allocs, 11 frees, 1,720 bytes allocated
==2440==
==2440== All heap blocks were freed — no leaks are possible
==2440==
==2440== For counts of detected and suppressed errors, rerun with: -v
==2440== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

결과는 성공!

문제 해결의 포인트는 다음과 같았다.

1. event_new()/evtimer_new() 의 사용을 줄이는 것. 가능하면 event_assign()/evtimer_assign() 을 사용하도록 했다.
2. new() 가 있으면 free() 도 해 준다는 것.

Tags: , , , ,

Leave a Reply