Bugzilla bug #15906: added GC_LEAK_DETECTOR support for Linux. NSPRPUB_RELEASE_4_0_BRANCH
authorwtc%netscape.com
Mon, 14 Aug 2000 22:37:30 +0000
branchNSPRPUB_RELEASE_4_0_BRANCH
changeset 1539 6e12b3e9a9b6db2689df94fb39b63fb2e6eade92
parent 1529 818f08c1a0f458c5a19146a5d1f4540a467fd6e5
child 1540 374dcc413e52b0aa372a097ca578414720a5da22
push idunknown
push userunknown
push dateunknown
bugs15906
Bugzilla bug #15906: added GC_LEAK_DETECTOR support for Linux. Modified files: config.mk, primpl.h, pr/src/Makefile, pr/src/Makefile.in, pr/src/memory/Makefile, pr/src/memory/Makefile.in, prthinfo.c, ptthread.c Added file: pr/src/memory/prgcleak.c (NSPRPUB_RELEASE_4_0_BRANCH)
config/config.mk
pr/include/private/primpl.h
pr/src/Makefile
pr/src/Makefile.in
pr/src/memory/Makefile
pr/src/memory/Makefile.in
pr/src/memory/prgcleak.c
pr/src/misc/prthinfo.c
pr/src/pthreads/ptthread.c
--- a/config/config.mk
+++ b/config/config.mk
@@ -183,16 +183,20 @@ endif
 ifeq ($(PTHREADS_USER), 1)
 OS_CFLAGS += -DPTHREADS_USER -UHAVE_CVAR_BUILT_ON_SEM
 endif
 
 ifeq ($(USE_IPV6),1)
 OS_CFLAGS += -D_PR_INET6
 endif
 
+ifdef GC_LEAK_DETECTOR
+OS_CFLAGS += -DGC_LEAK_DETECTOR
+endif
+
 ####################################################################
 #
 # Configuration for the release process
 #
 ####################################################################
 
 MDIST = /m/dist
 ifeq ($(OS_ARCH),WINNT)
--- a/pr/include/private/primpl.h
+++ b/pr/include/private/primpl.h
@@ -167,16 +167,24 @@ struct _PT_Notified
 #define PT_THREAD_BOUND     0x100    /* a bound-global thread */
 
 #define _PT_THREAD_INTERRUPTED(thr)					\
 		(!(thr->interrupt_blocked) && (thr->state & PT_THREAD_ABORTED))
 #define _PT_THREAD_BLOCK_INTERRUPT(thr)				\
 		(thr->interrupt_blocked = 1)
 #define _PT_THREAD_UNBLOCK_INTERRUPT(thr)			\
 		(thr->interrupt_blocked = 0)
+
+#ifdef GC_LEAK_DETECTOR
+/* All threads are GCable. */
+#define _PT_IS_GCABLE_THREAD(thr) 1
+#else
+#define _PT_IS_GCABLE_THREAD(thr) ((thr)->state & PT_THREAD_GCABLE)
+#endif /* GC_LEAK_DETECTOR */
+
 /* 
 ** Possible values for thread's suspend field
 ** Note that the first two can be the same as they are really mutually exclusive,
 ** i.e. both cannot be happening at the same time. We have two symbolic names
 ** just as a mnemonic.
 **/
 #define PT_THREAD_RESUMED   0x80    /* thread has been resumed */
 #define PT_THREAD_SETGCABLE 0x100   /* set the GCAble flag */
--- a/pr/src/Makefile
+++ b/pr/src/Makefile
@@ -140,16 +140,20 @@ OS_LIBS		= -lsocket -lnsl -lgen -lresolv
 endif
 
 ifeq ($(OS_ARCH),WINNT)
 ifneq ($(OS_TARGET),WIN16)
 OS_LIBS		= advapi32.lib wsock32.lib winmm.lib
 endif
 endif
 
+ifdef GC_LEAK_DETECTOR
+OS_LIBS		+= -L$(DIST)/lib -lboehm
+endif
+
 #
 # Define platform-dependent OBJS
 #
 
 OBJS = \
     $(OBJDIR)/prvrsion.$(OBJ_SUFFIX) \
     io/$(OBJDIR)/prfdcach.$(OBJ_SUFFIX) \
     io/$(OBJDIR)/prmwait.$(OBJ_SUFFIX) \
@@ -237,16 +241,20 @@ OBJS += \
 	cplus/$(OBJDIR)/rcio.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rclock.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rcnetdb.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rcnetio.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rcthread.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rctime.$(OBJ_SUFFIX)
 endif
 
+ifdef GC_LEAK_DETECTOR
+OBJS += memory/$(OBJDIR)/prgcleak.$(OBJ_SUFFIX)
+endif
+
 ifeq ($(OS_ARCH), WINNT)
 ifneq ($(OS_TARGET),WIN16)
 DLLBASE=/BASE:0x30000000
 RES=$(OBJDIR)/nspr.res
 RESNAME=nspr.rc
 ifdef MOZ_DEBUG
 ifdef GLOWCODE
 EXTRA_LIBS += $(GLOWDIR)/glowcode.lib
