mem/q_malloc.c
47f59303
 /*
84d8e165
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
e4f42ce1
  * This file is part of sip-router, 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.
6fa79282
  */
47f59303
 
e09e5e44
 /*
  * History:
  * --------
  *  ????-??-??  created by andrei
  *  2003-04-14  more debugging added in DBG_QM_MALLOC mode (andrei)
f22b996b
  *  2003-06-29  added qm_realloc (andrei)
84d8e165
  *  2004-07-19  fragments book keeping code and support for 64 bits
46b467df
  *               memory blocks (64 bits machine & size>=2^32) (andrei)
  *              GET_HASH s/</<=/ (avoids waste of 1 hash cell) (andrei)
c082177a
  *  2004-11-10  support for > 4Gb mem., switched to long (andrei)
51dffb0a
  *  2005-03-02  added qm_info() (andrei)
fb9d6e50
  *  2005-12-12  fixed realloc shrink real_used & used accounting;
  *              fixed initial size (andrei)
9b1c8956
  *  2006-02-03  fixed realloc out of mem. free bug (andrei)
58d0d1b5
  *  2006-04-07  s/DBG/MDBG (andrei)
8799e028
  *  2007-02-23  added fm_available() (andrei)
ed20ee1d
  *  2009-09-28  added fm_sums() (patch from Dragos Vingarzan)
e09e5e44
  */
6fa79282
 
47f59303
 /**
  * \file
  * \brief Simple & fast malloc library
  * \ingroup mem
  */
 
7dd0b342
 
f92113d8
 #if !defined(q_malloc) && !(defined F_MALLOC)
6fa79282
 #define q_malloc
 
f48312e2
 #include <stdlib.h>
 #include <string.h>
 
6fa79282
 #include "q_malloc.h"
dda9dab1
 #include "../dprint.h"
843b2927
 #include "../globals.h"
58d0d1b5
 #include "memdbg.h"
e4f42ce1
 #include "../cfg/cfg.h" /* memlog */
ed6f5bca
 #ifdef MALLOC_STATS
 #include "../events.h"
 #endif
6fa79282
 
 
dda9dab1
 /*useful macros*/
6fa79282
 #define FRAG_END(f)  \
dda9dab1
 	((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \
 	   (f)->size))
6fa79282
 
 #define FRAG_NEXT(f) \
dda9dab1
 	((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \
 	   sizeof(struct qm_frag_end)))
6fa79282
 			
 #define FRAG_PREV(f) \
dda9dab1
 	( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \
 	((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \
 	   sizeof(struct qm_frag) ) )
6fa79282
 
2c65bd8b
 #define PREV_FRAG_END(f) \
dda9dab1
 	((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))
2c65bd8b
 
eca7f442
 
 #define FRAG_OVERHEAD	(sizeof(struct qm_frag)+sizeof(struct qm_frag_end))
 
 
7b5c6965
 #define ROUNDTO_MASK	(~((unsigned long)ROUNDTO-1))
 #define ROUNDUP(s)		(((s)+(ROUNDTO-1))&ROUNDTO_MASK)
 #define ROUNDDOWN(s)	((s)&ROUNDTO_MASK)
 
eca7f442
 
 
 	/* finds the hash value for s, s=ROUNDTO multiple*/
c082177a
 #define GET_HASH(s)   ( ((unsigned long)(s)<=QM_MALLOC_OPTIMIZE)?\
 							(unsigned long)(s)/ROUNDTO: \
 							QM_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \
 								QM_MALLOC_OPTIMIZE_FACTOR+1 )
 
 #define UN_HASH(h)	( ((unsigned long)(h)<=(QM_MALLOC_OPTIMIZE/ROUNDTO))?\
 							(unsigned long)(h)*ROUNDTO: \
 							1UL<<((h)-QM_MALLOC_OPTIMIZE/ROUNDTO+\
 								QM_MALLOC_OPTIMIZE_FACTOR-1)\
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
 
eca7f442
 
 /* computes hash number for big buckets*/
c082177a
 inline static unsigned long big_hash_idx(unsigned long s)
eca7f442
 {
 	int idx;
 	/* s is rounded => s = k*2^n (ROUNDTO=2^n) 
 	 * index= i such that 2^i > s >= 2^(i-1)
 	 *
 	 * => index = number of the first non null bit in s*/
46b467df
 	idx=sizeof(long)*8-1;
4d080f49
 	for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--);
