mem/f_malloc.c
47f59303
 /*
84d8e165
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
4c4e5766
  * This file is part of Kamailio, a free SIP server.
7dd0b342
  *
e4f42ce1
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
7dd0b342
  *
e4f42ce1
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1c4b78f3
  */
47f59303
 
1c4b78f3
 
47f59303
 /**
  * \file
  * \brief Simple, very fast, malloc library
  * \ingroup mem
  */
 
7dd0b342
 
f92113d8
 #if !defined(q_malloc)  && (defined F_MALLOC)
1c4b78f3
 
e22bbdb8
 #include <string.h>
9c01c860
 #include <stdlib.h>
e22bbdb8
 
1c4b78f3
 #include "f_malloc.h"
 #include "../dprint.h"
843b2927
 #include "../globals.h"
dc36e273
 #include "../compiler_opt.h"
58d0d1b5
 #include "memdbg.h"
dc36e273
 #include "../bit_scan.h"
e4f42ce1
 #include "../cfg/cfg.h" /* memlog */
ed6f5bca
 #ifdef MALLOC_STATS
92d6b6c1
 #include "../events.h"
ed6f5bca
 #endif
1c4b78f3
 
 
c20927f7
 /* useful macros */
1c4b78f3
 
 #define FRAG_NEXT(f) \
 	((struct fm_frag*)((char*)(f)+sizeof(struct fm_frag)+(f)->size ))
 
 #define FRAG_OVERHEAD	(sizeof(struct fm_frag))
6aea241c
 #define INIT_OVERHEAD	\
2b2569ea
 	(ROUNDUP(sizeof(struct fm_block))+2*sizeof(struct fm_frag))
6aea241c
 
1c4b78f3
 
 
c20927f7
 /** ROUNDTO= 2^k so the following works */
7b5c6965
 #define ROUNDTO_MASK	(~((unsigned long)ROUNDTO-1))
 #define ROUNDUP(s)		(((s)+(ROUNDTO-1))&ROUNDTO_MASK)
 #define ROUNDDOWN(s)	((s)&ROUNDTO_MASK)
 
1c4b78f3
 
 
c20927f7
 /** finds the hash value for s, s=ROUNDTO multiple */
c082177a
 #define GET_HASH(s)   ( ((unsigned long)(s)<=F_MALLOC_OPTIMIZE)?\
 							(unsigned long)(s)/ROUNDTO: \
 							F_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \
 								F_MALLOC_OPTIMIZE_FACTOR+1 )
 
 #define UN_HASH(h)	( ((unsigned long)(h)<=(F_MALLOC_OPTIMIZE/ROUNDTO))?\
 						(unsigned long)(h)*ROUNDTO: \
 						1UL<<((unsigned long)(h)-F_MALLOC_OPTIMIZE/ROUNDTO+\
bf0fab3f
 							F_MALLOC_OPTIMIZE_FACTOR-1)\
 					)
 
c20927f7
 
dc36e273
 #ifdef F_MALLOC_HASH_BITMAP
 
 #define fm_bmp_set(qm, b) \
 	do{ \
 		(qm)->free_bitmap[(b)/FM_HASH_BMP_BITS] |= \
 											1UL<<((b)%FM_HASH_BMP_BITS); \
 	}while(0)
 
 #define fm_bmp_reset(qm, b) \
 	do{ \
 		(qm)->free_bitmap[(b)/FM_HASH_BMP_BITS] &= \
 											~(1UL<<((b)%FM_HASH_BMP_BITS)); \
 	}while(0)
 
c20927f7
 /** returns 0 if not set, !=0 if set */
dc36e273
 #define fm_bmp_is_set(qm, b) \
 	((qm)->free_bitmap[(b)/FM_HASH_BMP_BITS] & (1UL<<((b)%FM_HASH_BMP_BITS)))
 
c20927f7
 
 
 /**
  * \brief Find the first free fragment in a memory block
  * 
  * Find the first free fragment in a memory block
  * \param qm searched memory block
  * \param start start value
  * \return index for free fragment
  */