--- a/pr/src/Makefile.in
+++ b/pr/src/Makefile.in
@@ -146,16 +146,20 @@ ifeq ($(OS_TARGET),OS2)
 # We define this in os2.mk.
 else
 ifneq ($(OS_TARGET),WIN16)
 OS_LIBS		= wsock32.lib winmm.lib
 endif
 endif
 endif
 
+ifdef GC_LEAK_DETECTOR
+OS_LIBS		+= -L$(DIST)/lib -lboehm
+endif
+
 endif # USE_AUTOCONF
 #
 # Define platform-dependent OBJS
 #
 
 OBJS = \
     $(OBJDIR)/prvrsion.$(OBJ_SUFFIX) \
     io/$(OBJDIR)/prfdcach.$(OBJ_SUFFIX) \
@@ -239,16 +243,20 @@ OBJS += \
 	cplus/$(OBJDIR)/rcio.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rclock.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rcnetdb.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rcnetio.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rcthread.$(OBJ_SUFFIX) \
 	cplus/$(OBJDIR)/rctime.$(OBJ_SUFFIX)
 endif
 
+ifdef GC_LEAK_DETECTOR
+OBJS += memory/$(OBJDIR)/prgcleak.$(OBJ_SUFFIX)
+endif
+
 ifdef USE_AUTOCONF
 
 include $(srcdir)/md/$(PR_MD_ARCH_DIR)/objs.mk
 ifdef USE_BTHREADS
 include $(srcdir)/bthreads/objs.mk
 endif
 
 else
--- a/pr/src/memory/Makefile
+++ b/pr/src/memory/Makefile
@@ -25,16 +25,20 @@ include $(MOD_DEPTH)/config/config.mk
 ifeq ($(OS_ARCH),SunOS)
 ifeq ($(OS_RELEASE),4.1.3_U1)
 OPTIMIZER =
 endif
 endif
 
 CSRCS = prseg.c prshm.c prshma.c
 
+ifdef GC_LEAK_DETECTOR
+CSRCS += prgcleak.c
+endif
+
 TARGETS	= $(OBJS)
 
 INCLUDES = -I$(DIST)/include -I$(MOD_DEPTH)/pr/include -I$(MOD_DEPTH)/pr/include/private
 
 DEFINES     += -D_NSPR_BUILD_
 
 include $(MOD_DEPTH)/config/rules.mk
 
--- a/pr/src/memory/Makefile.in
+++ b/pr/src/memory/Makefile.in
@@ -32,16 +32,20 @@ ifeq ($(OS_ARCH),SunOS)
 ifeq ($(OS_RELEASE),4.1.3_U1)
 OPTIMIZER =
 endif
 endif
 endif #!USE_AUTOCONF
 
 CSRCS = prseg.c prshm.c prshma.c
 
+ifdef GC_LEAK_DETECTOR
+CSRCS += prgcleak.c
+endif
+
 TARGETS	= $(OBJS)
 
 INCLUDES = -I$(DIST)/include -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
 
 DEFINES += -D_NSPR_BUILD_
 
 include $(topsrcdir)/config/rules.mk
 
--- a/pr/src/memory/prgcleak.c
+++ b/pr/src/memory/prgcleak.c
@@ -1,41 +1,27 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- * 
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- * 
- * The Original Code is the Netscape Portable Runtime (NSPR).
- * 
- * The Initial Developer of the Original Code is Netscape
- * Communications Corporation.  Portions created by Netscape are 
- * Copyright (C) 1999-2000 Netscape Communications Corporation.  All
- * Rights Reserved.
- * 
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "NPL"); you may not use this file except in
+ * compliance with the NPL.  You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation.  Portions created by Netscape are
+ * Copyright (C) 1999 Netscape Communications Corporation.  All Rights
+ * Reserved.
+ *
  * Contributor(s):
  *   Patrick Beard <beard@netscape.com>
- * 
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable 
- * instead of those above.  If you wish to allow use of your 
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL.  If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
  */
 
 /*
  * prgcleak.c
  */
 
 #ifdef GC_LEAK_DETECTOR
 