eca7f442
 	return idx;
 }
 
 
2c65bd8b
 #ifdef DBG_QM_MALLOC
 #define ST_CHECK_PATTERN   0xf0f0f0f0
 #define END_CHECK_PATTERN1 0xc0c0c0c0
 #define END_CHECK_PATTERN2 0xabcdefed
6fa79282
 
 
2c65bd8b
 static  void qm_debug_frag(struct qm_block* qm, struct qm_frag* f)
 {
 	if (f->check!=ST_CHECK_PATTERN){
e09e5e44
 		LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
 				"beginning overwritten(%lx)!\n",
 				f, (char*)f+sizeof(struct qm_frag),
 				f->check);
2c65bd8b
 		qm_status(qm);
 		abort();
 	};
 	if ((FRAG_END(f)->check1!=END_CHECK_PATTERN1)||
 		(FRAG_END(f)->check2!=END_CHECK_PATTERN2)){
e09e5e44
 		LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)"
 					" end overwritten(%lx, %lx)!\n",
 				f, (char*)f+sizeof(struct qm_frag), 
 				FRAG_END(f)->check1, FRAG_END(f)->check2);
2c65bd8b
 		qm_status(qm);
 		abort();
 	}
 	if ((f>qm->first_frag)&&
 			((PREV_FRAG_END(f)->check1!=END_CHECK_PATTERN1) ||
 				(PREV_FRAG_END(f)->check2!=END_CHECK_PATTERN2) ) ){
e09e5e44
 		LOG(L_CRIT, "BUG: qm_*: prev. fragm. tail overwritten(%lx, %lx)[%p:%p]!"
 					"\n",
 				PREV_FRAG_END(f)->check1, PREV_FRAG_END(f)->check2, f,
 				(char*)f+sizeof(struct qm_frag));
2c65bd8b
 		qm_status(qm);
 		abort();
 	}
 }
 #endif
 
6fa79282
 
 
eca7f442
 static inline void qm_insert_free(struct qm_block* qm, struct qm_frag* frag)
 {
 	struct qm_frag* f;
 	struct qm_frag* prev;
 	int hash;
 	
 	hash=GET_HASH(frag->size);
 	for(f=qm->free_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head);
 			f=f->u.nxt_free){
 		if (frag->size <= f->size) break;
 	}
 	/*insert it here*/
 	prev=FRAG_END(f)->prev_free;
 	prev->u.nxt_free=frag;
 	FRAG_END(frag)->prev_free=prev;
 	frag->u.nxt_free=f;
 	FRAG_END(f)->prev_free=frag;
46b467df
 	qm->free_hash[hash].no++;
eca7f442
 }
 
 
 
6fa79282
 /* init malloc and return a qm_block*/
c082177a
 struct qm_block* qm_malloc_init(char* address, unsigned long size)