dc36e273
 inline static int fm_bmp_first_set(struct fm_block* qm, int start)
 {
 	int bmp_idx;
 	int bit;
 	int r;
 	fm_hash_bitmap_t test_val;
 	fm_hash_bitmap_t v;
 	
 	bmp_idx=start/FM_HASH_BMP_BITS;
 	bit=start%FM_HASH_BMP_BITS;
 	test_val=1UL <<((unsigned long)bit);
 	if (qm->free_bitmap[bmp_idx] & test_val)
 		return start;
 	else if (qm->free_bitmap[bmp_idx] & ~(test_val-1)){
 #if 0
 		test_val<<=1;
 		for (r=bit+1; r<FM_HASH_BMP_BITS; r++, test_val<<=1){
 			if (qm->free_bitmap[bmp_idx] & test_val)
 				return (start-bit+r);
 		}
 #endif
 		v=qm->free_bitmap[bmp_idx]>>(bit+1);
 		return start+1+bit_scan_forward((unsigned long)v);
 	}
 	for (r=bmp_idx+1;r<FM_HASH_BMP_SIZE; r++){
 		if (qm->free_bitmap[r]){
 			/* find first set bit */
 			return r*FM_HASH_BMP_BITS+
 						bit_scan_forward((unsigned long)qm->free_bitmap[r]);
 		}
 	}
 	/* not found, nothing free */
 	return -1;
 }
 #endif /* F_MALLOC_HASH_BITMAP */
 
 
 
46b467df
 /* mark/test used/unused frags */
 #define FRAG_MARK_USED(f)
 #define FRAG_CLEAR_USED(f)
 #define FRAG_WAS_USED(f)   (1)
 
 /* other frag related defines:
  * MEM_COALESCE_FRAGS 
  * MEM_FRAG_AVOIDANCE
  */
 #define MEM_FRAG_AVOIDANCE
 
1c4b78f3
 
c20927f7
 /** computes hash number for big buckets */
dc36e273
 #define big_hash_idx(s) ((unsigned long)bit_scan_reverse((unsigned long)(s)))
1c4b78f3
 
 
c20927f7
 /**
  * \name Memory manager boundary check pattern
  */
 /*@{ */
1c4b78f3
 #ifdef DBG_F_MALLOC
c20927f7
 #define ST_CHECK_PATTERN   0xf0f0f0f0 /** inserted at the beginning */
 #define END_CHECK_PATTERN1 0xc0c0c0c0 /** inserted at the end       */
 #define END_CHECK_PATTERN2 0xabcdefed /** inserted at the end       */
 /*@} */
1c4b78f3
 #endif
 
 
c20927f7
 /**
aac0c211
  * \brief Extract memory fragment from free list
  * \param qm memory block
  * \param frag memory fragment
  */
 static inline void fm_extract_free(struct fm_block* qm, struct fm_frag* frag)
 {
 	struct fm_frag** pf;
 	int hash;
 
 	pf = frag->prv_free;
 	hash = GET_HASH(frag->size);
 
 	*pf=frag->u.nxt_free;
 
 	if(frag->u.nxt_free) frag->u.nxt_free->prv_free = pf;
 
 	qm->ffrags--;
 	qm->free_hash[hash].no--;
 #ifdef F_MALLOC_HASH_BITMAP
 	if (qm->free_hash[hash].no==0)
 		fm_bmp_reset(qm, hash);
 #endif /* F_MALLOC_HASH_BITMAP */
 	frag->prv_free = NULL;
 
 	qm->real_used+=frag->size;
 	qm->used+=frag->size;
 }
 
 /**
c20927f7
  * \brief Insert a memory fragment in a memory block
  * \param qm memory block
  * \param frag memory fragment
  */
1c4b78f3
 static inline void fm_insert_free(struct fm_block* qm, struct fm_frag* frag)
 {
 	struct fm_frag** f;
 	int hash;
 	
 	hash=GET_HASH(frag->size);
46b467df
 	f=&(qm->free_hash[hash].first);
 	if (frag->size > F_MALLOC_OPTIMIZE){ /* because of '<=' in GET_HASH,
 											(different from 0.8.1[24] on
 											 purpose --andrei ) */
1c4b78f3
 		for(; *f; f=&((*f)->u.nxt_free)){
 			if (frag->size <= (*f)->size) break;
 		}
 	}
 	
 	/*insert it here*/
4e7f95bf
 	frag->prv_free = f;
1c4b78f3
 	frag->u.nxt_free=*f;
4e7f95bf
 	if (*f) (*f)->prv_free = &(frag->u.nxt_free);
1c4b78f3
 	*f=frag;
aac0c211
 	qm->ffrags++;
46b467df
 	qm->free_hash[hash].no++;
dc36e273
 #ifdef F_MALLOC_HASH_BITMAP
 	fm_bmp_set(qm, hash);
 #endif /* F_MALLOC_HASH_BITMAP */
aac0c211
 	qm->used-=frag->size;
 	qm->real_used-=frag->size;
1c4b78f3
 }
 
 
