This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
docpatch for perltie.pod
[perl5.git] / malloc.c
index d39927a..6d56352 100644 (file)
--- a/malloc.c
+++ b/malloc.c
@@ -6,6 +6,12 @@
  * "'The Chamber of Records,' said Gimli. 'I guess that is where we now stand.'"
  */
 
+/* This file contains Perl's own implementation of the malloc library.
+ * It is used if Configure decides that, on your platform, Perl's
+ * version is better than the OS's, or if you give Configure the
+ * -Dusemymalloc command-line option.
+ */
+
 /*
   Here are some notes on configuring Perl's malloc.  (For non-perl
   usage see below.)
 #      define Free_t void
 #    endif
 #    define Copy(s,d,n,t) (void)memcpy((char*)(d),(char*)(s), (n) * sizeof(t))
+#    define CopyD(s,d,n,t) memcpy((char*)(d),(char*)(s), (n) * sizeof(t))
 #    define PerlEnv_getenv getenv
 #    define PerlIO_printf fprintf
 #    define PerlIO_stderr() stderr
@@ -566,6 +573,9 @@ union       overhead {
        union   overhead *ov_next;      /* when free */
 #if MEM_ALIGNBYTES > 4
        double  strut;                  /* alignment problems */
+#  if MEM_ALIGNBYTES > 8
+       char    sstrut[MEM_ALIGNBYTES]; /* for the sizing */
+#  endif
 #endif
        struct {
 /*
@@ -576,6 +586,7 @@ union       overhead {
                u_char  ovu_index;      /* bucket # */
                u_char  ovu_magic;      /* magic number */
 #ifdef RCHECK
+           /* Subtract one to fit into u_short for an extra bucket */
                u_short ovu_size;       /* block size (requested + overhead - 1) */
                u_int   ovu_rmagic;     /* range magic number */
 #endif
@@ -591,14 +602,14 @@ union     overhead {
 #define RMAGIC_C       0x55            /* magic # on range info */
 
 #ifdef RCHECK
-#  define      RSLOP           sizeof (u_int)
+#  define      RMAGIC_SZ       sizeof (u_int) /* Overhead at end of bucket */
 #  ifdef TWO_POT_OPTIMIZE
 #    define MAX_SHORT_BUCKET (12 * BUCKETS_PER_POW2) /* size-1 fits in short */
 #  else
 #    define MAX_SHORT_BUCKET (13 * BUCKETS_PER_POW2)
 #  endif 
 #else
-#  define      RSLOP           0
+#  define      RMAGIC_SZ       0
 #endif
 
 #if !defined(PACK_MALLOC) && defined(BUCKETS_ROOT2)
@@ -634,15 +645,16 @@ static u_short buck_size[MAX_BUCKET_BY_TABLE + 1] =
   { 
       0, 0, 0, 0, 4, 4, 8, 12, 16, 24, 32, 48, 64, 80,
   };
-#  define BUCKET_SIZE(i) ((i) % 2 ? buck_size[i] : (1 << ((i) >> BUCKET_POW2_SHIFT)))
+#  define BUCKET_SIZE_NO_SURPLUS(i) ((i) % 2 ? buck_size[i] : (1 << ((i) >> BUCKET_POW2_SHIFT)))
 #  define BUCKET_SIZE_REAL(i) ((i) <= MAX_BUCKET_BY_TABLE              \
                               ? buck_size[i]                           \
                               : ((1 << ((i) >> BUCKET_POW2_SHIFT))     \
                                  - MEM_OVERHEAD(i)                     \
                                  + POW2_OPTIMIZE_SURPLUS(i)))
 #else
-#  define BUCKET_SIZE(i) (1 << ((i) >> BUCKET_POW2_SHIFT))
-#  define BUCKET_SIZE_REAL(i) (BUCKET_SIZE(i) - MEM_OVERHEAD(i) + POW2_OPTIMIZE_SURPLUS(i))
+#  define BUCKET_SIZE_NO_SURPLUS(i) (1 << ((i) >> BUCKET_POW2_SHIFT))
+#  define BUCKET_SIZE(i) (BUCKET_SIZE_NO_SURPLUS(i) + POW2_OPTIMIZE_SURPLUS(i))
+#  define BUCKET_SIZE_REAL(i) (BUCKET_SIZE(i) - MEM_OVERHEAD(i))
 #endif 
 
 
@@ -787,7 +799,7 @@ static u_short buck_size[MAX_BUCKET_BY_TABLE + 1] =
 #ifdef IGNORE_SMALL_BAD_FREE
 #define FIRST_BUCKET_WITH_CHECK (6 * BUCKETS_PER_POW2) /* 64 */
 #  define N_BLKS(bucket) ( (bucket) < FIRST_BUCKET_WITH_CHECK          \
-                        ? ((1<<LOG_OF_MIN_ARENA) - 1)/BUCKET_SIZE(bucket) \
+                        ? ((1<<LOG_OF_MIN_ARENA) - 1)/BUCKET_SIZE_NO_SURPLUS(bucket) \
                         : n_blks[bucket] )
 #else
 #  define N_BLKS(bucket) n_blks[bucket]