6fa79282
 {
 	char* start;
 	char* end;
 	struct qm_block* qm;
85f1f3ee
 	unsigned long init_overhead;
eca7f442
 	int h;
6fa79282
 	
 	/* make address and size multiple of 8*/
85f1f3ee
 	start=(char*)ROUNDUP((unsigned long) address);
c082177a
 	DBG("qm_malloc_init: QM_OPTIMIZE=%lu, /ROUNDTO=%lu\n",
eca7f442
 			QM_MALLOC_OPTIMIZE, QM_MALLOC_OPTIMIZE/ROUNDTO);
c082177a
 	DBG("qm_malloc_init: QM_HASH_SIZE=%lu, qm_block size=%lu\n",
 			QM_HASH_SIZE, (long)sizeof(struct qm_block));
 	DBG("qm_malloc_init(%p, %lu), start=%p\n", address, size, start);
6fa79282
 	if (size<start-address) return 0;
 	size-=(start-address);
eca7f442
 	if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
 	size=ROUNDDOWN(size);
6fa79282
 	
b2dec9c6
 	init_overhead=ROUNDUP(sizeof(struct qm_block))+sizeof(struct qm_frag)+
6fa79282
 		sizeof(struct qm_frag_end);
c082177a
 	DBG("qm_malloc_init: size= %lu, init_overhead=%lu\n", size, init_overhead);
eca7f442
 	
6fa79282
 	if (size < init_overhead)
 	{
 		/* not enough mem to create our control structures !!!*/
 		return 0;
 	}
 	end=start+size;
 	qm=(struct qm_block*)start;
 	memset(qm, 0, sizeof(struct qm_block));
02690ea6
 	qm->size=size;
6fa79282
 	qm->real_used=init_overhead;
02690ea6
 	qm->max_real_used=qm->real_used;
fb9d6e50
 	size-=init_overhead;
6fa79282
 	
b2dec9c6
 	qm->first_frag=(struct qm_frag*)(start+ROUNDUP(sizeof(struct qm_block)));
6fa79282
 	qm->last_frag_end=(struct qm_frag_end*)(end-sizeof(struct qm_frag_end));
 	/* init initial fragment*/
 	qm->first_frag->size=size;
 	qm->last_frag_end->size=size;
eca7f442
 	
2c65bd8b
 #ifdef DBG_QM_MALLOC
 	qm->first_frag->check=ST_CHECK_PATTERN;
 	qm->last_frag_end->check1=END_CHECK_PATTERN1;
 	qm->last_frag_end->check2=END_CHECK_PATTERN2;
 #endif
eca7f442
 	/* init free_hash* */
 	for (h=0; h<QM_HASH_SIZE;h++){
 		qm->free_hash[h].head.u.nxt_free=&(qm->free_hash[h].head);
 		qm->free_hash[h].tail.prev_free=&(qm->free_hash[h].head);
 		qm->free_hash[h].head.size=0;
 		qm->free_hash[h].tail.size=0;
 	}
 	
 	/* link initial fragment into the free list*/
 	
 	qm_insert_free(qm, qm->first_frag);
 	
 	/*qm->first_frag->u.nxt_free=&(qm->free_lst);
 	  qm->last_frag_end->prev_free=&(qm->free_lst);
 	*/
6fa79282
 	
 	
 	return qm;
 }
 
 
 
 static inline void qm_detach_free(struct qm_block* qm, struct qm_frag* frag)
 {
 	struct qm_frag *prev;
 	struct qm_frag *next;
 	
 	prev=FRAG_END(frag)->prev_free;
 	next=frag->u.nxt_free;
 	prev->u.nxt_free=next;
 	FRAG_END(next)->prev_free=prev;
 	
 }
 
 
2ffa396a
 
e09e5e44
 #ifdef DBG_QM_MALLOC
 static inline struct qm_frag* qm_find_free(struct qm_block* qm, 
c082177a
 											unsigned long size,
46b467df
 											int *h,
e09e5e44
 											unsigned int *count)
 #else
eca7f442
 static inline struct qm_frag* qm_find_free(struct qm_block* qm, 
c082177a
 											unsigned long size,
46b467df
 											int* h)
e09e5e44
 #endif
eca7f442
 {
 	int hash;
 	struct qm_frag* f;
 
 	for (hash=GET_HASH(size); hash<QM_HASH_SIZE; hash++){
 		for (f=qm->free_hash[hash].head.u.nxt_free; 
 					f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){
e09e5e44
 #ifdef DBG_QM_MALLOC
 			*count+=1; /* *count++ generates a warning with gcc 2.9* -Wall */
 #endif
46b467df
 			if (f->size>=size){ *h=hash; return f; }
eca7f442
 		}
 	/*try in a bigger bucket*/
 	}
 	/* not found */
 	return 0;
 }
 
 
f22b996b
 /* returns 0 on success, -1 on error;
9c01c860
  * new_size < size & rounded-up already!*/
f22b996b
 static inline
 #ifdef DBG_QM_MALLOC
c082177a
 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size,
4168f707
 				const char* file, const char* func, unsigned int line)
f22b996b
 #else
c082177a
 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size)