c20927f7
 /**
  *\brief Split a memory fragement
  *
  * Split a memory fragement, size should be already rounded-up
  * \param qm memory block
  * \param frag memory fragment
  * \param size fragement size
  */
9c01c860
 static inline
 #ifdef DBG_F_MALLOC 
c082177a
 void fm_split_frag(struct fm_block* qm, struct fm_frag* frag,
 					unsigned long size,
4168f707
 					const char* file, const char* func, unsigned int line)
9c01c860
 #else
c082177a
 void fm_split_frag(struct fm_block* qm, struct fm_frag* frag,
 					unsigned long size)
9c01c860
 #endif
 {
c082177a
 	unsigned long rest;
9c01c860
 	struct fm_frag* n;
 	
 	rest=frag->size-size;
46b467df
 #ifdef MEM_FRAG_AVOIDANCE
 	if ((rest> (FRAG_OVERHEAD+F_MALLOC_OPTIMIZE))||
 		(rest>=(FRAG_OVERHEAD+size))){ /* the residue fragm. is big enough*/
 #else
9c01c860
 	if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
46b467df
 #endif
9c01c860
 		frag->size=size;
 		/*split the fragment*/
 		n=FRAG_NEXT(frag);
 		n->size=rest-FRAG_OVERHEAD;
46b467df
 		FRAG_CLEAR_USED(n); /* never used */
51dffb0a
 #ifdef DBG_F_MALLOC
9c01c860
 		/* frag created by malloc, mark it*/
 		n->file=file;
aac0c211
 		n->func="frag. from fm_split_frag";
9c01c860
 		n->line=line;
 		n->check=ST_CHECK_PATTERN;
 #endif
 		/* reinsert n in free list*/
2b2569ea
 		qm->used-=FRAG_OVERHEAD;
9c01c860
 		fm_insert_free(qm, n);
 	}else{
 		/* we cannot split this fragment any more => alloc all of it*/
 	}
 }
 
 
c20927f7
 /**
  * \brief Initialize memory manager malloc
  * \param address start address for memory block
  * \param size Size of allocation
  * \return return the fm_block
  */
aac0c211
 struct fm_block* fm_malloc_init(char* address, unsigned long size, int type)
1c4b78f3
 {
 	char* start;
 	char* end;
 	struct fm_block* qm;
c082177a
 	unsigned long init_overhead;
1c4b78f3
 	
 	/* make address and size multiple of 8*/
9a694681
 	start=(char*)ROUNDUP((unsigned long) address);
451bccb6
 	DBG("fm_malloc_init: F_OPTIMIZE=%lu, /ROUNDTO=%lu\n",
 			F_MALLOC_OPTIMIZE, F_MALLOC_OPTIMIZE/ROUNDTO);
 	DBG("fm_malloc_init: F_HASH_SIZE=%lu, fm_block size=%lu\n",
 			F_HASH_SIZE, (long)sizeof(struct fm_block));
 	DBG("fm_malloc_init(%p, %lu), start=%p\n", address, size, start);
 
1c4b78f3
 	if (size<start-address) return 0;
 	size-=(start-address);
 	if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
 	size=ROUNDDOWN(size);
 
6aea241c
 	init_overhead=INIT_OVERHEAD;
1c4b78f3
 	
 	
 	if (size < init_overhead)
 	{
 		/* not enough mem to create our control structures !!!*/
 		return 0;
 	}
 	end=start+size;
 	qm=(struct fm_block*)start;
 	memset(qm, 0, sizeof(struct fm_block));
 	qm->size=size;
a7d59bac
 	qm->used = size - init_overhead;
 	qm->real_used=size;
aac0c211
 	qm->max_real_used=init_overhead;
 	qm->type = type;
fb9d6e50
 	size-=init_overhead;
1c4b78f3
 	
 	qm->first_frag=(struct fm_frag*)(start+ROUNDUP(sizeof(struct fm_block)));
 	qm->last_frag=(struct fm_frag*)(end-sizeof(struct fm_frag));
4e7f95bf
 	/* init first fragment*/
1c4b78f3
 	qm->first_frag->size=size;
4e7f95bf
 	qm->first_frag->prv_free=0;
 	/* init last fragment*/
1c4b78f3
 	qm->last_frag->size=0;
4e7f95bf
 	qm->last_frag->prv_free=0;
1c4b78f3
 	
 #ifdef DBG_F_MALLOC
 	qm->first_frag->check=ST_CHECK_PATTERN;
 	qm->last_frag->check=END_CHECK_PATTERN1;
 #endif
 	
 	/* link initial fragment into the free list*/
 	
 	fm_insert_free(qm, qm->first_frag);
aac0c211
 
1c4b78f3
 	return qm;
 }
 
