mem: fix real_used stats for realloc
[sip-router] / mem / f_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-07-06  added fm_realloc (andrei)
25  *  2004-07-19  fragments book keeping code and support for 64 bits
26  *               memory blocks (64 bits machine & size >=2^32) 
27  *              GET_HASH s/</<=/ (avoids waste of 1 hash cell)   (andrei)
28  *  2004-11-10  support for > 4Gb mem., switched to long (andrei)
29  *  2005-03-02  added fm_info() (andrei)
30  *  2005-12-12  fixed realloc shrink real_used accounting (andrei)
31  *              fixed initial size (andrei)
32  *  2006-02-03  fixed realloc out of mem. free bug (andrei)
33  *  2006-04-07  s/DBG/MDBG (andrei)
34  *  2007-02-23  added fm_available() (andrei)
35  *  2007-06-23  added hash bitmap (andrei)
36  *  2009-09-28  added fm_sums() (patch from Dragos Vingarzan)
37  */
38
39
40 #if !defined(q_malloc)  && (defined F_MALLOC)
41
42 #include <string.h>
43 #include <stdlib.h>
44
45 #include "f_malloc.h"
46 #include "../dprint.h"
47 #include "../globals.h"
48 #include "../compiler_opt.h"
49 #include "memdbg.h"
50 #include "../bit_scan.h"
51 #include "../cfg/cfg.h" /* memlog */
52
53
54 /*useful macros*/
55
56 #define FRAG_NEXT(f) \
57         ((struct fm_frag*)((char*)(f)+sizeof(struct fm_frag)+(f)->size ))
58
59 #define FRAG_OVERHEAD   (sizeof(struct fm_frag))
60 #define INIT_OVERHEAD   \
61         (ROUNDUP(sizeof(struct fm_block))+sizeof(struct fm_frag))
62
63
64
65 /* ROUNDTO= 2^k so the following works */
66 #define ROUNDTO_MASK    (~((unsigned long)ROUNDTO-1))
67 #define ROUNDUP(s)              (((s)+(ROUNDTO-1))&ROUNDTO_MASK)
68 #define ROUNDDOWN(s)    ((s)&ROUNDTO_MASK)
69
70 /*
71  #define ROUNDUP(s)             (((s)%ROUNDTO)?((s)+ROUNDTO)/ROUNDTO*ROUNDTO:(s))
72  #define ROUNDDOWN(s)   (((s)%ROUNDTO)?((s)-ROUNDTO)/ROUNDTO*ROUNDTO:(s))
73 */
74
75
76
77         /* finds the hash value for s, s=ROUNDTO multiple*/
78 #define GET_HASH(s)   ( ((unsigned long)(s)<=F_MALLOC_OPTIMIZE)?\
79                                                         (unsigned long)(s)/ROUNDTO: \
80                                                         F_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \
81                                                                 F_MALLOC_OPTIMIZE_FACTOR+1 )
82
83 #define UN_HASH(h)      ( ((unsigned long)(h)<=(F_MALLOC_OPTIMIZE/ROUNDTO))?\
84                                                 (unsigned long)(h)*ROUNDTO: \
85                                                 1UL<<((unsigned long)(h)-F_MALLOC_OPTIMIZE/ROUNDTO+\
86                                                         F_MALLOC_OPTIMIZE_FACTOR-1)\
87                                         )
88
89 #ifdef F_MALLOC_HASH_BITMAP
90
91 #define fm_bmp_set(qm, b) \
92         do{ \
93                 (qm)->free_bitmap[(b)/FM_HASH_BMP_BITS] |= \
94                                                                                         1UL<<((b)%FM_HASH_BMP_BITS); \
95         }while(0)
96
97 #define fm_bmp_reset(qm, b) \
98         do{ \
99                 (qm)->free_bitmap[(b)/FM_HASH_BMP_BITS] &= \
100                                                                                         ~(1UL<<((b)%FM_HASH_BMP_BITS)); \
101         }while(0)
102
103 /* returns 0 if not set, !=0 if set */
104 #define fm_bmp_is_set(qm, b) \
105         ((qm)->free_bitmap[(b)/FM_HASH_BMP_BITS] & (1UL<<((b)%FM_HASH_BMP_BITS)))
106
107 inline static int fm_bmp_first_set(struct fm_block* qm, int start)
108 {
109         int bmp_idx;
110         int bit;
111         int r;
112         fm_hash_bitmap_t test_val;
113         fm_hash_bitmap_t v;
114         
115         bmp_idx=start/FM_HASH_BMP_BITS;
116         bit=start%FM_HASH_BMP_BITS;
117         test_val=1UL <<((unsigned long)bit);
118         if (qm->free_bitmap[bmp_idx] & test_val)
119                 return start;
120         else if (qm->free_bitmap[bmp_idx] & ~(test_val-1)){
121 #if 0
122                 test_val<<=1;
123                 for (r=bit+1; r<FM_HASH_BMP_BITS; r++, test_val<<=1){
124                         if (qm->free_bitmap[bmp_idx] & test_val)
125                                 return (start-bit+r);
126                 }
127 #endif
128                 v=qm->free_bitmap[bmp_idx]>>(bit+1);
129                 return start+1+bit_scan_forward((unsigned long)v);
130         }
131         for (r=bmp_idx+1;r<FM_HASH_BMP_SIZE; r++){
132                 if (qm->free_bitmap[r]){
133                         /* find first set bit */
134                         return r*FM_HASH_BMP_BITS+
135                                                 bit_scan_forward((unsigned long)qm->free_bitmap[r]);
136                 }
137         }
138         /* not found, nothing free */
139         return -1;
140 }
141 #endif /* F_MALLOC_HASH_BITMAP */
142
143
144
145 /* mark/test used/unused frags */
146 #define FRAG_MARK_USED(f)
147 #define FRAG_CLEAR_USED(f)
148 #define FRAG_WAS_USED(f)   (1)
149
150 /* other frag related defines:
151  * MEM_COALESCE_FRAGS 
152  * MEM_FRAG_AVOIDANCE
153  */
154 #define MEM_FRAG_AVOIDANCE
155
156
157 /* computes hash number for big buckets*/
158 #if 0
159 inline static unsigned long big_hash_idx(unsigned long s)
160 {
161         unsigned long idx;
162         /* s is rounded => s = k*2^n (ROUNDTO=2^n) 
163          * index= i such that 2^(i+1) > s >= 2^i
164          *
165          * => index = number of the first non null bit in s*/
166         idx=sizeof(long)*8-1;
167         for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--);
168         return idx;
169 }
170 #else
171 #define big_hash_idx(s) ((unsigned long)bit_scan_reverse((unsigned long)(s)))
172 #endif
173
174
175 #ifdef DBG_F_MALLOC
176 #define ST_CHECK_PATTERN   0xf0f0f0f0
177 #define END_CHECK_PATTERN1 0xc0c0c0c0
178 #define END_CHECK_PATTERN2 0xabcdefed
179 #endif
180
181
182
183 static inline void fm_insert_free(struct fm_block* qm, struct fm_frag* frag)
184 {
185         struct fm_frag** f;
186         int hash;
187         
188         hash=GET_HASH(frag->size);
189         f=&(qm->free_hash[hash].first);
190         if (frag->size > F_MALLOC_OPTIMIZE){ /* because of '<=' in GET_HASH,
191                                                                                         (different from 0.8.1[24] on
192                                                                                          purpose --andrei ) */
193                 for(; *f; f=&((*f)->u.nxt_free)){
194                         if (frag->size <= (*f)->size) break;
195                 }
196         }
197         
198         /*insert it here*/
199         frag->u.nxt_free=*f;
200         *f=frag;
201         qm->free_hash[hash].no++;
202 #ifdef F_MALLOC_HASH_BITMAP
203         fm_bmp_set(qm, hash);
204 #endif /* F_MALLOC_HASH_BITMAP */
205 }
206
207
208
209  /* size should be already rounded-up */
210 static inline
211 #ifdef DBG_F_MALLOC 
212 void fm_split_frag(struct fm_block* qm, struct fm_frag* frag,
213                                         unsigned long size,
214                                         const char* file, const char* func, unsigned int line)
215 #else
216 void fm_split_frag(struct fm_block* qm, struct fm_frag* frag,
217                                         unsigned long size)
218 #endif
219 {
220         unsigned long rest;
221         struct fm_frag* n;
222         
223         rest=frag->size-size;
224 #ifdef MEM_FRAG_AVOIDANCE
225         if ((rest> (FRAG_OVERHEAD+F_MALLOC_OPTIMIZE))||
226                 (rest>=(FRAG_OVERHEAD+size))){ /* the residue fragm. is big enough*/
227 #else
228         if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
229 #endif
230                 frag->size=size;
231                 /*split the fragment*/
232                 n=FRAG_NEXT(frag);
233                 n->size=rest-FRAG_OVERHEAD;
234                 FRAG_CLEAR_USED(n); /* never used */
235 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
236                 qm->real_used+=FRAG_OVERHEAD;
237 #endif
238 #ifdef DBG_F_MALLOC
239                 /* frag created by malloc, mark it*/
240                 n->file=file;
241                 n->func="frag. from fm_malloc";
242                 n->line=line;
243                 n->check=ST_CHECK_PATTERN;
244 #endif
245                 /* reinsert n in free list*/
246                 fm_insert_free(qm, n);
247         }else{
248                 /* we cannot split this fragment any more => alloc all of it*/
249         }
250 }
251
252
253
254 /* init malloc and return a fm_block*/
255 struct fm_block* fm_malloc_init(char* address, unsigned long size)
256 {
257         char* start;
258         char* end;
259         struct fm_block* qm;
260         unsigned long init_overhead;
261         
262         /* make address and size multiple of 8*/
263         start=(char*)ROUNDUP((unsigned long) address);
264         DBG("fm_malloc_init: F_OPTIMIZE=%lu, /ROUNDTO=%lu\n",
265                         F_MALLOC_OPTIMIZE, F_MALLOC_OPTIMIZE/ROUNDTO);
266         DBG("fm_malloc_init: F_HASH_SIZE=%lu, fm_block size=%lu\n",
267                         F_HASH_SIZE, (long)sizeof(struct fm_block));
268         DBG("fm_malloc_init(%p, %lu), start=%p\n", address, size, start);
269
270         if (size<start-address) return 0;
271         size-=(start-address);
272         if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
273         size=ROUNDDOWN(size);
274
275         init_overhead=INIT_OVERHEAD;
276         
277         
278         if (size < init_overhead)
279         {
280                 /* not enough mem to create our control structures !!!*/
281                 return 0;
282         }
283         end=start+size;
284         qm=(struct fm_block*)start;
285         memset(qm, 0, sizeof(struct fm_block));
286         qm->size=size;
287 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
288         qm->real_used=init_overhead;
289         qm->max_real_used=qm->real_used;
290 #endif
291         size-=init_overhead;
292         
293         qm->first_frag=(struct fm_frag*)(start+ROUNDUP(sizeof(struct fm_block)));
294         qm->last_frag=(struct fm_frag*)(end-sizeof(struct fm_frag));
295         /* init initial fragment*/
296         qm->first_frag->size=size;
297         qm->last_frag->size=0;
298         
299 #ifdef DBG_F_MALLOC
300         qm->first_frag->check=ST_CHECK_PATTERN;
301         qm->last_frag->check=END_CHECK_PATTERN1;
302 #endif
303         
304         /* link initial fragment into the free list*/
305         
306         fm_insert_free(qm, qm->first_frag);
307         
308         
309         return qm;
310 }
311
312
313
314 #ifdef DBG_F_MALLOC
315 void* fm_malloc(struct fm_block* qm, unsigned long size,
316                                         const char* file, const char* func, unsigned int line)
317 #else
318 void* fm_malloc(struct fm_block* qm, unsigned long size)
319 #endif
320 {
321         struct fm_frag** f;
322         struct fm_frag* frag;
323         int hash;
324         
325 #ifdef DBG_F_MALLOC
326         MDBG("fm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func,
327                         line);
328 #endif
329         /*size must be a multiple of 8*/
330         size=ROUNDUP(size);
331 /*      if (size>(qm->size-qm->real_used)) return 0; */
332
333         
334         /*search for a suitable free frag*/
335
336 #ifdef F_MALLOC_HASH_BITMAP
337         hash=fm_bmp_first_set(qm, GET_HASH(size));
338         if (likely(hash>=0)){
339                 f=&(qm->free_hash[hash].first);
340         if (likely(hash<=F_MALLOC_OPTIMIZE/ROUNDTO)) /* return first match */
341                         goto found; 
342                 for(;(*f); f=&((*f)->u.nxt_free))
343                         if ((*f)->size>=size) goto found;
344         }
345 #else /* F_MALLOC_HASH_BITMAP */
346         for(hash=GET_HASH(size);hash<F_HASH_SIZE;hash++){
347                 f=&(qm->free_hash[hash].first);
348 #if 0
349                 if (likely(hash<=F_MALLOC_OPTIMIZE/ROUNDTO)) /* return first match */
350                                 goto found; 
351 #endif
352                 for(;(*f); f=&((*f)->u.nxt_free))
353                         if ((*f)->size>=size) goto found;
354                 /* try in a bigger bucket */
355         }
356 #endif /* F_MALLOC_HASH_BITMAP */
357         /* not found, bad! */
358         return 0;
359
360 found:
361         /* we found it!*/
362         /* detach it from the free list*/
363         frag=*f;
364         *f=frag->u.nxt_free;
365         frag->u.nxt_free=0; /* mark it as 'taken' */
366         qm->free_hash[hash].no--;
367 #ifdef F_MALLOC_HASH_BITMAP
368         if (qm->free_hash[hash].no==0)
369                 fm_bmp_reset(qm, hash);
370 #endif /* F_MALLOC_HASH_BITMAP */
371         
372         /*see if we'll use full frag, or we'll split it in 2*/
373         
374 #ifdef DBG_F_MALLOC
375         fm_split_frag(qm, frag, size, file, func, line);
376
377         frag->file=file;
378         frag->func=func;
379         frag->line=line;
380         frag->check=ST_CHECK_PATTERN;
381         MDBG("fm_malloc(%p, %lu) returns address %p \n", qm, size,
382                 (char*)frag+sizeof(struct fm_frag));
383 #else
384         fm_split_frag(qm, frag, size);
385 #endif
386 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
387         qm->real_used+=frag->size;
388         qm->used+=frag->size;
389         if (qm->max_real_used<qm->real_used)
390                 qm->max_real_used=qm->real_used;
391 #endif
392         FRAG_MARK_USED(frag); /* mark it as used */
393         return (char*)frag+sizeof(struct fm_frag);
394 }
395
396
397
398 #ifdef DBG_F_MALLOC
399 void fm_free(struct fm_block* qm, void* p, const char* file, const char* func, 
400                                 unsigned int line)
401 #else
402 void fm_free(struct fm_block* qm, void* p)
403 #endif
404 {
405         struct fm_frag* f;
406         unsigned long size;
407
408 #ifdef DBG_F_MALLOC
409         MDBG("fm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line);
410         if (p>(void*)qm->last_frag || p<(void*)qm->first_frag){
411                 LOG(L_CRIT, "BUG: fm_free: bad pointer %p (out of memory block!) - "
412                                 "aborting\n", p);
413                 abort();
414         }
415 #endif
416         if (p==0) {
417                 LOG(L_WARN, "WARNING:fm_free: free(0) called\n");
418                 return;
419         }
420         f=(struct fm_frag*) ((char*)p-sizeof(struct fm_frag));
421 #ifdef DBG_F_MALLOC
422         MDBG("fm_free: freeing block alloc'ed from %s: %s(%ld)\n",
423                         f->file, f->func, f->line);
424 #endif
425         size=f->size;
426 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
427         qm->used-=size;
428         qm->real_used-=size;
429 #endif
430 #ifdef DBG_F_MALLOC
431         f->file=file;
432         f->func=func;
433         f->line=line;
434 #endif
435         fm_insert_free(qm, f);
436 }
437
438
439 #ifdef DBG_F_MALLOC
440 void* fm_realloc(struct fm_block* qm, void* p, unsigned long size,
441                                         const char* file, const char* func, unsigned int line)
442 #else
443 void* fm_realloc(struct fm_block* qm, void* p, unsigned long size)
444 #endif
445 {
446         struct fm_frag *f;
447         struct fm_frag **pf;
448         unsigned long diff;
449         unsigned long orig_size;
450         struct fm_frag *n;
451         void *ptr;
452         int hash;
453         
454 #ifdef DBG_F_MALLOC
455         MDBG("fm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size,
456                         file, func, line);
457         if ((p)&&(p>(void*)qm->last_frag || p<(void*)qm->first_frag)){
458                 LOG(L_CRIT, "BUG: fm_free: bad pointer %p (out of memory block!) - "
459                                 "aborting\n", p);
460                 abort();
461         }
462 #endif
463         if (size==0) {
464                 if (p)
465 #ifdef DBG_F_MALLOC
466                         fm_free(qm, p, file, func, line);
467 #else
468                         fm_free(qm, p);
469 #endif
470                 return 0;
471         }
472         if (p==0)
473 #ifdef DBG_F_MALLOC
474                 return fm_malloc(qm, size, file, func, line);
475 #else
476                 return fm_malloc(qm, size);
477 #endif
478         f=(struct fm_frag*) ((char*)p-sizeof(struct fm_frag));
479 #ifdef DBG_F_MALLOC
480         MDBG("fm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
481                         f, f->file, f->func, f->line);
482 #endif
483         size=ROUNDUP(size);
484         orig_size=f->size;
485         if (f->size > size){
486                 /* shrink */
487 #ifdef DBG_F_MALLOC
488                 MDBG("fm_realloc: shrinking from %lu to %lu\n", f->size, size);
489                 fm_split_frag(qm, f, size, file, "frag. from fm_realloc", line);
490 #else
491                 fm_split_frag(qm, f, size);
492 #endif
493 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
494                 /* fm_split frag already adds FRAG_OVERHEAD for the newly created
495                    free frag, so here we only need orig_size-f->size for real used */
496                 qm->real_used-=(orig_size-f->size);
497                 qm->used-=(orig_size-f->size);
498 #endif
499         }else if (f->size<size){
500                 /* grow */
501 #ifdef DBG_F_MALLOC
502                 MDBG("fm_realloc: growing from %lu to %lu\n", f->size, size);
503 #endif
504                 diff=size-f->size;
505                 n=FRAG_NEXT(f);
506                 if (((char*)n < (char*)qm->last_frag) && 
507                                 (n->u.nxt_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
508                         /* join  */
509                         /* detach n from the free list */
510                         hash=GET_HASH(n->size);
511                         pf=&(qm->free_hash[hash].first);
512                         /* find it */
513                         for(;(*pf)&&(*pf!=n); pf=&((*pf)->u.nxt_free)); /*FIXME slow */
514                         if (*pf==0){
515                                 /* not found, bad! */
516                                 LOG(L_CRIT, "BUG: fm_realloc: could not find %p in free "
517                                                 "list (hash=%ld)\n", n, GET_HASH(n->size));
518                                 abort();
519                         }
520                         /* detach */
521                         *pf=n->u.nxt_free;
522                         qm->free_hash[hash].no--;
523 #ifdef F_MALLOC_HASH_BITMAP
524                         if (qm->free_hash[hash].no==0)
525                                 fm_bmp_reset(qm, hash);
526 #endif /* F_MALLOC_HASH_BITMAP */
527                         /* join */
528                         f->size+=n->size+FRAG_OVERHEAD;
529                 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
530                         qm->real_used-=FRAG_OVERHEAD;
531                 #endif
532                         /* split it if necessary */
533                         if (f->size > size){
534                 #ifdef DBG_F_MALLOC
535                                 fm_split_frag(qm, f, size, file, "fragm. from fm_realloc",
536                                                 line);
537                 #else
538                                 fm_split_frag(qm, f, size);
539                 #endif
540                         }
541                 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
542                         qm->real_used+=(f->size-orig_size);
543                         qm->used+=(f->size-orig_size);
544                 #endif
545                 }else{
546                         /* could not join => realloc */
547         #ifdef DBG_F_MALLOC
548                         ptr=fm_malloc(qm, size, file, func, line);
549         #else
550                         ptr=fm_malloc(qm, size);
551         #endif
552                         if (ptr){
553                                 /* copy, need by libssl */
554                                 memcpy(ptr, p, orig_size);
555         #ifdef DBG_F_MALLOC
556                                 fm_free(qm, p, file, func, line);
557         #else
558                                 fm_free(qm, p);
559         #endif
560                         }
561                         p=ptr;
562                 }
563         }else{
564                 /* do nothing */
565 #ifdef DBG_F_MALLOC
566                 MDBG("fm_realloc: doing nothing, same size: %lu - %lu\n", 
567                                 f->size, size);
568 #endif
569         }
570 #ifdef DBG_F_MALLOC
571         MDBG("fm_realloc: returning %p\n", p);
572 #endif
573         return p;
574 }
575
576
577
578 void fm_status(struct fm_block* qm)
579 {
580         struct fm_frag* f;
581         int i,j;
582         int h;
583         int unused;
584         unsigned long size;
585         int memlog;
586
587         memlog=cfg_get(core, core_cfg, memlog);
588         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", "fm_status (%p):\n", qm);
589         if (!qm) return;
590
591         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", " heap size= %ld\n",
592                         qm->size);
593 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
594         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
595                         " used= %lu, used+overhead=%lu, free=%lu\n",
596                         qm->used, qm->real_used, qm->size-qm->real_used);
597         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
598                         " max used (+overhead)= %lu\n", qm->max_real_used);
599 #endif
600         /*
601         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", "dumping all fragments:\n");
602         for (f=qm->first_frag, i=0;((char*)f<(char*)qm->last_frag) && (i<10);
603                         f=FRAG_NEXT(f), i++){
604                 LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
605                                 "    %3d. %c  address=%x  size=%d\n", i,
606                                 (f->u.reserved)?'a':'N',
607                                 (char*)f+sizeof(struct fm_frag), f->size);
608 #ifdef DBG_F_MALLOC
609                 LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
610                                 "            %s from %s: %s(%d)\n",
611                                 (f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line);
612 #endif
613         }
614 */
615         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ", "dumping free list:\n");
616         for(h=0,i=0,size=0;h<F_HASH_SIZE;h++){
617                 unused=0;
618                 for (f=qm->free_hash[h].first,j=0; f;
619                                 size+=f->size,f=f->u.nxt_free,i++,j++){
620                         if (!FRAG_WAS_USED(f)){
621                                 unused++;
622 #ifdef DBG_F_MALLOC
623                                 LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
624                                                         "unused fragm.: hash = %3d, fragment %p,"
625                                                         " address %p size %lu, created from %s: %s(%ld)\n",
626                                                     h, f, (char*)f+sizeof(struct fm_frag), f->size,
627                                                         f->file, f->func, f->line);
628 #endif
629                         };
630                 }
631                 if (j) LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
632                                                         "hash = %3d fragments no.: %5d, unused: %5d\n\t\t"
633                                                         " bucket size: %9lu - %9lu (first %9lu)\n",
634                                                         h, j, unused, UN_HASH(h),
635                                                 ((h<=F_MALLOC_OPTIMIZE/ROUNDTO)?1:2)* UN_HASH(h),
636                                                         qm->free_hash[h].first->size
637                                 );
638                 if (j!=qm->free_hash[h].no){
639                         LOG(L_CRIT, "BUG: fm_status: different free frag. count: %d!=%ld"
640                                         " for hash %3d\n", j, qm->free_hash[h].no, h);
641                 }
642                 /*
643                 {
644                         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
645                                         "   %5d.[%3d:%3d] %c  address=%x  size=%d(%x)\n",
646                                         i, h, j,
647                                         (f->u.reserved)?'a':'N',
648                                         (char*)f+sizeof(struct fm_frag), f->size, f->size);
649 #ifdef DBG_F_MALLOC
650                         DBG("            %s from %s: %s(%d)\n", 
651                                 (f->u.reserved)?"freed":"alloc'd", f->file, f->func, f->line);
652 #endif
653                 }
654         */
655         }
656         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
657                         "TOTAL: %6d free fragments = %6lu free bytes\n", i, size);
658         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
659                         "-----------------------------\n");
660 }
661
662
663
664 /* fills a malloc info structure with info about the block
665  * if a parameter is not supported, it will be filled with 0 */
666 void fm_info(struct fm_block* qm, struct mem_info* info)
667 {
668         int r;
669         long total_frags;
670 #if !defined(DBG_F_MALLOC) && !defined(MALLOC_STATS)
671         struct fm_frag* f;
672 #endif
673         
674         memset(info,0, sizeof(*info));
675         total_frags=0;
676         info->total_size=qm->size;
677         info->min_frag=MIN_FRAG_SIZE;
678 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
679         info->free=qm->size-qm->real_used;
680         info->used=qm->used;
681         info->real_used=qm->real_used;
682         info->max_used=qm->max_real_used;
683         for(r=0;r<F_HASH_SIZE; r++){
684                 total_frags+=qm->free_hash[r].no;
685         }
686 #else
687         /* we'll have to compute it all */
688         for (r=0; r<=F_MALLOC_OPTIMIZE/ROUNDTO; r++){
689                 info->free+=qm->free_hash[r].no*UN_HASH(r);
690                 total_frags+=qm->free_hash[r].no;
691         }
692         for(;r<F_HASH_SIZE; r++){
693                 total_frags+=qm->free_hash[r].no;
694                 for(f=qm->free_hash[r].first;f;f=f->u.nxt_free){
695                         info->free+=f->size;
696                 }
697         }
698         info->real_used=info->total_size-info->free;
699         info->used=0; /* we don't really now */
700         info->used=info->real_used-total_frags*FRAG_OVERHEAD-INIT_OVERHEAD-
701                                         FRAG_OVERHEAD;
702         info->max_used=0; /* we don't really now */
703 #endif
704         info->total_frags=total_frags;
705 }
706
707
708
709 /* returns how much free memory is available
710  * on error (not compiled with bookkeeping code) returns (unsigned long)(-1) */
711 unsigned long fm_available(struct fm_block* qm)
712 {
713
714 #if defined(DBG_F_MALLOC) || defined(MALLOC_STATS)
715         return qm->size-qm->real_used;
716 #else
717         /* we don't know how much free memory we have and it's to expensive
718          * to compute it */
719         return ((unsigned long)-1);
720 #endif
721 }
722
723
724 #ifdef DBG_F_MALLOC
725
726 typedef struct _mem_counter{
727         const char *file;
728         const char *func;
729         unsigned long line;
730         
731         unsigned long size;
732         int count;
733         
734         struct _mem_counter *next;
735 } mem_counter;
736
737 static mem_counter* get_mem_counter(mem_counter **root,struct fm_frag* f)
738 {
739         mem_counter *x;
740         
741         if (!*root) goto make_new;
742         for(x=*root;x;x=x->next)
743                 if (x->file == f->file && x->func == f->func && x->line == f->line)
744                         return x;
745 make_new:       
746         x = malloc(sizeof(mem_counter));
747         x->file = f->file;
748         x->func = f->func;
749         x->line = f->line;
750         x->count = 0;
751         x->size = 0;
752         x->next = *root;
753         *root = x;
754         return x;
755 }
756
757
758
759 void fm_sums(struct fm_block* qm)
760 {
761         struct fm_frag* f;
762         struct fm_frag* free_frag;
763         int i, hash;
764         int memlog;
765         mem_counter *root,*x;
766         
767         root=0;
768         if (!qm) return;
769
770         memlog=cfg_get(core, core_cfg, memlog);
771         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
772                         "summarizing all alloc'ed. fragments:\n");
773         
774         for (f=qm->first_frag, i=0; (char*)f<(char*)qm->last_frag;
775                         f=FRAG_NEXT(f), i++){
776                 if (f->u.nxt_free==0){
777                         /* it might be in-use or the last free fragm. in a free list 
778                            => search the free frags of the same size for a possible
779                            match --andrei*/
780                         hash=GET_HASH(f->size);
781                         for(free_frag=qm->free_hash[hash].first;
782                                         free_frag && (free_frag!=f);
783                                         free_frag=free_frag->u.nxt_free);
784                         if (free_frag==0){ /* not found among the free frag */
785                                 x = get_mem_counter(&root,f);
786                                 x->count++;
787                                 x->size+=f->size;
788                         }
789                 }
790         }
791         x = root;
792         while(x){
793                 LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
794                                 " count=%6d size=%10lu bytes from %s: %s(%ld)\n",
795                         x->count,x->size,
796                         x->file, x->func, x->line
797                         );
798                 root = x->next;
799                 free(x);
800                 x = root;
801         }
802         LOG_(DEFAULT_FACILITY, memlog, "fm_status: ",
803                         "-----------------------------\n");
804 }
805 #endif /* DBG_F_MALLOC */
806
807
808
809 #endif