f22b996b
 #endif
 {
c082177a
 	unsigned long rest;
f22b996b
 	struct qm_frag* n;
 	struct qm_frag_end* end;
 	
 	rest=f->size-new_size;
46b467df
 #ifdef MEM_FRAG_AVOIDANCE
 	if ((rest> (FRAG_OVERHEAD+QM_MALLOC_OPTIMIZE))||
 		(rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/
 #else
f22b996b
 	if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
46b467df
 #endif
f22b996b
 		f->size=new_size;
 		/*split the fragment*/
 		end=FRAG_END(f);
 		end->size=new_size;
 		n=(struct qm_frag*)((char*)end+sizeof(struct qm_frag_end));
 		n->size=rest-FRAG_OVERHEAD;
 		FRAG_END(n)->size=n->size;
46b467df
 		FRAG_CLEAR_USED(n); /* never used */
f22b996b
 		qm->real_used+=FRAG_OVERHEAD;
 #ifdef DBG_QM_MALLOC
 		end->check1=END_CHECK_PATTERN1;
 		end->check2=END_CHECK_PATTERN2;
 		/* frag created by malloc, mark it*/
 		n->file=file;
 		n->func=func;
 		n->line=line;
 		n->check=ST_CHECK_PATTERN;
 #endif
 		/* reinsert n in free list*/
 		qm_insert_free(qm, n);
 		return 0;
 	}else{
 			/* we cannot split this fragment any more */
 		return -1;
 	}
 }
 
 
eca7f442
 
b4d048d4
 #ifdef DBG_QM_MALLOC
c082177a
 void* qm_malloc(struct qm_block* qm, unsigned long size,
4168f707
 					const char* file, const char* func, unsigned int line)
b4d048d4
 #else
c082177a
 void* qm_malloc(struct qm_block* qm, unsigned long size)
b4d048d4
 #endif
6fa79282
 {
 	struct qm_frag* f;
46b467df
 	int hash;
6fa79282
 	
b4d048d4
 #ifdef DBG_QM_MALLOC
dda9dab1
 	unsigned int list_cntr;
 
 	list_cntr = 0;
58d0d1b5
 	MDBG("qm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func,
b4d048d4
 			line);
 #endif
6fa79282
 	/*size must be a multiple of 8*/
eca7f442
 	size=ROUNDUP(size);
6fa79282
 	if (size>(qm->size-qm->real_used)) return 0;
eca7f442
 
b2dec9c6
 	/*search for a suitable free frag*/
e09e5e44
 #ifdef DBG_QM_MALLOC
46b467df
 	if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){
e09e5e44
 #else
46b467df
 	if ((f=qm_find_free(qm, size, &hash))!=0){
e09e5e44
 #endif
eca7f442
 		/* we found it!*/
 		/*detach it from the free list*/
2c65bd8b
 #ifdef DBG_QM_MALLOC
 			qm_debug_frag(qm, f);
 #endif
eca7f442
 		qm_detach_free(qm, f);
 		/*mark it as "busy"*/
 		f->u.is_free=0;
46b467df
 		qm->free_hash[hash].no--;
f22b996b
 		/* we ignore split return */
b4d048d4
 #ifdef DBG_QM_MALLOC
f22b996b
 		split_frag(qm, f, size, file, "fragm. from qm_malloc", line);
 #else
 		split_frag(qm, f, size);
b4d048d4
 #endif
eca7f442
 		qm->real_used+=f->size;
 		qm->used+=f->size;
 		if (qm->max_real_used<qm->real_used)
 			qm->max_real_used=qm->real_used;
ed6f5bca
 #ifdef MALLOC_STATS
 		sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
 		sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
 #endif
b4d048d4
 #ifdef DBG_QM_MALLOC
eca7f442
 		f->file=file;
 		f->func=func;
 		f->line=line;
 		f->check=ST_CHECK_PATTERN;
2c65bd8b
 		/*  FRAG_END(f)->check1=END_CHECK_PATTERN1;
 			FRAG_END(f)->check2=END_CHECK_PATTERN2;*/
58d0d1b5
 		MDBG("qm_malloc(%p, %lu) returns address %p frag. %p (size=%lu) on %d"
c082177a
 				" -th hit\n",
e09e5e44
 			 qm, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr );
b4d048d4
 #endif
eca7f442
 		return (char*)f+sizeof(struct qm_frag);
6fa79282
 	}
 	return 0;
 }
 
 
 
b4d048d4
 #ifdef DBG_QM_MALLOC
4168f707
 void qm_free(struct qm_block* qm, void* p, const char* file, const char* func, 
b4d048d4
 				unsigned int line)
 #else
6fa79282
 void qm_free(struct qm_block* qm, void* p)
b4d048d4
 #endif
6fa79282
 {
 	struct qm_frag* f;
9a694681
 	unsigned long size;
d89420df
 #ifdef MEM_JOIN_FREE
e4a75063
 	struct qm_frag* next;
 	struct qm_frag* prev;
d89420df
 #endif /* MEM_JOIN_FREE*/
6fa79282
 
b4d048d4
 #ifdef DBG_QM_MALLOC
58d0d1b5
 	MDBG("qm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line);
927a8a1a
 #endif
 
 	if (p==0) {
41fa8653
 #ifdef DBG_QM_MALLOC
 		LOG(L_WARN, "WARNING:qm_free: free(0) called from %s: %s(%d)\n", file, func, line);
 #else
927a8a1a
 		LOG(L_WARN, "WARNING:qm_free: free(0) called\n");
41fa8653
 #endif
927a8a1a
 		return;
 	}
 
 #ifdef DBG_QM_MALLOC
b4d048d4
 	if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){
ffb8754f
 		LOG(L_CRIT, "BUG: qm_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;
b4d048d4
 	}
 #endif
927a8a1a
 
6fa79282
 	f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
927a8a1a
 
b4d048d4
 #ifdef DBG_QM_MALLOC
2c65bd8b
 	qm_debug_frag(qm, f);
b4d048d4
 	if (f->u.is_free){
ffb8754f
 		LOG(L_CRIT, "BUG: qm_free: freeing already freed pointer (%p),"
 				" called from %s: %s(%d), first free %s: %s(%ld) - aborting\n",
 				p, file, func, line, f->file, f->func, f->line);
 		if(likely(cfg_get(core, core_cfg, mem_safety)==0))
 			abort();
927a8a1a
 		else return;
b4d048d4
 	}
58d0d1b5
 	MDBG("qm_free: freeing frag. %p alloc'ed from %s: %s(%ld)\n",
e09e5e44
 			f, f->file, f->func, f->line);
b4d048d4
 #endif
6fa79282
 	size=f->size;
 	qm->used-=size;
 	qm->real_used-=size;
ed6f5bca
 #ifdef MALLOC_STATS
 	sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
 	sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
 #endif
eca7f442
 
d89420df
 #ifdef MEM_JOIN_FREE
 	if(unlikely(cfg_get(core, core_cfg, mem_join)!=0)) {
 		next=prev=0;
 		/* mark this fragment as used (might fall into the middle of joined frags)
 		  to give us an extra chance of detecting a double free call (if the joined
 		  fragment has not yet been reused) */
 		f->u.nxt_free=(void*)0x1L; /* bogus value, just to mark it as free */
 		/* join packets if possible*/
 		next=FRAG_NEXT(f);
cceb39f9
 		if (((char*)next < (char*)qm->last_frag_end) && (next->u.is_free)){
 			/* join next packet */
2ffa396a
 #ifdef DBG_QM_MALLOC
d89420df
 			qm_debug_frag(qm, next);
2ffa396a
 #endif
d89420df
 			qm_detach_free(qm, next);
 			size+=next->size+FRAG_OVERHEAD;
 			qm->real_used-=FRAG_OVERHEAD;
 			qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */
 		}
6fa79282
 	
d89420df
 		if (f > qm->first_frag){
 			prev=FRAG_PREV(f);
 			/*	(struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f-
6fa79282
 								sizeof(struct qm_frag_end))->size);*/
cceb39f9
 			if (prev->u.is_free){
 				/* join prev packet */
2c65bd8b
 #ifdef DBG_QM_MALLOC
cceb39f9
 				qm_debug_frag(qm, prev);
2c65bd8b
 #endif
d89420df
 				qm_detach_free(qm, prev);
 				size+=prev->size+FRAG_OVERHEAD;
 				qm->real_used-=FRAG_OVERHEAD;
cceb39f9
 				qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */
d89420df
 				f=prev;
 			}
6fa79282
 		}
d89420df
 		f->size=size;
 		FRAG_END(f)->size=f->size;
 	} /* if cfg_core->mem_join */
 #endif /* MEM_JOIN_FREE*/
b4d048d4
 #ifdef DBG_QM_MALLOC
 	f->file=file;
 	f->func=func;
 	f->line=line;
 #endif
6fa79282
 	qm_insert_free(qm, f);
 }
 
 
 
f22b996b
 #ifdef DBG_QM_MALLOC
c082177a
 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size,
4168f707
 					const char* file, const char* func, unsigned int line)
f22b996b
 #else
c082177a
 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size)