aac0c211
 /**
  * \brief Try merging free fragments to fit requested size
  * \param qm memory block
  * \param size memory allocation size
  * \return address of allocated memory
  */
 struct fm_frag* fm_search_defrag(struct fm_block* qm, unsigned long size)
 {
 	struct fm_frag* frag;
 	struct fm_frag* nxt;
 
 	frag = qm->first_frag;
 	while((char*)frag < (char*)qm->last_frag) {
 		nxt = FRAG_NEXT(frag);
 
 		if ( ((char*)nxt < (char*)qm->last_frag) && frag->prv_free
 				&& nxt->prv_free) {
 			/* join frag + nxt */
 			fm_extract_free(qm, frag);
 			do {
 				fm_extract_free(qm, nxt);
 				frag->size += nxt->size + FRAG_OVERHEAD;
 
2b2569ea
 				/* join - one frag less, add overhead to used */
 				qm->used += FRAG_OVERHEAD;
aac0c211
 
 				if( frag->size >size )
 					return frag;
 
 				nxt = FRAG_NEXT(frag);
 			} while (((char*)nxt < (char*)qm->last_frag) && nxt->prv_free);
 
 			fm_insert_free(qm, frag);
 		}
 		frag = nxt;
 	}
 
 	return 0;
 }
1c4b78f3
 
c20927f7
 /**
  * \brief Main memory manager allocation function
  * 
  * Main memory manager allocation function, provide functionality necessary for pkg_malloc
  * \param qm memory block
  * \param size memory allocation size
  * \return address of allocated memory
  */
1c4b78f3
 #ifdef DBG_F_MALLOC
c082177a
 void* fm_malloc(struct fm_block* qm, unsigned long size,
4168f707
 					const char* file, const char* func, unsigned int line)
1c4b78f3
 #else
c082177a
 void* fm_malloc(struct fm_block* qm, unsigned long size)
1c4b78f3
 #endif
 {
 	struct fm_frag** f;
 	struct fm_frag* frag;
 	int hash;
 	
 #ifdef DBG_F_MALLOC
58d0d1b5
 	MDBG("fm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func,
1c4b78f3
 			line);
 #endif
f61ff34a
 	/*malloc(0) should return a valid pointer according to specs*/
 	if(unlikely(size==0)) size=4;
1c4b78f3
 	/*size must be a multiple of 8*/
 	size=ROUNDUP(size);
 	
 	/*search for a suitable free frag*/
 
dc36e273
 #ifdef F_MALLOC_HASH_BITMAP
 	hash=fm_bmp_first_set(qm, GET_HASH(size));
 	if (likely(hash>=0)){
1ccbd335
 		if (likely(hash<=F_MALLOC_OPTIMIZE/ROUNDTO)) { /* return first match */
 			f=&(qm->free_hash[hash].first);
892a24f7
 			if(likely(*f)) goto found;
 #ifdef DBG_F_MALLOC
 			MDBG(" block %p hash %d empty but no. is %lu\n", qm,
 					hash, qm->free_hash[hash].no);
 #endif
 			/* reset slot and try next hash */
 			qm->free_hash[hash].no=0;
 			fm_bmp_reset(qm, hash);
 			hash++;
1ccbd335
 		}
892a24f7
 		/* if we are here we are searching next hash slot or a "big" fragment
1ccbd335
 		   between F_MALLOC_OPTIMIZE/ROUNDTO+1
 		   and F_MALLOC_OPTIMIZE/ROUNDTO + (32|64) - F_MALLOC_OPTIMIZE_FACTOR
 		   => 18 hash buckets on 32 bits and 50 buckets on 64 bits
 		   The free hash bitmap is used to jump directly to non-empty
 		   hash buckets.
 		*/
 		do {
 			for(f=&(qm->free_hash[hash].first);(*f); f=&((*f)->u.nxt_free))
 				if ((*f)->size>=size) goto found;
 			hash++; /* try in next hash cell */
 		}while((hash < F_HASH_SIZE) &&
 				((hash=fm_bmp_first_set(qm, hash)) >= 0));
dc36e273
 	}
 #else /* F_MALLOC_HASH_BITMAP */
1c4b78f3
 	for(hash=GET_HASH(size);hash<F_HASH_SIZE;hash++){
46b467df
 		f=&(qm->free_hash[hash].first);
1c4b78f3
 		for(;(*f); f=&((*f)->u.nxt_free))
 			if ((*f)->size>=size) goto found;
 		/* try in a bigger bucket */
 	}
dc36e273
 #endif /* F_MALLOC_HASH_BITMAP */
aac0c211
 	/* not found, search by defrag */
 
 	frag = fm_search_defrag(qm, size);
 
 	if(frag) goto finish;
 
1c4b78f3
 	return 0;
 
 found:
 	/* we found it!*/
 	/* detach it from the free list*/
 	frag=*f;
aac0c211
 	fm_extract_free(qm, frag);
 
 	/*see if use full frag or split it in two*/
1c4b78f3
 #ifdef DBG_F_MALLOC
9c01c860
 	fm_split_frag(qm, frag, size, file, func, line);
aac0c211
 #else
 	fm_split_frag(qm, frag, size);
 #endif
 
 finish:
1c4b78f3
 
aac0c211
 #ifdef DBG_F_MALLOC
1c4b78f3
 	frag->file=file;
 	frag->func=func;
 	frag->line=line;
 	frag->check=ST_CHECK_PATTERN;
58d0d1b5
 	MDBG("fm_malloc(%p, %lu) returns address %p \n", qm, size,
1c4b78f3
 		(char*)frag+sizeof(struct fm_frag));
 #endif
aac0c211
 
51dffb0a
 	if (qm->max_real_used<qm->real_used)
 		qm->max_real_used=qm->real_used;
46b467df
 	FRAG_MARK_USED(frag); /* mark it as used */
aac0c211
 	if(qm->type==MEM_TYPE_PKG) {
 		sr_event_exec(SREV_PKG_UPDATE_STATS, 0);
 	}
1c4b78f3
 	return (char*)frag+sizeof(struct fm_frag);
 }
 
 
