mem: fix real_used stats for realloc
[sip-router] / mem / q_malloc.c
1 /* $Id$
2  *
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  *
6  * This file is part of sip-router, a free SIP server.
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 /*
21  * History:
22  * --------
23  *  ????-??-??  created by andrei
24  *  2003-04-14  more debugging added in DBG_QM_MALLOC mode (andrei)
25  *  2003-06-29  added qm_realloc (andrei)
26  *  2004-07-19  fragments book keeping code and support for 64 bits
27  *               memory blocks (64 bits machine & size>=2^32) (andrei)
28  *              GET_HASH s/</<=/ (avoids waste of 1 hash cell) (andrei)
29  *  2004-11-10  support for > 4Gb mem., switched to long (andrei)
30  *  2005-03-02  added qm_info() (andrei)
31  *  2005-12-12  fixed realloc shrink real_used & used accounting;
32  *              fixed initial size (andrei)
33  *  2006-02-03  fixed realloc out of mem. free bug (andrei)
34  *  2006-04-07  s/DBG/MDBG (andrei)
35  *  2007-02-23  added fm_available() (andrei)
36  *  2009-09-28  added fm_sums() (patch from Dragos Vingarzan)
37  */
38
39
40 #if !defined(q_malloc) && !(defined F_MALLOC)
41 #define q_malloc
42
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "q_malloc.h"
47 #include "../dprint.h"
48 #include "../globals.h"
49 #include "memdbg.h"
50 #include "../cfg/cfg.h" /* memlog */
51
52
53 /*useful macros*/
54 #define FRAG_END(f)  \
55         ((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \
56            (f)->size))
57
58 #define FRAG_NEXT(f) \
59         ((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \
60            sizeof(struct qm_frag_end)))
61                         
62 #define FRAG_PREV(f) \
63         ( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \
64         ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \
65            sizeof(struct qm_frag) ) )
66
67 #define PREV_FRAG_END(f) \
68         ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))
69
70
71 #define FRAG_OVERHEAD   (sizeof(struct qm_frag)+sizeof(struct qm_frag_end))
72
73
74 #define ROUNDTO_MASK    (~((unsigned long)ROUNDTO-1))
75 #define ROUNDUP(s)              (((s)+(ROUNDTO-1))&ROUNDTO_MASK)
76 #define ROUNDDOWN(s)    ((s)&ROUNDTO_MASK)
77
78 /*
79 #define ROUNDUP(s)              (((s)%ROUNDTO)?((s)+ROUNDTO)/ROUNDTO*ROUNDTO:(s))
80 #define ROUNDDOWN(s)    (((s)%ROUNDTO)?((s)-ROUNDTO)/ROUNDTO*ROUNDTO:(s))
81 */
82
83
84
85         /* finds the hash value for s, s=ROUNDTO multiple*/
86 #define GET_HASH(s)   ( ((unsigned long)(s)<=QM_MALLOC_OPTIMIZE)?\
87                                                         (unsigned long)(s)/ROUNDTO: \
88                                                         QM_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \
89                                                                 QM_MALLOC_OPTIMIZE_FACTOR+1 )
90
91 #define UN_HASH(h)      ( ((unsigned long)(h)<=(QM_MALLOC_OPTIMIZE/ROUNDTO))?\
92                                                         (unsigned long)(h)*ROUNDTO: \
93                                                         1UL<<((h)-QM_MALLOC_OPTIMIZE/ROUNDTO+\
94                                                                 QM_MALLOC_OPTIMIZE_FACTOR-1)\
95                                         )
96
97
98 /* mark/test used/unused frags */
99 #define FRAG_MARK_USED(f)
100 #define FRAG_CLEAR_USED(f)
101 #define FRAG_WAS_USED(f)   (1)
102
103 /* other frag related defines:
104  * MEM_COALESCE_FRAGS 
105  * MEM_FRAG_AVOIDANCE
106  */
107
108 #define MEM_FRAG_AVOIDANCE
109
110
111 /* computes hash number for big buckets*/
112 inline static unsigned long big_hash_idx(unsigned long s)
113 {
114         int idx;
115         /* s is rounded => s = k*2^n (ROUNDTO=2^n) 
116          * index= i such that 2^i > s >= 2^(i-1)
117          *
118          * => index = number of the first non null bit in s*/
119         idx=sizeof(long)*8-1;
120         for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--);
121         return idx;
122 }
123
124
125 #ifdef DBG_QM_MALLOC
126 #define ST_CHECK_PATTERN   0xf0f0f0f0
127 #define END_CHECK_PATTERN1 0xc0c0c0c0
128 #define END_CHECK_PATTERN2 0xabcdefed
129
130
131 static  void qm_debug_frag(struct qm_block* qm, struct qm_frag* f)
132 {
133         if (f->check!=ST_CHECK_PATTERN){
134                 LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
135                                 "beginning overwritten(%lx)!\n",
136                                 f, (char*)f+sizeof(struct qm_frag),
137                                 f->check);
138                 qm_status(qm);
139                 abort();
140         };
141         if ((FRAG_END(f)->check1!=END_CHECK_PATTERN1)||
142                 (FRAG_END(f)->check2!=END_CHECK_PATTERN2)){
143                 LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)"
144                                         " end overwritten(%lx, %lx)!\n",
145                                 f, (char*)f+sizeof(struct qm_frag), 
146                                 FRAG_END(f)->check1, FRAG_END(f)->check2);
147                 qm_status(qm);
148                 abort();
149         }
150         if ((f>qm->first_frag)&&
151                         ((PREV_FRAG_END(f)->check1!=END_CHECK_PATTERN1) ||
152                                 (PREV_FRAG_END(f)->check2!=END_CHECK_PATTERN2) ) ){
153                 LOG(L_CRIT, "BUG: qm_*: prev. fragm. tail overwritten(%lx, %lx)[%p:%p]!"
154                                         "\n",
155                                 PREV_FRAG_END(f)->check1, PREV_FRAG_END(f)->check2, f,
156                                 (char*)f+sizeof(struct qm_frag));
157                 qm_status(qm);
158                 abort();
159         }
160 }
161 #endif
162
163
164
165 static inline void qm_insert_free(struct qm_block* qm, struct qm_frag* frag)
166 {
167         struct qm_frag* f;
168         struct qm_frag* prev;
169         int hash;
170         
171         hash=GET_HASH(frag->size);
172         for(f=qm->free_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head);
173                         f=f->u.nxt_free){
174                 if (frag->size <= f->size) break;
175         }
176         /*insert it here*/
177         prev=FRAG_END(f)->prev_free;
178         prev->u.nxt_free=frag;
179         FRAG_END(frag)->prev_free=prev;
180         frag->u.nxt_free=f;
181         FRAG_END(f)->prev_free=frag;
182         qm->free_hash[hash].no++;
183 }
184
185
186
187 /* init malloc and return a qm_block*/
188 struct qm_block* qm_malloc_init(char* address, unsigned long size)
189 {
190         char* start;
191         char* end;
192         struct qm_block* qm;
193         unsigned long init_overhead;
194         int h;
195         
196         /* make address and size multiple of 8*/
197         start=(char*)ROUNDUP((unsigned long) address);
198         DBG("qm_malloc_init: QM_OPTIMIZE=%lu, /ROUNDTO=%lu\n",
199                         QM_MALLOC_OPTIMIZE, QM_MALLOC_OPTIMIZE/ROUNDTO);
200         DBG("qm_malloc_init: QM_HASH_SIZE=%lu, qm_block size=%lu\n",
201                         QM_HASH_SIZE, (long)sizeof(struct qm_block));
202         DBG("qm_malloc_init(%p, %lu), start=%p\n", address, size, start);
203         if (size<start-address) return 0;
204         size-=(start-address);
205         if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
206         size=ROUNDDOWN(size);
207         
208         init_overhead=ROUNDUP(sizeof(struct qm_block))+sizeof(struct qm_frag)+
209                 sizeof(struct qm_frag_end);
210         DBG("qm_malloc_init: size= %lu, init_overhead=%lu\n", size, init_overhead);
211         
212         if (size < init_overhead)
213         {
214                 /* not enough mem to create our control structures !!!*/
215                 return 0;
216         }
217         end=start+size;
218         qm=(struct qm_block*)start;
219         memset(qm, 0, sizeof(struct qm_block));
220         qm->size=size;
221         qm->real_used=init_overhead;
222         qm->max_real_used=qm->real_used;
223         size-=init_overhead;
224         
225         qm->first_frag=(struct qm_frag*)(start+ROUNDUP(sizeof(struct qm_block)));
226         qm->last_frag_end=(struct qm_frag_end*)(end-sizeof(struct qm_frag_end));
227         /* init initial fragment*/
228         qm->first_frag->size=size;
229         qm->last_frag_end->size=size;
230         
231 #ifdef DBG_QM_MALLOC
232         qm->first_frag->check=ST_CHECK_PATTERN;
233         qm->last_frag_end->check1=END_CHECK_PATTERN1;
234         qm->last_frag_end->check2=END_CHECK_PATTERN2;
235 #endif
236         /* init free_hash* */
237         for (h=0; h<QM_HASH_SIZE;h++){
238                 qm->free_hash[h].head.u.nxt_free=&(qm->free_hash[h].head);
239                 qm->free_hash[h].tail.prev_free=&(qm->free_hash[h].head);
240                 qm->free_hash[h].head.size=0;
241                 qm->free_hash[h].tail.size=0;
242         }
243         
244         /* link initial fragment into the free list*/
245         
246         qm_insert_free(qm, qm->first_frag);
247         
248         /*qm->first_frag->u.nxt_free=&(qm->free_lst);
249           qm->last_frag_end->prev_free=&(qm->free_lst);
250         */
251         
252         
253         return qm;
254 }
255
256
257
258 static inline void qm_detach_free(struct qm_block* qm, struct qm_frag* frag)
259 {
260         struct qm_frag *prev;
261         struct qm_frag *next;
262         
263         prev=FRAG_END(frag)->prev_free;
264         next=frag->u.nxt_free;
265         prev->u.nxt_free=next;
266         FRAG_END(next)->prev_free=prev;
267         
268 }
269
270
271
272 #ifdef DBG_QM_MALLOC
273 static inline struct qm_frag* qm_find_free(struct qm_block* qm, 
274                                                                                         unsigned long size,
275                                                                                         int *h,
276                                                                                         unsigned int *count)
277 #else
278 static inline struct qm_frag* qm_find_free(struct qm_block* qm, 
279                                                                                         unsigned long size,
280                                                                                         int* h)
281 #endif
282 {
283         int hash;
284         struct qm_frag* f;
285
286         for (hash=GET_HASH(size); hash<QM_HASH_SIZE; hash++){
287                 for (f=qm->free_hash[hash].head.u.nxt_free; 
288                                         f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){
289 #ifdef DBG_QM_MALLOC
290                         *count+=1; /* *count++ generates a warning with gcc 2.9* -Wall */
291 #endif
292                         if (f->size>=size){ *h=hash; return f; }
293                 }
294         /*try in a bigger bucket*/
295         }
296         /* not found */
297         return 0;
298 }
299
300
301 /* returns 0 on success, -1 on error;
302  * new_size < size & rounded-up already!*/
303 static inline
304 #ifdef DBG_QM_MALLOC
305 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size,
306                                 const char* file, const char* func, unsigned int line)
307 #else
308 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size)
309 #endif
310 {
311         unsigned long rest;
312         struct qm_frag* n;
313         struct qm_frag_end* end;
314         
315         rest=f->size-new_size;
316 #ifdef MEM_FRAG_AVOIDANCE
317         if ((rest> (FRAG_OVERHEAD+QM_MALLOC_OPTIMIZE))||
318                 (rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/
319 #else
320         if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
321 #endif
322                 f->size=new_size;
323                 /*split the fragment*/
324                 end=FRAG_END(f);
325                 end->size=new_size;
326                 n=(struct qm_frag*)((char*)end+sizeof(struct qm_frag_end));
327                 n->size=rest-FRAG_OVERHEAD;
328                 FRAG_END(n)->size=n->size;
329                 FRAG_CLEAR_USED(n); /* never used */
330                 qm->real_used+=FRAG_OVERHEAD;
331 #ifdef DBG_QM_MALLOC
332                 end->check1=END_CHECK_PATTERN1;
333                 end->check2=END_CHECK_PATTERN2;
334                 /* frag created by malloc, mark it*/
335                 n->file=file;
336                 n->func=func;
337                 n->line=line;
338                 n->check=ST_CHECK_PATTERN;
339 #endif
340                 /* reinsert n in free list*/
341                 qm_insert_free(qm, n);
342                 return 0;
343         }else{
344                         /* we cannot split this fragment any more */
345                 return -1;
346         }
347 }
348
349
350
351 #ifdef DBG_QM_MALLOC
352 void* qm_malloc(struct qm_block* qm, unsigned long size,
353                                         const char* file, const char* func, unsigned int line)
354 #else
355 void* qm_malloc(struct qm_block* qm, unsigned long size)
356 #endif
357 {
358         struct qm_frag* f;
359         int hash;
360         
361 #ifdef DBG_QM_MALLOC
362         unsigned int list_cntr;
363
364         list_cntr = 0;
365         MDBG("qm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func,
366                         line);
367 #endif
368         /*size must be a multiple of 8*/
369         size=ROUNDUP(size);
370         if (size>(qm->size-qm->real_used)) return 0;
371
372         /*search for a suitable free frag*/
373 #ifdef DBG_QM_MALLOC
374         if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){
375 #else
376         if ((f=qm_find_free(qm, size, &hash))!=0){
377 #endif
378                 /* we found it!*/
379                 /*detach it from the free list*/
380 #ifdef DBG_QM_MALLOC
381                         qm_debug_frag(qm, f);
382 #endif
383                 qm_detach_free(qm, f);
384                 /*mark it as "busy"*/
385                 f->u.is_free=0;
386                 qm->free_hash[hash].no--;
387                 /* we ignore split return */
388 #ifdef DBG_QM_MALLOC
389                 split_frag(qm, f, size, file, "fragm. from qm_malloc", line);
390 #else
391                 split_frag(qm, f, size);
392 #endif
393                 qm->real_used+=f->size;
394                 qm->used+=f->size;
395                 if (qm->max_real_used<qm->real_used)
396                         qm->max_real_used=qm->real_used;
397 #ifdef DBG_QM_MALLOC
398                 f->file=file;
399                 f->func=func;
400                 f->line=line;
401                 f->check=ST_CHECK_PATTERN;
402                 /*  FRAG_END(f)->check1=END_CHECK_PATTERN1;
403                         FRAG_END(f)->check2=END_CHECK_PATTERN2;*/
404                 MDBG("qm_malloc(%p, %lu) returns address %p frag. %p (size=%lu) on %d"
405                                 " -th hit\n",
406                          qm, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr );
407 #endif
408                 return (char*)f+sizeof(struct qm_frag);
409         }
410         return 0;
411 }
412
413
414
415 #ifdef DBG_QM_MALLOC
416 void qm_free(struct qm_block* qm, void* p, const char* file, const char* func, 
417                                 unsigned int line)
418 #else
419 void qm_free(struct qm_block* qm, void* p)
420 #endif
421 {
422         struct qm_frag* f;
423         struct qm_frag* prev;
424         struct qm_frag* next;
425         unsigned long size;
426
427 #ifdef DBG_QM_MALLOC
428         MDBG("qm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line);
429         if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){
430                 LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!) - "
431                                 "aborting\n", p);
432                 abort();
433         }
434 #endif
435         if (p==0) {
436                 LOG(L_WARN, "WARNING:qm_free: free(0) called\n");
437                 return;
438         }
439         prev=next=0;
440         f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
441 #ifdef DBG_QM_MALLOC
442         qm_debug_frag(qm, f);
443         if (f->u.is_free){
444                 LOG(L_CRIT, "BUG: qm_free: freeing already freed pointer,"
445                                 " first free: %s: %s(%ld) - aborting\n",
446                                 f->file, f->func, f->line);
447                 abort();
448         }
449         MDBG("qm_free: freeing frag. %p alloc'ed from %s: %s(%ld)\n",
450                         f, f->file, f->func, f->line);
451 #endif
452         size=f->size;
453         qm->used-=size;
454         qm->real_used-=size;
455
456 #ifdef QM_JOIN_FREE
457         /* mark this fragment as used (might fall into the middle of joined frags)
458           to give us an extra change of detecting a double free call (if the joined
459           fragment has not yet been reused) */
460         f->u.nxt_free=(void*)0x1L; /* bogus value, just to mark it as free */
461         /* join packets if possible*/
462         next=FRAG_NEXT(f);
463         if (((char*)next < (char*)qm->last_frag_end) &&( next->u.is_free)){
464                 /* join */
465 #ifdef DBG_QM_MALLOC
466                 qm_debug_frag(qm, next);
467 #endif
468                 qm_detach_free(qm, next);
469                 size+=next->size+FRAG_OVERHEAD;
470                 qm->real_used-=FRAG_OVERHEAD;
471                 qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */
472         }
473         
474         if (f > qm->first_frag){
475                 prev=FRAG_PREV(f);
476                 /*      (struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f-
477                                                                 sizeof(struct qm_frag_end))->size);*/
478 #ifdef DBG_QM_MALLOC
479                 qm_debug_frag(qm, prev);
480 #endif
481                 if (prev->u.is_free){
482                         /*join*/
483                         qm_detach_free(qm, prev);
484                         size+=prev->size+FRAG_OVERHEAD;
485                         qm->real_used-=FRAG_OVERHEAD;
486                         qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */
487                         f=prev;
488                 }
489         }
490         f->size=size;
491         FRAG_END(f)->size=f->size;
492 #endif /* QM_JOIN_FREE*/
493 #ifdef DBG_QM_MALLOC
494         f->file=file;
495         f->func=func;
496         f->line=line;
497 #endif
498         qm_insert_free(qm, f);
499 }
500
501
502
503 #ifdef DBG_QM_MALLOC
504 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size,
505                                         const char* file, const char* func, unsigned int line)
506 #else
507 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size)
508 #endif
509 {
510         struct qm_frag* f;
511         unsigned long diff;
512         unsigned long orig_size;
513         struct qm_frag* n;
514         void* ptr;
515         
516         
517 #ifdef DBG_QM_MALLOC
518         MDBG("qm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size,
519                         file, func, line);
520         if ((p)&&(p>(void*)qm->last_frag_end || p<(void*)qm->first_frag)){
521                 LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!) - "
522                                 "aborting\n", p);
523                 abort();
524         }
525 #endif
526         
527         if (size==0) {
528                 if (p)
529 #ifdef DBG_QM_MALLOC
530                         qm_free(qm, p, file, func, line);
531 #else
532                         qm_free(qm, p);
533 #endif
534                 return 0;
535         }
536         if (p==0)
537 #ifdef DBG_QM_MALLOC
538                 return qm_malloc(qm, size, file, func, line);
539 #else
540                 return qm_malloc(qm, size);
541 #endif
542         f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
543 #ifdef DBG_QM_MALLOC
544         qm_debug_frag(qm, f);
545         MDBG("qm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
546                         f, f->file, f->func, f->line);
547         if (f->u.is_free){
548                 LOG(L_CRIT, "BUG:qm_realloc: trying to realloc an already freed "
549                                 "pointer %p , fragment %p -- aborting\n", p, f);
550                 abort();
551         }
552 #endif
553         /* find first acceptable size */
554         size=ROUNDUP(size);
555         if (f->size > size){
556                 orig_size=f->size;
557                 /* shrink */
558 #ifdef DBG_QM_MALLOC
559                 MDBG("qm_realloc: shrinking from %lu to %lu\n", f->size, size);
560                 if(split_frag(qm, f, size, file, "fragm. from qm_realloc", line)!=0){
561                 MDBG("qm_realloc : shrinked successful\n");
562 #else
563                 if(split_frag(qm, f, size)!=0){
564 #endif
565                         /* update used sizes: freed the splited frag */
566                         /* split frag already adds FRAG_OVERHEAD for the newly created
567                            free frag, so here we only need orig_size-f->size for real used
568                          */
569                         qm->real_used-=(orig_size-f->size);
570                         qm->used-=(orig_size-f->size);
571                 }
572                 
573         }else if (f->size < size){
574                 /* grow */
575 #ifdef DBG_QM_MALLOC
576                 MDBG("qm_realloc: growing from %lu to %lu\n", f->size, size);
577 #endif
578                         orig_size=f->size;
579                         diff=size-f->size;
580                         n=FRAG_NEXT(f);
581                         if (((char*)n < (char*)qm->last_frag_end) && 
582                                         (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
583                                 /* join  */
584                                 qm_detach_free(qm, n);
585                                 qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/
586                                 f->size+=n->size+FRAG_OVERHEAD;
587                                 qm->real_used-=FRAG_OVERHEAD;
588                                 FRAG_END(f)->size=f->size;
589                                 /* end checks should be ok */
590                                 /* split it if necessary */
591                                 if (f->size > size ){
592         #ifdef DBG_QM_MALLOC
593                                         split_frag(qm, f, size, file, "fragm. from qm_realloc",
594                                                                                 line);
595         #else
596                                         split_frag(qm, f, size);
597         #endif
598                                 }
599                                 qm->real_used+=(f->size-orig_size);
600                                 qm->used+=(f->size-orig_size);
601                         }else{
602                                 /* could not join => realloc */
603         #ifdef DBG_QM_MALLOC
604                                 ptr=qm_malloc(qm, size, file, func, line);
605         #else
606                                 ptr=qm_malloc(qm, size);
607         #endif
608                                 if (ptr){
609                                         /* copy, need by libssl */
610                                         memcpy(ptr, p, orig_size);
611         #ifdef DBG_QM_MALLOC
612                                         qm_free(qm, p, file, func, line);
613         #else
614                                         qm_free(qm, p);
615         #endif
616                                 }
617                                 p=ptr;
618                         }
619         }else{
620                 /* do nothing */
621 #ifdef DBG_QM_MALLOC
622                 MDBG("qm_realloc: doing nothing, same size: %lu - %lu\n",
623                                 f->size, size);
624 #endif
625         }
626 #ifdef DBG_QM_MALLOC
627         MDBG("qm_realloc: returning %p\n", p);
628 #endif
629         return p;
630 }
631
632
633 void qm_check(struct qm_block* qm)
634 {
635         struct qm_frag* f;
636         long fcount = 0;
637         int memlog;
638         
639         memlog=cfg_get(core, core_cfg, memlog);
640         LOG(memlog, "DEBUG: qm_check()\n");
641         f = qm->first_frag;
642         while ((char*)f < (char*)qm->last_frag_end) {
643                 fcount++;
644                 /* check struct qm_frag */
645 #ifdef DBG_QM_MALLOC
646                 if (f->check!=ST_CHECK_PATTERN){
647                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
648                                         "beginning overwritten(%lx)!\n",
649                                         f, (char*)f + sizeof(struct qm_frag),
650                                         f->check);
651                         qm_status(qm);
652                         abort();
653                 };
654 #endif
655                 if (f + sizeof(struct qm_frag) + f->size + sizeof(struct qm_frag_end) > qm->first_frag + qm->size) {
656                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
657                                 "bad size: %lu (frag end: %p > end of block: %p)\n",
658                                 f, (char*)f + sizeof(struct qm_frag) + sizeof(struct qm_frag_end), f->size,
659                                 f + sizeof(struct qm_frag) + f->size, qm->first_frag + qm->size);
660                         qm_status(qm);
661                         abort();
662                 }
663                 /* check struct qm_frag_end */
664                 if (FRAG_END(f)->size != f->size) {
665                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
666                                 "size in qm_frag and qm_frag_end does not match: frag->size=%lu, frag_end->size=%lu)\n",
667                                 f, (char*)f + sizeof(struct qm_frag),
668                                 f->size, FRAG_END(f)->size);
669                         qm_status(qm);
670                         abort();
671                 }
672 #ifdef DBG_QM_MALLOC
673                 if ((FRAG_END(f)->check1 != END_CHECK_PATTERN1) ||
674                         (FRAG_END(f)->check2 != END_CHECK_PATTERN2)) {
675                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)"
676                                                 " end overwritten(%lx, %lx)!\n",
677                                         f, (char*)f + sizeof(struct qm_frag), 
678                                         FRAG_END(f)->check1, FRAG_END(f)->check2);
679                         qm_status(qm);
680                         abort();
681                 }
682 #endif
683                 f = FRAG_NEXT(f);
684         }
685
686         LOG(memlog, "DEBUG: qm_check: %lu fragments OK\n", fcount);
687 }
688
689 void qm_status(struct qm_block* qm)
690 {
691         struct qm_frag* f;
692         int i,j;
693         int h;
694         int unused;
695         int memlog;
696
697
698         memlog=cfg_get(core, core_cfg, memlog);
699         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "(%p):\n", qm);
700         if (!qm) return;
701
702         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "heap size= %lu\n",
703                         qm->size);
704         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
705                         "used= %lu, used+overhead=%lu, free=%lu\n",
706                         qm->used, qm->real_used, qm->size-qm->real_used);
707         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
708                         "max used (+overhead)= %lu\n", qm->max_real_used);
709         
710         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
711                         "dumping all alloc'ed. fragments:\n");
712         for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;f=FRAG_NEXT(f)
713                         ,i++){
714                 if (! f->u.is_free){
715                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
716                                         "   %3d. %c  address=%p frag=%p size=%lu used=%d\n",
717                                 i,
718                                 (f->u.is_free)?'a':'N',
719                                 (char*)f+sizeof(struct qm_frag), f, f->size, FRAG_WAS_USED(f));
720 #ifdef DBG_QM_MALLOC
721                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
722                                         "          %s from %s: %s(%ld)\n",
723                                 (f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line);
724                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
725                                         "         start check=%lx, end check= %lx, %lx\n",
726                                 f->check, FRAG_END(f)->check1, FRAG_END(f)->check2);
727 #endif
728                 }
729         }
730         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
731                         "dumping free list stats :\n");
732         for(h=0,i=0;h<QM_HASH_SIZE;h++){
733                 unused=0;
734                 for (f=qm->free_hash[h].head.u.nxt_free,j=0; 
735                                 f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){
736                                 if (!FRAG_WAS_USED(f)){
737                                         unused++;
738 #ifdef DBG_QM_MALLOC
739                                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
740                                                 "unused fragm.: hash = %3d, fragment %p,"
741                                                 " address %p size %lu, created from %s: %s(%lu)\n",
742                                             h, f, (char*)f+sizeof(struct qm_frag), f->size,
743                                                 f->file, f->func, f->line);
744 #endif
745                                 }
746                 }
747
748                 if (j) LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
749                                 "hash= %3d. fragments no.: %5d, unused: %5d\n"
750                                         "\t\t bucket size: %9lu - %9ld (first %9lu)\n",
751                                         h, j, unused, UN_HASH(h),
752                                         ((h<=QM_MALLOC_OPTIMIZE/ROUNDTO)?1:2)*UN_HASH(h),
753                                         qm->free_hash[h].head.u.nxt_free->size
754                                 );
755                 if (j!=qm->free_hash[h].no){
756                         LOG(L_CRIT, "BUG: qm_status: different free frag. count: %d!=%lu"
757                                 " for hash %3d\n", j, qm->free_hash[h].no, h);
758                 }
759
760         }
761         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
762                         "-----------------------------\n");
763 }
764
765
766 /* fills a malloc info structure with info about the block
767  * if a parameter is not supported, it will be filled with 0 */
768 void qm_info(struct qm_block* qm, struct mem_info* info)
769 {
770         int r;
771         long total_frags;
772         
773         total_frags=0;
774         memset(info,0, sizeof(*info));
775         info->total_size=qm->size;
776         info->min_frag=MIN_FRAG_SIZE;
777         info->free=qm->size-qm->real_used;
778         info->used=qm->used;
779         info->real_used=qm->real_used;
780         info->max_used=qm->max_real_used;
781         for(r=0;r<QM_HASH_SIZE; r++){
782                 total_frags+=qm->free_hash[r].no;
783         }
784         info->total_frags=total_frags;
785 }
786
787
788 /* returns how much free memory is available
789  * it never returns an error (unlike fm_available) */
790 unsigned long qm_available(struct qm_block* qm)
791 {
792         return qm->size-qm->real_used;
793 }
794
795
796
797 #ifdef DBG_QM_MALLOC
798
799 typedef struct _mem_counter{
800         const char *file;
801         const char *func;
802         unsigned long line;
803         
804         unsigned long size;
805         int count;
806         
807         struct _mem_counter *next;
808 } mem_counter;
809
810 static mem_counter* get_mem_counter(mem_counter **root, struct qm_frag* f)
811 {
812         mem_counter *x;
813         if (!*root) goto make_new;
814         for(x=*root;x;x=x->next)
815                 if (x->file == f->file && x->func == f->func && x->line == f->line)
816                         return x;
817 make_new:       
818         x = malloc(sizeof(mem_counter));
819         x->file = f->file;
820         x->func = f->func;
821         x->line = f->line;
822         x->count = 0;
823         x->size = 0;
824         x->next = *root;
825         *root = x;
826         return x;
827 }
828
829
830
831 void qm_sums(struct qm_block* qm)
832 {
833         struct qm_frag* f;
834         int i;
835         mem_counter *root, *x;
836         int memlog;
837         
838         root=0;
839         if (!qm) return;
840         
841         memlog=cfg_get(core, core_cfg, memlog);
842         LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
843                         "summarizing all alloc'ed. fragments:\n");
844         
845         for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;
846                         f=FRAG_NEXT(f),i++){
847                 if (! f->u.is_free){
848                         x = get_mem_counter(&root,f);
849                         x->count++;
850                         x->size+=f->size;
851                 }
852         }
853         x = root;
854         while(x){
855                 LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
856                                 " count=%6d size=%10lu bytes from %s: %s(%ld)\n",
857                         x->count,x->size,
858                         x->file, x->func, x->line
859                         );
860                 root = x->next;
861                 free(x);
862                 x = root;
863         }
864         LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
865                         "-----------------------------\n");
866 }
867 #endif /* DBG_QM_MALLOC */
868
869
870 #endif