gmem.c: add ability to exclude memory chunks from memory profiling
[platform/upstream/glib.git] / gmem.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include <stdlib.h>
20 #include <string.h>
21 #include "glib.h"
22
23 /* #define ENABLE_MEM_PROFILE */
24 /* #define ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS */
25 /* #define ENABLE_MEM_CHECK */
26
27 #if defined(ENABLE_MEM_PROFILE) && defined(ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS)
28 #define ENTER_MEM_CHUNK_ROUTINE() allocating_for_mem_chunk++
29 #define LEAVE_MEM_CHUNK_ROUTINE() allocating_for_mem_chunk--
30 #else
31 #define ENTER_MEM_CHUNK_ROUTINE()
32 #define LEAVE_MEM_CHUNK_ROUTINE()
33 #endif
34
35
36 #define MAX_MEM_AREA  65536L
37 #define MEM_AREA_SIZE 4L
38
39 #if SIZEOF_VOID_P > SIZEOF_LONG
40 #define MEM_ALIGN     SIZEOF_VOID_P
41 #else
42 #define MEM_ALIGN     SIZEOF_LONG
43 #endif
44
45
46 typedef struct _GFreeAtom      GFreeAtom;
47 typedef struct _GMemArea       GMemArea;
48 typedef struct _GRealMemChunk  GRealMemChunk;
49
50 struct _GFreeAtom
51 {
52   GFreeAtom *next;
53 };
54
55 struct _GMemArea
56 {
57   GMemArea *next;            /* the next mem area */
58   GMemArea *prev;            /* the previous mem area */
59   gulong index;              /* the current index into the "mem" array */
60   gulong free;               /* the number of free bytes in this mem area */
61   gulong allocated;          /* the number of atoms allocated from this area */
62   gulong mark;               /* is this mem area marked for deletion */
63   gchar mem[MEM_AREA_SIZE];  /* the mem array from which atoms get allocated
64                               * the actual size of this array is determined by
65                               *  the mem chunk "area_size". ANSI says that it
66                               *  must be declared to be the maximum size it
67                               *  can possibly be (even though the actual size
68                               *  may be less).
69                               */
70 };
71
72 struct _GRealMemChunk
73 {
74   gchar *name;               /* name of this MemChunk...used for debugging output */
75   gint type;                 /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
76   gint num_mem_areas;        /* the number of memory areas */
77   gint num_marked_areas;     /* the number of areas marked for deletion */
78   guint atom_size;           /* the size of an atom */
79   gulong area_size;          /* the size of a memory area */
80   GMemArea *mem_area;        /* the current memory area */
81   GMemArea *mem_areas;       /* a list of all the mem areas owned by this chunk */
82   GMemArea *free_mem_area;   /* the free area...which is about to be destroyed */
83   GFreeAtom *free_atoms;     /* the free atoms list */
84   GTree *mem_tree;           /* tree of mem areas sorted by memory address */
85   GRealMemChunk *next;       /* pointer to the next chunk */
86   GRealMemChunk *prev;       /* pointer to the previous chunk */
87 };
88
89
90 static gulong g_mem_chunk_compute_size (gulong    size);
91 static gint   g_mem_chunk_area_compare (GMemArea *a,
92                                         GMemArea *b);
93 static gint   g_mem_chunk_area_search  (GMemArea *a,
94                                         gchar    *addr);
95
96
97 static GRealMemChunk *mem_chunks = NULL;
98
99 #ifdef ENABLE_MEM_PROFILE
100 static gulong allocations[4096] = { 0 };
101 static gulong allocated_mem = 0;
102 static gulong freed_mem = 0;
103 static gint allocating_for_mem_chunk = 0;
104 #endif /* ENABLE_MEM_PROFILE */
105
106
107 #ifndef USE_DMALLOC
108
109 gpointer
110 g_malloc (gulong size)
111 {
112   gpointer p;
113   
114   
115 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
116   gulong *t;
117 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
118   
119   
120   if (size == 0)
121     return NULL;
122   
123   
124 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
125   size += SIZEOF_LONG;
126 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
127   
128 #ifdef ENABLE_MEM_CHECK
129   size += SIZEOF_LONG;
130 #endif /* ENABLE_MEM_CHECK */
131   
132   
133   p = (gpointer) malloc (size);
134   if (!p)
135     g_error ("could not allocate %ld bytes", size);
136   
137   
138 #ifdef ENABLE_MEM_CHECK
139   size -= SIZEOF_LONG;
140   
141   t = p;
142   p = ((guchar*) p + SIZEOF_LONG);
143   *t = 0;
144 #endif /* ENABLE_MEM_CHECK */
145   
146 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
147   size -= SIZEOF_LONG;
148   
149   t = p;
150   p = ((guchar*) p + SIZEOF_LONG);
151   *t = size;
152   
153 #ifdef ENABLE_MEM_PROFILE
154 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
155   if(!allocating_for_mem_chunk) {
156 #endif
157     if (size <= 4095)
158       allocations[size-1] += 1;
159     else
160       allocations[4095] += 1;
161     allocated_mem += size;
162 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
163   }
164 #endif
165 #endif /* ENABLE_MEM_PROFILE */
166 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
167   
168   
169   return p;
170 }
171
172 gpointer
173 g_malloc0 (gulong size)
174 {
175   gpointer p;
176   
177   
178 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
179   gulong *t;
180 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
181   
182   
183   if (size == 0)
184     return NULL;
185   
186   
187 #ifdef ENABLE_MEM_PROFILE
188   size += SIZEOF_LONG;
189 #endif /* ENABLE_MEM_PROFILE */
190   
191 #ifdef ENABLE_MEM_CHECK
192   size += SIZEOF_LONG;
193 #endif /* ENABLE_MEM_CHECK */
194   
195   
196   p = (gpointer) calloc (size, 1);
197   if (!p)
198     g_error ("could not allocate %ld bytes", size);
199   
200   
201 #ifdef ENABLE_MEM_CHECK
202   size -= SIZEOF_LONG;
203   
204   t = p;
205   p = ((guchar*) p + SIZEOF_LONG);
206   *t = 0;
207 #endif /* ENABLE_MEM_CHECK */
208   
209 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
210   size -= SIZEOF_LONG;
211   
212   t = p;
213   p = ((guchar*) p + SIZEOF_LONG);
214   *t = size;
215   
216 #ifdef ENABLE_MEM_PROFILE
217 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
218   if(!allocating_for_mem_chunk) {
219 #endif
220     if (size <= 4095)
221       allocations[size-1] += 1;
222     else
223       allocations[4095] += 1;
224     allocated_mem += size;
225 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
226   }
227 #endif
228 #endif /* ENABLE_MEM_PROFILE */
229 #endif /* ENABLE_MEM_PROFILE */
230   
231   
232   return p;
233 }
234
235 gpointer
236 g_realloc (gpointer mem,
237            gulong   size)
238 {
239   gpointer p;
240   
241 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
242   gulong *t;
243 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
244   
245   
246   if (size == 0)
247     return NULL;
248   
249   
250 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
251   size += SIZEOF_LONG;
252 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
253   
254 #ifdef ENABLE_MEM_CHECK
255   size += SIZEOF_LONG;
256 #endif /* ENABLE_MEM_CHECK */
257   
258   
259   if (!mem)
260     p = (gpointer) malloc (size);
261   else
262     {
263 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
264       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
265 #ifdef ENABLE_MEM_PROFILE
266       freed_mem += *t;
267 #endif /* ENABLE_MEM_PROFILE */
268       mem = t;
269 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
270       
271 #ifdef ENABLE_MEM_CHECK
272       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
273       if (*t >= 1)
274         g_warning ("trying to realloc freed memory\n");
275       mem = t;
276 #endif /* ENABLE_MEM_CHECK */
277       
278       p = (gpointer) realloc (mem, size);
279     }
280   
281   if (!p)
282     g_error ("could not reallocate %ld bytes", size);
283   
284   
285 #ifdef ENABLE_MEM_CHECK
286   size -= SIZEOF_LONG;
287   
288   t = p;
289   p = ((guchar*) p + SIZEOF_LONG);
290   *t = 0;
291 #endif /* ENABLE_MEM_CHECK */
292   
293 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
294   size -= SIZEOF_LONG;
295   
296   t = p;
297   p = ((guchar*) p + SIZEOF_LONG);
298   *t = size;
299   
300 #ifdef ENABLE_MEM_PROFILE
301 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
302   if(!allocating_for_mem_chunk) {
303 #endif
304     if (size <= 4095)
305       allocations[size-1] += 1;
306     else
307       allocations[4095] += 1;
308     allocated_mem += size;
309 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
310   }
311 #endif
312 #endif /* ENABLE_MEM_PROFILE */
313 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
314   
315   
316   return p;
317 }
318
319 void
320 g_free (gpointer mem)
321 {
322   if (mem)
323     {
324 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
325       gulong *t;
326       gulong size;
327 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
328       
329 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
330       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
331       size = *t;
332 #ifdef ENABLE_MEM_PROFILE
333       freed_mem += size;
334 #endif /* ENABLE_MEM_PROFILE */
335       mem = t;
336 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
337       
338 #ifdef ENABLE_MEM_CHECK
339       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
340       if (*t >= 1)
341         g_warning ("freeing previously freed memory\n");
342       *t += 1;
343       mem = t;
344       
345       memset ((guchar*) mem + 8, 0, size);
346 #else /* ENABLE_MEM_CHECK */
347       free (mem);
348 #endif /* ENABLE_MEM_CHECK */
349     }
350 }
351
352 #endif /* ! USE_DMALLOC */
353
354
355 void
356 g_mem_profile (void)
357 {
358 #ifdef ENABLE_MEM_PROFILE
359   gint i;
360   
361   for (i = 0; i < 4095; i++)
362     if (allocations[i] > 0)
363       g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
364              "%lu allocations of %d bytes\n", allocations[i], i + 1);
365   
366   if (allocations[4095] > 0)
367     g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
368            "%lu allocations of greater than 4095 bytes\n", allocations[4095]);
369   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes allocated\n", allocated_mem);
370   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes freed\n", freed_mem);
371   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes in use\n", allocated_mem - freed_mem);
372 #endif /* ENABLE_MEM_PROFILE */
373 }
374
375 void
376 g_mem_check (gpointer mem)
377 {
378 #ifdef ENABLE_MEM_CHECK
379   gulong *t;
380   
381   t = (gulong*) ((guchar*) mem - SIZEOF_LONG - SIZEOF_LONG);
382   
383   if (*t >= 1)
384     g_warning ("mem: 0x%08x has been freed: %lu\n", (gulong) mem, *t);
385 #endif /* ENABLE_MEM_CHECK */
386 }
387
388 GMemChunk*
389 g_mem_chunk_new (gchar  *name,
390                  gint    atom_size,
391                  gulong  area_size,
392                  gint    type)
393 {
394   GRealMemChunk *mem_chunk;
395   gulong rarea_size;
396
397   ENTER_MEM_CHUNK_ROUTINE();
398
399   mem_chunk = g_new (struct _GRealMemChunk, 1);
400   mem_chunk->name = name;
401   mem_chunk->type = type;
402   mem_chunk->num_mem_areas = 0;
403   mem_chunk->num_marked_areas = 0;
404   mem_chunk->mem_area = NULL;
405   mem_chunk->free_mem_area = NULL;
406   mem_chunk->free_atoms = NULL;
407   mem_chunk->mem_tree = NULL;
408   mem_chunk->mem_areas = NULL;
409   mem_chunk->atom_size = atom_size;
410   
411   if (mem_chunk->type == G_ALLOC_AND_FREE)
412     mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
413   
414   if (mem_chunk->atom_size % MEM_ALIGN)
415     mem_chunk->atom_size += MEM_ALIGN - (mem_chunk->atom_size % MEM_ALIGN);
416   
417   mem_chunk->area_size = area_size;
418   if (mem_chunk->area_size > MAX_MEM_AREA)
419     mem_chunk->area_size = MAX_MEM_AREA;
420   while (mem_chunk->area_size < mem_chunk->atom_size)
421     mem_chunk->area_size *= 2;
422   
423   rarea_size = mem_chunk->area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
424   rarea_size = g_mem_chunk_compute_size (rarea_size);
425   mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
426   
427   /*
428     mem_chunk->area_size -= (sizeof (GMemArea) - MEM_AREA_SIZE);
429     if (mem_chunk->area_size < mem_chunk->atom_size)
430     {
431     mem_chunk->area_size = (mem_chunk->area_size + sizeof (GMemArea) - MEM_AREA_SIZE) * 2;
432     mem_chunk->area_size -= (sizeof (GMemArea) - MEM_AREA_SIZE);
433     }
434     
435     if (mem_chunk->area_size % mem_chunk->atom_size)
436     mem_chunk->area_size += mem_chunk->atom_size - (mem_chunk->area_size % mem_chunk->atom_size);
437   */
438   
439   mem_chunk->next = mem_chunks;
440   mem_chunk->prev = NULL;
441   if (mem_chunks)
442     mem_chunks->prev = mem_chunk;
443   mem_chunks = mem_chunk;
444
445   LEAVE_MEM_CHUNK_ROUTINE();
446
447   return ((GMemChunk*) mem_chunk);
448 }
449
450 void
451 g_mem_chunk_destroy (GMemChunk *mem_chunk)
452 {
453   GRealMemChunk *rmem_chunk;
454   GMemArea *mem_areas;
455   GMemArea *temp_area;
456   
457   g_assert (mem_chunk != NULL);
458
459   ENTER_MEM_CHUNK_ROUTINE();
460
461   rmem_chunk = (GRealMemChunk*) mem_chunk;
462   
463   mem_areas = rmem_chunk->mem_areas;
464   while (mem_areas)
465     {
466       temp_area = mem_areas;
467       mem_areas = mem_areas->next;
468       g_free (temp_area);
469     }
470   
471   if (rmem_chunk->next)
472     rmem_chunk->next->prev = rmem_chunk->prev;
473   if (rmem_chunk->prev)
474     rmem_chunk->prev->next = rmem_chunk->next;
475   
476   if (rmem_chunk == mem_chunks)
477     mem_chunks = mem_chunks->next;
478   
479   if (rmem_chunk->type == G_ALLOC_AND_FREE)
480     g_tree_destroy (rmem_chunk->mem_tree);
481   
482   g_free (rmem_chunk);
483
484   LEAVE_MEM_CHUNK_ROUTINE();
485 }
486
487 gpointer
488 g_mem_chunk_alloc (GMemChunk *mem_chunk)
489 {
490   GRealMemChunk *rmem_chunk;
491   GMemArea *temp_area;
492   gpointer mem;
493
494   ENTER_MEM_CHUNK_ROUTINE();
495
496   g_assert (mem_chunk != NULL);
497   
498   rmem_chunk = (GRealMemChunk*) mem_chunk;
499   
500   while (rmem_chunk->free_atoms)
501     {
502       /* Get the first piece of memory on the "free_atoms" list.
503        * We can go ahead and destroy the list node we used to keep
504        *  track of it with and to update the "free_atoms" list to
505        *  point to its next element.
506        */
507       mem = rmem_chunk->free_atoms;
508       rmem_chunk->free_atoms = rmem_chunk->free_atoms->next;
509       
510       /* Determine which area this piece of memory is allocated from */
511       temp_area = g_tree_search (rmem_chunk->mem_tree,
512                                  (GSearchFunc) g_mem_chunk_area_search,
513                                  mem);
514       
515       /* If the area has been marked, then it is being destroyed.
516        *  (ie marked to be destroyed).
517        * We check to see if all of the segments on the free list that
518        *  reference this area have been removed. This occurs when
519        *  the ammount of free memory is less than the allocatable size.
520        * If the chunk should be freed, then we place it in the "free_mem_area".
521        * This is so we make sure not to free the mem area here and then
522        *  allocate it again a few lines down.
523        * If we don't allocate a chunk a few lines down then the "free_mem_area"
524        *  will be freed.
525        * If there is already a "free_mem_area" then we'll just free this mem area.
526        */
527       if (temp_area->mark)
528         {
529           /* Update the "free" memory available in that area */
530           temp_area->free += rmem_chunk->atom_size;
531           
532           if (temp_area->free == rmem_chunk->area_size)
533             {
534               if (temp_area == rmem_chunk->mem_area)
535                 rmem_chunk->mem_area = NULL;
536               
537               if (rmem_chunk->free_mem_area)
538                 {
539                   rmem_chunk->num_mem_areas -= 1;
540                   
541                   if (temp_area->next)
542                     temp_area->next->prev = temp_area->prev;
543                   if (temp_area->prev)
544                     temp_area->prev->next = temp_area->next;
545                   if (temp_area == rmem_chunk->mem_areas)
546                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
547                   
548                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
549                     g_tree_remove (rmem_chunk->mem_tree, temp_area);
550                   g_free (temp_area);
551                 }
552               else
553                 rmem_chunk->free_mem_area = temp_area;
554               
555               rmem_chunk->num_marked_areas -= 1;
556             }
557         }
558       else
559         {
560           /* Update the number of allocated atoms count.
561            */
562           temp_area->allocated += 1;
563           
564           /* The area wasn't marked...return the memory
565            */
566           goto outa_here;
567         }
568     }
569   
570   /* If there isn't a current mem area or the current mem area is out of space
571    *  then allocate a new mem area. We'll first check and see if we can use
572    *  the "free_mem_area". Otherwise we'll just malloc the mem area.
573    */
574   if ((!rmem_chunk->mem_area) ||
575       ((rmem_chunk->mem_area->index + rmem_chunk->atom_size) > rmem_chunk->area_size))
576     {
577       if (rmem_chunk->free_mem_area)
578         {
579           rmem_chunk->mem_area = rmem_chunk->free_mem_area;
580           rmem_chunk->free_mem_area = NULL;
581         }
582       else
583         {
584           rmem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
585                                                        MEM_AREA_SIZE +
586                                                        rmem_chunk->area_size);
587           
588           rmem_chunk->num_mem_areas += 1;
589           rmem_chunk->mem_area->next = rmem_chunk->mem_areas;
590           rmem_chunk->mem_area->prev = NULL;
591           
592           if (rmem_chunk->mem_areas)
593             rmem_chunk->mem_areas->prev = rmem_chunk->mem_area;
594           rmem_chunk->mem_areas = rmem_chunk->mem_area;
595           
596           if (rmem_chunk->type == G_ALLOC_AND_FREE)
597             g_tree_insert (rmem_chunk->mem_tree, rmem_chunk->mem_area, rmem_chunk->mem_area);
598         }
599       
600       rmem_chunk->mem_area->index = 0;
601       rmem_chunk->mem_area->free = rmem_chunk->area_size;
602       rmem_chunk->mem_area->allocated = 0;
603       rmem_chunk->mem_area->mark = 0;
604     }
605   
606   /* Get the memory and modify the state variables appropriately.
607    */
608   mem = (gpointer) &rmem_chunk->mem_area->mem[rmem_chunk->mem_area->index];
609   rmem_chunk->mem_area->index += rmem_chunk->atom_size;
610   rmem_chunk->mem_area->free -= rmem_chunk->atom_size;
611   rmem_chunk->mem_area->allocated += 1;
612
613 outa_here:
614
615   LEAVE_MEM_CHUNK_ROUTINE();
616
617   return mem;
618 }
619
620 void
621 g_mem_chunk_free (GMemChunk *mem_chunk,
622                   gpointer   mem)
623 {
624   GRealMemChunk *rmem_chunk;
625   GMemArea *temp_area;
626   GFreeAtom *free_atom;
627   
628   g_assert (mem_chunk != NULL);
629   g_assert (mem != NULL);
630
631   ENTER_MEM_CHUNK_ROUTINE();
632
633   rmem_chunk = (GRealMemChunk*) mem_chunk;
634   
635   /* Don't do anything if this is an ALLOC_ONLY chunk
636    */
637   if (rmem_chunk->type == G_ALLOC_AND_FREE)
638     {
639       /* Place the memory on the "free_atoms" list
640        */
641       free_atom = (GFreeAtom*) mem;
642       free_atom->next = rmem_chunk->free_atoms;
643       rmem_chunk->free_atoms = free_atom;
644       
645       temp_area = g_tree_search (rmem_chunk->mem_tree,
646                                  (GSearchFunc) g_mem_chunk_area_search,
647                                  mem);
648       
649       temp_area->allocated -= 1;
650       
651       if (temp_area->allocated == 0)
652         {
653           temp_area->mark = 1;
654           rmem_chunk->num_marked_areas += 1;
655         }
656     }
657
658   LEAVE_MEM_CHUNK_ROUTINE();
659 }
660
661 /* This doesn't free the free_area if there is one */
662 void
663 g_mem_chunk_clean (GMemChunk *mem_chunk)
664 {
665   GRealMemChunk *rmem_chunk;
666   GMemArea *mem_area;
667   GFreeAtom *prev_free_atom;
668   GFreeAtom *temp_free_atom;
669   gpointer mem;
670   
671   g_assert (mem_chunk != NULL);
672   
673   rmem_chunk = (GRealMemChunk*) mem_chunk;
674   
675   if (rmem_chunk->type == G_ALLOC_AND_FREE)
676     {
677       prev_free_atom = NULL;
678       temp_free_atom = rmem_chunk->free_atoms;
679       
680       while (temp_free_atom)
681         {
682           mem = (gpointer) temp_free_atom;
683           
684           mem_area = g_tree_search (rmem_chunk->mem_tree,
685                                     (GSearchFunc) g_mem_chunk_area_search,
686                                     mem);
687           
688           /* If this mem area is marked for destruction then delete the
689            *  area and list node and decrement the free mem.
690            */
691           if (mem_area->mark)
692             {
693               if (prev_free_atom)
694                 prev_free_atom->next = temp_free_atom->next;
695               else
696                 rmem_chunk->free_atoms = temp_free_atom->next;
697               temp_free_atom = temp_free_atom->next;
698               
699               mem_area->free += rmem_chunk->atom_size;
700               if (mem_area->free == rmem_chunk->area_size)
701                 {
702                   rmem_chunk->num_mem_areas -= 1;
703                   rmem_chunk->num_marked_areas -= 1;
704                   
705                   if (mem_area->next)
706                     mem_area->next->prev = mem_area->prev;
707                   if (mem_area->prev)
708                     mem_area->prev->next = mem_area->next;
709                   if (mem_area == rmem_chunk->mem_areas)
710                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
711                   if (mem_area == rmem_chunk->mem_area)
712                     rmem_chunk->mem_area = NULL;
713                   
714                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
715                     g_tree_remove (rmem_chunk->mem_tree, mem_area);
716                   g_free (mem_area);
717                 }
718             }
719           else
720             {
721               prev_free_atom = temp_free_atom;
722               temp_free_atom = temp_free_atom->next;
723             }
724         }
725     }
726 }
727
728 void
729 g_mem_chunk_reset (GMemChunk *mem_chunk)
730 {
731   GRealMemChunk *rmem_chunk;
732   GMemArea *mem_areas;
733   GMemArea *temp_area;
734   
735   g_assert (mem_chunk != NULL);
736   
737   rmem_chunk = (GRealMemChunk*) mem_chunk;
738   
739   mem_areas = rmem_chunk->mem_areas;
740   rmem_chunk->num_mem_areas = 0;
741   rmem_chunk->mem_areas = NULL;
742   rmem_chunk->mem_area = NULL;
743   
744   while (mem_areas)
745     {
746       temp_area = mem_areas;
747       mem_areas = mem_areas->next;
748       g_free (temp_area);
749     }
750   
751   rmem_chunk->free_atoms = NULL;
752   
753   if (rmem_chunk->mem_tree)
754     g_tree_destroy (rmem_chunk->mem_tree);
755   rmem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
756 }
757
758 void
759 g_mem_chunk_print (GMemChunk *mem_chunk)
760 {
761   GRealMemChunk *rmem_chunk;
762   GMemArea *mem_areas;
763   gulong mem;
764   
765   g_assert (mem_chunk != NULL);
766   
767   rmem_chunk = (GRealMemChunk*) mem_chunk;
768   mem_areas = rmem_chunk->mem_areas;
769   mem = 0;
770   
771   while (mem_areas)
772     {
773       mem += rmem_chunk->area_size - mem_areas->free;
774       mem_areas = mem_areas->next;
775     }
776   
777   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
778          "%s: %ld bytes using %d mem areas\n",
779          rmem_chunk->name, mem, rmem_chunk->num_mem_areas);
780 }
781
782 void
783 g_mem_chunk_info (void)
784 {
785   GRealMemChunk *mem_chunk;
786   gint count;
787   
788   count = 0;
789   mem_chunk = mem_chunks;
790   while (mem_chunk)
791     {
792       count += 1;
793       mem_chunk = mem_chunk->next;
794     }
795   
796   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%d mem chunks\n", count);
797   
798   mem_chunk = mem_chunks;
799   while (mem_chunk)
800     {
801       g_mem_chunk_print ((GMemChunk*) mem_chunk);
802       mem_chunk = mem_chunk->next;
803     }
804 }
805
806 void
807 g_blow_chunks (void)
808 {
809   GRealMemChunk *mem_chunk;
810   
811   mem_chunk = mem_chunks;
812   while (mem_chunk)
813     {
814       g_mem_chunk_clean ((GMemChunk*) mem_chunk);
815       mem_chunk = mem_chunk->next;
816     }
817 }
818
819
820 static gulong
821 g_mem_chunk_compute_size (gulong size)
822 {
823   gulong power_of_2;
824   gulong lower, upper;
825   
826   power_of_2 = 16;
827   while (power_of_2 < size)
828     power_of_2 <<= 1;
829   
830   lower = power_of_2 >> 1;
831   upper = power_of_2;
832   
833   if ((size - lower) < (upper - size))
834     return lower;
835   return upper;
836 }
837
838 static gint
839 g_mem_chunk_area_compare (GMemArea *a,
840                           GMemArea *b)
841 {
842   return (a->mem - b->mem);
843 }
844
845 static gint
846 g_mem_chunk_area_search (GMemArea *a,
847                          gchar    *addr)
848 {
849   if (a->mem <= addr)
850     {
851       if (addr < &a->mem[a->index])
852         return 0;
853       return 1;
854     }
855   return -1;
856 }