1124b8cb
 #ifdef MEM_JOIN_FREE
 /**
  * join fragment f with next one (if it is free)
  */
 static void fm_join_frag(struct fm_block* qm, struct fm_frag* f)
 {
 	int hash;
 	struct fm_frag **pf;
 	struct fm_frag* n;
 
 	n=FRAG_NEXT(f);
 	/* check if valid and if in free list */
aac0c211
 	if (((char*)n >= (char*)qm->last_frag) || (n->prv_free==NULL))
1124b8cb
 		return;
 
 	/* detach n from the free list */
 	hash=GET_HASH(n->size);
4e7f95bf
 	pf=n->prv_free;
1124b8cb
 	if (*pf==0){
 		/* not found, bad! */
 		LM_WARN("could not find %p in free list (hash=%ld)\n", n, GET_HASH(n->size));
 		return;
 	}
 	/* detach */
 	*pf=n->u.nxt_free;
4e7f95bf
 	if(n->u.nxt_free) n->u.nxt_free->prv_free = pf;
aac0c211
 	qm->ffrags--;
1124b8cb
 	qm->free_hash[hash].no--;
 #ifdef F_MALLOC_HASH_BITMAP
 	if (qm->free_hash[hash].no==0)
 		fm_bmp_reset(qm, hash);
 #endif /* F_MALLOC_HASH_BITMAP */
 	/* join */
 	f->size+=n->size+FRAG_OVERHEAD;
2b2569ea
 	qm->real_used+=n->size;
 	qm->used+=n->size + FRAG_OVERHEAD;
1124b8cb
 }
 #endif /*MEM_JOIN_FREE*/
 
c20927f7
 /**
  * \brief Main memory manager free function
  * 
  * Main memory manager free function, provide functionality necessary for pkg_free
  * \param qm memory block
  * \param p freed memory
  */
1c4b78f3
 #ifdef DBG_F_MALLOC