@@ -810,7 +822,7 @@ static u_short n_blks[LOG_OF_MIN_ARENA * BUCKETS_PER_POW2] =
 #ifdef IGNORE_SMALL_BAD_FREE
 #  define BLK_SHIFT(bucket) ( (bucket) < FIRST_BUCKET_WITH_CHECK       \
                              ? ((1<<LOG_OF_MIN_ARENA)                  \
-                                - BUCKET_SIZE(bucket) * N_BLKS(bucket)) \
+                                - BUCKET_SIZE_NO_SURPLUS(bucket) * N_BLKS(bucket)) \
                              : blk_shift[bucket])
 #else
 #  define BLK_SHIFT(bucket) blk_shift[bucket]
@@ -851,7 +863,7 @@ static u_short blk_shift[LOG_OF_MIN_ARENA * BUCKETS_PER_POW2] =
 
 #endif /* !PACK_MALLOC */
 
-#define M_OVERHEAD (sizeof(union overhead) + RSLOP)
+#define M_OVERHEAD (sizeof(union overhead) + RMAGIC_SZ) /* overhead at start+end */
 
 #ifdef PACK_MALLOC
 #  define MEM_OVERHEAD(bucket) \
@@ -962,7 +974,7 @@ static char bucket_of[] =
 
 static void    morecore        (register int bucket);
 #  if defined(DEBUGGING)
-static void    botch           (char *diag, char *s);
+static void    botch           (char *diag, char *s, char *file, int line);
 #  endif
 static void    add_to_chain    (void *p, MEM_SIZE size, MEM_SIZE chip);
 static void*   get_from_chain  (MEM_SIZE size);
@@ -1035,6 +1047,16 @@ extern   Malloc_t sbrk(int);
 #ifndef NO_MALLOC_DYNAMIC_CFG
 #  define PERL_MALLOC_OPT_CHARS "FMfAPGdac"
 
