From owner-freebsd-stable@FreeBSD.ORG Mon Feb 25 05:24:51 2008 Return-Path: Delivered-To: freebsd-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2924216A400 for ; Mon, 25 Feb 2008 05:24:51 +0000 (UTC) (envelope-from arun.balakrishnan@wipro.com) Received: from wipro-blr-out01.wipro.com (wipro-blr-out01.wipro.com [203.91.198.74]) by mx1.freebsd.org (Postfix) with ESMTP id E2EAC13C45B for ; Mon, 25 Feb 2008 05:24:49 +0000 (UTC) (envelope-from arun.balakrishnan@wipro.com) X-AuditID: cb5bdd57-ae738bb000000741-73-47c25ddacb93 Received: from blr-ec-bh02.wipro.com (unknown [10.201.50.92]) by wipro-blr-out01.wipro.com (Symantec Mail Security) with ESMTP id 3FD254E4002; Mon, 25 Feb 2008 11:49:06 +0530 (IST) Received: from BLR-SJP-MBX01.wipro.com ([10.101.50.182]) by blr-ec-bh02.wipro.com with Microsoft SMTPSVC(6.0.3790.3959); Mon, 25 Feb 2008 10:54:47 +0530 Received: from [127.0.0.1] ([10.115.5.159]) by BLR-SJP-MBX01.wipro.com with Microsoft SMTPSVC(6.0.3790.3959); Mon, 25 Feb 2008 10:54:46 +0530 Message-ID: <47C2511D.5040202@wipro.com> Date: Mon, 25 Feb 2008 10:54:45 +0530 From: Arun Balakrishnan User-Agent: Thunderbird 2.0.0.9 (Windows/20071031) To: Kostik Belousov References: <47C00A1B.5030708@wipro.com> <20080223214313.GF57756@deviant.kiev.zoral.com.ua> In-Reply-To: <20080223214313.GF57756@deviant.kiev.zoral.com.ua> content-transfer-encoding: 7bit X-OriginalArrivalTime: 25 Feb 2008 05:24:46.0833 (UTC) FILETIME=[BC3DBE10:01C8776E] X-Brightmail-Tracker: AAAAAA== MIME-Version: 1.0 Content-Type: text/plain; charset="ISO-8859-1" X-Content-Filtered-By: Mailman/MimeDel 2.1.5 Cc: kan@freebsd.org, freebsd-stable@freebsd.org Subject: Re: Memory Leak under FreeBSD 6.0 RELEASE X-BeenThere: freebsd-stable@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Production branch of FreeBSD source code List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 25 Feb 2008 05:24:51 -0000 Wow! Thanks a lot for the reply. The patch you provided really gave some insight on the underlying problem. In our final product, the library will be loaded and used only once per instance. However the automated test suites for the library, do this for over 2000 times as part of functional testing and memory leak testing. In this we were getting a huge leak on FreeBSD. One more question though. (Second one in the list of queries I had posted in my first mail.) 2. While executing this without Valgrind, in another terminal we did a "ps -Aopid,rss | grep LibLoader_" continuously in a loop and saw that the RSS (resident set size) field value keeps increasing by 4KB every now and then. The same experiment on GNU/Linux shows that RSS remains at the same value. What could be the cause for the ever rising RSS value? Could you throw some light on what could be the possible reason for this? Is RSS value directly mappable to the leak that we see in libc? This is another issue that is acting as a show stopper for us. Thanks again, ~Arun Kostik Belousov wrote: On Sat, Feb 23, 2008 at 05:27:15PM +0530, Arun Balakrishnan (WT01 - Computing, Storage & Software Products) wrote: Hi, We are currently working on a project wherein we are porting a library from GNU/Linux to FreeBSD 6.0 - RELEASE 32-bit and 64-bit. As part of the standard memory leak tests, we noticed that the ported library is leaking memory. After lots of analysis we found something very strange. Just repeatedly loading and unloading our library was itself throwing up a leak. We are able to reproduce a similar leak using the following steps: 1. SimpleLib.cpp - Simple dummy library 2. LibLoader.cpp - Utility to repeatedly load the library 3. Compile as mentioned 4. Run under Valgrind for multiple times (31 times in our example. Hard coded for simpilicity) =================SimpleLib.cpp=================== #include #include class CLeaker { public: CLeaker() { }; virtual ~CLeaker() { }; }; CLeaker obj; ================LibLoader.cpp====================== #include "stdio.h" #include "dlfcn.h" #include #include #include int main() { int i = 0; int loop = 31; while (i #include #include +#include #include "atexit.h" #include "un-namespace.h" @@ -56,7 +57,7 @@ static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZ ER; #define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) struct atexit { - struct atexit *next; /* next in list */ + LIST_ENTRY(atexit) link; int ind; /* next index in this table */ struct atexit_fn { int fn_type; /* ATEXIT_? from above */ @@ -69,7 +70,10 @@ struct atexit { } fns[ATEXIT_SIZE]; /* the table itself */ }; -static struct atexit *__atexit; /* points to head of LIFO stack */ +/* Head of LIFO stack */ +LIST_HEAD(, atexit) __atexit = LIST_HEAD_INITIALIZER(__atexit); +static struct atexit __atexit0; /* one guaranteed table */ +static unsigned long __atexit_gen; /* * Register the function described by 'fptr' to be called at application @@ -79,30 +83,33 @@ static struct atexit *__atexit; /* points to he ad of LIFO stack */ static int atexit_register(struct atexit_fn *fptr) { - static struct atexit __atexit0; /* one guaranteed table */ struct atexit *p; + unsigned long old__atexit_gen; _MUTEX_LOCK(&atexit_mutex); - if ((p = __atexit) == NULL) - __atexit = p = &__atexit0; - else while (p->ind >= ATEXIT_SIZE) { - struct atexit *old__atexit; - old__atexit = __atexit; - _MUTEX_UNLOCK(&atexit_mutex); - if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL) - return (-1); - _MUTEX_LOCK(&atexit_mutex); - if (old__atexit != __atexit) { - /* Lost race, retry operation */ + if (LIST_EMPTY(&__atexit)) { + p = &__atexit0; + LIST_INSERT_HEAD(&__atexit, p, link); + } else { + retry: + p = LIST_FIRST(&__atexit); + if (p->ind >= ATEXIT_SIZE) { + old__atexit_gen = __atexit_gen; _MUTEX_UNLOCK(&atexit_mutex); - free(p); + if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL) + return (-1); _MUTEX_LOCK(&atexit_mutex); - p = __atexit; - continue; + if (old__atexit_gen != __atexit_gen) { + /* Lost race, retry operation */ + _MUTEX_UNLOCK(&atexit_mutex); + free(p); + _MUTEX_LOCK(&atexit_mutex); + goto retry; + } + p->ind = 0; + LIST_INSERT_HEAD(&__atexit, p, link); + __atexit_gen++; } - p->ind = 0; - p->next = __atexit; - __atexit = p; } p->fns[p->ind++] = *fptr; _MUTEX_UNLOCK(&atexit_mutex); @@ -119,7 +126,7 @@ atexit(void (*func)(void)) int error; fn.fn_type = ATEXIT_FN_STD; - fn.fn_ptr.std_func = func;; + fn.fn_ptr.std_func = func; fn.fn_arg = NULL; fn.fn_dso = NULL; @@ -138,7 +145,7 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso) int error; fn.fn_type = ATEXIT_FN_CXA; - fn.fn_ptr.cxa_func = func;; + fn.fn_ptr.cxa_func = func; fn.fn_arg = arg; fn.fn_dso = dso; @@ -154,32 +161,55 @@ __cxa_atexit(void (*func)(void *), void *arg, void *dso) void __cxa_finalize(void *dso) { - struct atexit *p; - struct atexit_fn fn; - int n; + struct atexit *p, *p1, cp; + struct atexit_fn *fn; + int i, n, inuse; + unsigned long orig__atexit_gen; _MUTEX_LOCK(&atexit_mutex); - for (p = __atexit; p; p = p->next) { + restart: + inuse = 0; + LIST_FOREACH_SAFE(p, &__atexit, link, p1) { + cp.ind = 0; for (n = p->ind; --n >= 0;) { if (p->fns[n].fn_type == ATEXIT_FN_EMPTY) continue; /* already been called */ - if (dso != NULL && dso != p->fns[n].fn_dso) + if (dso != NULL && dso != p->fns[n].fn_dso) { + inuse = 1; continue; /* wrong DSO */ - fn = p->fns[n]; + } + cp.fns[cp.ind++] = p->fns[n]; /* Mark entry to indicate that this particular handler has already been called. */ p->fns[n].fn_type = ATEXIT_FN_EMPTY; - _MUTEX_UNLOCK(&atexit_mutex); - + } + if (!inuse && p != &__atexit0) { + LIST_REMOVE(p, link); + __atexit_gen++; + } else { + /* + * The current entry cannot be removed, and so + * any consequent entries. + */ + inuse = 1; + p = NULL; + } + orig__atexit_gen = __atexit_gen; + _MUTEX_UNLOCK(&atexit_mutex); + free(p); + for (i = 0; i < cp.ind; i++) { + fn = &cp.fns[i]; /* Call the function of correct type. */ - if (fn.fn_type == ATEXIT_FN_CXA) - fn.fn_ptr.cxa_func(fn.fn_arg); - else if (fn.fn_type == ATEXIT_FN_STD) - fn.fn_ptr.std_func(); - _MUTEX_LOCK(&atexit_mutex); + if (fn->fn_type == ATEXIT_FN_CXA) + fn->fn_ptr.cxa_func(fn->fn_arg); + else if (fn->fn_type == ATEXIT_FN_STD) + fn->fn_ptr.std_func(); } + _MUTEX_LOCK(&atexit_mutex); + if (orig__atexit_gen != __atexit_gen) + goto restart; } _MUTEX_UNLOCK(&atexit_mutex); } The information contained in this electronic message and any attachments to this message are intended for the exclusive use of the addressee(s) and may contain proprietary, confidential or privileged information. If you are not the intended recipient, you should not disseminate, distribute or copy this e-mail. Please notify the sender immediately and destroy all copies of this message and any attachments. WARNING: Computer viruses can be transmitted via email. The recipient should check this email and any attachments for the presence of viruses. The company accepts no liability for any damage caused by any virus transmitted by this email. www.wipro.com