4168f707
 void fm_free(struct fm_block* qm, void* p, const char* file, const char* func, 
1c4b78f3
 				unsigned int line)
 #else
 void fm_free(struct fm_block* qm, void* p)
 #endif
 {
 	struct fm_frag* f;
 
 #ifdef DBG_F_MALLOC
58d0d1b5
 	MDBG("fm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line);
927a8a1a
 #endif
 	if (p==0) {
0193d296
 		MDBG("WARNING:fm_free: free(0) called\n");
927a8a1a
 		return;
 	}
 #ifdef DBG_F_MALLOC
1c4b78f3
 	if (p>(void*)qm->last_frag || p<(void*)qm->first_frag){
1124b8cb
 		LOG(L_CRIT, "BUG: fm_free: bad pointer %p (out of memory block!),"
 				" called from %s: %s(%d) - aborting\n", p,
 				file, func, line);
 		if(likely(cfg_get(core, core_cfg, mem_safety)==0))
 			abort();
927a8a1a
 		else return;
1c4b78f3
 	}
 #endif
 	f=(struct fm_frag*) ((char*)p-sizeof(struct fm_frag));
 #ifdef DBG_F_MALLOC
58d0d1b5
 	MDBG("fm_free: freeing block alloc'ed from %s: %s(%ld)\n",
 			f->file, f->func, f->line);
1c4b78f3
 #endif
aac0c211
 	if(unlikely(f->prv_free!=NULL)) {
fbf65a94
 		LM_INFO("freeing a free fragment (%p/%p) - ignore\n",
 				f, p);
 		return;
 	}
aac0c211
 	if(qm->type==MEM_TYPE_PKG) {
 		sr_event_exec(SREV_PKG_UPDATE_STATS, 0);
 	}
51dffb0a
 #ifdef DBG_F_MALLOC
1c4b78f3
 	f->file=file;
 	f->func=func;
 	f->line=line;
 #endif
1124b8cb
 #ifdef MEM_JOIN_FREE
 	if(unlikely(cfg_get(core, core_cfg, mem_join)!=0))
 		fm_join_frag(qm, f);
 #endif /*MEM_JOIN_FREE*/
1c4b78f3
 	fm_insert_free(qm, f);
 }
 
 
c20927f7
 /**
  * \brief Main memory manager realloc function
  * 
  * Main memory manager realloc function, provide functionality for pkg_realloc
  * \param qm memory block
  * \param p reallocated memory block
  * \param size
  * \return reallocated memory block
  */
9c01c860
 #ifdef DBG_F_MALLOC
c082177a
 void* fm_realloc(struct fm_block* qm, void* p, unsigned long size,
4168f707
 					const char* file, const char* func, unsigned int line)
9c01c860
 #else
c082177a
 void* fm_realloc(struct fm_block* qm, void* p, unsigned long size)
9c01c860
 #endif
 {
 	struct fm_frag *f;
c082177a
 	unsigned long diff;
 	unsigned long orig_size;
9c01c860
 	struct fm_frag *n;
 	void *ptr;
 	
 #ifdef DBG_F_MALLOC
58d0d1b5
 	MDBG("fm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size,
9c01c860
 			file, func, line);
a0b5a680
 	if ((p)&&(p>(void*)qm->last_frag || p<(void*)qm->first_frag)){
9c01c860
 		LOG(L_CRIT, "BUG: fm_free: bad pointer %p (out of memory block!) - "
 				"aborting\n", p);
 		abort();
 	}
 #endif
 	if (size==0) {
 		if (p)
 #ifdef DBG_F_MALLOC
 			fm_free(qm, p, file, func, line);
 #else
 			fm_free(qm, p);
 #endif
 		return 0;
 	}
 	if (p==0)
 #ifdef DBG_F_MALLOC
 		return fm_malloc(qm, size, file, func, line);
 #else
 		return fm_malloc(qm, size);
 #endif
 	f=(struct fm_frag*) ((char*)p-sizeof(struct fm_frag));
 #ifdef DBG_F_MALLOC
58d0d1b5
 	MDBG("fm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
9c01c860
 			f, f->file, f->func, f->line);
 #endif
 	size=ROUNDUP(size);
 	orig_size=f->size;
 	if (f->size > size){
 		/* shrink */
 #ifdef DBG_F_MALLOC
58d0d1b5
 		MDBG("fm_realloc: shrinking from %lu to %lu\n", f->size, size);
9c01c860
 		fm_split_frag(qm, f, size, file, "frag. from fm_realloc", line);
 #else
 		fm_split_frag(qm, f, size);
 #endif
 	}else if (f->size<size){
 		/* grow */
 #ifdef DBG_F_MALLOC
58d0d1b5
 		MDBG("fm_realloc: growing from %lu to %lu\n", f->size, size);
9c01c860
 #endif
 		diff=size-f->size;
 		n=FRAG_NEXT(f);
 		if (((char*)n < (char*)qm->last_frag) && 
aac0c211
 				(n->prv_free) && ((n->size+FRAG_OVERHEAD)>=diff)){
9c01c860
 			/* detach n from the free list */
2b2569ea
 			fm_extract_free(qm, n);
 			/* join  */
9c01c860
 			f->size+=n->size+FRAG_OVERHEAD;
2b2569ea
 			qm->used+=FRAG_OVERHEAD;
aac0c211
 
9c01c860
 			/* split it if necessary */
 			if (f->size > size){
 		#ifdef DBG_F_MALLOC
 				fm_split_frag(qm, f, size, file, "fragm. from fm_realloc",
 						line);
 		#else
 				fm_split_frag(qm, f, size);
 		#endif
 			}
 		}else{
 			/* could not join => realloc */
 	#ifdef DBG_F_MALLOC
 			ptr=fm_malloc(qm, size, file, func, line);
 	#else
 			ptr=fm_malloc(qm, size);
 	#endif
9b1c8956
 			if (ptr){
9c01c860
 				/* copy, need by libssl */
 				memcpy(ptr, p, orig_size);
41a37b57
 			}
9c01c860
 	#ifdef DBG_F_MALLOC
41a37b57
 			fm_free(qm, p, file, func, line);
9c01c860
 	#else
41a37b57
 			fm_free(qm, p);
9c01c860
 	#endif
9b1c8956
 			p=ptr;
 		}
9c01c860
 	}else{
 		/* do nothing */
 #ifdef DBG_F_MALLOC
58d0d1b5
 		MDBG("fm_realloc: doing nothing, same size: %lu - %lu\n", 
c082177a
 				f->size, size);
9c01c860
 #endif
 	}
 #ifdef DBG_F_MALLOC
58d0d1b5
 	MDBG("fm_realloc: returning %p\n", p);
9c01c860
 #endif
aac0c211
 	if(qm->type==MEM_TYPE_PKG) {
 		sr_event_exec(SREV_PKG_UPDATE_STATS, 0);
 	}
9c01c860
 	return p;
 }
 
 
c20927f7
 /**
  * \brief Report internal memory manager status
  * \param qm memory block
  */
1c4b78f3
 void fm_status(struct fm_block* qm)
 {
 	struct fm_frag* f;
 	int i,j;
 	int h;
46b467df
 	int unused;
c082177a
 	unsigned long size;
e4f42ce1
 	int memlog;
c191f7b6
 	int mem_summary;
1c4b78f3
 
e4f42ce1
 	memlog=cfg_get(core, core_cfg, memlog);
c191f7b6
 	mem_summary=cfg_get(core, core_cfg, mem_summary);
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", "fm_status (%p):\n", qm);
1c4b78f3
 	if (!qm) return;
 
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", " heap size= %ld\n",
 			qm->size);
51dffb0a
 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 			" used= %lu, used+overhead=%lu, free=%lu\n",
1c4b78f3
 			qm->used, qm->real_used, qm->size-qm->real_used);
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 			" max used (+overhead)= %lu\n", qm->max_real_used);
1c4b78f3
 #endif