f22b996b
 #endif
 {
 	struct qm_frag* f;
c082177a
 	unsigned long diff;
 	unsigned long orig_size;
f22b996b
 	struct qm_frag* n;
 	void* ptr;
 	
 	
 #ifdef DBG_QM_MALLOC
58d0d1b5
 	MDBG("qm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size,
f22b996b
 			file, func, line);
a0b5a680
 	if ((p)&&(p>(void*)qm->last_frag_end || p<(void*)qm->first_frag)){
f22b996b
 		LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!) - "
 				"aborting\n", p);
 		abort();
 	}
 #endif
 	
 	if (size==0) {
 		if (p)
 #ifdef DBG_QM_MALLOC
 			qm_free(qm, p, file, func, line);
 #else
 			qm_free(qm, p);
 #endif
 		return 0;
 	}
 	if (p==0)
 #ifdef DBG_QM_MALLOC
 		return qm_malloc(qm, size, file, func, line);
 #else
 		return qm_malloc(qm, size);
 #endif
 	f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
 #ifdef DBG_QM_MALLOC
 	qm_debug_frag(qm, f);
58d0d1b5
 	MDBG("qm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
f22b996b
 			f, f->file, f->func, f->line);
 	if (f->u.is_free){
 		LOG(L_CRIT, "BUG:qm_realloc: trying to realloc an already freed "
 				"pointer %p , fragment %p -- aborting\n", p, f);
 		abort();
 	}
 #endif
 	/* find first acceptable size */
 	size=ROUNDUP(size);
 	if (f->size > size){
fb9d6e50
 		orig_size=f->size;
f22b996b
 		/* shrink */
 #ifdef DBG_QM_MALLOC
58d0d1b5
 		MDBG("qm_realloc: shrinking from %lu to %lu\n", f->size, size);
f22b996b
 		if(split_frag(qm, f, size, file, "fragm. from qm_realloc", line)!=0){
58d0d1b5
 		MDBG("qm_realloc : shrinked successful\n");
f22b996b
 #else
 		if(split_frag(qm, f, size)!=0){
 #endif
75c1e9a7
 			/* update used sizes: freed the splited frag */
 			/* split frag already adds FRAG_OVERHEAD for the newly created
 			   free frag, so here we only need orig_size-f->size for real used
 			 */
 			qm->real_used-=(orig_size-f->size);
fb9d6e50
 			qm->used-=(orig_size-f->size);
ed6f5bca
 #ifdef MALLOC_STATS
 			sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
 			sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
 #endif
f22b996b
 		}
 		
 	}else if (f->size < size){
 		/* grow */
 #ifdef DBG_QM_MALLOC
58d0d1b5
 		MDBG("qm_realloc: growing from %lu to %lu\n", f->size, size);
f22b996b
 #endif
 			orig_size=f->size;
 			diff=size-f->size;
 			n=FRAG_NEXT(f);
 			if (((char*)n < (char*)qm->last_frag_end) && 
 					(n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
 				/* join  */
 				qm_detach_free(qm, n);
46b467df
 				qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/
f22b996b
 				f->size+=n->size+FRAG_OVERHEAD;
 				qm->real_used-=FRAG_OVERHEAD;
 				FRAG_END(f)->size=f->size;
 				/* end checks should be ok */
 				/* split it if necessary */
 				if (f->size > size ){
 	#ifdef DBG_QM_MALLOC
 					split_frag(qm, f, size, file, "fragm. from qm_realloc",
 										line);
 	#else
 					split_frag(qm, f, size);
 	#endif
 				}
 				qm->real_used+=(f->size-orig_size);
 				qm->used+=(f->size-orig_size);
ed6f5bca
 #ifdef MALLOC_STATS
 				sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
 				sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
 #endif
f22b996b
 			}else{
 				/* could not join => realloc */
 	#ifdef DBG_QM_MALLOC
 				ptr=qm_malloc(qm, size, file, func, line);
 	#else
 				ptr=qm_malloc(qm, size);
 	#endif
9b1c8956
 				if (ptr){
f22b996b
 					/* copy, need by libssl */
 					memcpy(ptr, p, orig_size);
 	#ifdef DBG_QM_MALLOC
 					qm_free(qm, p, file, func, line);
 	#else
 					qm_free(qm, p);
 	#endif
9b1c8956
 				}
f22b996b
 				p=ptr;
 			}
 	}else{
 		/* do nothing */
 #ifdef DBG_QM_MALLOC
58d0d1b5
 		MDBG("qm_realloc: doing nothing, same size: %lu - %lu\n",
c082177a
 				f->size, size);
f22b996b
 #endif
 	}
 #ifdef DBG_QM_MALLOC
58d0d1b5
 	MDBG("qm_realloc: returning %p\n", p);
f22b996b
 #endif
 	return p;
 }
 
 
e5931954
 void qm_check(struct qm_block* qm)
 {
 	struct qm_frag* f;
 	long fcount = 0;
e4f42ce1
 	int memlog;
e5931954
 	
e4f42ce1
 	memlog=cfg_get(core, core_cfg, memlog);
e5931954
 	LOG(memlog, "DEBUG: qm_check()\n");
 	f = qm->first_frag;
 	while ((char*)f < (char*)qm->last_frag_end) {
 		fcount++;
 		/* check struct qm_frag */
 #ifdef DBG_QM_MALLOC
 		if (f->check!=ST_CHECK_PATTERN){
 			LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
 					"beginning overwritten(%lx)!\n",
 					f, (char*)f + sizeof(struct qm_frag),
 					f->check);
 			qm_status(qm);
 			abort();
 		};
 #endif
 		if (f + sizeof(struct qm_frag) + f->size + sizeof(struct qm_frag_end) > qm->first_frag + qm->size) {
 			LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
 				"bad size: %lu (frag end: %p > end of block: %p)\n",
 				f, (char*)f + sizeof(struct qm_frag) + sizeof(struct qm_frag_end), f->size,
 				f + sizeof(struct qm_frag) + f->size, qm->first_frag + qm->size);
 			qm_status(qm);
 			abort();
 		}
 		/* check struct qm_frag_end */
 		if (FRAG_END(f)->size != f->size) {
 			LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
 				"size in qm_frag and qm_frag_end does not match: frag->size=%lu, frag_end->size=%lu)\n",
 				f, (char*)f + sizeof(struct qm_frag),
 				f->size, FRAG_END(f)->size);
 			qm_status(qm);
 			abort();
 		}
 #ifdef DBG_QM_MALLOC
 		if ((FRAG_END(f)->check1 != END_CHECK_PATTERN1) ||
 			(FRAG_END(f)->check2 != END_CHECK_PATTERN2)) {
 			LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)"
 						" end overwritten(%lx, %lx)!\n",
 					f, (char*)f + sizeof(struct qm_frag), 
 					FRAG_END(f)->check1, FRAG_END(f)->check2);
 			qm_status(qm);
 			abort();
 		}
 #endif
 		f = FRAG_NEXT(f);
 	}
