[MALLOC_DEBUG]: Keep track of current maximum number of mmaps.
[kopensolaris-gnu/glibc.git] / malloc / hooks.c
1 /* Malloc implementation for multiple threads without lock contention.
2    Copyright (C) 2001-2006, 2007 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation; either version 2.1 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* What to do if the standard debugging hooks are in place and a
22    corrupt pointer is detected: do nothing (0), print an error message
23    (1), or call abort() (2). */
24
25 /* Hooks for debugging versions.  The initial hooks just call the
26    initialization routine, then do the normal work. */
27
28 static Void_t*
29 #if __STD_C
30 malloc_hook_ini(size_t sz, const __malloc_ptr_t caller)
31 #else
32 malloc_hook_ini(sz, caller)
33      size_t sz; const __malloc_ptr_t caller;
34 #endif
35 {
36   __malloc_hook = NULL;
37   ptmalloc_init();
38   return public_mALLOc(sz);
39 }
40
41 static Void_t*
42 #if __STD_C
43 realloc_hook_ini(Void_t* ptr, size_t sz, const __malloc_ptr_t caller)
44 #else
45 realloc_hook_ini(ptr, sz, caller)
46      Void_t* ptr; size_t sz; const __malloc_ptr_t caller;
47 #endif
48 {
49   __malloc_hook = NULL;
50   __realloc_hook = NULL;
51   ptmalloc_init();
52   return public_rEALLOc(ptr, sz);
53 }
54
55 static Void_t*
56 #if __STD_C
57 memalign_hook_ini(size_t alignment, size_t sz, const __malloc_ptr_t caller)
58 #else
59 memalign_hook_ini(alignment, sz, caller)
60      size_t alignment; size_t sz; const __malloc_ptr_t caller;
61 #endif
62 {
63   __memalign_hook = NULL;
64   ptmalloc_init();
65   return public_mEMALIGn(alignment, sz);
66 }
67
68 /* Whether we are using malloc checking.  */
69 static int using_malloc_checking;
70
71 /* A flag that is set by malloc_set_state, to signal that malloc checking
72    must not be enabled on the request from the user (via the MALLOC_CHECK_
73    environment variable).  It is reset by __malloc_check_init to tell
74    malloc_set_state that the user has requested malloc checking.
75
76    The purpose of this flag is to make sure that malloc checking is not
77    enabled when the heap to be restored was constructed without malloc
78    checking, and thus does not contain the required magic bytes.
79    Otherwise the heap would be corrupted by calls to free and realloc.  If
80    it turns out that the heap was created with malloc checking and the
81    user has requested it malloc_set_state just calls __malloc_check_init
82    again to enable it.  On the other hand, reusing such a heap without
83    further malloc checking is safe.  */
84 static int disallow_malloc_check;
85
86 /* Activate a standard set of debugging hooks. */
87 void
88 __malloc_check_init()
89 {
90   if (disallow_malloc_check) {
91     disallow_malloc_check = 0;
92     return;
93   }
94   using_malloc_checking = 1;
95   __malloc_hook = malloc_check;
96   __free_hook = free_check;
97   __realloc_hook = realloc_check;
98   __memalign_hook = memalign_check;
99   if(check_action & 1)
100     malloc_printerr (5, "malloc: using debugging hooks", NULL);
101 }
102
103 /* A simple, standard set of debugging hooks.  Overhead is `only' one
104    byte per chunk; still this will catch most cases of double frees or
105    overruns.  The goal here is to avoid obscure crashes due to invalid
106    usage, unlike in the MALLOC_DEBUG code. */
107
108 #define MAGICBYTE(p) ( ( ((size_t)p >> 3) ^ ((size_t)p >> 11)) & 0xFF )
109
110 /* Instrument a chunk with overrun detector byte(s) and convert it
111    into a user pointer with requested size sz. */
112
113 static Void_t*
114 internal_function
115 #if __STD_C
116 mem2mem_check(Void_t *ptr, size_t sz)
117 #else
118 mem2mem_check(ptr, sz) Void_t *ptr; size_t sz;
119 #endif
120 {
121   mchunkptr p;
122   unsigned char* m_ptr = (unsigned char*)BOUNDED_N(ptr, sz);
123   size_t i;
124
125   if (!ptr)
126     return ptr;
127   p = mem2chunk(ptr);
128   for(i = chunksize(p) - (chunk_is_mmapped(p) ? 2*SIZE_SZ+1 : SIZE_SZ+1);
129       i > sz;
130       i -= 0xFF) {
131     if(i-sz < 0x100) {
132       m_ptr[i] = (unsigned char)(i-sz);
133       break;
134     }
135     m_ptr[i] = 0xFF;
136   }
137   m_ptr[sz] = MAGICBYTE(p);
138   return (Void_t*)m_ptr;
139 }
140
141 /* Convert a pointer to be free()d or realloc()ed to a valid chunk
142    pointer.  If the provided pointer is not valid, return NULL. */
143
144 static mchunkptr
145 internal_function
146 #if __STD_C
147 mem2chunk_check(Void_t* mem, unsigned char **magic_p)
148 #else
149 mem2chunk_check(mem, magic_p) Void_t* mem; unsigned char **magic_p;
150 #endif
151 {
152   mchunkptr p;
153   INTERNAL_SIZE_T sz, c;
154   unsigned char magic;
155
156   if(!aligned_OK(mem)) return NULL;
157   p = mem2chunk(mem);
158   if (!chunk_is_mmapped(p)) {
159     /* Must be a chunk in conventional heap memory. */
160     int contig = contiguous(&main_arena);
161     sz = chunksize(p);
162     if((contig &&
163         ((char*)p<mp_.sbrk_base ||
164          ((char*)p + sz)>=(mp_.sbrk_base+main_arena.system_mem) )) ||
165        sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ||
166        ( !prev_inuse(p) && (p->prev_size&MALLOC_ALIGN_MASK ||
167                             (contig && (char*)prev_chunk(p)<mp_.sbrk_base) ||
168                             next_chunk(prev_chunk(p))!=p) ))
169       return NULL;
170     magic = MAGICBYTE(p);
171     for(sz += SIZE_SZ-1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
172       if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
173     }
174   } else {
175     unsigned long offset, page_mask = malloc_getpagesize-1;
176
177     /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
178        alignment relative to the beginning of a page.  Check this
179        first. */
180     offset = (unsigned long)mem & page_mask;
181     if((offset!=MALLOC_ALIGNMENT && offset!=0 && offset!=0x10 &&
182         offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
183         offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
184         offset<0x2000) ||
185        !chunk_is_mmapped(p) || (p->size & PREV_INUSE) ||
186        ( (((unsigned long)p - p->prev_size) & page_mask) != 0 ) ||
187        ( (sz = chunksize(p)), ((p->prev_size + sz) & page_mask) != 0 ) )
188       return NULL;
189     magic = MAGICBYTE(p);
190     for(sz -= 1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
191       if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
192     }
193   }
194   ((unsigned char*)p)[sz] ^= 0xFF;
195   if (magic_p)
196     *magic_p = (unsigned char *)p + sz;
197   return p;
198 }
199
200 /* Check for corruption of the top chunk, and try to recover if
201    necessary. */
202
203 static int
204 internal_function
205 #if __STD_C
206 top_check(void)
207 #else
208 top_check()
209 #endif
210 {
211   mchunkptr t = top(&main_arena);
212   char* brk, * new_brk;
213   INTERNAL_SIZE_T front_misalign, sbrk_size;
214   unsigned long pagesz = malloc_getpagesize;
215
216   if (t == initial_top(&main_arena) ||
217       (!chunk_is_mmapped(t) &&
218        chunksize(t)>=MINSIZE &&
219        prev_inuse(t) &&
220        (!contiguous(&main_arena) ||
221         (char*)t + chunksize(t) == mp_.sbrk_base + main_arena.system_mem)))
222     return 0;
223
224   malloc_printerr (check_action, "malloc: top chunk is corrupt", t);
225
226   /* Try to set up a new top chunk. */
227   brk = MORECORE(0);
228   front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
229   if (front_misalign > 0)
230     front_misalign = MALLOC_ALIGNMENT - front_misalign;
231   sbrk_size = front_misalign + mp_.top_pad + MINSIZE;
232   sbrk_size += pagesz - ((unsigned long)(brk + sbrk_size) & (pagesz - 1));
233   new_brk = (char*)(MORECORE (sbrk_size));
234   if (new_brk == (char*)(MORECORE_FAILURE))
235     {
236       MALLOC_FAILURE_ACTION;
237       return -1;
238     }
239   /* Call the `morecore' hook if necessary.  */
240   if (__after_morecore_hook)
241     (*__after_morecore_hook) ();
242   main_arena.system_mem = (new_brk - mp_.sbrk_base) + sbrk_size;
243
244   top(&main_arena) = (mchunkptr)(brk + front_misalign);
245   set_head(top(&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
246
247   return 0;
248 }
249
250 static Void_t*
251 #if __STD_C
252 malloc_check(size_t sz, const Void_t *caller)
253 #else
254 malloc_check(sz, caller) size_t sz; const Void_t *caller;
255 #endif
256 {
257   Void_t *victim;
258
259   if (sz+1 == 0) {
260     MALLOC_FAILURE_ACTION;
261     return NULL;
262   }
263
264   (void)mutex_lock(&main_arena.mutex);
265   victim = (top_check() >= 0) ? _int_malloc(&main_arena, sz+1) : NULL;
266   (void)mutex_unlock(&main_arena.mutex);
267   return mem2mem_check(victim, sz);
268 }
269
270 static void
271 #if __STD_C
272 free_check(Void_t* mem, const Void_t *caller)
273 #else
274 free_check(mem, caller) Void_t* mem; const Void_t *caller;
275 #endif
276 {
277   mchunkptr p;
278
279   if(!mem) return;
280   (void)mutex_lock(&main_arena.mutex);
281   p = mem2chunk_check(mem, NULL);
282   if(!p) {
283     (void)mutex_unlock(&main_arena.mutex);
284
285     malloc_printerr(check_action, "free(): invalid pointer", mem);
286     return;
287   }
288 #if HAVE_MMAP
289   if (chunk_is_mmapped(p)) {
290     (void)mutex_unlock(&main_arena.mutex);
291     munmap_chunk(p);
292     return;
293   }
294 #endif
295 #if 0 /* Erase freed memory. */
296   memset(mem, 0, chunksize(p) - (SIZE_SZ+1));
297 #endif
298   _int_free(&main_arena, mem);
299   (void)mutex_unlock(&main_arena.mutex);
300 }
301
302 static Void_t*
303 #if __STD_C
304 realloc_check(Void_t* oldmem, size_t bytes, const Void_t *caller)
305 #else
306 realloc_check(oldmem, bytes, caller)
307      Void_t* oldmem; size_t bytes; const Void_t *caller;
308 #endif
309 {
310   mchunkptr oldp;
311   INTERNAL_SIZE_T nb, oldsize;
312   Void_t* newmem = 0;
313   unsigned char *magic_p;
314
315   if (bytes+1 == 0) {
316     MALLOC_FAILURE_ACTION;
317     return NULL;
318   }
319   if (oldmem == 0) return malloc_check(bytes, NULL);
320   if (bytes == 0) {
321     free_check (oldmem, NULL);
322     return NULL;
323   }
324   (void)mutex_lock(&main_arena.mutex);
325   oldp = mem2chunk_check(oldmem, &magic_p);
326   (void)mutex_unlock(&main_arena.mutex);
327   if(!oldp) {
328     malloc_printerr(check_action, "realloc(): invalid pointer", oldmem);
329     return malloc_check(bytes, NULL);
330   }
331   oldsize = chunksize(oldp);
332
333   checked_request2size(bytes+1, nb);
334   (void)mutex_lock(&main_arena.mutex);
335
336 #if HAVE_MMAP
337   if (chunk_is_mmapped(oldp)) {
338 #if HAVE_MREMAP
339     mchunkptr newp = mremap_chunk(oldp, nb);
340     if(newp)
341       newmem = chunk2mem(newp);
342     else
343 #endif
344     {
345       /* Note the extra SIZE_SZ overhead. */
346       if(oldsize - SIZE_SZ >= nb)
347         newmem = oldmem; /* do nothing */
348       else {
349         /* Must alloc, copy, free. */
350         if (top_check() >= 0)
351           newmem = _int_malloc(&main_arena, bytes+1);
352         if (newmem) {
353           MALLOC_COPY(BOUNDED_N(newmem, bytes+1), oldmem, oldsize - 2*SIZE_SZ);
354           munmap_chunk(oldp);
355         }
356       }
357     }
358   } else {
359 #endif /* HAVE_MMAP */
360     if (top_check() >= 0)
361       newmem = _int_realloc(&main_arena, oldmem, bytes+1);
362 #if 0 /* Erase freed memory. */
363     if(newmem)
364       newp = mem2chunk(newmem);
365     nb = chunksize(newp);
366     if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
367       memset((char*)oldmem + 2*sizeof(mbinptr), 0,
368              oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
369     } else if(nb > oldsize+SIZE_SZ) {
370       memset((char*)BOUNDED_N(chunk2mem(newp), bytes) + oldsize,
371              0, nb - (oldsize+SIZE_SZ));
372     }
373 #endif
374 #if HAVE_MMAP
375   }
376 #endif
377
378   /* mem2chunk_check changed the magic byte in the old chunk.
379      If newmem is NULL, then the old chunk will still be used though,
380      so we need to invert that change here.  */
381   if (newmem == NULL) *magic_p ^= 0xFF;
382
383   (void)mutex_unlock(&main_arena.mutex);
384
385   return mem2mem_check(newmem, bytes);
386 }
387
388 static Void_t*
389 #if __STD_C
390 memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
391 #else
392 memalign_check(alignment, bytes, caller)
393      size_t alignment; size_t bytes; const Void_t *caller;
394 #endif
395 {
396   INTERNAL_SIZE_T nb;
397   Void_t* mem;
398
399   if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
400   if (alignment <  MINSIZE) alignment = MINSIZE;
401
402   if (bytes+1 == 0) {
403     MALLOC_FAILURE_ACTION;
404     return NULL;
405   }
406   checked_request2size(bytes+1, nb);
407   (void)mutex_lock(&main_arena.mutex);
408   mem = (top_check() >= 0) ? _int_memalign(&main_arena, alignment, bytes+1) :
409     NULL;
410   (void)mutex_unlock(&main_arena.mutex);
411   return mem2mem_check(mem, bytes);
412 }
413
414 #ifndef NO_THREADS
415
416 # ifdef _LIBC
417 #  if USE___THREAD || !defined SHARED
418     /* These routines are never needed in this configuration.  */
419 #   define NO_STARTER
420 #  endif
421 # endif
422
423 # ifdef NO_STARTER
424 #  undef NO_STARTER
425 # else
426
427 /* The following hooks are used when the global initialization in
428    ptmalloc_init() hasn't completed yet. */
429
430 static Void_t*
431 #if __STD_C
432 malloc_starter(size_t sz, const Void_t *caller)
433 #else
434 malloc_starter(sz, caller) size_t sz; const Void_t *caller;
435 #endif
436 {
437   Void_t* victim;
438
439   victim = _int_malloc(&main_arena, sz);
440
441   return victim ? BOUNDED_N(victim, sz) : 0;
442 }
443
444 static Void_t*
445 #if __STD_C
446 memalign_starter(size_t align, size_t sz, const Void_t *caller)
447 #else
448 memalign_starter(align, sz, caller) size_t align, sz; const Void_t *caller;
449 #endif
450 {
451   Void_t* victim;
452
453   victim = _int_memalign(&main_arena, align, sz);
454
455   return victim ? BOUNDED_N(victim, sz) : 0;
456 }
457
458 static void
459 #if __STD_C
460 free_starter(Void_t* mem, const Void_t *caller)
461 #else
462 free_starter(mem, caller) Void_t* mem; const Void_t *caller;
463 #endif
464 {
465   mchunkptr p;
466
467   if(!mem) return;
468   p = mem2chunk(mem);
469 #if HAVE_MMAP
470   if (chunk_is_mmapped(p)) {
471     munmap_chunk(p);
472     return;
473   }
474 #endif
475   _int_free(&main_arena, mem);
476 }
477
478 # endif /* !defiend NO_STARTER */
479 #endif /* NO_THREADS */
480
481
482 /* Get/set state: malloc_get_state() records the current state of all
483    malloc variables (_except_ for the actual heap contents and `hook'
484    function pointers) in a system dependent, opaque data structure.
485    This data structure is dynamically allocated and can be free()d
486    after use.  malloc_set_state() restores the state of all malloc
487    variables to the previously obtained state.  This is especially
488    useful when using this malloc as part of a shared library, and when
489    the heap contents are saved/restored via some other method.  The
490    primary example for this is GNU Emacs with its `dumping' procedure.
491    `Hook' function pointers are never saved or restored by these
492    functions, with two exceptions: If malloc checking was in use when
493    malloc_get_state() was called, then malloc_set_state() calls
494    __malloc_check_init() if possible; if malloc checking was not in
495    use in the recorded state but the user requested malloc checking,
496    then the hooks are reset to 0.  */
497
498 #define MALLOC_STATE_MAGIC   0x444c4541l
499 #define MALLOC_STATE_VERSION (0*0x100l + 2l) /* major*0x100 + minor */
500
501 struct malloc_save_state {
502   long          magic;
503   long          version;
504   mbinptr       av[NBINS * 2 + 2];
505   char*         sbrk_base;
506   int           sbrked_mem_bytes;
507   unsigned long trim_threshold;
508   unsigned long top_pad;
509   unsigned int  n_mmaps_max;
510 #if MALLOC_DEBUG
511   unsigned int  n_mmaps_cmax;
512 #endif
513   unsigned long mmap_threshold;
514   int           check_action;
515   unsigned long max_sbrked_mem;
516   unsigned long max_total_mem;
517   unsigned int  n_mmaps;
518   unsigned int  max_n_mmaps;
519   unsigned long mmapped_mem;
520   unsigned long max_mmapped_mem;
521   int           using_malloc_checking;
522 };
523
524 Void_t*
525 public_gET_STATe(void)
526 {
527   struct malloc_save_state* ms;
528   int i;
529   mbinptr b;
530
531   ms = (struct malloc_save_state*)public_mALLOc(sizeof(*ms));
532   if (!ms)
533     return 0;
534   (void)mutex_lock(&main_arena.mutex);
535   malloc_consolidate(&main_arena);
536   ms->magic = MALLOC_STATE_MAGIC;
537   ms->version = MALLOC_STATE_VERSION;
538   ms->av[0] = 0;
539   ms->av[1] = 0; /* used to be binblocks, now no longer used */
540   ms->av[2] = top(&main_arena);
541   ms->av[3] = 0; /* used to be undefined */
542   for(i=1; i<NBINS; i++) {
543     b = bin_at(&main_arena, i);
544     if(first(b) == b)
545       ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin */
546     else {
547       ms->av[2*i+2] = first(b);
548       ms->av[2*i+3] = last(b);
549     }
550   }
551   ms->sbrk_base = mp_.sbrk_base;
552   ms->sbrked_mem_bytes = main_arena.system_mem;
553   ms->trim_threshold = mp_.trim_threshold;
554   ms->top_pad = mp_.top_pad;
555   ms->n_mmaps_max = mp_.n_mmaps_max;
556 #if MALLOC_DEBUG
557   ms->n_mmaps_cmax = mp_.n_mmaps_cmax;
558 #endif
559   ms->mmap_threshold = mp_.mmap_threshold;
560   ms->check_action = check_action;
561   ms->max_sbrked_mem = main_arena.max_system_mem;
562 #ifdef NO_THREADS
563   ms->max_total_mem = mp_.max_total_mem;
564 #else
565   ms->max_total_mem = 0;
566 #endif
567   ms->n_mmaps = mp_.n_mmaps;
568   ms->max_n_mmaps = mp_.max_n_mmaps;
569   ms->mmapped_mem = mp_.mmapped_mem;
570   ms->max_mmapped_mem = mp_.max_mmapped_mem;
571   ms->using_malloc_checking = using_malloc_checking;
572   (void)mutex_unlock(&main_arena.mutex);
573   return (Void_t*)ms;
574 }
575
576 int
577 public_sET_STATe(Void_t* msptr)
578 {
579   struct malloc_save_state* ms = (struct malloc_save_state*)msptr;
580   size_t i;
581   mbinptr b;
582
583   disallow_malloc_check = 1;
584   ptmalloc_init();
585   if(ms->magic != MALLOC_STATE_MAGIC) return -1;
586   /* Must fail if the major version is too high. */
587   if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
588   (void)mutex_lock(&main_arena.mutex);
589   /* There are no fastchunks.  */
590   clear_fastchunks(&main_arena);
591   set_max_fast(DEFAULT_MXFAST);
592   for (i=0; i<NFASTBINS; ++i)
593     main_arena.fastbins[i] = 0;
594   for (i=0; i<BINMAPSIZE; ++i)
595     main_arena.binmap[i] = 0;
596   top(&main_arena) = ms->av[2];
597   main_arena.last_remainder = 0;
598   for(i=1; i<NBINS; i++) {
599     b = bin_at(&main_arena, i);
600     if(ms->av[2*i+2] == 0) {
601       assert(ms->av[2*i+3] == 0);
602       first(b) = last(b) = b;
603     } else {
604       if(i<NSMALLBINS || (largebin_index(chunksize(ms->av[2*i+2]))==i &&
605                           largebin_index(chunksize(ms->av[2*i+3]))==i)) {
606         first(b) = ms->av[2*i+2];
607         last(b) = ms->av[2*i+3];
608         /* Make sure the links to the bins within the heap are correct.  */
609         first(b)->bk = b;
610         last(b)->fd = b;
611         /* Set bit in binblocks.  */
612         mark_bin(&main_arena, i);
613       } else {
614         /* Oops, index computation from chunksize must have changed.
615            Link the whole list into unsorted_chunks.  */
616         first(b) = last(b) = b;
617         b = unsorted_chunks(&main_arena);
618         ms->av[2*i+2]->bk = b;
619         ms->av[2*i+3]->fd = b->fd;
620         b->fd->bk = ms->av[2*i+3];
621         b->fd = ms->av[2*i+2];
622       }
623     }
624   }
625   mp_.sbrk_base = ms->sbrk_base;
626   main_arena.system_mem = ms->sbrked_mem_bytes;
627   mp_.trim_threshold = ms->trim_threshold;
628   mp_.top_pad = ms->top_pad;
629   mp_.n_mmaps_max = ms->n_mmaps_max;
630 #if MALLOC_DEBUG
631   mp_.n_mmaps_cmax = ms->n_mmaps_cmax;
632 #endif
633   mp_.mmap_threshold = ms->mmap_threshold;
634   check_action = ms->check_action;
635   main_arena.max_system_mem = ms->max_sbrked_mem;
636 #ifdef NO_THREADS
637   mp_.max_total_mem = ms->max_total_mem;
638 #endif
639   mp_.n_mmaps = ms->n_mmaps;
640   mp_.max_n_mmaps = ms->max_n_mmaps;
641   mp_.mmapped_mem = ms->mmapped_mem;
642   mp_.max_mmapped_mem = ms->max_mmapped_mem;
643   /* add version-dependent code here */
644   if (ms->version >= 1) {
645     /* Check whether it is safe to enable malloc checking, or whether
646        it is necessary to disable it.  */
647     if (ms->using_malloc_checking && !using_malloc_checking &&
648         !disallow_malloc_check)
649       __malloc_check_init ();
650     else if (!ms->using_malloc_checking && using_malloc_checking) {
651       __malloc_hook = 0;
652       __free_hook = 0;
653       __realloc_hook = 0;
654       __memalign_hook = 0;
655       using_malloc_checking = 0;
656     }
657   }
658   check_malloc_state(&main_arena);
659
660   (void)mutex_unlock(&main_arena.mutex);
661   return 0;
662 }
663
664 /*
665  * Local variables:
666  * c-basic-offset: 2
667  * End:
668  */