c191f7b6
 
 	if (mem_summary & 16) return;
 
0eb1315e
 	/*
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", "dumping all fragments:\n");
1c4b78f3
 	for (f=qm->first_frag, i=0;((char*)f<(char*)qm->last_frag) && (i<10);
0eb1315e
 			f=FRAG_NEXT(f), i++){
0b79ce29
 		LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 				"    %3d. %c  address=%x  size=%d\n", i,
1c4b78f3
 				(f->u.reserved)?'a':'N',
 				(char*)f+sizeof(struct fm_frag), f->size);
 #ifdef DBG_F_MALLOC
0b79ce29
 		LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 				"            %s from %s: %s(%d)\n",
1c4b78f3
 				(f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line);
 #endif
 	}
0eb1315e
 */
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", "dumping free list:\n");
bf0fab3f
 	for(h=0,i=0,size=0;h<F_HASH_SIZE;h++){
46b467df
 		unused=0;
 		for (f=qm->free_hash[h].first,j=0; f;
 				size+=f->size,f=f->u.nxt_free,i++,j++){
 			if (!FRAG_WAS_USED(f)){
 				unused++;
3e927c43
 #ifdef DBG_F_MALLOC
0b79ce29
 				LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 							"unused fragm.: hash = %3d, fragment %p,"
f48afcf2
 							" address %p size %lu, created from %s: %s(%ld)\n",
46b467df
 						    h, f, (char*)f+sizeof(struct fm_frag), f->size,
 							f->file, f->func, f->line);
 #endif
 			};
 		}
0b79ce29
 		if (j) LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 							"hash = %3d fragments no.: %5d, unused: %5d\n\t\t"
c082177a
 							" bucket size: %9lu - %9lu (first %9lu)\n",
 							h, j, unused, UN_HASH(h),
 						((h<=F_MALLOC_OPTIMIZE/ROUNDTO)?1:2)* UN_HASH(h),