+#  ifndef FILL_DEAD_DEFAULT
+#    define FILL_DEAD_DEFAULT  1
+#  endif
+#  ifndef FILL_ALIVE_DEFAULT
+#    define FILL_ALIVE_DEFAULT 1
+#  endif
+#  ifndef FILL_CHECK_DEFAULT
+#    define FILL_CHECK_DEFAULT 1
+#  endif
+
 static IV MallocCfg[MallocCfg_last] = {
   FIRST_SBRK,
   MIN_SBRK,
@@ -1042,9 +1064,9 @@ static IV MallocCfg[MallocCfg_last] = {
   SBRK_ALLOW_FAILURES,
   SBRK_FAILURE_PRICE,
   SBRK_ALLOW_FAILURES * SBRK_FAILURE_PRICE,    /* sbrk_goodness */
-  1,                   /* FILL_DEAD */
-  1,                   /* FILL_ALIVE */
-  1,                   /* FILL_CHECK */
+  FILL_DEAD_DEFAULT,   /* FILL_DEAD */
+  FILL_ALIVE_DEFAULT,  /* FILL_ALIVE */
+  FILL_CHECK_DEFAULT,  /* FILL_CHECK */
   0,                   /* MallocCfg_skip_cfg_env */
   0,                   /* MallocCfg_cfg_env_read */
   0,                   /* MallocCfg_emergency_buffer_size */
@@ -1053,6 +1075,12 @@ static IV MallocCfg[MallocCfg_last] = {
 };
 IV *MallocCfg_ptr = MallocCfg;
 
+static char* MallocCfgP[MallocCfg_last] = {
+  0,                   /* MallocCfgP_emergency_buffer */
+  0,                   /* MallocCfgP_emergency_buffer_prepared */
+};
+char **MallocCfgP_ptr = MallocCfgP;
+
 #  undef MIN_SBRK
 #  undef FIRST_SBRK
 #  undef MIN_SBRK_FRAC1000
@@ -1075,6 +1103,9 @@ IV *MallocCfg_ptr = MallocCfg;
 #  define FILL_CHECK_CFG       MallocCfg[MallocCfg_fillcheck]
 #  define FILL_CHECK           (FILL_DEAD && FILL_CHECK_CFG)
 
+#  define emergency_buffer     MallocCfgP[MallocCfgP_emergency_buffer]
+#  define emergency_buffer_prepared    MallocCfgP[MallocCfgP_emergency_buffer_prepared]
+
 #else  /* defined(NO_MALLOC_DYNAMIC_CFG) */
 
 #  define FILL_DEAD    1
@@ -1106,14 +1137,13 @@ static  u_int goodsbrk;
 #    define BIG_SIZE (1<<16)           /* 64K */
 #  endif
 
-static char *emergency_buffer;
-static char *emergency_buffer_prepared;
-
 #  ifdef NO_MALLOC_DYNAMIC_CFG
 static MEM_SIZE emergency_buffer_size;
        /* 0 if the last request for more memory succeeded.
           Otherwise the size of the failing request. */
 static MEM_SIZE emergency_buffer_last_req;
+static char *emergency_buffer;
+static char *emergency_buffer_prepared;
 #  endif
 
 #  ifndef emergency_sbrk_croak
@@ -1261,23 +1291,36 @@ write2(char *mess)
 
 #ifdef DEBUGGING
 #undef ASSERT
-#define        ASSERT(p,diag)   if (!(p)) botch(diag,STRINGIFY(p));  else
+#define        ASSERT(p,diag)   if (!(p)) botch(diag,STRINGIFY(p),__FILE__,__LINE__);  else
 static void
-botch(char *diag, char *s)
+botch(char *diag, char *s, char *file, int line)
 {
     if (!(PERL_MAYBE_ALIVE && PERL_GET_THX))
        goto do_write;
     else {
        dTHX;
-
        if (PerlIO_printf(PerlIO_stderr(),
-                         "assertion botched (%s?): %s\n", diag, s) != 0) {
+                         "assertion botched (%s?): %s %s:%d\n",
+                         diag, s, file, line) != 0) {
         do_write:              /* Can be initializing interpreter */
            write2("assertion botched (");
            write2(diag);
            write2("?): ");
            write2(s);
-           write2("\n");
+           write2(" (");
+           write2(file);
+           write2(":");
+           {
+             char linebuf[10];
+             char *s = linebuf + sizeof(linebuf) - 1;
+             int n = line;
+             *s = 0;
+             do {
+               *--s = '0' + (n % 10);
+             } while (n /= 10);
+             write2(s);
+           }
+           write2(")\n");
        }
        PerlProc_abort();
     }
@@ -1487,7 +1530,7 @@ Perl_malloc(register size_t nbytes)
                              (long)size));
 
        FILLCHECK_DEADBEEF((unsigned char*)(p + CHUNK_SHIFT),
-                          BUCKET_SIZE_REAL(bucket));
+                          BUCKET_SIZE_REAL(bucket) + RMAGIC_SZ);
 
 #ifdef IGNORE_SMALL_BAD_FREE
        if (bucket >= FIRST_BUCKET_WITH_CHECK)
@@ -1507,13 +1550,14 @@ Perl_malloc(register size_t nbytes)
            
            nbytes = size + M_OVERHEAD; 
            p->ov_size = nbytes - 1;
