--- libelwix/src/mem.c 2014/01/29 14:11:45 1.2.28.1 +++ libelwix/src/mem.c 2024/10/28 09:58:51 1.11 @@ -3,7 +3,7 @@ * by Michael Pounov * * $Author: misho $ -* $Id: mem.c,v 1.2.28.1 2014/01/29 14:11:45 misho Exp $ +* $Id: mem.c,v 1.11 2024/10/28 09:58:51 misho Exp $ * ************************************************************************** The ELWIX and AITNET software is distributed under the following @@ -12,7 +12,7 @@ terms: All of the documentation and software included in the ELWIX and AITNET Releases is copyrighted by ELWIX - Sofia/Bulgaria -Copyright 2004 - 2014 +Copyright 2004 - 2024 by Michael Pounov . All rights reserved. Redistribution and use in source and binary forms, with or without @@ -91,23 +91,23 @@ mpool_init(u_long maxmem) void mpool_destroy(mpool_t ** __restrict mp) { - struct tagAlloc *m; + struct tagAlloc *m, *n; register int i; - if (!mp && !*mp) + if (!mp || !*mp) return; mpool_lock(*mp); for (i = 0; i < MEM_BUCKETS; i++) { - while ((m = TAILQ_FIRST(&(*mp)->pool_active[i]))) { - TAILQ_REMOVE(&(*mp)->pool_active[i], m, alloc_node); + for (m = TAILQ_FIRST(&(*mp)->pool_active[i]); m; m = n) { + n = TAILQ_NEXT(m, alloc_node); if (m->alloc_mem) free(m->alloc_mem); free(m); } - while ((m = TAILQ_FIRST(&(*mp)->pool_inactive[i]))) { - TAILQ_REMOVE(&(*mp)->pool_inactive[i], m, alloc_node); + for (m = TAILQ_FIRST(&(*mp)->pool_inactive[i]); m; m = n) { + n = TAILQ_NEXT(m, alloc_node); if (m->alloc_mem) free(m->alloc_mem); free(m); @@ -218,7 +218,10 @@ mpool_malloc(mpool_t * __restrict mp, u_int size, cons return NULL; } else { /* quota */ mp->pool_quota.curr += size; + mp->pool_quota.real += 1 << (idx + MEM_MIN_BUCKET); +#ifdef MPOOL_MEM_ZERO memset(m->alloc_mem, 0, align + 12); +#endif } } @@ -235,6 +238,29 @@ mpool_malloc(mpool_t * __restrict mp, u_int size, cons } /* + * mpool_calloc() - Multiple memory block allocation + * + * @mp = Memory pool + * @nmemb = Number of memory blocks + * @size = Size + * @memname = Optional memory block name + * return: NULL error or !=NULL ok allocated memory + */ +void * +mpool_calloc(mpool_t * __restrict mp, u_int nmemb, u_int size, const char *memname) +{ + void *m; + u_int total = nmemb * size; + + m = mpool_malloc(mp, total, memname); +#ifndef MPOOL_MEM_ZERO + if (m) + memset(m, 0, total); +#endif + return m; +} + +/* * mpool_realloc() Reallocate memory block with new size * * @mp = Memory pool @@ -246,10 +272,9 @@ mpool_malloc(mpool_t * __restrict mp, u_int size, cons void * mpool_realloc(mpool_t * __restrict mp, void * __restrict data, u_int newsize, const char *memname) { - struct tagAlloc *m, *tmp; int idx, oidx; void *p; - u_int align, osize; + u_int osize; /* if !data execute mpool_malloc() */ if (!data) @@ -263,21 +288,20 @@ mpool_realloc(mpool_t * __restrict mp, void * __restri if (MEM_BADADDR(data) || MEM_CORRUPT(data)) { elwix_SetErr(EFAULT, "Corrupted memory address"); return NULL; - } else { - osize = ((u_int*)data)[-2] * sizeof(u_int); - oidx = BucketIndex(osize); } /* prepare new size */ if (newsize > MEM_ALLOC_MAX) { elwix_SetErr(ENOMEM, "Memory size is too large"); return NULL; - } else { - newsize = (newsize + 3) & ~3; /* must align to 4 because needed room for sentinels */ - idx = BucketIndex(newsize); } mpool_lock(mp); + osize = ((u_int*)data)[-2] * sizeof(u_int); + oidx = BucketIndex(osize); + newsize = (newsize + 3) & ~3; /* must align to 4 because needed room for sentinels */ + idx = BucketIndex(newsize); + /* quota */ if (mp->pool_quota.max && (mp->pool_quota.curr + ((u_long) newsize - osize)) > mp->pool_quota.max) { @@ -286,63 +310,27 @@ mpool_realloc(mpool_t * __restrict mp, void * __restri return NULL; } - /* find old memory block */ - TAILQ_FOREACH_SAFE(m, &mp->pool_active[oidx], alloc_node, tmp) { - if (mem_data(m, void*) == data && mem_size(m) == osize) { - /* case in different buckets */ - if (oidx != idx) { - TAILQ_REMOVE(&mp->pool_active[oidx], m, alloc_node); - /* statistics */ - mp->pool_calls.alloc--; - } - mp->pool_bytes.alloc -= osize; - break; - } - } - /* memory block not found! */ - if (!m) { + if (oidx != idx) { mpool_unlock(mp); - elwix_SetErr(EFAULT, "Memory block not found"); - return NULL; - } + p = mpool_malloc(mp, newsize, memname); + if (!p) + return NULL; - /* try to reallocate memory block to new bucket */ - if (oidx != idx) { - align = 1 << (idx + MEM_MIN_BUCKET); - p = realloc(m->alloc_mem, align + 12); - if (!p) { - LOGERR; + memcpy(p, data, MIN(osize, newsize)); + mpool_free(mp, data, 0); + } else { + p = data; - /* restore to old bucket pulled memory block for reallocation */ - TAILQ_INSERT_HEAD(&mp->pool_active[oidx], m, alloc_node); - /* statistics */ - mp->pool_calls.alloc++; - mp->pool_bytes.alloc += osize; + ((u_int*) p)[-2] = newsize / sizeof(u_int); + ((u_int*) p)[newsize / sizeof(u_int)] = MEM_MAGIC_STOP; - mpool_unlock(mp); - return NULL; - } else - m->alloc_mem = (u_int*) p; - } - /* quota */ - mp->pool_quota.curr += (u_long) newsize - osize; + mp->pool_bytes.alloc += (u_long) newsize - osize; + mp->pool_quota.curr += (u_long) newsize - osize; - m->alloc_mem[0] = newsize / sizeof(u_int); - m->alloc_mem[1] = MEM_MAGIC_START; - m->alloc_mem[2 + newsize / sizeof(u_int)] = MEM_MAGIC_STOP; - - if (oidx != idx) { - TAILQ_INSERT_HEAD(&mp->pool_active[idx], m, alloc_node); - /* statistics */ - mp->pool_calls.alloc++; + mpool_unlock(mp); } - mp->pool_bytes.alloc += newsize; - if (memname) - strlcpy(m->alloc_name, memname, sizeof m->alloc_name); - - mpool_unlock(mp); - return mem_data(m, void*); + return p; } /* @@ -382,6 +370,7 @@ mpool_purge(mpool_t * __restrict mp, u_int atmost) mp->pool_bytes.free += mem_size(m); /* quota */ mp->pool_quota.curr -= mem_size(m); + mp->pool_quota.real -= 1 << (i + MEM_MIN_BUCKET); if (m->alloc_mem) free(m->alloc_mem); @@ -407,7 +396,8 @@ mpool_free(mpool_t * __restrict mp, void * __restrict int idx; struct tagAlloc *m, *tmp; - assert(data); + if (!data) + return 0; if (!mp) { elwix_SetErr(EINVAL, "Pool not specified"); return -1; @@ -439,6 +429,7 @@ mpool_free(mpool_t * __restrict mp, void * __restrict mp->pool_bytes.free += mem_size(m); /* quota */ mp->pool_quota.curr -= mem_size(m); + mp->pool_quota.real -= 1 << (idx + MEM_MIN_BUCKET); if (m->alloc_mem) free(m->alloc_mem); @@ -491,6 +482,7 @@ mpool_free2(mpool_t * __restrict mp, u_int size, const mp->pool_bytes.free += mem_size(m); /* quota */ mp->pool_quota.curr -= mem_size(m); + mp->pool_quota.real -= 1 << (idx + MEM_MIN_BUCKET); if (m->alloc_mem) free(m->alloc_mem); @@ -626,18 +618,21 @@ mpool_setquota(mpool_t * __restrict mp, u_long maxmem) * mpool_getquota() - Get memory quota * * @mp = Memory pool - * @currmem = Return current memory + * @currmem = Return current memory usage + * @realmem = Return current real memory usage * @maxmem = Return max quota size * return: none */ void -mpool_getquota(mpool_t * __restrict mp, u_long *currmem, u_long *maxmem) +mpool_getquota(mpool_t * __restrict mp, u_long *currmem, u_long *realmem, u_long *maxmem) { if (!mp) return; if (maxmem) *maxmem = mp->pool_quota.max; + if (realmem) + *realmem = mp->pool_quota.real; if (currmem) *currmem = mp->pool_quota.curr; } @@ -649,16 +644,17 @@ mpool_getquota(mpool_t * __restrict mp, u_long *currme * * @mp = Memory pool * @cb = Export statistics to callback - * return: none + * return: -1 error or >0 bytes in data buffer */ -void -mpool_statistics(mpool_t * __restrict mp, mpool_stat_cb cb) +int +mpool_statistics(mpool_t * __restrict mp, mpool_stat_cb cb, void *data, u_int datlen) { struct tagAlloc *m; register int i, act, inact; + int len = 0; if (!mp || !cb) - return; + return -1; for (i = act = inact = 0; i < MEM_BUCKETS; act = inact = 0, i++) { TAILQ_FOREACH(m, &mp->pool_active[i], alloc_node) @@ -666,8 +662,10 @@ mpool_statistics(mpool_t * __restrict mp, mpool_stat_c TAILQ_FOREACH(m, &mp->pool_inactive[i], alloc_node) inact++; - cb(1 << (i + MEM_MIN_BUCKET), act, inact); + len += cb(1 << (i + MEM_MIN_BUCKET), act, inact, data, datlen); } + + return len; } /* ----------------------------------------------------------- */ @@ -694,7 +692,7 @@ mpool_xmalloc(size_t size) void * mpool_xcalloc(size_t num, size_t size) { - return mpool_malloc(elwix_mpool, num * size, elwix_Prog); + return mpool_calloc(elwix_mpool, num, size, elwix_Prog); } /* @@ -732,4 +730,128 @@ char * mpool_xstrdup(const char *str) { return mpool_strdup(elwix_mpool, str, elwix_Prog); +} + +/* + * mpool_xstatistics() - elwix default memory pool statistics wrapper + * + * @cb = Export statistics to callback + * @data = data buffer + * @datlen = data buffer length + * return: >0 data in string buffer + */ +int +mpool_xstatistics(mpool_stat_cb cb, void *data, u_int datlen) +{ + return mpool_statistics(elwix_mpool, cb, data, datlen); +} + +static int +xdump_show(u_int size, u_int act, u_int inact, void *data, u_int datlen) +{ + if (!act && !inact) + return 0; /* skip empty bucket */ + + if (size < 1024) + printf("\t\t* BUCKET %uB size, %u active, %u inactive\n", + size, act, inact); + else if (size < 1048576) + printf("\t\t* BUCKET %uKB size, %u active, %u inactive\n", + size / 1024, act, inact); + else + printf("\t\t* BUCKET %uMB size, %u active, %u inactive\n", + size / 1048576, act, inact); + + return 0; +} + +static int +xdump_show2(u_int size, u_int act, u_int inact, void *data, u_int datlen) +{ + char szStr[STRSIZ], *str = data; + int len = 0; + + if (!data || !datlen) + return 0; /* skip missing data buffer */ + if (!act && !inact) + return 0; /* skip empty bucket */ + + if (size < 1024) + len = snprintf(szStr, sizeof szStr, "\t\t* BUCKET %uB size, %u active, %u inactive\n", + size, act, inact); + else if (size < 1048576) + len = snprintf(szStr, sizeof szStr, "\t\t* BUCKET %uKB size, %u active, %u inactive\n", + size / 1024, act, inact); + else + len = snprintf(szStr, sizeof szStr, "\t\t* BUCKET %uMB size, %u active, %u inactive\n", + size / 1048576, act, inact); + + strlcat(str, szStr, datlen); + return len; +} + +/* + * mpool_dump() - Dump elwix memory pool statistics + * + * @mp = memory pool, if =NULL dump elwix default memory pool + * @fmt = prefix info format string + * @... = argument(s) + * return: none + */ +void +mpool_dump(mpool_t * __restrict mp, const char *fmt, ...) +{ + va_list lst; + mpool_t *p = mp ? mp : elwix_mpool; + + if (fmt) { + va_start(lst, fmt); + vprintf(fmt, lst); + va_end(lst); + } else + printf("\n%s(%d)\n", __func__, __LINE__); + + printf("------------------------------------------------------------\n"); + printf( " ELWIX memory pool %p ::\n" + "\t- quotas Current/Real/Max = %lu/%lu/%lu\n" + "\t- calls Alloc/Free/Cache = %lu/%lu/%lu\n" + "\t- bytes Alloc/Free/Cache = %lu/%lu/%lu\n" + "\t- buckets :\n", mp, + p->pool_quota.curr, p->pool_quota.real, p->pool_quota.max, + p->pool_calls.alloc, p->pool_calls.free, p->pool_calls.cache, + p->pool_bytes.alloc, p->pool_bytes.free, p->pool_bytes.cache); + + mpool_statistics(p, xdump_show, NULL, 0); + printf("------------------------------------------------------------\n"); +} + +/* + * mpool_dump2() - Dump elwix memory pool statistics to string + * + * @mp = memory pool, if =NULL dump elwix default memory pool + * @str = string buffer + * @strlen = string buffer length + * return: >0 data in string buffer + */ +int +mpool_dump2(mpool_t * __restrict mp, char *str, int strlen) +{ + int len; + mpool_t *p = mp ? mp : elwix_mpool; + + len = snprintf(str, strlen, + "------------------------------------------------------------\n"); + len += snprintf(str + len, strlen - len, " ELWIX memory pool %p ::\n" + "\t- quotas Current/Real/Max = %lu/%lu/%lu\n" + "\t- calls Alloc/Free/Cache = %lu/%lu/%lu\n" + "\t- bytes Alloc/Free/Cache = %lu/%lu/%lu\n" + "\t- buckets :\n", mp, + p->pool_quota.curr, p->pool_quota.real, p->pool_quota.max, + p->pool_calls.alloc, p->pool_calls.free, p->pool_calls.cache, + p->pool_bytes.alloc, p->pool_bytes.free, p->pool_bytes.cache); + len += mpool_statistics(p, xdump_show2, str + len, strlen - len); + len += snprintf(str + len, strlen - len, + "------------------------------------------------------------\n"); + + return len; }