Deprecate and drop support for memory vtables

The memory vtables no longer work, because glib contructors are called
before main(), so there is no way to set it them before use. This stops using
the vtable at all, and deprecates and stubs out the related functions.

https://bugzilla.gnome.org/show_bug.cgi?id=751592
This commit is contained in:
Alexander Larsson
2015-06-27 18:38:42 +02:00
committed by Matthias Clasen
parent 08a3f3f3d2
commit 3be6ed60aa
2 changed files with 34 additions and 360 deletions

View File

@@ -40,9 +40,6 @@
#include "gthread.h" #include "gthread.h"
#include "glib_trace.h" #include "glib_trace.h"
#define MEM_PROFILE_TABLE_SIZE 4096
/* notes on macros: /* notes on macros:
* having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and * having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and
* g_mem_profile(). * g_mem_profile().
@@ -74,7 +71,7 @@ static GMemVTable glib_mem_vtable = {
* g_slice_free(), plain malloc() with free(), and (if you're using C++) * 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, * new with delete and new[] with delete[]. Otherwise bad things can happen,
* since these allocators may use different memory pools (and new/delete call * since these allocators may use different memory pools (and new/delete call
* constructors and destructors). See also g_mem_set_vtable(). * constructors and destructors).
*/ */
/* --- functions --- */ /* --- functions --- */
@@ -94,7 +91,7 @@ g_malloc (gsize n_bytes)
{ {
gpointer mem; gpointer mem;
mem = glib_mem_vtable.malloc (n_bytes); mem = malloc (n_bytes);
TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0)); TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0));
if (mem) if (mem)
return mem; return mem;
@@ -124,7 +121,7 @@ g_malloc0 (gsize n_bytes)
{ {
gpointer mem; gpointer mem;
mem = glib_mem_vtable.calloc (1, n_bytes); mem = calloc (1, n_bytes);
TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0)); TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0));
if (mem) if (mem)
return mem; return mem;
@@ -159,7 +156,7 @@ g_realloc (gpointer mem,
if (G_LIKELY (n_bytes)) if (G_LIKELY (n_bytes))
{ {
newmem = glib_mem_vtable.realloc (mem, n_bytes); newmem = realloc (mem, n_bytes);
TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0)); TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0));
if (newmem) if (newmem)
return newmem; return newmem;
@@ -169,7 +166,7 @@ g_realloc (gpointer mem,
} }
if (mem) if (mem)
glib_mem_vtable.free (mem); free (mem);
TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0)); TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0));
@@ -189,7 +186,7 @@ void
g_free (gpointer mem) g_free (gpointer mem)
{ {
if (G_LIKELY (mem)) if (G_LIKELY (mem))
glib_mem_vtable.free (mem); free (mem);
TRACE(GLIB_MEM_FREE((void*) mem)); TRACE(GLIB_MEM_FREE((void*) mem));
} }
@@ -241,7 +238,7 @@ g_try_malloc (gsize n_bytes)
gpointer mem; gpointer mem;
if (G_LIKELY (n_bytes)) if (G_LIKELY (n_bytes))
mem = glib_mem_vtable.try_malloc (n_bytes); mem = malloc (n_bytes);
else else
mem = NULL; mem = NULL;
@@ -266,13 +263,10 @@ g_try_malloc0 (gsize n_bytes)
gpointer mem; gpointer mem;
if (G_LIKELY (n_bytes)) if (G_LIKELY (n_bytes))
mem = glib_mem_vtable.try_malloc (n_bytes); mem = calloc (1, n_bytes);
else else
mem = NULL; mem = NULL;
if (mem)
memset (mem, 0, n_bytes);
return mem; return mem;
} }
@@ -296,12 +290,12 @@ g_try_realloc (gpointer mem,
gpointer newmem; gpointer newmem;
if (G_LIKELY (n_bytes)) if (G_LIKELY (n_bytes))
newmem = glib_mem_vtable.try_realloc (mem, n_bytes); newmem = realloc (mem, n_bytes);
else else
{ {
newmem = NULL; newmem = NULL;
if (mem) if (mem)
glib_mem_vtable.free (mem); free (mem);
} }
TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1)); TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1));
@@ -451,23 +445,6 @@ g_try_realloc_n (gpointer mem,
return g_try_realloc (mem, n_blocks * n_block_bytes); return g_try_realloc (mem, n_blocks * n_block_bytes);
} }
static gpointer
fallback_calloc (gsize n_blocks,
gsize n_block_bytes)
{
gsize l = n_blocks * n_block_bytes;
gpointer mem = glib_mem_vtable.malloc (l);
if (mem)
memset (mem, 0, l);
return mem;
}
static gboolean vtable_set = FALSE;
/** /**
* g_mem_is_system_malloc: * g_mem_is_system_malloc:
* *
@@ -477,358 +454,53 @@ static gboolean vtable_set = FALSE;
* This function is useful for avoiding an extra copy of allocated memory returned * This function is useful for avoiding an extra copy of allocated memory returned
* by a non-GLib-based API. * 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. * Returns: if %TRUE, malloc() and g_malloc() can be mixed.
*
* Deprecated: 2.46: GLib always uses the system malloc, so this function always
* returns %TRUE.
**/ **/
gboolean gboolean
g_mem_is_system_malloc (void) g_mem_is_system_malloc (void)
{ {
return !vtable_set; return TRUE;
} }
/** /**
* g_mem_set_vtable: * g_mem_set_vtable:
* @vtable: table of memory allocation routines. * @vtable: table of memory allocation routines.
* *
* Sets the #GMemVTable to use for memory allocation. You can use this * This function used to let you override the memory allocation function.
* to provide custom memory allocation routines. * However, its use was incompatible with the use of global constructors
* in GLib and GIO, because those use the GLib allocators before main is
* reached. Therefore this function is now deprecated and is just a stub.
* *
* The @vtable only needs to provide malloc(), realloc(), and free() * Deprecated: 2.46: Use other memory profiling tools instead
* 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 void
g_mem_set_vtable (GMemVTable *vtable) g_mem_set_vtable (GMemVTable *vtable)
{ {
if (!vtable_set) g_warning (G_STRLOC ": custom memory allocation vtable not supported");
{
if (vtable->malloc && vtable->realloc && vtable->free)
{
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: * glib_mem_profiler_table:
* *
* A #GMemVTable containing profiling variants of the memory * Deprecated: 2.46: Use other memory profiling tools instead
* 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; GMemVTable *glib_mem_profiler_table = &glib_mem_vtable;
void
g_mem_profile (void)
{
}
#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)
{
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;
}
}
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);
}
static void
profile_print_locked (guint *local_data,
gboolean success)
{
gboolean need_header = TRUE;
guint i;
for (i = 0; i <= MEM_PROFILE_TABLE_SIZE; i++)
{
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);
}
if (need_header)
g_print (" --- none ---\n");
}
/** /**
* g_mem_profile: * g_mem_profile:
* *
* Outputs a summary of memory usage. * GLib used to support some tools for memory profiling, but this
* * no longer works. There are many other useful tools for memory
* It outputs the frequency of allocations of different sizes, * profiling these days which can be used instead.
* the total number of bytes which have been allocated, *
* the total number of bytes which have been freed, * Deprecated: 2.46: Use other memory profiling tools instead
* 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 void
g_mem_profile (void) g_mem_profile (void)
{ {
guint local_data[(MEM_PROFILE_TABLE_SIZE + 1) * 8]; g_warning (G_STRLOC ": memory profiling not supported");
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)
{
g_mutex_unlock (&gmem_profile_mutex);
return;
}
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);
} }
static gpointer
profiler_try_malloc (gsize n_bytes)
{
gsize *p;
p = malloc (sizeof (gsize) * 2 + n_bytes);
if (p)
{
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 gpointer
profiler_malloc (gsize n_bytes)
{
gpointer mem = profiler_try_malloc (n_bytes);
if (!mem)
g_mem_profile ();
return mem;
}
static gpointer
profiler_calloc (gsize n_blocks,
gsize n_block_bytes)
{
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 void
profiler_free (gpointer mem)
{
gsize *p = mem;
p -= 2;
if (p[0]) /* free count */
{
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);
}
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;
}
static gpointer
profiler_try_realloc (gpointer mem,
gsize n_bytes)
{
gsize *p = mem;
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);
return NULL;
}
else
{
p = realloc (mem ? p : NULL, sizeof (gsize) * 2 + n_bytes);
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 p;
}
}
static gpointer
profiler_realloc (gpointer mem,
gsize n_bytes)
{
mem = profiler_try_realloc (mem, n_bytes);
if (!mem)
g_mem_profile ();
return mem;
}
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 */