--- a/pr/src/misc/prthinfo.c
+++ b/pr/src/misc/prthinfo.c
@@ -129,22 +129,25 @@ PR_ThreadScanStackPointers(PRThread* t,
     **
     ** The execution environment better be accounted for otherwise it
     ** will be collected
     */
     status = scanFun(t, (void**)&t->environment, 1, scanClosure);
     if (status != PR_SUCCESS)
         return status;
 
+#ifndef GC_LEAK_DETECTOR
+    /* if thread is not allocated on stack, this is redundant. */
     ptd = t->privateData;
     for (index = 0; index < t->tpdLength; index++, ptd++) {
         status = scanFun(t, (void**)ptd, 1, scanClosure);
         if (status != PR_SUCCESS)
             return status;
     }
+#endif
     
     return PR_SUCCESS;
 }
 
 /* transducer for PR_EnumerateThreads */
 typedef struct PRScanStackData {
     PRScanStackFun      scanFun;
     void*               scanClosure;
--- a/pr/src/pthreads/ptthread.c
+++ b/pr/src/pthreads/ptthread.c
@@ -70,35 +70,76 @@ static PRIntn pt_PriorityMap(PRThreadPri
     return 10;
 #else
     return pt_book.minPrio +
 	    pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
 #endif
 }
 #endif
 
+#if defined(GC_LEAK_DETECTOR) && (__GLIBC__ >= 2) && defined(__i386__) 
+
+#include <setjmp.h>
+
+typedef struct stack_frame stack_frame;
+
+struct stack_frame {
+    stack_frame* next;
+    void* pc;
+};
+
+static stack_frame* GetStackFrame()
+{
+    jmp_buf jb;
+    stack_frame* currentFrame;
+    setjmp(jb);
+    currentFrame = (stack_frame*)(jb[0].__jmpbuf[JB_BP]);
+    currentFrame = currentFrame->next;
+    return currentFrame;
+}
+
+static void* GetStackTop()
+{
+    stack_frame* frame;
+    frame = GetStackFrame();
+    while (frame != NULL)
+    {
+        ptrdiff_t pc = (ptrdiff_t)frame->pc;
+        if ((pc < 0x08000000) || (pc > 0x7fffffff) || (frame->next < frame))
+            return frame;
+        frame = frame->next;
+    }
+    return NULL;
+}
+#endif /* GC_LEAK_DETECTOR && (__GLIBC__ >= 2) && __i386__ */
+
 /*
 ** Initialize a stack for a native pthread thread
 */
 static void _PR_InitializeStack(PRThreadStack *ts)
 {
     if( ts && (ts->stackTop == 0) ) {
         ts->allocBase = (char *) &ts;
         ts->allocSize = ts->stackSize;
 
         /*
         ** Setup stackTop and stackBottom values.
         */
 #ifdef HAVE_STACK_GROWING_UP
         ts->stackBottom = ts->allocBase + ts->stackSize;
         ts->stackTop = ts->allocBase;
 #else
+#ifdef GC_LEAK_DETECTOR
+        ts->stackTop    = GetStackTop();
+        ts->stackBottom = ts->stackTop - ts->stackSize;
+#else
         ts->stackTop    = ts->allocBase;
         ts->stackBottom = ts->allocBase - ts->stackSize;
 #endif
+#endif
     }
 }
 
 static void *_pt_root(void *arg)
 {
     PRIntn rv;
     PRThread *thred = (PRThread*)arg;
     PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
@@ -1026,17 +1067,17 @@ PR_IMPLEMENT(PRStatus) PR_EnumerateThrea
          * which case qp->next no longer points to the next entry in the original
          * queue.
          *
          * To get around this problem, we save qp->next in qp_next before applying
          * "func" and use that saved value as the next value after applying "func".
          */
         PRThread* next = thred->next;
 
-        if (thred->state & PT_THREAD_GCABLE)
+        if (_PT_IS_GCABLE_THREAD(thred))
         {
 #if !defined(_PR_DCETHREADS)
             PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
 #endif
             PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
                    ("In PR_EnumerateThreads callback thread %X thid = %X\n", 
                     thred, thred->id));
 
@@ -1090,17 +1131,17 @@ static void null_signal_handler(PRIntn s
 }
 #endif
 
 static void suspend_signal_handler(PRIntn sig)
 {
 	PRThread *me = PR_CurrentThread();
 
 	PR_ASSERT(me != NULL);
-	PR_ASSERT(me->state & PT_THREAD_GCABLE);
+	PR_ASSERT(_PT_IS_GCABLE_THREAD(me));
 	PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
 
 	PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
         ("Begin suspend_signal_handler thred %X thread id = %X\n", 
 		me, me->id));
 
 	/*
 	 * save stack pointer
@@ -1281,26 +1322,26 @@ PR_IMPLEMENT(void) PR_SuspendAll()
      */
     PR_Lock(pt_book.ml);
 #ifdef DEBUG
     suspendAllOn = PR_TRUE;
     stime = PR_IntervalNow();
 #endif
     while (thred != NULL)
     {
-	    if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
     		PR_SuspendSet(thred);
         thred = thred->next;
     }
 
     /* Wait till they are really suspended */
     thred = pt_book.first;
     while (thred != NULL)
     {
-	    if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
             PR_SuspendTest(thred);
         thred = thred->next;
     }
 
     suspendAllSuspended = PR_TRUE;
 
 #ifdef DEBUG
     etime = PR_IntervalNow();
@@ -1323,25 +1364,25 @@ PR_IMPLEMENT(void) PR_ResumeAll()
      */
     suspendAllSuspended = PR_FALSE;
 #ifdef DEBUG
     stime = PR_IntervalNow();
 #endif
 
     while (thred != NULL)
     {
-	    if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
     	    PR_ResumeSet(thred);
         thred = thred->next;
     }
 
     thred = pt_book.first;
     while (thred != NULL)
     {
-	    if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+	    if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
     	    PR_ResumeTest(thred);
         thred = thred->next;
     }
 
     PR_Unlock(pt_book.ml);
 #ifdef DEBUG
     suspendAllOn = PR_FALSE;
     etime = PR_IntervalNow();