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