46b467df
 							qm->free_hash[h].first->size
bf0fab3f
 				);
46b467df
 		if (j!=qm->free_hash[h].no){
 			LOG(L_CRIT, "BUG: fm_status: different free frag. count: %d!=%ld"
 					" for hash %3d\n", j, qm->free_hash[h].no, h);
 		}
0eb1315e
 		/*
 		{
0b79ce29
 			LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 					"   %5d.[%3d:%3d] %c  address=%x  size=%d(%x)\n",
1c4b78f3
 					i, h, j,
 					(f->u.reserved)?'a':'N',
 					(char*)f+sizeof(struct fm_frag), f->size, f->size);
 #ifdef DBG_F_MALLOC
 			DBG("            %s from %s: %s(%d)\n", 
 				(f->u.reserved)?"freed":"alloc'd", f->file, f->func, f->line);
 #endif
 		}
0eb1315e
 	*/
1c4b78f3
 	}
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 			"TOTAL: %6d free fragments = %6lu free bytes\n", i, size);
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 			"-----------------------------\n");
1c4b78f3
 }
 
 
c20927f7
 /**
  * \brief Fills a malloc info structure with info about the block
  *
  * Fills a malloc info structure with info about the block, if a
  * parameter is not supported, it will be filled with 0
  * \param qm memory block
f157bd9c
  * \param info memory information
c20927f7
  */
473f6f2e
 void fm_info(struct fm_block* qm, struct mem_info* info)
51dffb0a
 {
 	memset(info,0, sizeof(*info));
 	info->total_size=qm->size;
 	info->min_frag=MIN_FRAG_SIZE;
 	info->free=qm->size-qm->real_used;
 	info->used=qm->used;
 	info->real_used=qm->real_used;
 	info->max_used=qm->max_real_used;
aac0c211
 	info->total_frags=qm->ffrags;
51dffb0a
 }
 
1c4b78f3
 
c20927f7
 /**
  * \brief Helper function for available memory report
  * \param qm memory block
  * \return Returns how much free memory is available, on error (not compiled
  * with bookkeeping code) returns (unsigned long)(-1)
  */
8799e028
 unsigned long fm_available(struct fm_block* qm)
 {
 	return qm->size-qm->real_used;
 }
 
ed20ee1d
 
 #ifdef DBG_F_MALLOC
 
 typedef struct _mem_counter{
 	const char *file;
 	const char *func;
 	unsigned long line;
 	
 	unsigned long size;
 	int count;
 	
 	struct _mem_counter *next;
 } mem_counter;
 
 static mem_counter* get_mem_counter(mem_counter **root,struct fm_frag* f)
 {
 	mem_counter *x;
 	
 	if (!*root) goto make_new;
 	for(x=*root;x;x=x->next)
 		if (x->file == f->file && x->func == f->func && x->line == f->line)
 			return x;
 make_new:	
 	x = malloc(sizeof(mem_counter));
 	x->file = f->file;
 	x->func = f->func;
 	x->line = f->line;
 	x->count = 0;
 	x->size = 0;
 	x->next = *root;
 	*root = x;
 	return x;
 }
 
 
c20927f7
 /**
  * \brief Debugging helper, summary and logs all allocated memory blocks
  * \param qm memory block
  */
ed20ee1d
 void fm_sums(struct fm_block* qm)
 {
 	struct fm_frag* f;
aac0c211
 	int i;
e4f42ce1
 	int memlog;
ed20ee1d
 	mem_counter *root,*x;
 	
 	root=0;
 	if (!qm) return;
 
e4f42ce1
 	memlog=cfg_get(core, core_cfg, memlog);
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 			"summarizing all alloc'ed. fragments:\n");
ed20ee1d
 	
 	for (f=qm->first_frag, i=0; (char*)f<(char*)qm->last_frag;
 			f=FRAG_NEXT(f), i++){
aac0c211
 		if (f->prv_free==0){
 			x = get_mem_counter(&root,f);
 			x->count++;
 			x->size+=f->size;
ed20ee1d
 		}
 	}
 	x = root;
 	while(x){
0b79ce29
 		LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 				" count=%6d size=%10lu bytes from %s: %s(%ld)\n",
ed20ee1d
 			x->count,x->size,
 			x->file, x->func, x->line
 			);
 		root = x->next;
 		free(x);
 		x = root;
 	}
0b79ce29
 	LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
 			"-----------------------------\n");
ed20ee1d
 }
 #endif /* DBG_F_MALLOC */
 
 
 
1c4b78f3
 #endif