X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgmem.c;h=6a108ee9e0e38627a3d963b9cc651f11b121f7c1;hb=ea4f9ce8a060d53cbc299e4c384089f6cc926caa;hp=a29cb6dc20adba792635412184d7e80994a827d9;hpb=ddfc68fc98bd688024d7c4b10655e553f1f31857;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gmem.c b/glib/gmem.c index a29cb6d..6a108ee 100644 --- a/glib/gmem.c +++ b/glib/gmem.c @@ -2,23 +2,21 @@ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . */ /* - * Modified by the GLib Team and others 1997-1999. See the AUTHORS + * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. @@ -28,977 +26,805 @@ * MT safe */ -#ifdef HAVE_CONFIG_H -#include -#endif +#include "config.h" + +#include "gmem.h" #include #include -#include "glib.h" - -/* #define ENABLE_MEM_PROFILE */ -/* #define ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS */ -/* #define ENABLE_MEM_CHECK */ -#define MEM_PROFILE_TABLE_SIZE 8192 +#include -/* - * This library can check for some attempts to do illegal things to - * memory (ENABLE_MEM_CHECK), and can do profiling - * (ENABLE_MEM_PROFILE). Both features are implemented by storing - * words before the start of the memory chunk. - * - * The first, at offset -2*SIZEOF_LONG, is used only if - * ENABLE_MEM_CHECK is set, and stores 0 after the memory has been - * allocated and 1 when it has been freed. The second, at offset - * -SIZEOF_LONG, is used if either flag is set and stores the size of - * the block. - * - * The MEM_CHECK flag is checked when memory is realloc'd and free'd, - * and it can be explicitly checked before using a block by calling - * g_mem_check(). - */ +#include "gslice.h" +#include "gbacktrace.h" +#include "gtestutils.h" +#include "gthread.h" +#include "glib_trace.h" -#if defined(ENABLE_MEM_PROFILE) && defined(ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS) -#define ENTER_MEM_CHUNK_ROUTINE() \ - g_private_set (allocating_for_mem_chunk, \ - g_private_get (allocating_for_mem_chunk) + 1) -#define LEAVE_MEM_CHUNK_ROUTINE() \ - g_private_set (allocating_for_mem_chunk, \ - g_private_get (allocating_for_mem_chunk) - 1) -#else -#define ENTER_MEM_CHUNK_ROUTINE() -#define LEAVE_MEM_CHUNK_ROUTINE() -#endif +#define MEM_PROFILE_TABLE_SIZE 4096 -#define MEM_AREA_SIZE 4L - -#if SIZEOF_VOID_P > SIZEOF_LONG -#define MEM_ALIGN SIZEOF_VOID_P -#else -#define MEM_ALIGN SIZEOF_LONG -#endif - - -typedef struct _GFreeAtom GFreeAtom; -typedef struct _GMemArea GMemArea; -typedef struct _GRealMemChunk GRealMemChunk; +/* notes on macros: + * having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and + * g_mem_profile(). + * If g_mem_gc_friendly is TRUE, freed memory should be 0-wiped. + */ -struct _GFreeAtom -{ - GFreeAtom *next; +/* --- variables --- */ +static GMemVTable glib_mem_vtable = { + malloc, + realloc, + free, + calloc, + malloc, + realloc, }; -struct _GMemArea -{ - GMemArea *next; /* the next mem area */ - GMemArea *prev; /* the previous mem area */ - gulong index; /* the current index into the "mem" array */ - gulong free; /* the number of free bytes in this mem area */ - gulong allocated; /* the number of atoms allocated from this area */ - gulong mark; /* is this mem area marked for deletion */ - gchar mem[MEM_AREA_SIZE]; /* the mem array from which atoms get allocated - * the actual size of this array is determined by - * the mem chunk "area_size". ANSI says that it - * must be declared to be the maximum size it - * can possibly be (even though the actual size - * may be less). - */ -}; +/** + * SECTION:memory + * @Short_Description: general memory-handling + * @Title: Memory Allocation + * + * These functions provide support for allocating and freeing memory. + * + * If any call to allocate memory fails, the application is terminated. + * This also means that there is no need to check if the call succeeded. + * + * It's important to match g_malloc() (and wrappers such as g_new()) with + * g_free(), g_slice_alloc() (and wrappers such as g_slice_new()) with + * g_slice_free(), plain malloc() with free(), and (if you're using C++) + * new with delete and new[] with delete[]. Otherwise bad things can happen, + * since these allocators may use different memory pools (and new/delete call + * constructors and destructors). See also g_mem_set_vtable(). + */ -struct _GRealMemChunk +/* --- functions --- */ +/** + * g_malloc: + * @n_bytes: the number of bytes to allocate + * + * Allocates @n_bytes bytes of memory. + * If @n_bytes is 0 it returns %NULL. + * + * Returns: a pointer to the allocated memory + */ +gpointer +g_malloc (gsize n_bytes) { - gchar *name; /* name of this MemChunk...used for debugging output */ - gint type; /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */ - gint num_mem_areas; /* the number of memory areas */ - gint num_marked_areas; /* the number of areas marked for deletion */ - guint atom_size; /* the size of an atom */ - gulong area_size; /* the size of a memory area */ - GMemArea *mem_area; /* the current memory area */ - GMemArea *mem_areas; /* a list of all the mem areas owned by this chunk */ - GMemArea *free_mem_area; /* the free area...which is about to be destroyed */ - GFreeAtom *free_atoms; /* the free atoms list */ - GTree *mem_tree; /* tree of mem areas sorted by memory address */ - GRealMemChunk *next; /* pointer to the next chunk */ - GRealMemChunk *prev; /* pointer to the previous chunk */ -}; + if (G_LIKELY (n_bytes)) + { + gpointer mem; + mem = glib_mem_vtable.malloc (n_bytes); + TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0)); + if (mem) + return mem; -static gulong g_mem_chunk_compute_size (gulong size, - gulong min_size); -static gint g_mem_chunk_area_compare (GMemArea *a, - GMemArea *b); -static gint g_mem_chunk_area_search (GMemArea *a, - gchar *addr); + g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_bytes); + } + TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0)); -/* here we can't use StaticMutexes, as they depend upon a working - * g_malloc, the same holds true for StaticPrivate */ -static GMutex* mem_chunks_lock = NULL; -static GRealMemChunk *mem_chunks = NULL; + return NULL; +} -#ifdef ENABLE_MEM_PROFILE -static GMutex* mem_profile_lock; -static gulong allocations[MEM_PROFILE_TABLE_SIZE] = { 0 }; -static gulong allocated_mem = 0; -static gulong freed_mem = 0; -static GPrivate* allocating_for_mem_chunk = NULL; -#define IS_IN_MEM_CHUNK_ROUTINE() \ - GPOINTER_TO_UINT (g_private_get (allocating_for_mem_chunk)) -#endif /* ENABLE_MEM_PROFILE */ +/** + * g_malloc0: + * @n_bytes: the number of bytes to allocate + * + * Allocates @n_bytes bytes of memory, initialized to 0's. + * If @n_bytes is 0 it returns %NULL. + * + * Returns: a pointer to the allocated memory + */ +gpointer +g_malloc0 (gsize n_bytes) +{ + if (G_LIKELY (n_bytes)) + { + gpointer mem; + mem = glib_mem_vtable.calloc (1, n_bytes); + TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0)); + if (mem) + return mem; -#ifndef USE_DMALLOC + g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_bytes); + } -gpointer -g_malloc (gulong size) -{ - gpointer p; - - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - gulong *t; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - - - if (size == 0) - return NULL; - - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - size += SIZEOF_LONG; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - -#ifdef ENABLE_MEM_CHECK - size += SIZEOF_LONG; -#endif /* ENABLE_MEM_CHECK */ - - - p = (gpointer) malloc (size); - if (!p) - g_error ("could not allocate %ld bytes", size); - - -#ifdef ENABLE_MEM_CHECK - size -= SIZEOF_LONG; - - t = p; - p = ((guchar*) p + SIZEOF_LONG); - *t = 0; -#endif /* ENABLE_MEM_CHECK */ - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - size -= SIZEOF_LONG; - - t = p; - p = ((guchar*) p + SIZEOF_LONG); - *t = size; - -#ifdef ENABLE_MEM_PROFILE - g_mutex_lock (mem_profile_lock); -# ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS - if(!IS_IN_MEM_CHUNK_ROUTINE()) { -# endif - if (size <= MEM_PROFILE_TABLE_SIZE - 1) - allocations[size-1] += 1; - else - allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1; - allocated_mem += size; -# ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS - } -# endif - g_mutex_unlock (mem_profile_lock); -#endif /* ENABLE_MEM_PROFILE */ -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - - - return p; -} + TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0)); -gpointer -g_malloc0 (gulong size) -{ - gpointer p; - - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - gulong *t; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - - - if (size == 0) - return NULL; - - -#if defined (ENABLE_MEM_PROFILE) || defined (ENABLE_MEM_CHECK) - size += SIZEOF_LONG; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - -#ifdef ENABLE_MEM_CHECK - size += SIZEOF_LONG; -#endif /* ENABLE_MEM_CHECK */ - - - p = (gpointer) calloc (size, 1); - if (!p) - g_error ("could not allocate %ld bytes", size); - - -#ifdef ENABLE_MEM_CHECK - size -= SIZEOF_LONG; - - t = p; - p = ((guchar*) p + SIZEOF_LONG); - *t = 0; -#endif /* ENABLE_MEM_CHECK */ - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - size -= SIZEOF_LONG; - - t = p; - p = ((guchar*) p + SIZEOF_LONG); - *t = size; - -# ifdef ENABLE_MEM_PROFILE - g_mutex_lock (mem_profile_lock); -# ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS - if(!IS_IN_MEM_CHUNK_ROUTINE()) { -# endif - if (size <= (MEM_PROFILE_TABLE_SIZE - 1)) - allocations[size-1] += 1; - else - allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1; - allocated_mem += size; -# ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS - } -# endif - g_mutex_unlock (mem_profile_lock); -# endif /* ENABLE_MEM_PROFILE */ -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - - - return p; + return NULL; } +/** + * g_realloc: + * @mem: (allow-none): the memory to reallocate + * @n_bytes: new size of the memory in bytes + * + * Reallocates the memory pointed to by @mem, so that it now has space for + * @n_bytes bytes of memory. It returns the new address of the memory, which may + * have been moved. @mem may be %NULL, in which case it's considered to + * have zero-length. @n_bytes may be 0, in which case %NULL will be returned + * and @mem will be freed unless it is %NULL. + * + * Returns: the new address of the allocated memory + */ gpointer g_realloc (gpointer mem, - gulong size) + gsize n_bytes) { - gpointer p; - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - gulong *t; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - - - if (size == 0) - { - g_free (mem); - - return NULL; - } - - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - size += SIZEOF_LONG; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - -#ifdef ENABLE_MEM_CHECK - size += SIZEOF_LONG; -#endif /* ENABLE_MEM_CHECK */ - - - if (!mem) - { -#ifdef REALLOC_0_WORKS - p = (gpointer) realloc (NULL, size); -#else /* !REALLOC_0_WORKS */ - p = (gpointer) malloc (size); -#endif /* !REALLOC_0_WORKS */ - } - else + gpointer newmem; + + if (G_LIKELY (n_bytes)) { -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - t = (gulong*) ((guchar*) mem - SIZEOF_LONG); -#ifdef ENABLE_MEM_PROFILE - g_mutex_lock (mem_profile_lock); - freed_mem += *t; - g_mutex_unlock (mem_profile_lock); -#endif /* ENABLE_MEM_PROFILE */ - mem = t; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - -#ifdef ENABLE_MEM_CHECK - t = (gulong*) ((guchar*) mem - SIZEOF_LONG); - if (*t >= 1) - g_warning ("trying to realloc freed memory\n"); - mem = t; -#endif /* ENABLE_MEM_CHECK */ - - p = (gpointer) realloc (mem, size); + newmem = glib_mem_vtable.realloc (mem, n_bytes); + TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0)); + if (newmem) + return newmem; + + g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_bytes); } - - if (!p) - g_error ("could not reallocate %lu bytes", (gulong) size); - - -#ifdef ENABLE_MEM_CHECK - size -= SIZEOF_LONG; - - t = p; - p = ((guchar*) p + SIZEOF_LONG); - *t = 0; -#endif /* ENABLE_MEM_CHECK */ - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - size -= SIZEOF_LONG; - - t = p; - p = ((guchar*) p + SIZEOF_LONG); - *t = size; - -#ifdef ENABLE_MEM_PROFILE - g_mutex_lock (mem_profile_lock); -#ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS - if(!IS_IN_MEM_CHUNK_ROUTINE()) { -#endif - if (size <= (MEM_PROFILE_TABLE_SIZE - 1)) - allocations[size-1] += 1; - else - allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1; - allocated_mem += size; -#ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS - } -#endif - g_mutex_unlock (mem_profile_lock); -#endif /* ENABLE_MEM_PROFILE */ -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - - - return p; -} -void -g_free (gpointer mem) -{ if (mem) - { -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - gulong *t; - gulong size; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - -#if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK) - t = (gulong*) ((guchar*) mem - SIZEOF_LONG); - size = *t; -#ifdef ENABLE_MEM_PROFILE - g_mutex_lock (mem_profile_lock); - freed_mem += size; - g_mutex_unlock (mem_profile_lock); -#endif /* ENABLE_MEM_PROFILE */ - mem = t; -#endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */ - -#ifdef ENABLE_MEM_CHECK - t = (gulong*) ((guchar*) mem - SIZEOF_LONG); - if (*t >= 1) - g_warning ("freeing previously freed memory\n"); - *t += 1; - mem = t; - - memset ((guchar*) mem + 8, 0, size); -#else /* ENABLE_MEM_CHECK */ - free (mem); -#endif /* ENABLE_MEM_CHECK */ - } -} + glib_mem_vtable.free (mem); -#endif /* ! USE_DMALLOC */ + TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0)); + return NULL; +} +/** + * g_free: + * @mem: (allow-none): the memory to free + * + * Frees the memory pointed to by @mem. + * If @mem is %NULL it simply returns. + */ void -g_mem_profile (void) +g_free (gpointer mem) { -#ifdef ENABLE_MEM_PROFILE - gint i; - gulong local_allocations[MEM_PROFILE_TABLE_SIZE]; - gulong local_allocated_mem; - gulong local_freed_mem; - - g_mutex_lock (mem_profile_lock); - for (i = 0; i < MEM_PROFILE_TABLE_SIZE; i++) - local_allocations[i] = allocations[i]; - local_allocated_mem = allocated_mem; - local_freed_mem = freed_mem; - g_mutex_unlock (mem_profile_lock); - - for (i = 0; i < (MEM_PROFILE_TABLE_SIZE - 1); i++) - if (local_allocations[i] > 0) - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, - "%lu allocations of %d bytes", local_allocations[i], i + 1); - - if (local_allocations[MEM_PROFILE_TABLE_SIZE - 1] > 0) - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, - "%lu allocations of greater than %d bytes", - local_allocations[MEM_PROFILE_TABLE_SIZE - 1], MEM_PROFILE_TABLE_SIZE - 1); - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes allocated", local_allocated_mem); - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes freed", local_freed_mem); - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes in use", local_allocated_mem - local_freed_mem); -#endif /* ENABLE_MEM_PROFILE */ + if (G_LIKELY (mem)) + glib_mem_vtable.free (mem); + TRACE(GLIB_MEM_FREE((void*) mem)); } +/** + * g_clear_pointer: (skip) + * @pp: a pointer to a variable, struct member etc. holding a pointer + * @destroy: a function to which a gpointer can be passed, to destroy *@pp + * + * Clears a reference to a variable. + * + * @pp must not be %NULL. + * + * If the reference is %NULL then this function does nothing. + * Otherwise, the variable is destroyed using @destroy and the + * pointer is set to %NULL. + * + * A macro is also included that allows this function to be used without + * pointer casts. + * + * Since: 2.34 + **/ +#undef g_clear_pointer void -g_mem_check (gpointer mem) +g_clear_pointer (gpointer *pp, + GDestroyNotify destroy) { -#ifdef ENABLE_MEM_CHECK - gulong *t; - - t = (gulong*) ((guchar*) mem - SIZEOF_LONG - SIZEOF_LONG); - - if (*t >= 1) - g_warning ("mem: 0x%08x has been freed %lu times\n", (gulong) mem, *t); -#endif /* ENABLE_MEM_CHECK */ + gpointer _p; + + _p = *pp; + if (_p) + { + *pp = NULL; + destroy (_p); + } } -GMemChunk* -g_mem_chunk_new (gchar *name, - gint atom_size, - gulong area_size, - gint type) +/** + * g_try_malloc: + * @n_bytes: number of bytes to allocate. + * + * Attempts to allocate @n_bytes, and returns %NULL on failure. + * Contrast with g_malloc(), which aborts the program on failure. + * + * Returns: the allocated memory, or %NULL. + */ +gpointer +g_try_malloc (gsize n_bytes) { - GRealMemChunk *mem_chunk; - gulong rarea_size; - - g_return_val_if_fail (atom_size > 0, NULL); - g_return_val_if_fail (area_size >= atom_size, NULL); - - ENTER_MEM_CHUNK_ROUTINE(); - - area_size = (area_size + atom_size - 1) / atom_size; - area_size *= atom_size; - - mem_chunk = g_new (struct _GRealMemChunk, 1); - mem_chunk->name = name; - mem_chunk->type = type; - mem_chunk->num_mem_areas = 0; - mem_chunk->num_marked_areas = 0; - mem_chunk->mem_area = NULL; - mem_chunk->free_mem_area = NULL; - mem_chunk->free_atoms = NULL; - mem_chunk->mem_tree = NULL; - mem_chunk->mem_areas = NULL; - mem_chunk->atom_size = atom_size; - - if (mem_chunk->type == G_ALLOC_AND_FREE) - mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare); - - if (mem_chunk->atom_size % MEM_ALIGN) - mem_chunk->atom_size += MEM_ALIGN - (mem_chunk->atom_size % MEM_ALIGN); - - rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE; - rarea_size = g_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE); - mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE); + gpointer mem; - g_mutex_lock (mem_chunks_lock); - mem_chunk->next = mem_chunks; - mem_chunk->prev = NULL; - if (mem_chunks) - mem_chunks->prev = mem_chunk; - mem_chunks = mem_chunk; - g_mutex_unlock (mem_chunks_lock); + if (G_LIKELY (n_bytes)) + mem = glib_mem_vtable.try_malloc (n_bytes); + else + mem = NULL; - LEAVE_MEM_CHUNK_ROUTINE(); + TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 1)); - return ((GMemChunk*) mem_chunk); + return mem; } -void -g_mem_chunk_destroy (GMemChunk *mem_chunk) +/** + * g_try_malloc0: + * @n_bytes: number of bytes to allocate + * + * Attempts to allocate @n_bytes, initialized to 0's, and returns %NULL on + * failure. Contrast with g_malloc0(), which aborts the program on failure. + * + * Since: 2.8 + * Returns: the allocated memory, or %NULL + */ +gpointer +g_try_malloc0 (gsize n_bytes) { - GRealMemChunk *rmem_chunk; - GMemArea *mem_areas; - GMemArea *temp_area; - - g_return_if_fail (mem_chunk != NULL); + gpointer mem; - ENTER_MEM_CHUNK_ROUTINE(); + if (G_LIKELY (n_bytes)) + mem = glib_mem_vtable.try_malloc (n_bytes); + else + mem = NULL; - rmem_chunk = (GRealMemChunk*) mem_chunk; - - mem_areas = rmem_chunk->mem_areas; - while (mem_areas) + if (mem) + memset (mem, 0, n_bytes); + + return mem; +} + +/** + * g_try_realloc: + * @mem: (allow-none): previously-allocated memory, or %NULL. + * @n_bytes: number of bytes to allocate. + * + * Attempts to realloc @mem to a new size, @n_bytes, and returns %NULL + * on failure. Contrast with g_realloc(), which aborts the program + * on failure. If @mem is %NULL, behaves the same as g_try_malloc(). + * + * Returns: the allocated memory, or %NULL. + */ +gpointer +g_try_realloc (gpointer mem, + gsize n_bytes) +{ + gpointer newmem; + + if (G_LIKELY (n_bytes)) + newmem = glib_mem_vtable.try_realloc (mem, n_bytes); + else { - temp_area = mem_areas; - mem_areas = mem_areas->next; - g_free (temp_area); + newmem = NULL; + if (mem) + glib_mem_vtable.free (mem); } - - if (rmem_chunk->next) - rmem_chunk->next->prev = rmem_chunk->prev; - if (rmem_chunk->prev) - rmem_chunk->prev->next = rmem_chunk->next; - - g_mutex_lock (mem_chunks_lock); - if (rmem_chunk == mem_chunks) - mem_chunks = mem_chunks->next; - g_mutex_unlock (mem_chunks_lock); - - if (rmem_chunk->type == G_ALLOC_AND_FREE) - g_tree_destroy (rmem_chunk->mem_tree); - - g_free (rmem_chunk); - LEAVE_MEM_CHUNK_ROUTINE(); + TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1)); + + return newmem; } + +#define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b))) + +/** + * g_malloc_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: a pointer to the allocated memory + */ gpointer -g_mem_chunk_alloc (GMemChunk *mem_chunk) +g_malloc_n (gsize n_blocks, + gsize n_block_bytes) { - GRealMemChunk *rmem_chunk; - GMemArea *temp_area; - gpointer mem; + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } - ENTER_MEM_CHUNK_ROUTINE(); + return g_malloc (n_blocks * n_block_bytes); +} - g_return_val_if_fail (mem_chunk != NULL, NULL); - - rmem_chunk = (GRealMemChunk*) mem_chunk; - - while (rmem_chunk->free_atoms) +/** + * g_malloc0_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: a pointer to the allocated memory + */ +gpointer +g_malloc0_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) { - /* Get the first piece of memory on the "free_atoms" list. - * We can go ahead and destroy the list node we used to keep - * track of it with and to update the "free_atoms" list to - * point to its next element. - */ - mem = rmem_chunk->free_atoms; - rmem_chunk->free_atoms = rmem_chunk->free_atoms->next; - - /* Determine which area this piece of memory is allocated from */ - temp_area = g_tree_search (rmem_chunk->mem_tree, - (GSearchFunc) g_mem_chunk_area_search, - mem); - - /* If the area has been marked, then it is being destroyed. - * (ie marked to be destroyed). - * We check to see if all of the segments on the free list that - * reference this area have been removed. This occurs when - * the ammount of free memory is less than the allocatable size. - * If the chunk should be freed, then we place it in the "free_mem_area". - * This is so we make sure not to free the mem area here and then - * allocate it again a few lines down. - * If we don't allocate a chunk a few lines down then the "free_mem_area" - * will be freed. - * If there is already a "free_mem_area" then we'll just free this mem area. - */ - if (temp_area->mark) - { - /* Update the "free" memory available in that area */ - temp_area->free += rmem_chunk->atom_size; - - if (temp_area->free == rmem_chunk->area_size) - { - if (temp_area == rmem_chunk->mem_area) - rmem_chunk->mem_area = NULL; - - if (rmem_chunk->free_mem_area) - { - rmem_chunk->num_mem_areas -= 1; - - if (temp_area->next) - temp_area->next->prev = temp_area->prev; - if (temp_area->prev) - temp_area->prev->next = temp_area->next; - if (temp_area == rmem_chunk->mem_areas) - rmem_chunk->mem_areas = rmem_chunk->mem_areas->next; - - if (rmem_chunk->type == G_ALLOC_AND_FREE) - g_tree_remove (rmem_chunk->mem_tree, temp_area); - g_free (temp_area); - } - else - rmem_chunk->free_mem_area = temp_area; - - rmem_chunk->num_marked_areas -= 1; - } - } - else - { - /* Update the number of allocated atoms count. - */ - temp_area->allocated += 1; - - /* The area wasn't marked...return the memory - */ - goto outa_here; - } + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); } - - /* If there isn't a current mem area or the current mem area is out of space - * then allocate a new mem area. We'll first check and see if we can use - * the "free_mem_area". Otherwise we'll just malloc the mem area. - */ - if ((!rmem_chunk->mem_area) || - ((rmem_chunk->mem_area->index + rmem_chunk->atom_size) > rmem_chunk->area_size)) + + return g_malloc0 (n_blocks * n_block_bytes); +} + +/** + * g_realloc_n: + * @mem: (allow-none): the memory to reallocate + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the new address of the allocated memory + */ +gpointer +g_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) { - if (rmem_chunk->free_mem_area) - { - rmem_chunk->mem_area = rmem_chunk->free_mem_area; - rmem_chunk->free_mem_area = NULL; - } - else - { - rmem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) - - MEM_AREA_SIZE + - rmem_chunk->area_size); - - rmem_chunk->num_mem_areas += 1; - rmem_chunk->mem_area->next = rmem_chunk->mem_areas; - rmem_chunk->mem_area->prev = NULL; - - if (rmem_chunk->mem_areas) - rmem_chunk->mem_areas->prev = rmem_chunk->mem_area; - rmem_chunk->mem_areas = rmem_chunk->mem_area; - - if (rmem_chunk->type == G_ALLOC_AND_FREE) - g_tree_insert (rmem_chunk->mem_tree, rmem_chunk->mem_area, rmem_chunk->mem_area); - } - - rmem_chunk->mem_area->index = 0; - rmem_chunk->mem_area->free = rmem_chunk->area_size; - rmem_chunk->mem_area->allocated = 0; - rmem_chunk->mem_area->mark = 0; + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); } - - /* Get the memory and modify the state variables appropriately. - */ - mem = (gpointer) &rmem_chunk->mem_area->mem[rmem_chunk->mem_area->index]; - rmem_chunk->mem_area->index += rmem_chunk->atom_size; - rmem_chunk->mem_area->free -= rmem_chunk->atom_size; - rmem_chunk->mem_area->allocated += 1; -outa_here: + return g_realloc (mem, n_blocks * n_block_bytes); +} - LEAVE_MEM_CHUNK_ROUTINE(); +/** + * g_try_malloc_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_try_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the allocated memory, or %NULL. + */ +gpointer +g_try_malloc_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; - return mem; + return g_try_malloc (n_blocks * n_block_bytes); } +/** + * g_try_malloc0_n: + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_try_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the allocated memory, or %NULL + */ gpointer -g_mem_chunk_alloc0 (GMemChunk *mem_chunk) +g_try_malloc0_n (gsize n_blocks, + gsize n_block_bytes) { - gpointer mem; + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; - mem = g_mem_chunk_alloc (mem_chunk); - if (mem) - { - GRealMemChunk *rmem_chunk = (GRealMemChunk*) mem_chunk; + return g_try_malloc0 (n_blocks * n_block_bytes); +} - memset (mem, 0, rmem_chunk->atom_size); - } +/** + * g_try_realloc_n: + * @mem: (allow-none): previously-allocated memory, or %NULL. + * @n_blocks: the number of blocks to allocate + * @n_block_bytes: the size of each block in bytes + * + * This function is similar to g_try_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, + * but care is taken to detect possible overflow during multiplication. + * + * Since: 2.24 + * Returns: the allocated memory, or %NULL. + */ +gpointer +g_try_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; - return mem; + return g_try_realloc (mem, n_blocks * n_block_bytes); } -void -g_mem_chunk_free (GMemChunk *mem_chunk, - gpointer mem) + + +static gpointer +fallback_calloc (gsize n_blocks, + gsize n_block_bytes) { - GRealMemChunk *rmem_chunk; - GMemArea *temp_area; - GFreeAtom *free_atom; - - g_return_if_fail (mem_chunk != NULL); - g_return_if_fail (mem != NULL); + gsize l = n_blocks * n_block_bytes; + gpointer mem = glib_mem_vtable.malloc (l); - ENTER_MEM_CHUNK_ROUTINE(); + if (mem) + memset (mem, 0, l); - rmem_chunk = (GRealMemChunk*) mem_chunk; - - /* Don't do anything if this is an ALLOC_ONLY chunk - */ - if (rmem_chunk->type == G_ALLOC_AND_FREE) - { - /* Place the memory on the "free_atoms" list - */ - free_atom = (GFreeAtom*) mem; - free_atom->next = rmem_chunk->free_atoms; - rmem_chunk->free_atoms = free_atom; - - temp_area = g_tree_search (rmem_chunk->mem_tree, - (GSearchFunc) g_mem_chunk_area_search, - mem); - - temp_area->allocated -= 1; - - if (temp_area->allocated == 0) - { - temp_area->mark = 1; - rmem_chunk->num_marked_areas += 1; - } - } + return mem; +} + +static gboolean vtable_set = FALSE; - LEAVE_MEM_CHUNK_ROUTINE(); +/** + * g_mem_is_system_malloc: + * + * Checks whether the allocator used by g_malloc() is the system's + * malloc implementation. If it returns %TRUE memory allocated with + * malloc() can be used interchangeable with memory allocated using g_malloc(). + * This function is useful for avoiding an extra copy of allocated memory returned + * by a non-GLib-based API. + * + * A different allocator can be set using g_mem_set_vtable(). + * + * Returns: if %TRUE, malloc() and g_malloc() can be mixed. + **/ +gboolean +g_mem_is_system_malloc (void) +{ + return !vtable_set; } -/* This doesn't free the free_area if there is one */ +/** + * g_mem_set_vtable: + * @vtable: table of memory allocation routines. + * + * Sets the #GMemVTable to use for memory allocation. You can use this + * to provide custom memory allocation routines. + * + * The @vtable only needs to provide malloc(), realloc(), and free() + * functions; GLib can provide default implementations of the others. + * The malloc() and realloc() implementations should return %NULL on + * failure, GLib will handle error-checking for you. @vtable is copied, + * so need not persist after this function has been called. + * + * Note that this function must be called before using any other GLib + * functions. + */ void -g_mem_chunk_clean (GMemChunk *mem_chunk) +g_mem_set_vtable (GMemVTable *vtable) { - GRealMemChunk *rmem_chunk; - GMemArea *mem_area; - GFreeAtom *prev_free_atom; - GFreeAtom *temp_free_atom; - gpointer mem; - - g_return_if_fail (mem_chunk != NULL); - - rmem_chunk = (GRealMemChunk*) mem_chunk; - - if (rmem_chunk->type == G_ALLOC_AND_FREE) + if (!vtable_set) { - prev_free_atom = NULL; - temp_free_atom = rmem_chunk->free_atoms; - - while (temp_free_atom) + if (vtable->malloc && vtable->realloc && vtable->free) { - mem = (gpointer) temp_free_atom; - - mem_area = g_tree_search (rmem_chunk->mem_tree, - (GSearchFunc) g_mem_chunk_area_search, - mem); - - /* If this mem area is marked for destruction then delete the - * area and list node and decrement the free mem. - */ - if (mem_area->mark) - { - if (prev_free_atom) - prev_free_atom->next = temp_free_atom->next; - else - rmem_chunk->free_atoms = temp_free_atom->next; - temp_free_atom = temp_free_atom->next; - - mem_area->free += rmem_chunk->atom_size; - if (mem_area->free == rmem_chunk->area_size) - { - rmem_chunk->num_mem_areas -= 1; - rmem_chunk->num_marked_areas -= 1; - - if (mem_area->next) - mem_area->next->prev = mem_area->prev; - if (mem_area->prev) - mem_area->prev->next = mem_area->next; - if (mem_area == rmem_chunk->mem_areas) - rmem_chunk->mem_areas = rmem_chunk->mem_areas->next; - if (mem_area == rmem_chunk->mem_area) - rmem_chunk->mem_area = NULL; - - if (rmem_chunk->type == G_ALLOC_AND_FREE) - g_tree_remove (rmem_chunk->mem_tree, mem_area); - g_free (mem_area); - } - } - else - { - prev_free_atom = temp_free_atom; - temp_free_atom = temp_free_atom->next; - } + glib_mem_vtable.malloc = vtable->malloc; + glib_mem_vtable.realloc = vtable->realloc; + glib_mem_vtable.free = vtable->free; + glib_mem_vtable.calloc = vtable->calloc ? vtable->calloc : fallback_calloc; + glib_mem_vtable.try_malloc = vtable->try_malloc ? vtable->try_malloc : glib_mem_vtable.malloc; + glib_mem_vtable.try_realloc = vtable->try_realloc ? vtable->try_realloc : glib_mem_vtable.realloc; + vtable_set = TRUE; } + else + g_warning (G_STRLOC ": memory allocation vtable lacks one of malloc(), realloc() or free()"); } + else + g_warning (G_STRLOC ": memory allocation vtable can only be set once at startup"); } + +/* --- memory profiling and checking --- */ +#ifdef G_DISABLE_CHECKS +/** + * glib_mem_profiler_table: + * + * A #GMemVTable containing profiling variants of the memory + * allocation functions. Use them together with g_mem_profile() + * in order to get information about the memory allocation pattern + * of your program. + */ +GMemVTable *glib_mem_profiler_table = &glib_mem_vtable; void -g_mem_chunk_reset (GMemChunk *mem_chunk) +g_mem_profile (void) { - GRealMemChunk *rmem_chunk; - GMemArea *mem_areas; - GMemArea *temp_area; - - g_return_if_fail (mem_chunk != NULL); - - rmem_chunk = (GRealMemChunk*) mem_chunk; - - mem_areas = rmem_chunk->mem_areas; - rmem_chunk->num_mem_areas = 0; - rmem_chunk->mem_areas = NULL; - rmem_chunk->mem_area = NULL; - - while (mem_areas) +} +#else /* !G_DISABLE_CHECKS */ +typedef enum { + PROFILER_FREE = 0, + PROFILER_ALLOC = 1, + PROFILER_RELOC = 2, + PROFILER_ZINIT = 4 +} ProfilerJob; +static guint *profile_data = NULL; +static gsize profile_allocs = 0; +static gsize profile_zinit = 0; +static gsize profile_frees = 0; +static GMutex gmem_profile_mutex; + +#define PROFILE_TABLE(f1,f2,f3) ( ( ((f3) << 2) | ((f2) << 1) | (f1) ) * (MEM_PROFILE_TABLE_SIZE + 1)) + +static void +profiler_log (ProfilerJob job, + gsize n_bytes, + gboolean success) +{ + g_mutex_lock (&gmem_profile_mutex); + if (!profile_data) { - temp_area = mem_areas; - mem_areas = mem_areas->next; - g_free (temp_area); + profile_data = calloc ((MEM_PROFILE_TABLE_SIZE + 1) * 8, + sizeof (profile_data[0])); + if (!profile_data) /* memory system kiddin' me, eh? */ + { + g_mutex_unlock (&gmem_profile_mutex); + return; + } } - - rmem_chunk->free_atoms = NULL; - - if (rmem_chunk->mem_tree) - g_tree_destroy (rmem_chunk->mem_tree); - rmem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare); + + if (n_bytes < MEM_PROFILE_TABLE_SIZE) + profile_data[n_bytes + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, + (job & PROFILER_RELOC) != 0, + success != 0)] += 1; + else + profile_data[MEM_PROFILE_TABLE_SIZE + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, + (job & PROFILER_RELOC) != 0, + success != 0)] += 1; + if (success) + { + if (job & PROFILER_ALLOC) + { + profile_allocs += n_bytes; + if (job & PROFILER_ZINIT) + profile_zinit += n_bytes; + } + else + profile_frees += n_bytes; + } + g_mutex_unlock (&gmem_profile_mutex); } -void -g_mem_chunk_print (GMemChunk *mem_chunk) +static void +profile_print_locked (guint *local_data, + gboolean success) { - GRealMemChunk *rmem_chunk; - GMemArea *mem_areas; - gulong mem; - - g_return_if_fail (mem_chunk != NULL); - - rmem_chunk = (GRealMemChunk*) mem_chunk; - mem_areas = rmem_chunk->mem_areas; - mem = 0; - - while (mem_areas) + gboolean need_header = TRUE; + guint i; + + for (i = 0; i <= MEM_PROFILE_TABLE_SIZE; i++) { - mem += rmem_chunk->area_size - mem_areas->free; - mem_areas = mem_areas->next; + glong t_malloc = local_data[i + PROFILE_TABLE (1, 0, success)]; + glong t_realloc = local_data[i + PROFILE_TABLE (1, 1, success)]; + glong t_free = local_data[i + PROFILE_TABLE (0, 0, success)]; + glong t_refree = local_data[i + PROFILE_TABLE (0, 1, success)]; + + if (!t_malloc && !t_realloc && !t_free && !t_refree) + continue; + else if (need_header) + { + need_header = FALSE; + g_print (" blocks of | allocated | freed | allocated | freed | n_bytes \n"); + g_print (" n_bytes | n_times by | n_times by | n_times by | n_times by | remaining \n"); + g_print (" | malloc() | free() | realloc() | realloc() | \n"); + g_print ("===========|============|============|============|============|===========\n"); + } + if (i < MEM_PROFILE_TABLE_SIZE) + g_print ("%10u | %10ld | %10ld | %10ld | %10ld |%+11ld\n", + i, t_malloc, t_free, t_realloc, t_refree, + (t_malloc - t_free + t_realloc - t_refree) * i); + else if (i >= MEM_PROFILE_TABLE_SIZE) + g_print (" >%6u | %10ld | %10ld | %10ld | %10ld | ***\n", + i, t_malloc, t_free, t_realloc, t_refree); } - - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, - "%s: %ld bytes using %d mem areas", - rmem_chunk->name, mem, rmem_chunk->num_mem_areas); + if (need_header) + g_print (" --- none ---\n"); } +/** + * g_mem_profile: + * + * Outputs a summary of memory usage. + * + * It outputs the frequency of allocations of different sizes, + * the total number of bytes which have been allocated, + * the total number of bytes which have been freed, + * and the difference between the previous two values, i.e. the number of bytes + * still in use. + * + * Note that this function will not output anything unless you have + * previously installed the #glib_mem_profiler_table with g_mem_set_vtable(). + */ + void -g_mem_chunk_info (void) +g_mem_profile (void) { - GRealMemChunk *mem_chunk; - gint count; - - count = 0; - g_mutex_lock (mem_chunks_lock); - mem_chunk = mem_chunks; - while (mem_chunk) + guint local_data[(MEM_PROFILE_TABLE_SIZE + 1) * 8]; + gsize local_allocs; + gsize local_zinit; + gsize local_frees; + + g_mutex_lock (&gmem_profile_mutex); + + local_allocs = profile_allocs; + local_zinit = profile_zinit; + local_frees = profile_frees; + + if (!profile_data) { - count += 1; - mem_chunk = mem_chunk->next; + g_mutex_unlock (&gmem_profile_mutex); + return; } - g_mutex_unlock (mem_chunks_lock); - - g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%d mem chunks", count); - - g_mutex_lock (mem_chunks_lock); - mem_chunk = mem_chunks; - g_mutex_unlock (mem_chunks_lock); - while (mem_chunk) - { - g_mem_chunk_print ((GMemChunk*) mem_chunk); - mem_chunk = mem_chunk->next; - } + memcpy (local_data, profile_data, + (MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])); + + g_mutex_unlock (&gmem_profile_mutex); + + g_print ("GLib Memory statistics (successful operations):\n"); + profile_print_locked (local_data, TRUE); + g_print ("GLib Memory statistics (failing operations):\n"); + profile_print_locked (local_data, FALSE); + g_print ("Total bytes: allocated=%"G_GSIZE_FORMAT", " + "zero-initialized=%"G_GSIZE_FORMAT" (%.2f%%), " + "freed=%"G_GSIZE_FORMAT" (%.2f%%), " + "remaining=%"G_GSIZE_FORMAT"\n", + local_allocs, + local_zinit, + ((gdouble) local_zinit) / local_allocs * 100.0, + local_frees, + ((gdouble) local_frees) / local_allocs * 100.0, + local_allocs - local_frees); } -void -g_blow_chunks (void) +static gpointer +profiler_try_malloc (gsize n_bytes) { - GRealMemChunk *mem_chunk; - - g_mutex_lock (mem_chunks_lock); - mem_chunk = mem_chunks; - g_mutex_unlock (mem_chunks_lock); - while (mem_chunk) + gsize *p; + + p = malloc (sizeof (gsize) * 2 + n_bytes); + + if (p) { - g_mem_chunk_clean ((GMemChunk*) mem_chunk); - mem_chunk = mem_chunk->next; + p[0] = 0; /* free count */ + p[1] = n_bytes; /* length */ + profiler_log (PROFILER_ALLOC, n_bytes, TRUE); + p += 2; } + else + profiler_log (PROFILER_ALLOC, n_bytes, FALSE); + + return p; } - -static gulong -g_mem_chunk_compute_size (gulong size, - gulong min_size) +static gpointer +profiler_malloc (gsize n_bytes) { - gulong power_of_2; - gulong lower, upper; - - power_of_2 = 16; - while (power_of_2 < size) - power_of_2 <<= 1; - - lower = power_of_2 >> 1; - upper = power_of_2; - - if (size - lower < upper - size && lower >= min_size) - return lower; - else - return upper; + gpointer mem = profiler_try_malloc (n_bytes); + + if (!mem) + g_mem_profile (); + + return mem; } -static gint -g_mem_chunk_area_compare (GMemArea *a, - GMemArea *b) +static gpointer +profiler_calloc (gsize n_blocks, + gsize n_block_bytes) { - return (a->mem - b->mem); + gsize l = n_blocks * n_block_bytes; + gsize *p; + + p = calloc (1, sizeof (gsize) * 2 + l); + + if (p) + { + p[0] = 0; /* free count */ + p[1] = l; /* length */ + profiler_log (PROFILER_ALLOC | PROFILER_ZINIT, l, TRUE); + p += 2; + } + else + { + profiler_log (PROFILER_ALLOC | PROFILER_ZINIT, l, FALSE); + g_mem_profile (); + } + + return p; } -static gint -g_mem_chunk_area_search (GMemArea *a, - gchar *addr) +static void +profiler_free (gpointer mem) { - if (a->mem <= addr) + gsize *p = mem; + + p -= 2; + if (p[0]) /* free count */ { - if (addr < &a->mem[a->index]) - return 0; - return 1; + g_warning ("free(%p): memory has been freed %"G_GSIZE_FORMAT" times already", + p + 2, p[0]); + profiler_log (PROFILER_FREE, + p[1], /* length */ + FALSE); } - return -1; + else + { + profiler_log (PROFILER_FREE, + p[1], /* length */ + TRUE); + memset (p + 2, 0xaa, p[1]); + + /* for all those that miss free (p); in this place, yes, + * we do leak all memory when profiling, and that is intentional + * to catch double frees. patch submissions are futile. + */ + } + p[0] += 1; } -/* generic allocators - */ -struct _GAllocator /* from gmem.c */ +static gpointer +profiler_try_realloc (gpointer mem, + gsize n_bytes) { - gchar *name; - guint16 n_preallocs; - guint is_unused : 1; - guint type : 4; - GAllocator *last; - GMemChunk *mem_chunk; - gpointer dummy; /* implementation specific */ -}; + gsize *p = mem; -GAllocator* -g_allocator_new (const gchar *name, - guint n_preallocs) -{ - GAllocator *allocator; + p -= 2; + + if (mem && p[0]) /* free count */ + { + g_warning ("realloc(%p, %"G_GSIZE_FORMAT"): " + "memory has been freed %"G_GSIZE_FORMAT" times already", + p + 2, (gsize) n_bytes, p[0]); + profiler_log (PROFILER_ALLOC | PROFILER_RELOC, n_bytes, FALSE); - g_return_val_if_fail (name != NULL, NULL); + return NULL; + } + else + { + p = realloc (mem ? p : NULL, sizeof (gsize) * 2 + n_bytes); - allocator = g_new0 (GAllocator, 1); - allocator->name = g_strdup (name); - allocator->n_preallocs = CLAMP (n_preallocs, 1, 65535); - allocator->is_unused = TRUE; - allocator->type = 0; - allocator->last = NULL; - allocator->mem_chunk = NULL; - allocator->dummy = NULL; + if (p) + { + if (mem) + profiler_log (PROFILER_FREE | PROFILER_RELOC, p[1], TRUE); + p[0] = 0; + p[1] = n_bytes; + profiler_log (PROFILER_ALLOC | PROFILER_RELOC, p[1], TRUE); + p += 2; + } + else + profiler_log (PROFILER_ALLOC | PROFILER_RELOC, n_bytes, FALSE); - return allocator; + return p; + } } -void -g_allocator_free (GAllocator *allocator) +static gpointer +profiler_realloc (gpointer mem, + gsize n_bytes) { - g_return_if_fail (allocator != NULL); - g_return_if_fail (allocator->is_unused == TRUE); + mem = profiler_try_realloc (mem, n_bytes); - g_free (allocator->name); - if (allocator->mem_chunk) - g_mem_chunk_destroy (allocator->mem_chunk); + if (!mem) + g_mem_profile (); - g_free (allocator); + return mem; } -void -g_mem_init (void) -{ - mem_chunks_lock = g_mutex_new(); -#ifdef ENABLE_MEM_PROFILE - mem_profile_lock = g_mutex_new(); - allocating_for_mem_chunk = g_private_new(NULL); -#endif -} +static GMemVTable profiler_table = { + profiler_malloc, + profiler_realloc, + profiler_free, + profiler_calloc, + profiler_try_malloc, + profiler_try_realloc, +}; +GMemVTable *glib_mem_profiler_table = &profiler_table; + +#endif /* !G_DISABLE_CHECKS */