f22b996b
 
e5931954
 	LOG(memlog, "DEBUG: qm_check: %lu fragments OK\n", fcount);
 }
f22b996b
 
6fa79282
 void qm_status(struct qm_block* qm)
 {
 	struct qm_frag* f;
eca7f442
 	int i,j;
 	int h;
46b467df
 	int unused;
e4f42ce1
 	int memlog;
c191f7b6
 	int mem_summary;
6fa79282
 
e4f42ce1
 	memlog=cfg_get(core, core_cfg, memlog);
c191f7b6
 	mem_summary=cfg_get(core, core_cfg, mem_summary);
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "(%p):\n", qm);
dda9dab1
 	if (!qm) return;
 
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "heap size= %lu\n",
 			qm->size);
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 			"used= %lu, used+overhead=%lu, free=%lu\n",
6fa79282
 			qm->used, qm->real_used, qm->size-qm->real_used);
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 			"max used (+overhead)= %lu\n", qm->max_real_used);
6fa79282
 	
c191f7b6
 	if (mem_summary & 16) return;
 
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 			"dumping all alloc'ed. fragments:\n");
6fa79282
 	for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;f=FRAG_NEXT(f)
b4d048d4
 			,i++){
b2dec9c6
 		if (! f->u.is_free){
e4f42ce1
 			LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 					"   %3d. %c  address=%p frag=%p size=%lu used=%d\n",
 				i,
9dfa5dc4
 				(f->u.is_free)?'a':'N',
46b467df
 				(char*)f+sizeof(struct qm_frag), f, f->size, FRAG_WAS_USED(f));
b4d048d4
 #ifdef DBG_QM_MALLOC
e4f42ce1
 			LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 					"          %s from %s: %s(%ld)\n",
b4d048d4
 				(f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line);
e4f42ce1
 			LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 					"         start check=%lx, end check= %lx, %lx\n",
2c65bd8b
 				f->check, FRAG_END(f)->check1, FRAG_END(f)->check2);
