summaryrefslogtreecommitdiff
path: root/src/lib/libast/vmalloc/malloc.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libast/vmalloc/malloc.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/lib/libast/vmalloc/malloc.c')
-rw-r--r--src/lib/libast/vmalloc/malloc.c1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/src/lib/libast/vmalloc/malloc.c b/src/lib/libast/vmalloc/malloc.c
new file mode 100644
index 0000000..e69523a
--- /dev/null
+++ b/src/lib/libast/vmalloc/malloc.c
@@ -0,0 +1,1438 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1985-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* Phong Vo <kpv@research.att.com> *
+* *
+***********************************************************************/
+#if defined(_UWIN) && defined(_BLD_ast)
+
+void _STUB_malloc(){}
+
+#else
+
+#if _UWIN
+
+#define calloc ______calloc
+#define _ast_free ______free
+#define malloc ______malloc
+#define mallinfo ______mallinfo
+#define mallopt ______mallopt
+#define mstats ______mstats
+#define realloc ______realloc
+
+#define _STDLIB_H_ 1
+
+extern int atexit(void(*)(void));
+extern char* getenv(const char*);
+
+#endif
+
+#include "vmhdr.h"
+#include <errno.h>
+
+#if _UWIN
+
+#include <malloc.h>
+
+#define _map_malloc 1
+#define _mal_alloca 1
+
+#undef calloc
+#define calloc _ast_calloc
+#undef _ast_free
+#define free _ast_free
+#undef malloc
+#define malloc _ast_malloc
+#undef mallinfo
+typedef struct ______mallinfo Mallinfo_t;
+#undef mallopt
+#undef mstats
+typedef struct ______mstats Mstats_t;
+#undef realloc
+#define realloc _ast_realloc
+
+#endif
+
+#if __STD_C
+#define F0(f,t0) f(t0)
+#define F1(f,t1,a1) f(t1 a1)
+#define F2(f,t1,a1,t2,a2) f(t1 a1, t2 a2)
+#else
+#define F0(f,t0) f()
+#define F1(f,t1,a1) f(a1) t1 a1;
+#define F2(f,t1,a1,t2,a2) f(a1, a2) t1 a1; t2 a2;
+#endif
+
+/*
+ * define _AST_std_malloc=1 to force the standard malloc
+ * if _map_malloc is also defined then _ast_malloc etc.
+ * will simply call malloc etc.
+ */
+
+#if !defined(_AST_std_malloc) && __CYGWIN__
+#define _AST_std_malloc 1
+#endif
+
+/* malloc compatibility functions
+**
+** These are aware of debugging/profiling and are driven by the
+** VMALLOC_OPTIONS environment variable which is a comma or space
+** separated list of [no]name[=value] options:
+**
+** abort if Vmregion==Vmdebug then VM_DBABORT is set,
+** otherwise _BLD_DEBUG enabled assertions abort()
+** on failure
+** break try sbrk() block allocator first
+** check if Vmregion==Vmbest then the region is checked every op
+** free disable addfreelist()
+** keep disable free -- if code works with this enabled then it
+** probably accesses free'd data
+** method=m sets Vmregion=m if not defined, m (Vm prefix optional)
+** may be one of { best debug last profile }
+** period=n sets Vmregion=Vmdebug if not defined, if
+** Vmregion==Vmdebug the region is checked every n ops
+** profile=f sets Vmregion=Vmprofile if not set, if
+** Vmregion==Vmprofile then profile info printed to file f
+** start=n sets Vmregion=Vmdebug if not defined, if
+** Vmregion==Vmdebug region checking starts after n ops
+** trace=f enables tracing to file f
+** warn=f sets Vmregion=Vmdebug if not defined, if
+** Vmregion==Vmdebug then warnings printed to file f
+** watch=a sets Vmregion=Vmdebug if not defined, if
+** Vmregion==Vmdebug then address a is watched
+**
+** Output files are created if they don't exist. &n and /dev/fd/n name
+** the file descriptor n which must be open for writing. The pattern %p
+** in a file name is replaced by the process ID.
+**
+** VMALLOC_OPTIONS combines the features of these previously used env vars:
+** { VMCHECK VMDEBUG VMETHOD VMPROFILE VMTRACE }
+**
+** Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94.
+*/
+
+#if _sys_stat
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+
+#ifdef S_IRUSR
+#define CREAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
+#else
+#define CREAT_MODE 0644
+#endif
+
+static Vmulong_t _Vmdbstart = 0;
+static Vmulong_t _Vmdbcheck = 0;
+static Vmulong_t _Vmdbtime = 0;
+static int _Vmpffd = -1;
+
+#if ( !_std_malloc || !_BLD_ast ) && !_AST_std_malloc
+
+#if !_map_malloc
+#undef calloc
+#undef cfree
+#undef free
+#undef mallinfo
+#undef malloc
+#undef mallopt
+#undef memalign
+#undef mstats
+#undef realloc
+#undef valloc
+
+#if _malloc_hook
+
+#include <malloc.h>
+
+#undef calloc
+#undef cfree
+#undef free
+#undef malloc
+#undef memalign
+#undef realloc
+
+#define calloc _ast_calloc
+#define cfree _ast_cfree
+#define free _ast_free
+#define malloc _ast_malloc
+#define memalign _ast_memalign
+#define realloc _ast_realloc
+
+#endif
+
+#endif
+
+#if _WINIX
+
+#include <ast_windows.h>
+
+#if _UWIN
+
+#define VMRECORD(p) _vmrecord(p)
+#define VMBLOCK { int _vmblock = _sigblock();
+#define VMUNBLOCK _sigunblock(_vmblock); }
+
+extern int _sigblock(void);
+extern void _sigunblock(int);
+extern unsigned long _record[2048];
+
+__inline Void_t* _vmrecord(Void_t* p)
+{
+ register unsigned long v = ((unsigned long)p)>>16;
+
+ _record[v>>5] |= 1<<((v&0x1f));
+ return p;
+}
+
+#else
+
+#define getenv(s) lcl_getenv(s)
+
+static char*
+lcl_getenv(const char* s)
+{
+ int n;
+ static char buf[512];
+
+ if (!(n = GetEnvironmentVariable(s, buf, sizeof(buf))) || n > sizeof(buf))
+ return 0;
+ return buf;
+}
+
+#endif /* _UWIN */
+
+#endif /* _WINIX */
+
+#ifndef VMRECORD
+#define VMRECORD(p) (p)
+#define VMBLOCK
+#define VMUNBLOCK
+#endif
+
+#if defined(__EXPORT__)
+#define extern extern __EXPORT__
+#endif
+
+static int _Vmflinit = 0;
+#define VMFLINIT() \
+ { if(!_Vmflinit) vmflinit(); \
+ if(_Vmdbcheck) \
+ { if(_Vmdbtime < _Vmdbstart) _Vmdbtime += 1; \
+ else if((_Vmdbtime += 1) < _Vmdbstart) _Vmdbtime = _Vmdbstart; \
+ if(_Vmdbtime >= _Vmdbstart && (_Vmdbtime % _Vmdbcheck) == 0 && \
+ Vmregion->meth.meth == VM_MTDEBUG) \
+ vmdbcheck(Vmregion); \
+ } \
+ }
+
+#if __STD_C
+static int vmflinit(void)
+#else
+static int vmflinit()
+#endif
+{
+ char* file;
+ int line;
+ Void_t* func;
+
+ /* this must be done now to avoid any inadvertent recursion (more below) */
+ _Vmflinit = 1;
+ VMFLF(Vmregion,file,line,func);
+
+ /* if getenv() calls malloc(), the options may not affect the eventual region */
+ VMOPTIONS();
+
+ /* reset file and line number to correct values for the call */
+ Vmregion->file = file;
+ Vmregion->line = line;
+ Vmregion->func = func;
+
+ return 0;
+}
+
+/* use multiple regions to reduce blocking by concurrent threads */
+#if _mem_mmap_anon || _mem_mmap_zero
+static Vmalloc_t *Region[64]; /* list of concurrent regions */
+static unsigned int Regmax = 64; /* max number of regions */
+#else
+static Vmalloc_t* Region[1]; /* list of concurrent regions */
+static unsigned int Regmax = 0;
+#endif
+static unsigned int Regnum = 0; /* current #concurrent regions */
+
+/* statistics */
+static unsigned int Regopen = 0; /* #allocation calls opened */
+static unsigned int Reglock = 0; /* #allocation calls locked */
+static unsigned int Regprobe = 0; /* #probes to find a region */
+
+int setregmax(int regmax)
+{
+ int oldmax = Regmax;
+
+ if(regmax >= Regnum && regmax <= sizeof(Region)/sizeof(Region[0]))
+ Regmax = regmax;
+
+ return oldmax;
+}
+
+/* return statistics */
+int _mallocstat(Vmstat_t* st)
+{
+ Vmstat_t vmst;
+ int k;
+
+ if(vmstat(Vmregion, st) < 0) /* add up all stats */
+ return -1;
+ for(k = 0; k < Regnum; ++k)
+ { if(!Region[k])
+ continue;
+ if(vmstat(Region[k], &vmst) < 0 )
+ return -1;
+ st->n_busy += vmst.n_busy;
+ st->n_free += vmst.n_free;
+ st->s_busy += vmst.s_busy;
+ st->s_free += vmst.s_free;
+ st->m_busy += vmst.m_busy;
+ st->m_free += vmst.m_free;
+ st->n_seg += vmst.n_seg;
+ st->extent += vmst.extent;
+ }
+
+ st->n_region = Regnum+1;
+ st->n_open = Regopen;
+ st->n_lock = Reglock;
+ st->n_probe = Regprobe;
+
+ return 0;
+}
+
+/* find the region that a block was allocated from */
+static Vmalloc_t* regionof(Void_t* addr)
+{
+ int k;
+
+#if USE_NATIVE
+#define CAUTIOUS 1
+#else
+#define CAUTIOUS 0
+#endif
+ if(CAUTIOUS || Vmregion->meth.meth != VM_MTBEST )
+ { /* addr will not be dereferenced here */
+ if(vmaddr(Vmregion,addr) == 0 )
+ return Vmregion;
+ for(k = 0; k < Regnum; ++k)
+ if(Region[k] && vmaddr(Region[k], addr) == 0 )
+ return Region[k];
+ return NIL(Vmalloc_t*);
+ }
+ else
+ { /* fast, but susceptible to bad data */
+ Vmdata_t *vd = SEG(BLOCK(addr))->vmdt;
+ if(Vmregion->data == vd )
+ return Vmregion;
+ for(k = 0; k < Regnum; ++k)
+ if(Region[k] && Region[k]->data == vd)
+ return Region[k];
+ return NIL(Vmalloc_t*);
+ }
+}
+
+/* manage a cache of free objects */
+typedef struct _regfree_s
+{ struct _regfree_s* next;
+} Regfree_t;
+static Regfree_t *Regfree;
+
+static void addfreelist(Regfree_t* data)
+{
+ unsigned int k;
+ Regfree_t *head;
+
+ for(k = 0;; ASOLOOP(k) )
+ { data->next = head = Regfree;
+ if(asocasptr(&Regfree, head, data) == (Void_t*)head )
+ return;
+ }
+}
+
+static void clrfreelist()
+{
+ Regfree_t *list, *next;
+ Vmalloc_t *vm;
+
+ if(!(list = Regfree) )
+ return; /* nothing to do */
+
+ if(asocasptr(&Regfree, list, NIL(Regfree_t*)) != list )
+ return; /* somebody else is doing it */
+
+ for(; list; list = next)
+ { next = list->next;
+ if(vm = regionof((Void_t*)list))
+ { if(asocasint(&vm->data->lock, 0, 1) == 0) /* can free this now */
+ { (void)(*vm->meth.freef)(vm, (Void_t*)list, 1);
+ vm->data->lock = 0;
+ }
+ else addfreelist(list); /* ah well, back in the queue */
+ }
+ }
+}
+
+/* get a suitable region to allocate from */
+typedef struct _regdisc_s
+{ Vmdisc_t disc;
+ char slop[64]; /* to absorb any extra data in Vmdcsystem */
+} Regdisc_t;
+
+static int regexcept(Vmalloc_t* vm, int type, Void_t* data, Vmdisc_t* disc)
+{
+ if(type == VM_OPEN)
+ { if(data) /* make vmopen allocate all memory using discipline */
+ *(Void_t**)data = data; /* just make it non-NULL */
+ return 0;
+ }
+ return 0;
+}
+
+static Vmalloc_t* getregion(int* local)
+{
+ Vmalloc_t *vm;
+ int p, pos;
+
+ static unsigned int Rand = 0xdeadbeef; /* a cheap prng */
+#define RAND() (Rand = Rand*16777617 + 3)
+
+ clrfreelist();
+
+ if(Regmax <= 0 )
+ { /* uni-process/thread */
+ *local = 1;
+ Vmregion->data->lock = 1;
+ return Vmregion;
+ }
+ else if(asocasint(&Vmregion->data->lock, 0, 1) == 0 )
+ { /* Vmregion is open, so use it */
+ *local = 1;
+ asoincint(&Regopen);
+ return Vmregion;
+ }
+
+ asoincint(&Regprobe); /* probe Region[] to find an open region */
+ if(Regnum == 0)
+ pos = 0;
+ else for(pos = p = RAND()%Regnum;; )
+ { if(Region[p] && asocasint(&Region[p]->data->lock, 0, 1) == 0 )
+ { *local = 1;
+ asoincint(&Regopen);
+ return Region[p];
+ }
+ if((p = (p+1)%Regnum) == pos )
+ break;
+ }
+
+ /* grab the next open slot for a new region */
+ while((p = Regnum) < Regmax)
+ if(asocasint(&Regnum, p, p+1) == p )
+ break;
+ if(p < Regmax) /* this slot is now ours */
+ { static Regdisc_t Regdisc;
+ if(!Regdisc.disc.exceptf) /* one time initialization */
+ { GETPAGESIZE(_Vmpagesize);
+ memcpy(&Regdisc, Vmdcsystem, Vmdcsystem->size);
+ Regdisc.disc.round = ROUND(_Vmpagesize, 64*1024);
+ Regdisc.disc.exceptf = regexcept;
+ }
+
+ /**/ASSERT(Region[p] == NIL(Vmalloc_t*));
+ if((vm = vmopen(&Regdisc.disc, Vmbest, VM_SHARE)) != NIL(Vmalloc_t*) )
+ { vm->data->lock = 1; /* lock new region now */
+ *local = 1;
+ asoincint(&Regopen);
+ return (Region[p] = vm);
+ }
+ else Region[p] = Vmregion; /* better than nothing */
+ }
+
+ /* must return something */
+ vm = Region[pos] ? Region[pos] : Vmregion;
+ if(asocasint(&vm->data->lock, 0, 1) == 0)
+ { *local = 1;
+ asoincint(&Regopen);
+ }
+ else
+ { *local = 0;
+ asoincint(&Reglock);
+ }
+ return vm;
+}
+
+#if __STD_C
+extern Void_t* calloc(reg size_t n_obj, reg size_t s_obj)
+#else
+extern Void_t* calloc(n_obj, s_obj)
+reg size_t n_obj;
+reg size_t s_obj;
+#endif
+{
+ Void_t *addr;
+ Vmalloc_t *vm;
+ int local = 0;
+ VMFLINIT();
+
+ vm = getregion(&local);
+ addr = (*vm->meth.resizef)(vm, NIL(Void_t*), n_obj*s_obj, VM_RSZERO, local);
+ if(local)
+ { /**/ASSERT(vm->data->lock == 1);
+ vm->data->lock = 0;
+ }
+ return VMRECORD(addr);
+}
+
+#if __STD_C
+extern Void_t* malloc(reg size_t size)
+#else
+extern Void_t* malloc(size)
+reg size_t size;
+#endif
+{
+ Void_t *addr;
+ Vmalloc_t *vm;
+ int local = 0;
+ VMFLINIT();
+
+ vm = getregion(&local);
+ addr = (*vm->meth.allocf)(vm, size, local);
+ if(local)
+ { /**/ASSERT(vm->data->lock == 1);
+ vm->data->lock = 0;
+ }
+ return VMRECORD(addr);
+}
+
+#if __STD_C
+extern Void_t* realloc(reg Void_t* data, reg size_t size)
+#else
+extern Void_t* realloc(data,size)
+reg Void_t* data; /* block to be reallocated */
+reg size_t size; /* new size */
+#endif
+{
+ ssize_t copy;
+ Void_t *addr;
+ Vmalloc_t *vm;
+ VMFLINIT();
+
+ if(!data)
+ return malloc(size);
+ else if((vm = regionof(data)) )
+ { if(vm == Vmregion && vm != Vmheap) /* no multiple region usage here */
+ { addr = (*vm->meth.resizef)(vm, data, size, VM_RSCOPY|VM_RSMOVE, 0);
+ return VMRECORD(addr);
+ }
+ if(asocasint(&vm->data->lock, 0, 1) == 0 ) /* region is open */
+ { addr = (*vm->meth.resizef)(vm, data, size, VM_RSCOPY|VM_RSMOVE, 1);
+ vm->data->lock = 0;
+ return VMRECORD(addr);
+ }
+ else if(Regmax > 0 && Vmregion == Vmheap && (addr = malloc(size)) )
+ { if((copy = SIZE(BLOCK(data))&~BITS) > size )
+ copy = size;
+ memcpy(addr, data, copy);
+ addfreelist((Regfree_t*)data);
+ return VMRECORD(addr);
+ }
+ else /* this may block but it is the best that we can do now */
+ { addr = (*vm->meth.resizef)(vm, data, size, VM_RSCOPY|VM_RSMOVE, 0);
+ return VMRECORD(addr);
+ }
+ }
+ else /* not our data */
+ {
+#if USE_NATIVE
+#undef realloc /* let the native realloc() take care of it */
+#if __STD_C
+ extern Void_t* realloc(Void_t*, size_t);
+#else
+ extern Void_t* realloc();
+#endif
+ return realloc(data, size);
+#else
+ return NIL(Void_t*);
+#endif
+ }
+}
+
+#if __STD_C
+extern void free(reg Void_t* data)
+#else
+extern void free(data)
+reg Void_t* data;
+#endif
+{
+ Vmalloc_t *vm;
+ VMFLINIT();
+
+ if(!data || (_Vmassert & VM_keep))
+ return;
+ else if((vm = regionof(data)) )
+ {
+ if(vm == Vmregion && Vmregion != Vmheap || (_Vmassert & VM_free))
+ (void)(*vm->meth.freef)(vm, data, 0);
+ else addfreelist((Regfree_t*)data);
+ return;
+ }
+ else /* not our data */
+ {
+#if USE_NATIVE
+#undef free /* let the native free() take care of it */
+#if __STD_C
+ extern void free(Void_t*);
+#else
+ extern void free();
+#endif
+ free(data);
+#endif
+ return;
+ }
+}
+
+#if __STD_C
+extern void cfree(reg Void_t* data)
+#else
+extern void cfree(data)
+reg Void_t* data;
+#endif
+{
+ free(data);
+}
+
+#if __STD_C
+extern Void_t* memalign(reg size_t align, reg size_t size)
+#else
+extern Void_t* memalign(align, size)
+reg size_t align;
+reg size_t size;
+#endif
+{
+ Void_t *addr;
+ Vmalloc_t *vm;
+ int local = 0;
+ VMFLINIT();
+
+ vm = getregion(&local);
+ VMBLOCK
+ addr = (*vm->meth.alignf)(vm, size, align, local);
+ if(local)
+ { /**/ASSERT(vm->data->lock == 1);
+ vm->data->lock = 0;
+ }
+ VMUNBLOCK
+ return VMRECORD(addr);
+}
+
+#if __STD_C
+extern int posix_memalign(reg Void_t **memptr, reg size_t align, reg size_t size)
+#else
+extern int posix_memalign(memptr, align, size)
+reg Void_t** memptr;
+reg size_t align;
+reg size_t size;
+#endif
+{
+ Void_t *mem;
+
+ if(align == 0 || (align%sizeof(Void_t*)) != 0 || ((align-1)&align) != 0 )
+ return EINVAL;
+
+ if(!(mem = memalign(align, size)) )
+ return ENOMEM;
+
+ *memptr = mem;
+ return 0;
+}
+
+#if __STD_C
+extern Void_t* valloc(reg size_t size)
+#else
+extern Void_t* valloc(size)
+reg size_t size;
+#endif
+{
+ VMFLINIT();
+
+ GETPAGESIZE(_Vmpagesize);
+ return VMRECORD(memalign(_Vmpagesize, size));
+}
+
+#if __STD_C
+extern Void_t* pvalloc(reg size_t size)
+#else
+extern Void_t* pvalloc(size)
+reg size_t size;
+#endif
+{
+ VMFLINIT();
+
+ GETPAGESIZE(_Vmpagesize);
+ return VMRECORD(memalign(_Vmpagesize, ROUND(size,_Vmpagesize)) );
+}
+
+#if !_PACKAGE_ast
+#if __STD_C
+char* strdup(const char* s)
+#else
+char* strdup(s)
+char* s;
+#endif
+{
+ char *ns;
+ size_t n;
+
+ if(!s)
+ return NIL(char*);
+ else
+ { n = strlen(s);
+ if((ns = malloc(n+1)) )
+ memcpy(ns,s,n+1);
+ return ns;
+ }
+}
+#endif /* _PACKAGE_ast */
+
+#if !_lib_alloca || _mal_alloca
+#ifndef _stk_down
+#define _stk_down 0
+#endif
+typedef struct _alloca_s Alloca_t;
+union _alloca_u
+{ struct
+ { char* addr;
+ Alloca_t* next;
+ } head;
+ char array[ALIGN];
+};
+struct _alloca_s
+{ union _alloca_u head;
+ Vmuchar_t data[1];
+};
+
+#if __STD_C
+extern Void_t* alloca(size_t size)
+#else
+extern Void_t* alloca(size)
+size_t size;
+#endif
+{ char array[ALIGN];
+ char* file;
+ int line;
+ Void_t* func;
+ Alloca_t* f;
+ Vmalloc_t *vm;
+ static Alloca_t* Frame;
+
+ VMFLINIT();
+
+ VMFLF(Vmregion,file,line,func); /* save info before freeing frames */
+
+ while(Frame) /* free unused frames */
+ { if(( _stk_down && &array[0] > Frame->head.head.addr) ||
+ (!_stk_down && &array[0] < Frame->head.head.addr) )
+ { f = Frame; Frame = f->head.head.next;
+ if((vm = regionof(f)) )
+ (void)(*vm->meth.freef)(vm, f, 0);
+ /* else: something bad happened. just keep going */
+ }
+ else break;
+ }
+
+ Vmregion->file = file; /* restore file/line info before allocation */
+ Vmregion->line = line;
+ Vmregion->func = func;
+
+ f = (Alloca_t*)(*Vmregion->meth.allocf)(Vmregion, size+sizeof(Alloca_t)-1, 0);
+
+ /* if f is NULL, this mimics a stack overflow with a memory error! */
+ f->head.head.addr = &array[0];
+ f->head.head.next = Frame;
+ Frame = f;
+
+ return (Void_t*)f->data;
+}
+#endif /*!_lib_alloca || _mal_alloca*/
+
+#if _map_malloc
+
+/* not sure of all the implications -- 0 is conservative for now */
+#define USE_NATIVE 0 /* native free/realloc on non-vmalloc ptrs */
+
+#else
+
+#if _malloc_hook
+
+static void vm_free_hook(void* ptr, const void* caller)
+{
+ free(ptr);
+}
+
+static void* vm_malloc_hook(size_t size, const void* caller)
+{
+ void* r;
+
+ r = malloc(size);
+ return r;
+}
+
+static void* vm_memalign_hook(size_t align, size_t size, const void* caller)
+{
+ void* r;
+
+ r = memalign(align, size);
+ return r;
+}
+
+static void* vm_realloc_hook(void* ptr, size_t size, const void* caller)
+{
+ void* r;
+
+ r = realloc(ptr, size);
+ return r;
+}
+
+static void vm_initialize_hook(void)
+{
+ __free_hook = vm_free_hook;
+ __malloc_hook = vm_malloc_hook;
+ __memalign_hook = vm_memalign_hook;
+ __realloc_hook = vm_realloc_hook;
+}
+
+void (*__malloc_initialize_hook)(void) = vm_initialize_hook;
+
+#if 0 /* 2012-02-29 this may be needed to cover shared libs */
+
+void __attribute__ ((constructor)) vm_initialize_initialize_hook(void)
+{
+ vm_initialize_hook();
+ __malloc_initialize_hook = vm_initialize_hook;
+}
+
+#endif
+
+#else
+
+/* intercept _* __* __libc_* variants */
+
+#if __lib__malloc
+extern Void_t* F2(_calloc, size_t,n, size_t,m) { return calloc(n, m); }
+extern Void_t F1(_cfree, Void_t*,p) { free(p); }
+extern Void_t F1(_free, Void_t*,p) { free(p); }
+extern Void_t* F1(_malloc, size_t,n) { return malloc(n); }
+#if _lib_memalign
+extern Void_t* F2(_memalign, size_t,a, size_t,n) { return memalign(a, n); }
+#endif
+#if _lib_pvalloc
+extern Void_t* F1(_pvalloc, size_t,n) { return pvalloc(n); }
+#endif
+extern Void_t* F2(_realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
+#if _lib_valloc
+extern Void_t* F1(_valloc, size_t,n) { return valloc(n); }
+#endif
+#endif
+
+#if _lib___malloc
+extern Void_t* F2(__calloc, size_t,n, size_t,m) { return calloc(n, m); }
+extern Void_t F1(__cfree, Void_t*,p) { free(p); }
+extern Void_t F1(__free, Void_t*,p) { free(p); }
+extern Void_t* F1(__malloc, size_t,n) { return malloc(n); }
+#if _lib_memalign
+extern Void_t* F2(__memalign, size_t,a, size_t,n) { return memalign(a, n); }
+#endif
+#if _lib_pvalloc
+extern Void_t* F1(__pvalloc, size_t,n) { return pvalloc(n); }
+#endif
+extern Void_t* F2(__realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
+#if _lib_valloc
+extern Void_t* F1(__valloc, size_t,n) { return valloc(n); }
+#endif
+#endif
+
+#if _lib___libc_malloc
+extern Void_t* F2(__libc_calloc, size_t,n, size_t,m) { return calloc(n, m); }
+extern Void_t F1(__libc_cfree, Void_t*,p) { free(p); }
+extern Void_t F1(__libc_free, Void_t*,p) { free(p); }
+extern Void_t* F1(__libc_malloc, size_t,n) { return malloc(n); }
+#if _lib_memalign
+extern Void_t* F2(__libc_memalign, size_t,a, size_t,n) { return memalign(a, n); }
+#endif
+#if _lib_pvalloc
+extern Void_t* F1(__libc_pvalloc, size_t,n) { return pvalloc(n); }
+#endif
+extern Void_t* F2(__libc_realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
+#if _lib_valloc
+extern Void_t* F1(__libc_valloc, size_t,n) { return valloc(n); }
+#endif
+#endif
+
+#endif /* _malloc_hook */
+
+#endif /* _map_malloc */
+
+#undef extern
+
+#if _hdr_malloc /* need the mallint interface for statistics, etc. */
+
+#undef calloc
+#define calloc ______calloc
+#undef cfree
+#define cfree ______cfree
+#undef free
+#define free ______free
+#undef malloc
+#define malloc ______malloc
+#undef pvalloc
+#define pvalloc ______pvalloc
+#undef realloc
+#define realloc ______realloc
+#undef valloc
+#define valloc ______valloc
+
+#if !_UWIN
+
+#include <malloc.h>
+
+typedef struct mallinfo Mallinfo_t;
+typedef struct mstats Mstats_t;
+
+#endif
+
+#if defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#if _lib_mallopt
+#if __STD_C
+extern int mallopt(int cmd, int value)
+#else
+extern int mallopt(cmd, value)
+int cmd;
+int value;
+#endif
+{
+ VMFLINIT();
+ return 0;
+}
+#endif /*_lib_mallopt*/
+
+#if _lib_mallinfo && _mem_arena_mallinfo
+#if __STD_C
+extern Mallinfo_t mallinfo(void)
+#else
+extern Mallinfo_t mallinfo()
+#endif
+{
+ Vmstat_t sb;
+ Mallinfo_t mi;
+
+ VMFLINIT();
+ memset(&mi,0,sizeof(mi));
+ if(vmstat(Vmregion,&sb) >= 0)
+ { mi.arena = sb.extent;
+ mi.ordblks = sb.n_busy+sb.n_free;
+ mi.uordblks = sb.s_busy;
+ mi.fordblks = sb.s_free;
+ }
+ return mi;
+}
+#endif /* _lib_mallinfo */
+
+#if _lib_mstats && _mem_bytes_total_mstats
+#if __STD_C
+extern Mstats_t mstats(void)
+#else
+extern Mstats_t mstats()
+#endif
+{
+ Vmstat_t sb;
+ Mstats_t ms;
+
+ VMFLINIT();
+ memset(&ms,0,sizeof(ms));
+ if(vmstat(Vmregion,&sb) >= 0)
+ { ms.bytes_total = sb.extent;
+ ms.chunks_used = sb.n_busy;
+ ms.bytes_used = sb.s_busy;
+ ms.chunks_free = sb.n_free;
+ ms.bytes_free = sb.s_free;
+ }
+ return ms;
+}
+#endif /*_lib_mstats*/
+
+#undef extern
+
+#endif/*_hdr_malloc*/
+
+#else
+
+/*
+ * even though there is no malloc override, still provide
+ * _ast_* counterparts for object compatibility
+ */
+
+#define setregmax(n)
+
+#undef calloc
+extern Void_t* calloc _ARG_((size_t, size_t));
+
+#undef cfree
+extern void cfree _ARG_((Void_t*));
+
+#undef free
+extern void free _ARG_((Void_t*));
+
+#undef malloc
+extern Void_t* malloc _ARG_((size_t));
+
+#if _lib_memalign
+#undef memalign
+extern Void_t* memalign _ARG_((size_t, size_t));
+#endif
+
+#if _lib_pvalloc
+#undef pvalloc
+extern Void_t* pvalloc _ARG_((size_t));
+#endif
+
+#undef realloc
+extern Void_t* realloc _ARG_((Void_t*, size_t));
+
+#if _lib_valloc
+#undef valloc
+extern Void_t* valloc _ARG_((size_t));
+#endif
+
+#if defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#if !_malloc_hook
+
+extern Void_t F1(_ast_free, Void_t*,p) { free(p); }
+extern Void_t* F1(_ast_malloc, size_t,n) { return malloc(n); }
+#if _lib_memalign
+extern Void_t* F2(_ast_memalign, size_t,a, size_t,n) { return memalign(a, n); }
+#endif
+extern Void_t* F2(_ast_realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
+
+#endif
+
+extern Void_t* F2(_ast_calloc, size_t,n, size_t,m) { return calloc(n, m); }
+extern Void_t F1(_ast_cfree, Void_t*,p) { free(p); }
+#if _lib_pvalloc
+extern Void_t* F1(_ast_pvalloc, size_t,n) { return pvalloc(n); }
+#endif
+#if _lib_valloc
+extern Void_t* F1(_ast_valloc, size_t,n) { return valloc(n); }
+#endif
+
+#undef extern
+
+#if _hdr_malloc
+
+#undef mallinfo
+#undef mallopt
+#undef mstats
+
+#define calloc ______calloc
+#define cfree ______cfree
+#define free ______free
+#define malloc ______malloc
+#define pvalloc ______pvalloc
+#define realloc ______realloc
+#define valloc ______valloc
+
+#if !_UWIN
+
+#if !_malloc_hook
+
+#include <malloc.h>
+
+#endif
+
+typedef struct mallinfo Mallinfo_t;
+typedef struct mstats Mstats_t;
+
+#endif
+
+#if defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#if _lib_mallopt
+extern int F2(_ast_mallopt, int,cmd, int,value) { return mallopt(cmd, value); }
+#endif
+
+#if _lib_mallinfo && _mem_arena_mallinfo
+extern Mallinfo_t F0(_ast_mallinfo, void) { return mallinfo(); }
+#endif
+
+#if _lib_mstats && _mem_bytes_total_mstats
+extern Mstats_t F0(_ast_mstats, void) { return mstats(); }
+#endif
+
+#undef extern
+
+#endif /*_hdr_malloc*/
+
+#endif /*!_std_malloc*/
+
+#if __STD_C
+static Vmulong_t atou(char** sp)
+#else
+static Vmulong_t atou(sp)
+char** sp;
+#endif
+{
+ char* s = *sp;
+ Vmulong_t v = 0;
+
+ if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X') )
+ { for(s += 2; *s; ++s)
+ { if(*s >= '0' && *s <= '9')
+ v = (v << 4) + (*s - '0');
+ else if(*s >= 'a' && *s <= 'f')
+ v = (v << 4) + (*s - 'a') + 10;
+ else if(*s >= 'A' && *s <= 'F')
+ v = (v << 4) + (*s - 'A') + 10;
+ else break;
+ }
+ }
+ else
+ { for(; *s; ++s)
+ { if(*s >= '0' && *s <= '9')
+ v = v*10 + (*s - '0');
+ else break;
+ }
+ }
+
+ *sp = s;
+ return v;
+}
+
+#if __STD_C
+static char* insertpid(char* begs, char* ends)
+#else
+static char* insertpid(begs,ends)
+char* begs;
+char* ends;
+#endif
+{ int pid;
+ char* s;
+
+ if((pid = getpid()) < 0)
+ return NIL(char*);
+
+ s = ends;
+ do
+ { if(s == begs)
+ return NIL(char*);
+ *--s = '0' + pid%10;
+ } while((pid /= 10) > 0);
+ while(s < ends)
+ *begs++ = *s++;
+
+ return begs;
+}
+
+#if __STD_C
+static int createfile(char* file)
+#else
+static int createfile(file)
+char* file;
+#endif
+{
+ char buf[1024];
+ char *next, *endb;
+ int fd;
+
+ next = buf;
+ endb = buf + sizeof(buf);
+ while(*file)
+ { if(*file == '%')
+ { switch(file[1])
+ {
+ case 'p' :
+ if(!(next = insertpid(next,endb)) )
+ return -1;
+ file += 2;
+ break;
+ default :
+ goto copy;
+ }
+ }
+ else
+ { copy:
+ *next++ = *file++;
+ }
+
+ if(next >= endb)
+ return -1;
+ }
+
+ *next = '\0';
+ file = buf;
+ if (*file == '&' && *(file += 1) || strncmp(file, "/dev/fd/", 8) == 0 && *(file += 8))
+ fd = dup((int)atou(&file));
+ else if (*file)
+#if _PACKAGE_ast
+ fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, CREAT_MODE);
+#else
+ fd = creat(file, CREAT_MODE);
+#endif
+ else
+ return -1;
+#if _PACKAGE_ast
+#ifdef FD_CLOEXEC
+ if (fd >= 0)
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+#endif
+ return fd;
+}
+
+#if __STD_C
+static void pfprint(void)
+#else
+static void pfprint()
+#endif
+{
+ if(Vmregion->meth.meth == VM_MTPROFILE)
+ vmprofile(Vmregion,_Vmpffd);
+}
+
+/*
+ * initialize runtime options from the VMALLOC_OPTIONS env var
+ */
+
+#define COPY(t,e,f) while ((*t = *f++) && t < e) t++
+
+#if __STD_C
+void _vmoptions(void)
+#else
+void _vmoptions()
+#endif
+{
+ Vmalloc_t* vm = 0;
+ char* trace = 0;
+ char* s;
+ char* t;
+ char* v;
+ Vmulong_t n;
+ int fd;
+ char buf[1024];
+
+ _Vmoptions = 1;
+ t = buf;
+ v = &buf[sizeof(buf)-1];
+ if (s = getenv("VMALLOC_OPTIONS"))
+ COPY(t, v, s);
+ if (t > buf)
+ {
+ *t = 0;
+ s = buf;
+ for (;;)
+ {
+ while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
+ s++;
+ if (!*(t = s))
+ break;
+ v = 0;
+ while (*s)
+ if (*s == ',' || *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
+ {
+ *s++ = 0;
+ break;
+ }
+ else if (!v && *s == '=')
+ {
+ *s++ = 0;
+ if (!*(v = s))
+ v = 0;
+ }
+ else
+ s++;
+ if (t[0] == 'n' && t[1] == 'o')
+ continue;
+ switch (t[0])
+ {
+ case 'a': /* abort */
+ if (!vm)
+ vm = vmopen(Vmdcsystem, Vmdebug, 0);
+ if (vm && vm->meth.meth == VM_MTDEBUG)
+ vmset(vm, VM_DBABORT, 1);
+ else
+ _Vmassert |= VM_abort;
+ break;
+ case 'b': /* break */
+ _Vmassert |= VM_break;
+ break;
+ case 'c': /* check */
+ _Vmassert |= VM_check;
+ break;
+ case 'f': /* free */
+ _Vmassert |= VM_free;
+ break;
+ case 'k': /* keep */
+ _Vmassert |= VM_keep;
+ break;
+ case 'm':
+ if (v && !vm)
+ {
+ if ((v[0] == 'V' || v[0] == 'v') && (v[1] == 'M' || v[1] == 'm'))
+ v += 2;
+ if (strcmp(v, "debug") == 0)
+ vm = vmopen(Vmdcsystem, Vmdebug, 0);
+ else if (strcmp(v, "profile") == 0)
+ vm = vmopen(Vmdcsystem, Vmprofile, 0);
+ else if (strcmp(v, "last") == 0)
+ vm = vmopen(Vmdcsystem, Vmlast, 0);
+ else if (strcmp(v, "best") == 0)
+ vm = Vmheap;
+ }
+ break;
+ case 'p':
+ if (v)
+ switch (t[1])
+ {
+ case 'e': /* period=<count> */
+ if (!vm)
+ vm = vmopen(Vmdcsystem, Vmdebug, 0);
+ if (vm && vm->meth.meth == VM_MTDEBUG)
+ _Vmdbcheck = atou(&v);
+ break;
+ case 'r': /* profile=<path> */
+ if (!vm)
+ vm = vmopen(Vmdcsystem, Vmprofile, 0);
+ if (v && vm && vm->meth.meth == VM_MTPROFILE)
+ _Vmpffd = createfile(v);
+ break;
+ }
+ break;
+ case 's': /* start=<count> */
+ if (!vm)
+ vm = vmopen(Vmdcsystem, Vmdebug, 0);
+ if (v && vm && vm->meth.meth == VM_MTDEBUG)
+ _Vmdbstart = atou(&v);
+ break;
+ case 't': /* trace=<path> */
+ trace = v;
+ break;
+ case 'w':
+ if (t[1] == 'a')
+ switch (t[2])
+ {
+ case 'r': /* warn=<path> */
+ if (!vm)
+ vm = vmopen(Vmdcsystem, Vmdebug, 0);
+ if (v && vm && vm->meth.meth == VM_MTDEBUG && (fd = createfile(v)) >= 0)
+ vmdebug(fd);
+ break;
+ case 't': /* watch=<addr> */
+ if (!vm)
+ vm = vmopen(Vmdcsystem, Vmdebug, 0);
+ if (v && vm && vm->meth.meth == VM_MTDEBUG && (n = atou(&v)) >= 0)
+ vmdbwatch((Void_t*)n);
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ /* slip in the new region now so that malloc() will work fine */
+
+ if (vm)
+ {
+ if (vm->meth.meth == VM_MTDEBUG)
+ _Vmdbcheck = 1;
+ Vmregion = vm;
+ }
+
+ /* enable tracing -- this currently disables multiple regions */
+
+ if (trace)
+ {
+ setregmax(0);
+ if ((fd = createfile(trace)) >= 0)
+ {
+ vmset(Vmregion, VM_TRACE, 1);
+ vmtrace(fd);
+ }
+ }
+ else if (Vmregion != Vmheap || asometh(0, 0)->type == ASO_SIGNAL)
+ setregmax(0);
+
+ /* make sure that profile data is output upon exiting */
+
+ if (vm && vm->meth.meth == VM_MTPROFILE)
+ {
+ if (_Vmpffd < 0)
+ _Vmpffd = 2;
+ /* this may wind up calling malloc(), but region is ok now */
+ atexit(pfprint);
+ }
+ else if (_Vmpffd >= 0)
+ {
+ close(_Vmpffd);
+ _Vmpffd = -1;
+ }
+}
+
+/*
+ * ast semi-private workaround for system functions
+ * that misbehave by passing bogus addresses to free()
+ *
+ * not prototyped in any header to keep it ast semi-private
+ *
+ * to keep malloc() data by disabling free()
+ * extern _vmkeep(int);
+ * int r = _vmkeep(1);
+ * and to restore to the previous state
+ * (void)_vmkeep(r);
+ */
+
+int
+#if __STD_C
+_vmkeep(int v)
+#else
+_vmkeep(v)
+int v;
+#endif
+{
+ int r;
+
+ r = !!(_Vmassert & VM_keep);
+ if (v)
+ _Vmassert |= VM_keep;
+ else
+ _Vmassert &= ~VM_keep;
+ return r;
+}
+
+#endif /*_UWIN*/