View File

@@ -45,6 +45,8 @@ G_BEGIN_DECLS
* A set of functions used to perform memory allocation. The same #GMemVTable must * A set of functions used to perform memory allocation. The same #GMemVTable must
* be used for all allocations in the same program; a call to g_mem_set_vtable(), * be used for all allocations in the same program; a call to g_mem_set_vtable(),
* if it exists, should be prior to any use of GLib. * if it exists, should be prior to any use of GLib.
*
* This functions related to this has been deprecated in 2.46, and no longer work.
*/ */
typedef struct _GMemVTable GMemVTable; typedef struct _GMemVTable GMemVTable;
@@ -351,9 +353,9 @@ struct _GMemVTable {
gpointer (*try_realloc) (gpointer mem, gpointer (*try_realloc) (gpointer mem,
gsize n_bytes); gsize n_bytes);
}; };
GLIB_AVAILABLE_IN_ALL GLIB_DEPRECATED_IN_2_46
void g_mem_set_vtable (GMemVTable *vtable); void g_mem_set_vtable (GMemVTable *vtable);
GLIB_AVAILABLE_IN_ALL GLIB_DEPRECATED_IN_2_46
gboolean g_mem_is_system_malloc (void); gboolean g_mem_is_system_malloc (void);
GLIB_VAR gboolean g_mem_gc_friendly; GLIB_VAR gboolean g_mem_gc_friendly;
@@ -361,7 +363,7 @@ GLIB_VAR gboolean g_mem_gc_friendly;
/* Memory profiler and checker, has to be enabled via g_mem_set_vtable() /* Memory profiler and checker, has to be enabled via g_mem_set_vtable()
*/ */
GLIB_VAR GMemVTable *glib_mem_profiler_table; GLIB_VAR GMemVTable *glib_mem_profiler_table;
GLIB_AVAILABLE_IN_ALL GLIB_DEPRECATED_IN_2_46
void g_mem_profile (void); void g_mem_profile (void);
G_END_DECLS G_END_DECLS