b4d048d4
 #endif
b2dec9c6
 		}
b4d048d4
 	}
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 			"dumping free list stats :\n");
eca7f442
 	for(h=0,i=0;h<QM_HASH_SIZE;h++){
46b467df
 		unused=0;
eca7f442
 		for (f=qm->free_hash[h].head.u.nxt_free,j=0; 
46b467df
 				f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){
 				if (!FRAG_WAS_USED(f)){
 					unused++;
 #ifdef DBG_QM_MALLOC
e4f42ce1
 					LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 						"unused fragm.: hash = %3d, fragment %p,"
46b467df
 						" address %p size %lu, created from %s: %s(%lu)\n",
 					    h, f, (char*)f+sizeof(struct qm_frag), f->size,
 						f->file, f->func, f->line);
 #endif
 				}
 		}
 
e4f42ce1
 		if (j) LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 				"hash= %3d. fragments no.: %5d, unused: %5d\n"
c082177a
 					"\t\t bucket size: %9lu - %9ld (first %9lu)\n",
 					h, j, unused, UN_HASH(h),
 					((h<=QM_MALLOC_OPTIMIZE/ROUNDTO)?1:2)*UN_HASH(h),
46b467df
 					qm->free_hash[h].head.u.nxt_free->size
 				);
 		if (j!=qm->free_hash[h].no){
 			LOG(L_CRIT, "BUG: qm_status: different free frag. count: %d!=%lu"
 				" for hash %3d\n", j, qm->free_hash[h].no, h);
 		}
 
