2009-03-03 03:27:01 +00:00
|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
* xalloc stubs for the ptmalloc2 Memory Manager
|
|
|
|
*
|
|
|
|
* ptmalloc2 was written by Wolfram Glober (www.malloc.de).
|
|
|
|
* ptmalloc2 is based on work of Doug Lea (gee.cs.oswego.edu).
|
|
|
|
* ptmalloc2 was adapted to ldmud by Christian Welzel (www.camlann.de)
|
|
|
|
*---------------------------------------------------------------------------
|
|
|
|
* This allocator supports REPLACE_MALLOC.
|
|
|
|
* Note: when using pthreads and GC_SUPPORT under *BSD, malloc()
|
|
|
|
* is called before the synchronisation data structures are available.
|
|
|
|
* This would cause a deadlock in the call to gc_lock(), therefore
|
|
|
|
* under *BSD the REPLACE_MALLOC feature is disabled.
|
|
|
|
* This allocator is threadsafe.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "mstrings.h"
|
|
|
|
#include "stdstrings.h"
|
|
|
|
#include "svalue.h"
|
|
|
|
|
|
|
|
#include "ptmalloc/malloc.h"
|
|
|
|
#include "../mudlib/sys/debug_info.h"
|
|
|
|
|
|
|
|
/* Functions defined by ptmalloc */
|
|
|
|
extern POINTER dlmalloc(size_t);
|
|
|
|
extern POINTER dlrealloc(POINTER, size_t);
|
|
|
|
extern void dlfree(POINTER);
|
|
|
|
extern int dlmalloc_usable_size(POINTER);
|
|
|
|
extern void dlmalloc_mark_collectable(POINTER);
|
|
|
|
extern void dlmalloc_mark_permanent(POINTER);
|
|
|
|
extern void dlmalloc_clear_ref(POINTER);
|
|
|
|
extern void dlmalloc_mark_ref(POINTER);
|
|
|
|
extern int dlmalloc_test_ref(POINTER);
|
|
|
|
extern void dlmalloc_clear_ref_flags();
|
|
|
|
extern void dlmalloc_free_unrefed_memory();
|
|
|
|
extern int dlmalloc_is_freed(POINTER, size_t);
|
|
|
|
extern struct mallinfo dlmallinfo();
|
|
|
|
|
|
|
|
static word_t *heap_end = NULL;
|
|
|
|
static word_t *heap_start = NULL;
|
|
|
|
/* Start and end address of the heap - currently always set to NULL,
|
|
|
|
* which disables the assert_stack_gap() logic. Considering that
|
|
|
|
* ptmalloc uses mmap, the logic wouldn't really work anyway.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PT_OVERHEAD (2*sizeof(size_t))
|
|
|
|
|
|
|
|
/* to allocate a memory block */
|
|
|
|
static POINTER mem_alloc(size_t size) {
|
|
|
|
return dlmalloc(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* to deallocate a memory block */
|
|
|
|
static void mem_free(POINTER p) {
|
|
|
|
return dlfree(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reallocate a block. */
|
|
|
|
static POINTER mem_realloc (POINTER p, size_t size) {
|
|
|
|
return dlrealloc(p, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increase a block size in place. If not possible, return NULL. */
|
|
|
|
static void * mem_increment_size (void *vp, size_t size) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the size of an allocated block.
|
|
|
|
* If this value is not available, implement this function as a dummy,
|
|
|
|
* but also #define NO_MEM_BLOCK_SIZE.
|
|
|
|
* For Garbage Collection or replacing malloc() this function must be
|
|
|
|
* valid!
|
|
|
|
* Returns the blocksize excl. overhead.
|
|
|
|
*/
|
|
|
|
static size_t mem_block_size (POINTER p) {
|
|
|
|
return dlmalloc_usable_size(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the size of the allocators internal overhead. */
|
|
|
|
static size_t mem_overhead () {
|
|
|
|
return PT_OVERHEAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark a block as permanent */
|
|
|
|
static void mem_mark_permanent (POINTER p) {
|
|
|
|
dlmalloc_mark_permanent(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark a block as collectable */
|
|
|
|
static void mem_mark_collectable (POINTER p) {
|
|
|
|
dlmalloc_mark_collectable(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do whatever consolidation is useful. */
|
|
|
|
void mem_consolidate (Bool force UNUSED)
|
|
|
|
{
|
|
|
|
# ifdef __MWERKS__
|
|
|
|
# pragma unused(force)
|
|
|
|
# endif
|
|
|
|
/* nothing to do here... malloc does it automagically */
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MALLOC_EXT_STATISTICS
|
|
|
|
/* Update whatever extended statistics the allocator has. Called every
|
|
|
|
* backend cycle or so to allow for the calculation of averages over time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
mem_update_stats (void)
|
|
|
|
{
|
|
|
|
/* Nothing */
|
|
|
|
} /* mem_update_stats() */
|
|
|
|
#endif /* MALLOC_EXT_STATISTICS */
|
|
|
|
|
|
|
|
/* For the status commands and functions: add the ptmalloc statistic
|
|
|
|
* to the buffer <sbuf>.
|
|
|
|
*/
|
|
|
|
void mem_dump_data (strbuf_t *sbuf) {
|
|
|
|
struct mallinfo stats;
|
|
|
|
|
|
|
|
/* Get a snapshot of the statistics - strbuf_add() might do further
|
|
|
|
* allocations while we're reading them.
|
|
|
|
*/
|
|
|
|
stats = dlmallinfo();
|
|
|
|
|
|
|
|
# define dump_stat(str,member) strbuf_addf(sbuf, str, stats.member)
|
|
|
|
|
|
|
|
strbuf_add(sbuf, "Type Amount\n");
|
|
|
|
dump_stat("total non-mmaped: %8d bytes\n", arena);
|
|
|
|
dump_stat("number of free chunks: %8d\n", ordblks);
|
|
|
|
dump_stat("number of fastbins: %8d\n", smblks);
|
|
|
|
dump_stat("number of mmap: %8d\n", hblks);
|
|
|
|
dump_stat("bytes in mmap: %8d bytes\n", hblkhd);
|
|
|
|
dump_stat("max alloced ever: %8d bytes\n", usmblks);
|
|
|
|
dump_stat("total in fastbin: %8d bytes\n", fsmblks);
|
|
|
|
dump_stat("total alloced: %8d bytes \n", uordblks);
|
|
|
|
dump_stat("total free: %8d bytes\n", fordblks);
|
|
|
|
dump_stat("freeable bytes: %8d bytes\n", keepcost);
|
|
|
|
|
|
|
|
#undef dump_stat
|
|
|
|
} /* mem_dump_data() */
|
|
|
|
|
|
|
|
void mem_dump_extdata (strbuf_t *sbuf) {
|
|
|
|
strbuf_add(sbuf, "ptmalloc doesn't have extended statistics.\n");
|
|
|
|
} /* mem_dump_extdata() */
|
|
|
|
|
|
|
|
/* Fill in the data for debug_info(DINFO_DATA, DID_MEMORY) into the
|
|
|
|
* svalue-block svp.
|
|
|
|
*/
|
|
|
|
void mem_dinfo_data (svalue_t *svp, int value) {
|
|
|
|
struct mallinfo stats;
|
|
|
|
|
|
|
|
#define ST_NUMBER(which,code) \
|
|
|
|
if (value == -1) svp[which].u.number = code; \
|
|
|
|
else if (value == which) svp->u.number = code
|
|
|
|
|
|
|
|
stats = dlmallinfo();
|
|
|
|
|
|
|
|
if (value == -1)
|
|
|
|
put_ref_string(svp+DID_MEM_NAME, STR_PTMALLOC);
|
|
|
|
else if (value == DID_MEM_NAME)
|
|
|
|
put_ref_string(svp, STR_PTMALLOC);
|
|
|
|
|
|
|
|
ST_NUMBER(DID_MEM_SBRK_SIZE, stats.arena);
|
|
|
|
ST_NUMBER(DID_MEM_FREE_CHUNKS, stats.ordblks);
|
|
|
|
ST_NUMBER(DID_MEM_FFREE, stats.smblks);
|
|
|
|
ST_NUMBER(DID_MEM_FFREE_SIZE, stats.fsmblks);
|
|
|
|
ST_NUMBER(DID_MEM_MMAP, stats.hblks);
|
|
|
|
ST_NUMBER(DID_MEM_MMAP_SIZE, stats.hblkhd);
|
|
|
|
ST_NUMBER(DID_MEM_MAX_ALLOCATED, stats.usmblks);
|
|
|
|
ST_NUMBER(DID_MEM_OVERHEAD, PT_OVERHEAD);
|
|
|
|
ST_NUMBER(DID_MEM_ALLOCATED, stats.uordblks);
|
|
|
|
ST_NUMBER(DID_MEM_USED, stats.uordblks - stats.fordblks);
|
|
|
|
ST_NUMBER(DID_MEM_TOTAL_UNUSED, stats.fordblks);
|
|
|
|
ST_NUMBER(DID_MEM_KEEP_COST, stats.keepcost);
|
|
|
|
|
|
|
|
#undef ST_NUMBER
|
|
|
|
} /* mem_dinfo_data() */
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef GC_SUPPORT
|
|
|
|
/* Clear, set, test the 'referenced' marker. */
|
|
|
|
static void mem_clear_ref (POINTER p) {
|
|
|
|
dlmalloc_clear_ref(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mem_mark_ref (POINTER p) {
|
|
|
|
dlmalloc_mark_ref(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool mem_test_ref (POINTER p) {
|
|
|
|
return !dlmalloc_test_ref(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear all 'referenced' markers. */
|
|
|
|
void mem_clear_ref_flags() {
|
|
|
|
dlmalloc_clear_ref_flags();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free all memory marked as 'unreferenced'. */
|
|
|
|
void mem_free_unrefed_memory() {
|
|
|
|
dlmalloc_free_unrefed_memory();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
Bool
|
|
|
|
mem_dump_memory (int fd)
|
|
|
|
|
|
|
|
/* Print the location, size, and (if available) the TRACE information
|
|
|
|
* of all memory blocks to file <fd>, and return TRUE.
|
|
|
|
* If the allocator doesn't support this operation, print nothing
|
|
|
|
* and return FALSE.
|
|
|
|
*
|
|
|
|
* If <fd> is -1, just return TRUE or FALSE (this is used to check if
|
|
|
|
* the allocator supports memory dumps).
|
|
|
|
*/
|
|
|
|
|
|
|
|
{
|
|
|
|
return MY_FALSE;
|
|
|
|
} /* mem_dump_memory() */
|
|
|
|
|
|
|
|
#ifdef MALLOC_TRACE
|
|
|
|
/* Return true if <p> is a free block. */
|
|
|
|
static Bool mem_is_freed (POINTER p, size_t minsize) {
|
|
|
|
return dlmalloc_is_freed(p, minsize);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif /* GC_SUPPORT */
|
|
|
|
|
|
|
|
/* the alignment guaranteed by the allocator */
|
|
|
|
//#define MEM_ALIGN (2*(sizeof(size_t)))
|
|
|
|
#define MEM_ALIGN (2*SIZEOF_INT)
|
|
|
|
|
|
|
|
/* If the allocator can replace the libc allocation routines.
|
|
|
|
* See above for the *BSD situation.
|
|
|
|
*/
|
|
|
|
|
2009-05-21 22:41:07 +00:00
|
|
|
#if defined(SBRK_OK) && \
|
|
|
|
!defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
|
2009-03-03 03:27:01 +00:00
|
|
|
#define REPLACE_MALLOC
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The allocator is threadsafe */
|
|
|
|
#define MEM_THREADSAFE
|
|
|
|
|
|
|
|
/* Reference a couple of unused variables and functions to avoid
|
|
|
|
* unnecessary warning.
|
|
|
|
*/
|
|
|
|
void ptmalloc_ref_unused(void)
|
|
|
|
{
|
|
|
|
in_malloc = 0;
|
|
|
|
print_block(0, 0);
|
2009-05-21 22:41:07 +00:00
|
|
|
#ifdef REPLACE_MALLOC
|
|
|
|
count_up(&clib_alloc_stat, 0);
|
|
|
|
#endif
|
2009-03-03 03:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************/
|