-           if ((i = nbytes & 3)) {
-               i = 4 - i;
-               while (i--)
-                   *((char *)((caddr_t)p + nbytes - RSLOP + i)) = RMAGIC_C;
+           if ((i = nbytes & (RMAGIC_SZ-1))) {
+               i = RMAGIC_SZ - i;
+               while (i--) /* nbytes - RMAGIC_SZ is end of alloced area */
+                   ((caddr_t)p + nbytes - RMAGIC_SZ)[i] = RMAGIC_C;
            }
-           nbytes = (nbytes + 3) &~ 3; 
-           *((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC;
+           /* Same at RMAGIC_SZ-aligned RMAGIC */
+           nbytes = (nbytes + RMAGIC_SZ - 1) & ~(RMAGIC_SZ - 1);
+           ((u_int *)((caddr_t)p + nbytes))[-1] = RMAGIC;
        }
        FILL_FEEDADAD((unsigned char *)(p + CHUNK_SHIFT), size);
 #endif
@@ -1608,7 +1652,7 @@ get_from_bigger_buckets(int bucket, MEM_SIZE size)
            nmalloc[bucket]--;
            start_slack -= M_OVERHEAD;
 #endif 
-           add_to_chain(ret, (BUCKET_SIZE(bucket) +
+           add_to_chain(ret, (BUCKET_SIZE_NO_SURPLUS(bucket) +
                               POW2_OPTIMIZE_SURPLUS(bucket)), 
                         size);
            return ret;
@@ -1913,7 +1957,7 @@ morecore(register int bucket)
         * Add new memory allocated to that on
         * free list for this hash bucket.
         */
-       siz = BUCKET_SIZE(bucket);
+       siz = BUCKET_SIZE_NO_SURPLUS(bucket); /* No surplus if nblks > 1 */
 #ifdef PACK_MALLOC
        *(u_char*)ovp = bucket; /* Fill index. */
        if (bucket <= MAX_PACKED) {
@@ -2024,19 +2068,22 @@ Perl_mfree(void *mp)
            int i;
            MEM_SIZE nbytes = ovp->ov_size + 1;
 
-           if ((i = nbytes & 3)) {
-               i = 4 - i;
-               while (i--) {
-                   ASSERT(*((char *)((caddr_t)ovp + nbytes - RSLOP + i))
-                          == RMAGIC_C, "chunk's tail overwrite");
+           if ((i = nbytes & (RMAGIC_SZ-1))) {
+               i = RMAGIC_SZ - i;
+               while (i--) {   /* nbytes - RMAGIC_SZ is end of alloced area */
+                   ASSERT(((caddr_t)ovp + nbytes - RMAGIC_SZ)[i] == RMAGIC_C,
+                          "chunk's tail overwrite");
                }
            }
-           nbytes = (nbytes + 3) &~ 3; 
-           ASSERT(*(u_int *)((caddr_t)ovp + nbytes - RSLOP) == RMAGIC, "chunk's tail overwrite");          
-           FILLCHECK_DEADBEEF((unsigned char*)((caddr_t)ovp + nbytes - RSLOP + sizeof(u_int)),
-                              BUCKET_SIZE_REAL(OV_INDEX(ovp)) - (nbytes - RSLOP + sizeof(u_int)));
+           /* Same at RMAGIC_SZ-aligned RMAGIC */
+           nbytes = (nbytes + (RMAGIC_SZ-1)) & ~(RMAGIC_SZ-1);
+           ASSERT(((u_int *)((caddr_t)ovp + nbytes))[-1] == RMAGIC,
+                  "chunk's tail overwrite");       
+           FILLCHECK_DEADBEEF((unsigned char*)((caddr_t)ovp + nbytes),
+                              BUCKET_SIZE(OV_INDEX(ovp)) - nbytes);
        }
-       FILL_DEADBEEF((unsigned char*)(ovp+1), BUCKET_SIZE_REAL(OV_INDEX(ovp)));
+       FILL_DEADBEEF((unsigned char*)(ovp+CHUNK_SHIFT),
+                     BUCKET_SIZE_REAL(OV_INDEX(ovp)) + RMAGIC_SZ);
        ovp->ov_rmagic = RMAGIC - 1;
 #endif
        ASSERT(OV_INDEX(ovp) < NBUCKETS, "chunk's head overwrite");
@@ -2166,22 +2213,24 @@ Perl_realloc(void *mp, size_t nbytes)
                if (OV_INDEX(ovp) <= MAX_SHORT_BUCKET) {
                       int i, nb = ovp->ov_size + 1;
 
-                      if ((i = nb & 3)) {
-                          i = 4 - i;
-                          while (i--) {
-                              ASSERT(*((char *)((caddr_t)ovp + nb - RSLOP + i)) == RMAGIC_C, "chunk's tail overwrite");
+                      if ((i = nb & (RMAGIC_SZ-1))) {
+                          i = RMAGIC_SZ - i;
+                          while (i--) { /* nb - RMAGIC_SZ is end of alloced area */
+                              ASSERT(((caddr_t)ovp + nb - RMAGIC_SZ)[i] == RMAGIC_C, "chunk's tail overwrite");
                           }
                       }
-                      nb = (nb + 3) &~ 3; 
-                      ASSERT(*(u_int *)((caddr_t)ovp + nb - RSLOP) == RMAGIC, "chunk's tail overwrite");
-                      FILLCHECK_DEADBEEF((unsigned char*)((caddr_t)ovp + nb - RSLOP + sizeof(u_int)),
-                              BUCKET_SIZE_REAL(OV_INDEX(ovp)) - (nb - RSLOP + sizeof(u_int)));
+                      /* Same at RMAGIC_SZ-aligned RMAGIC */
+                      nb = (nb + (RMAGIC_SZ-1)) & ~(RMAGIC_SZ-1);
+                      ASSERT(((u_int *)((caddr_t)ovp + nb))[-1] == RMAGIC,
+                             "chunk's tail overwrite");
+                      FILLCHECK_DEADBEEF((unsigned char*)((caddr_t)ovp + nb),
+                                         BUCKET_SIZE(OV_INDEX(ovp)) - nb);
                       if (nbytes > ovp->ov_size + 1 - M_OVERHEAD)
                           FILL_FEEDADAD((unsigned char*)cp + ovp->ov_size + 1 - M_OVERHEAD,
                                     nbytes - (ovp->ov_size + 1 - M_OVERHEAD));
                       else
                           FILL_DEADBEEF((unsigned char*)cp + nbytes,
-                                        nb - M_OVERHEAD + RSLOP - nbytes);
+                                        nb - M_OVERHEAD + RMAGIC_SZ - nbytes);
                        /*
                         * Convert amount of memory requested into
                         * closest block size stored in hash buckets
@@ -2190,14 +2239,15 @@ Perl_realloc(void *mp, size_t nbytes)
                         */
                        nbytes += M_OVERHEAD;
                        ovp->ov_size = nbytes - 1;
-                       if ((i = nbytes & 3)) {
-                           i = 4 - i;
-                           while (i--)
-                               *((char *)((caddr_t)ovp + nbytes - RSLOP + i))
+                       if ((i = nbytes & (RMAGIC_SZ-1))) {
+                           i = RMAGIC_SZ - i;
+                           while (i--) /* nbytes - RMAGIC_SZ is end of alloced area */
+                               ((caddr_t)ovp + nbytes - RMAGIC_SZ)[i]
                                    = RMAGIC_C;
                        }
-                       nbytes = (nbytes + 3) &~ 3; 
-                       *((u_int *)((caddr_t)ovp + nbytes - RSLOP)) = RMAGIC;
+                       /* Same at RMAGIC_SZ-aligned RMAGIC */
+                       nbytes = (nbytes + (RMAGIC_SZ-1)) & ~(RMAGIC_SZ - 1);
+                       ((u_int *)((caddr_t)ovp + nbytes))[-1] = RMAGIC;
                }
 #endif
                res = cp;
@@ -2268,8 +2318,7 @@ Perl_strdup(const char *s)
     MEM_SIZE l = strlen(s);
     char *s1 = (char *)Perl_malloc(l+1);
 
-    Copy(s, s1, (MEM_SIZE)(l+1), char);
-    return s1;
+    return CopyD(s, s1, (MEM_SIZE)(l+1), char);
 }
 
 #ifdef PERL_CORE
@@ -2314,7 +2363,7 @@ Perl_malloced_size(void *p)
     if (bucket <= MAX_SHORT_BUCKET) {
        MEM_SIZE size = BUCKET_SIZE_REAL(bucket);
        ovp->ov_size = size + M_OVERHEAD - 1;
-       *((u_int *)((caddr_t)ovp + size + M_OVERHEAD - RSLOP)) = RMAGIC;
+       *((u_int *)((caddr_t)ovp + size + M_OVERHEAD - RMAGIC_SZ)) = RMAGIC;
     }
 #endif
     return BUCKET_SIZE_REAL(bucket);
@@ -2370,7 +2419,7 @@ Perl_get_mstats(pTHX_ perl_mstats_t *buf, int buflen, int level)
            for (i = MIN_BUCKET ; i < NBUCKETS; i++) {
                if (i >= buflen)
                    break;
-               buf->bucket_mem_size[i] = BUCKET_SIZE(i);
+               buf->bucket_mem_size[i] = BUCKET_SIZE_NO_SURPLUS(i);
                buf->bucket_available_size[i] = BUCKET_SIZE_REAL(i);
            }
        }
@@ -2402,9 +2451,9 @@ Perl_dump_mstats(pTHX_ char *s)
                          "Memory allocation statistics %s (buckets %"IVdf"(%"IVdf")..%"IVdf"(%"IVdf")\n",
                          s, 
                          (IV)BUCKET_SIZE_REAL(MIN_BUCKET), 
-                         (IV)BUCKET_SIZE(MIN_BUCKET),
+                         (IV)BUCKET_SIZE_NO_SURPLUS(MIN_BUCKET),
                          (IV)BUCKET_SIZE_REAL(buffer.topbucket), 
-                         (IV)BUCKET_SIZE(buffer.topbucket));
+                         (IV)BUCKET_SIZE_NO_SURPLUS(buffer.topbucket));
        PerlIO_printf(Perl_error_log, "%8"IVdf" free:", buffer.totfree);
        for (i = MIN_EVEN_REPORT; i <= buffer.topbucket; i += BUCKETS_PER_POW2) {
                PerlIO_printf(Perl_error_log,