b4d048d4
 	}
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
 			"-----------------------------\n");
6fa79282
 }
 
 
51dffb0a
 /* fills a malloc info structure with info about the block
  * if a parameter is not supported, it will be filled with 0 */
473f6f2e
 void qm_info(struct qm_block* qm, struct mem_info* info)
51dffb0a
 {
 	int r;
 	long total_frags;
 	
 	total_frags=0;
 	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;
 	for(r=0;r<QM_HASH_SIZE; r++){
 		total_frags+=qm->free_hash[r].no;
 	}
 	info->total_frags=total_frags;
 }
 
6fa79282
 
8799e028
 /* returns how much free memory is available
  * it never returns an error (unlike fm_available) */
 unsigned long qm_available(struct qm_block* qm)
 {
 	return qm->size-qm->real_used;
 }
 
6fa79282
 
ed20ee1d
 
 #ifdef DBG_QM_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 qm_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;
 }
 
 
 
 void qm_sums(struct qm_block* qm)
 {
 	struct qm_frag* f;
 	int i;
 	mem_counter *root, *x;
e4f42ce1
 	int memlog;
ed20ee1d
 	
 	root=0;
 	if (!qm) return;
 	
e4f42ce1
 	memlog=cfg_get(core, core_cfg, memlog);
 	LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
 			"summarizing all alloc'ed. fragments:\n");
ed20ee1d
 	
 	for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;
 			f=FRAG_NEXT(f),i++){
 		if (! f->u.is_free){
 			x = get_mem_counter(&root,f);
 			x->count++;
 			x->size+=f->size;
 		}
 	}
 	x = root;
 	while(x){
e4f42ce1
 		LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
 				" 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;
 	}
e4f42ce1
 	LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
 			"-----------------------------\n");
ed20ee1d
 }
 #endif /* DBG_QM_MALLOC */
 
 
6fa79282
 #endif