q_malloc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 FhG Fokus
00003  *
00004  * This file is part of sip-router, a free SIP server.
00005  *
00006  * Permission to use, copy, modify, and distribute this software for any
00007  * purpose with or without fee is hereby granted, provided that the above
00008  * copyright notice and this permission notice appear in all copies.
00009  *
00010  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00011  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00012  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00013  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00014  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00015  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00016  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00017  */
00018 
00019 /*
00020  * History:
00021  * --------
00022  *  ????-??-??  created by andrei
00023  *  2003-04-14  more debugging added in DBG_QM_MALLOC mode (andrei)
00024  *  2003-06-29  added qm_realloc (andrei)
00025  *  2004-07-19  fragments book keeping code and support for 64 bits
00026  *               memory blocks (64 bits machine & size>=2^32) (andrei)
00027  *              GET_HASH s/</<=/ (avoids waste of 1 hash cell) (andrei)
00028  *  2004-11-10  support for > 4Gb mem., switched to long (andrei)
00029  *  2005-03-02  added qm_info() (andrei)
00030  *  2005-12-12  fixed realloc shrink real_used & used accounting;
00031  *              fixed initial size (andrei)
00032  *  2006-02-03  fixed realloc out of mem. free bug (andrei)
00033  *  2006-04-07  s/DBG/MDBG (andrei)
00034  *  2007-02-23  added fm_available() (andrei)
00035  *  2009-09-28  added fm_sums() (patch from Dragos Vingarzan)
00036  */
00037 
00045 #if !defined(q_malloc) && !(defined F_MALLOC)
00046 #define q_malloc
00047 
00048 #include <stdlib.h>
00049 #include <string.h>
00050 
00051 #include "q_malloc.h"
00052 #include "../dprint.h"
00053 #include "../globals.h"
00054 #include "memdbg.h"
00055 #include "../cfg/cfg.h" /* memlog */
00056 #ifdef MALLOC_STATS
00057 #include "../events.h"
00058 #endif
00059 
00060 
00061 /*useful macros*/
00062 #define FRAG_END(f)  \
00063         ((struct qm_frag_end*)((char*)(f)+sizeof(struct qm_frag)+ \
00064            (f)->size))
00065 
00066 #define FRAG_NEXT(f) \
00067         ((struct qm_frag*)((char*)(f)+sizeof(struct qm_frag)+(f)->size+ \
00068            sizeof(struct qm_frag_end)))
00069                         
00070 #define FRAG_PREV(f) \
00071         ( (struct qm_frag*) ( ((char*)(f)-sizeof(struct qm_frag_end))- \
00072         ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))->size- \
00073            sizeof(struct qm_frag) ) )
00074 
00075 #define PREV_FRAG_END(f) \
00076         ((struct qm_frag_end*)((char*)(f)-sizeof(struct qm_frag_end)))
00077 
00078 
00079 #define FRAG_OVERHEAD   (sizeof(struct qm_frag)+sizeof(struct qm_frag_end))
00080 
00081 
00082 #define ROUNDTO_MASK    (~((unsigned long)ROUNDTO-1))
00083 #define ROUNDUP(s)              (((s)+(ROUNDTO-1))&ROUNDTO_MASK)
00084 #define ROUNDDOWN(s)    ((s)&ROUNDTO_MASK)
00085 
00086 
00087 
00088         /* finds the hash value for s, s=ROUNDTO multiple*/
00089 #define GET_HASH(s)   ( ((unsigned long)(s)<=QM_MALLOC_OPTIMIZE)?\
00090                                                         (unsigned long)(s)/ROUNDTO: \
00091                                                         QM_MALLOC_OPTIMIZE/ROUNDTO+big_hash_idx((s))- \
00092                                                                 QM_MALLOC_OPTIMIZE_FACTOR+1 )
00093 
00094 #define UN_HASH(h)      ( ((unsigned long)(h)<=(QM_MALLOC_OPTIMIZE/ROUNDTO))?\
00095                                                         (unsigned long)(h)*ROUNDTO: \
00096                                                         1UL<<((h)-QM_MALLOC_OPTIMIZE/ROUNDTO+\
00097                                                                 QM_MALLOC_OPTIMIZE_FACTOR-1)\
00098                                         )
00099 
00100 
00101 /* mark/test used/unused frags */
00102 #define FRAG_MARK_USED(f)
00103 #define FRAG_CLEAR_USED(f)
00104 #define FRAG_WAS_USED(f)   (1)
00105 
00106 /* other frag related defines:
00107  * MEM_COALESCE_FRAGS 
00108  * MEM_FRAG_AVOIDANCE
00109  */
00110 
00111 #define MEM_FRAG_AVOIDANCE
00112 
00113 
00114 /* computes hash number for big buckets*/
00115 inline static unsigned long big_hash_idx(unsigned long s)
00116 {
00117         int idx;
00118         /* s is rounded => s = k*2^n (ROUNDTO=2^n) 
00119          * index= i such that 2^i > s >= 2^(i-1)
00120          *
00121          * => index = number of the first non null bit in s*/
00122         idx=sizeof(long)*8-1;
00123         for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--);
00124         return idx;
00125 }
00126 
00127 
00128 #ifdef DBG_QM_MALLOC
00129 #define ST_CHECK_PATTERN   0xf0f0f0f0
00130 #define END_CHECK_PATTERN1 0xc0c0c0c0
00131 #define END_CHECK_PATTERN2 0xabcdefed
00132 
00133 
00134 static  void qm_debug_frag(struct qm_block* qm, struct qm_frag* f)
00135 {
00136         if (f->check!=ST_CHECK_PATTERN){
00137                 LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
00138                                 "beginning overwritten(%lx)!\n",
00139                                 f, (char*)f+sizeof(struct qm_frag),
00140                                 f->check);
00141                 qm_status(qm);
00142                 abort();
00143         };
00144         if ((FRAG_END(f)->check1!=END_CHECK_PATTERN1)||
00145                 (FRAG_END(f)->check2!=END_CHECK_PATTERN2)){
00146                 LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)"
00147                                         " end overwritten(%lx, %lx)!\n",
00148                                 f, (char*)f+sizeof(struct qm_frag), 
00149                                 FRAG_END(f)->check1, FRAG_END(f)->check2);
00150                 qm_status(qm);
00151                 abort();
00152         }
00153         if ((f>qm->first_frag)&&
00154                         ((PREV_FRAG_END(f)->check1!=END_CHECK_PATTERN1) ||
00155                                 (PREV_FRAG_END(f)->check2!=END_CHECK_PATTERN2) ) ){
00156                 LOG(L_CRIT, "BUG: qm_*: prev. fragm. tail overwritten(%lx, %lx)[%p:%p]!"
00157                                         "\n",
00158                                 PREV_FRAG_END(f)->check1, PREV_FRAG_END(f)->check2, f,
00159                                 (char*)f+sizeof(struct qm_frag));
00160                 qm_status(qm);
00161                 abort();
00162         }
00163 }
00164 #endif
00165 
00166 
00167 
00168 static inline void qm_insert_free(struct qm_block* qm, struct qm_frag* frag)
00169 {
00170         struct qm_frag* f;
00171         struct qm_frag* prev;
00172         int hash;
00173         
00174         hash=GET_HASH(frag->size);
00175         for(f=qm->free_hash[hash].head.u.nxt_free; f!=&(qm->free_hash[hash].head);
00176                         f=f->u.nxt_free){
00177                 if (frag->size <= f->size) break;
00178         }
00179         /*insert it here*/
00180         prev=FRAG_END(f)->prev_free;
00181         prev->u.nxt_free=frag;
00182         FRAG_END(frag)->prev_free=prev;
00183         frag->u.nxt_free=f;
00184         FRAG_END(f)->prev_free=frag;
00185         qm->free_hash[hash].no++;
00186 }
00187 
00188 
00189 
00190 /* init malloc and return a qm_block*/
00191 struct qm_block* qm_malloc_init(char* address, unsigned long size)
00192 {
00193         char* start;
00194         char* end;
00195         struct qm_block* qm;
00196         unsigned long init_overhead;
00197         int h;
00198         
00199         /* make address and size multiple of 8*/
00200         start=(char*)ROUNDUP((unsigned long) address);
00201         DBG("qm_malloc_init: QM_OPTIMIZE=%lu, /ROUNDTO=%lu\n",
00202                         QM_MALLOC_OPTIMIZE, QM_MALLOC_OPTIMIZE/ROUNDTO);
00203         DBG("qm_malloc_init: QM_HASH_SIZE=%lu, qm_block size=%lu\n",
00204                         QM_HASH_SIZE, (long)sizeof(struct qm_block));
00205         DBG("qm_malloc_init(%p, %lu), start=%p\n", address, size, start);
00206         if (size<start-address) return 0;
00207         size-=(start-address);
00208         if (size <(MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
00209         size=ROUNDDOWN(size);
00210         
00211         init_overhead=ROUNDUP(sizeof(struct qm_block))+sizeof(struct qm_frag)+
00212                 sizeof(struct qm_frag_end);
00213         DBG("qm_malloc_init: size= %lu, init_overhead=%lu\n", size, init_overhead);
00214         
00215         if (size < init_overhead)
00216         {
00217                 /* not enough mem to create our control structures !!!*/
00218                 return 0;
00219         }
00220         end=start+size;
00221         qm=(struct qm_block*)start;
00222         memset(qm, 0, sizeof(struct qm_block));
00223         qm->size=size;
00224         qm->real_used=init_overhead;
00225         qm->max_real_used=qm->real_used;
00226         size-=init_overhead;
00227         
00228         qm->first_frag=(struct qm_frag*)(start+ROUNDUP(sizeof(struct qm_block)));
00229         qm->last_frag_end=(struct qm_frag_end*)(end-sizeof(struct qm_frag_end));
00230         /* init initial fragment*/
00231         qm->first_frag->size=size;
00232         qm->last_frag_end->size=size;
00233         
00234 #ifdef DBG_QM_MALLOC
00235         qm->first_frag->check=ST_CHECK_PATTERN;
00236         qm->last_frag_end->check1=END_CHECK_PATTERN1;
00237         qm->last_frag_end->check2=END_CHECK_PATTERN2;
00238 #endif
00239         /* init free_hash* */
00240         for (h=0; h<QM_HASH_SIZE;h++){
00241                 qm->free_hash[h].head.u.nxt_free=&(qm->free_hash[h].head);
00242                 qm->free_hash[h].tail.prev_free=&(qm->free_hash[h].head);
00243                 qm->free_hash[h].head.size=0;
00244                 qm->free_hash[h].tail.size=0;
00245         }
00246         
00247         /* link initial fragment into the free list*/
00248         
00249         qm_insert_free(qm, qm->first_frag);
00250         
00251         /*qm->first_frag->u.nxt_free=&(qm->free_lst);
00252           qm->last_frag_end->prev_free=&(qm->free_lst);
00253         */
00254         
00255         
00256         return qm;
00257 }
00258 
00259 
00260 
00261 static inline void qm_detach_free(struct qm_block* qm, struct qm_frag* frag)
00262 {
00263         struct qm_frag *prev;
00264         struct qm_frag *next;
00265         
00266         prev=FRAG_END(frag)->prev_free;
00267         next=frag->u.nxt_free;
00268         prev->u.nxt_free=next;
00269         FRAG_END(next)->prev_free=prev;
00270         
00271 }
00272 
00273 
00274 
00275 #ifdef DBG_QM_MALLOC
00276 static inline struct qm_frag* qm_find_free(struct qm_block* qm, 
00277                                                                                         unsigned long size,
00278                                                                                         int *h,
00279                                                                                         unsigned int *count)
00280 #else
00281 static inline struct qm_frag* qm_find_free(struct qm_block* qm, 
00282                                                                                         unsigned long size,
00283                                                                                         int* h)
00284 #endif
00285 {
00286         int hash;
00287         struct qm_frag* f;
00288 
00289         for (hash=GET_HASH(size); hash<QM_HASH_SIZE; hash++){
00290                 for (f=qm->free_hash[hash].head.u.nxt_free; 
00291                                         f!=&(qm->free_hash[hash].head); f=f->u.nxt_free){
00292 #ifdef DBG_QM_MALLOC
00293                         *count+=1; /* *count++ generates a warning with gcc 2.9* -Wall */
00294 #endif
00295                         if (f->size>=size){ *h=hash; return f; }
00296                 }
00297         /*try in a bigger bucket*/
00298         }
00299         /* not found */
00300         return 0;
00301 }
00302 
00303 
00304 /* returns 0 on success, -1 on error;
00305  * new_size < size & rounded-up already!*/
00306 static inline
00307 #ifdef DBG_QM_MALLOC
00308 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size,
00309                                 const char* file, const char* func, unsigned int line)
00310 #else
00311 int split_frag(struct qm_block* qm, struct qm_frag* f, unsigned long new_size)
00312 #endif
00313 {
00314         unsigned long rest;
00315         struct qm_frag* n;
00316         struct qm_frag_end* end;
00317         
00318         rest=f->size-new_size;
00319 #ifdef MEM_FRAG_AVOIDANCE
00320         if ((rest> (FRAG_OVERHEAD+QM_MALLOC_OPTIMIZE))||
00321                 (rest>=(FRAG_OVERHEAD+new_size))){/* the residue fragm. is big enough*/
00322 #else
00323         if (rest>(FRAG_OVERHEAD+MIN_FRAG_SIZE)){
00324 #endif
00325                 f->size=new_size;
00326                 /*split the fragment*/
00327                 end=FRAG_END(f);
00328                 end->size=new_size;
00329                 n=(struct qm_frag*)((char*)end+sizeof(struct qm_frag_end));
00330                 n->size=rest-FRAG_OVERHEAD;
00331                 FRAG_END(n)->size=n->size;
00332                 FRAG_CLEAR_USED(n); /* never used */
00333                 qm->real_used+=FRAG_OVERHEAD;
00334 #ifdef DBG_QM_MALLOC
00335                 end->check1=END_CHECK_PATTERN1;
00336                 end->check2=END_CHECK_PATTERN2;
00337                 /* frag created by malloc, mark it*/
00338                 n->file=file;
00339                 n->func=func;
00340                 n->line=line;
00341                 n->check=ST_CHECK_PATTERN;
00342 #endif
00343                 /* reinsert n in free list*/
00344                 qm_insert_free(qm, n);
00345                 return 0;
00346         }else{
00347                         /* we cannot split this fragment any more */
00348                 return -1;
00349         }
00350 }
00351 
00352 
00353 
00354 #ifdef DBG_QM_MALLOC
00355 void* qm_malloc(struct qm_block* qm, unsigned long size,
00356                                         const char* file, const char* func, unsigned int line)
00357 #else
00358 void* qm_malloc(struct qm_block* qm, unsigned long size)
00359 #endif
00360 {
00361         struct qm_frag* f;
00362         int hash;
00363         
00364 #ifdef DBG_QM_MALLOC
00365         unsigned int list_cntr;
00366 
00367         list_cntr = 0;
00368         MDBG("qm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func,
00369                         line);
00370 #endif
00371         /*size must be a multiple of 8*/
00372         size=ROUNDUP(size);
00373         if (size>(qm->size-qm->real_used)) return 0;
00374 
00375         /*search for a suitable free frag*/
00376 #ifdef DBG_QM_MALLOC
00377         if ((f=qm_find_free(qm, size, &hash, &list_cntr))!=0){
00378 #else
00379         if ((f=qm_find_free(qm, size, &hash))!=0){
00380 #endif
00381                 /* we found it!*/
00382                 /*detach it from the free list*/
00383 #ifdef DBG_QM_MALLOC
00384                         qm_debug_frag(qm, f);
00385 #endif
00386                 qm_detach_free(qm, f);
00387                 /*mark it as "busy"*/
00388                 f->u.is_free=0;
00389                 qm->free_hash[hash].no--;
00390                 /* we ignore split return */
00391 #ifdef DBG_QM_MALLOC
00392                 split_frag(qm, f, size, file, "fragm. from qm_malloc", line);
00393 #else
00394                 split_frag(qm, f, size);
00395 #endif
00396                 qm->real_used+=f->size;
00397                 qm->used+=f->size;
00398                 if (qm->max_real_used<qm->real_used)
00399                         qm->max_real_used=qm->real_used;
00400 #ifdef MALLOC_STATS
00401                 sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
00402                 sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
00403 #endif
00404 #ifdef DBG_QM_MALLOC
00405                 f->file=file;
00406                 f->func=func;
00407                 f->line=line;
00408                 f->check=ST_CHECK_PATTERN;
00409                 /*  FRAG_END(f)->check1=END_CHECK_PATTERN1;
00410                         FRAG_END(f)->check2=END_CHECK_PATTERN2;*/
00411                 MDBG("qm_malloc(%p, %lu) returns address %p frag. %p (size=%lu) on %d"
00412                                 " -th hit\n",
00413                          qm, size, (char*)f+sizeof(struct qm_frag), f, f->size, list_cntr );
00414 #endif
00415                 return (char*)f+sizeof(struct qm_frag);
00416         }
00417         return 0;
00418 }
00419 
00420 
00421 
00422 #ifdef DBG_QM_MALLOC
00423 void qm_free(struct qm_block* qm, void* p, const char* file, const char* func, 
00424                                 unsigned int line)
00425 #else
00426 void qm_free(struct qm_block* qm, void* p)
00427 #endif
00428 {
00429         struct qm_frag* f;
00430         unsigned long size;
00431 #ifdef MEM_JOIN_FREE
00432         struct qm_frag* next;
00433         struct qm_frag* prev;
00434 #endif /* MEM_JOIN_FREE*/
00435 
00436 #ifdef DBG_QM_MALLOC
00437         MDBG("qm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func, line);
00438         if (p>(void*)qm->last_frag_end || p<(void*)qm->first_frag){
00439                 LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!)"
00440                                 " called from %s: %s(%d) - aborting\n", p, file, func, line);
00441                 if(likely(cfg_get(core, core_cfg, mem_safety)==0))
00442                         abort();
00443         }
00444 #endif
00445         if (p==0) {
00446                 LOG(L_WARN, "WARNING:qm_free: free(0) called\n");
00447                 return;
00448         }
00449         f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
00450 #ifdef DBG_QM_MALLOC
00451         qm_debug_frag(qm, f);
00452         if (f->u.is_free){
00453                 LOG(L_CRIT, "BUG: qm_free: freeing already freed pointer (%p),"
00454                                 " called from %s: %s(%d), first free %s: %s(%ld) - aborting\n",
00455                                 p, file, func, line, f->file, f->func, f->line);
00456                 if(likely(cfg_get(core, core_cfg, mem_safety)==0))
00457                         abort();
00458         }
00459         MDBG("qm_free: freeing frag. %p alloc'ed from %s: %s(%ld)\n",
00460                         f, f->file, f->func, f->line);
00461 #endif
00462         size=f->size;
00463         qm->used-=size;
00464         qm->real_used-=size;
00465 #ifdef MALLOC_STATS
00466         sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
00467         sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
00468 #endif
00469 
00470 #ifdef MEM_JOIN_FREE
00471         if(unlikely(cfg_get(core, core_cfg, mem_join)!=0)) {
00472                 next=prev=0;
00473                 /* mark this fragment as used (might fall into the middle of joined frags)
00474                   to give us an extra chance of detecting a double free call (if the joined
00475                   fragment has not yet been reused) */
00476                 f->u.nxt_free=(void*)0x1L; /* bogus value, just to mark it as free */
00477                 /* join packets if possible*/
00478                 next=FRAG_NEXT(f);
00479                 if (((char*)next < (char*)qm->last_frag_end) &&( next->u.is_free)){
00480                 /* join */
00481 #ifdef DBG_QM_MALLOC
00482                         qm_debug_frag(qm, next);
00483 #endif
00484                         qm_detach_free(qm, next);
00485                         size+=next->size+FRAG_OVERHEAD;
00486                         qm->real_used-=FRAG_OVERHEAD;
00487                         qm->free_hash[GET_HASH(next->size)].no--; /* FIXME slow */
00488                 }
00489         
00490                 if (f > qm->first_frag){
00491                         prev=FRAG_PREV(f);
00492                         /*      (struct qm_frag*)((char*)f - (struct qm_frag_end*)((char*)f-
00493                                                                 sizeof(struct qm_frag_end))->size);*/
00494 #ifdef DBG_QM_MALLOC
00495                         qm_debug_frag(qm, prev);
00496 #endif
00497                         if (prev->u.is_free){
00498                                 /*join*/
00499                                 qm_detach_free(qm, prev);
00500                                 size+=prev->size+FRAG_OVERHEAD;
00501                                 qm->real_used-=FRAG_OVERHEAD;
00502                                         qm->free_hash[GET_HASH(prev->size)].no--; /* FIXME slow */
00503                                 f=prev;
00504                         }
00505                 }
00506                 f->size=size;
00507                 FRAG_END(f)->size=f->size;
00508         } /* if cfg_core->mem_join */
00509 #endif /* MEM_JOIN_FREE*/
00510 #ifdef DBG_QM_MALLOC
00511         f->file=file;
00512         f->func=func;
00513         f->line=line;
00514 #endif
00515         qm_insert_free(qm, f);
00516 }
00517 
00518 
00519 
00520 #ifdef DBG_QM_MALLOC
00521 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size,
00522                                         const char* file, const char* func, unsigned int line)
00523 #else
00524 void* qm_realloc(struct qm_block* qm, void* p, unsigned long size)
00525 #endif
00526 {
00527         struct qm_frag* f;
00528         unsigned long diff;
00529         unsigned long orig_size;
00530         struct qm_frag* n;
00531         void* ptr;
00532         
00533         
00534 #ifdef DBG_QM_MALLOC
00535         MDBG("qm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size,
00536                         file, func, line);
00537         if ((p)&&(p>(void*)qm->last_frag_end || p<(void*)qm->first_frag)){
00538                 LOG(L_CRIT, "BUG: qm_free: bad pointer %p (out of memory block!) - "
00539                                 "aborting\n", p);
00540                 abort();
00541         }
00542 #endif
00543         
00544         if (size==0) {
00545                 if (p)
00546 #ifdef DBG_QM_MALLOC
00547                         qm_free(qm, p, file, func, line);
00548 #else
00549                         qm_free(qm, p);
00550 #endif
00551                 return 0;
00552         }
00553         if (p==0)
00554 #ifdef DBG_QM_MALLOC
00555                 return qm_malloc(qm, size, file, func, line);
00556 #else
00557                 return qm_malloc(qm, size);
00558 #endif
00559         f=(struct qm_frag*) ((char*)p-sizeof(struct qm_frag));
00560 #ifdef DBG_QM_MALLOC
00561         qm_debug_frag(qm, f);
00562         MDBG("qm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
00563                         f, f->file, f->func, f->line);
00564         if (f->u.is_free){
00565                 LOG(L_CRIT, "BUG:qm_realloc: trying to realloc an already freed "
00566                                 "pointer %p , fragment %p -- aborting\n", p, f);
00567                 abort();
00568         }
00569 #endif
00570         /* find first acceptable size */
00571         size=ROUNDUP(size);
00572         if (f->size > size){
00573                 orig_size=f->size;
00574                 /* shrink */
00575 #ifdef DBG_QM_MALLOC
00576                 MDBG("qm_realloc: shrinking from %lu to %lu\n", f->size, size);
00577                 if(split_frag(qm, f, size, file, "fragm. from qm_realloc", line)!=0){
00578                 MDBG("qm_realloc : shrinked successful\n");
00579 #else
00580                 if(split_frag(qm, f, size)!=0){
00581 #endif
00582                         /* update used sizes: freed the splited frag */
00583                         /* split frag already adds FRAG_OVERHEAD for the newly created
00584                            free frag, so here we only need orig_size-f->size for real used
00585                          */
00586                         qm->real_used-=(orig_size-f->size);
00587                         qm->used-=(orig_size-f->size);
00588 #ifdef MALLOC_STATS
00589                         sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
00590                         sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
00591 #endif
00592                 }
00593                 
00594         }else if (f->size < size){
00595                 /* grow */
00596 #ifdef DBG_QM_MALLOC
00597                 MDBG("qm_realloc: growing from %lu to %lu\n", f->size, size);
00598 #endif
00599                         orig_size=f->size;
00600                         diff=size-f->size;
00601                         n=FRAG_NEXT(f);
00602                         if (((char*)n < (char*)qm->last_frag_end) && 
00603                                         (n->u.is_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
00604                                 /* join  */
00605                                 qm_detach_free(qm, n);
00606                                 qm->free_hash[GET_HASH(n->size)].no--; /*FIXME: slow*/
00607                                 f->size+=n->size+FRAG_OVERHEAD;
00608                                 qm->real_used-=FRAG_OVERHEAD;
00609                                 FRAG_END(f)->size=f->size;
00610                                 /* end checks should be ok */
00611                                 /* split it if necessary */
00612                                 if (f->size > size ){
00613         #ifdef DBG_QM_MALLOC
00614                                         split_frag(qm, f, size, file, "fragm. from qm_realloc",
00615                                                                                 line);
00616         #else
00617                                         split_frag(qm, f, size);
00618         #endif
00619                                 }
00620                                 qm->real_used+=(f->size-orig_size);
00621                                 qm->used+=(f->size-orig_size);
00622 #ifdef MALLOC_STATS
00623                                 sr_event_exec(SREV_PKG_SET_USED, (void*)qm->used);
00624                                 sr_event_exec(SREV_PKG_SET_REAL_USED, (void*)qm->real_used);
00625 #endif
00626                         }else{
00627                                 /* could not join => realloc */
00628         #ifdef DBG_QM_MALLOC
00629                                 ptr=qm_malloc(qm, size, file, func, line);
00630         #else
00631                                 ptr=qm_malloc(qm, size);
00632         #endif
00633                                 if (ptr){
00634                                         /* copy, need by libssl */
00635                                         memcpy(ptr, p, orig_size);
00636         #ifdef DBG_QM_MALLOC
00637                                         qm_free(qm, p, file, func, line);
00638         #else
00639                                         qm_free(qm, p);
00640         #endif
00641                                 }
00642                                 p=ptr;
00643                         }
00644         }else{
00645                 /* do nothing */
00646 #ifdef DBG_QM_MALLOC
00647                 MDBG("qm_realloc: doing nothing, same size: %lu - %lu\n",
00648                                 f->size, size);
00649 #endif
00650         }
00651 #ifdef DBG_QM_MALLOC
00652         MDBG("qm_realloc: returning %p\n", p);
00653 #endif
00654         return p;
00655 }
00656 
00657 
00658 void qm_check(struct qm_block* qm)
00659 {
00660         struct qm_frag* f;
00661         long fcount = 0;
00662         int memlog;
00663         
00664         memlog=cfg_get(core, core_cfg, memlog);
00665         LOG(memlog, "DEBUG: qm_check()\n");
00666         f = qm->first_frag;
00667         while ((char*)f < (char*)qm->last_frag_end) {
00668                 fcount++;
00669                 /* check struct qm_frag */
00670 #ifdef DBG_QM_MALLOC
00671                 if (f->check!=ST_CHECK_PATTERN){
00672                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
00673                                         "beginning overwritten(%lx)!\n",
00674                                         f, (char*)f + sizeof(struct qm_frag),
00675                                         f->check);
00676                         qm_status(qm);
00677                         abort();
00678                 };
00679 #endif
00680                 if (f + sizeof(struct qm_frag) + f->size + sizeof(struct qm_frag_end) > qm->first_frag + qm->size) {
00681                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
00682                                 "bad size: %lu (frag end: %p > end of block: %p)\n",
00683                                 f, (char*)f + sizeof(struct qm_frag) + sizeof(struct qm_frag_end), f->size,
00684                                 f + sizeof(struct qm_frag) + f->size, qm->first_frag + qm->size);
00685                         qm_status(qm);
00686                         abort();
00687                 }
00688                 /* check struct qm_frag_end */
00689                 if (FRAG_END(f)->size != f->size) {
00690                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p) "
00691                                 "size in qm_frag and qm_frag_end does not match: frag->size=%lu, frag_end->size=%lu)\n",
00692                                 f, (char*)f + sizeof(struct qm_frag),
00693                                 f->size, FRAG_END(f)->size);
00694                         qm_status(qm);
00695                         abort();
00696                 }
00697 #ifdef DBG_QM_MALLOC
00698                 if ((FRAG_END(f)->check1 != END_CHECK_PATTERN1) ||
00699                         (FRAG_END(f)->check2 != END_CHECK_PATTERN2)) {
00700                         LOG(L_CRIT, "BUG: qm_*: fragm. %p (address %p)"
00701                                                 " end overwritten(%lx, %lx)!\n",
00702                                         f, (char*)f + sizeof(struct qm_frag), 
00703                                         FRAG_END(f)->check1, FRAG_END(f)->check2);
00704                         qm_status(qm);
00705                         abort();
00706                 }
00707 #endif
00708                 f = FRAG_NEXT(f);
00709         }
00710 
00711         LOG(memlog, "DEBUG: qm_check: %lu fragments OK\n", fcount);
00712 }
00713 
00714 void qm_status(struct qm_block* qm)
00715 {
00716         struct qm_frag* f;
00717         int i,j;
00718         int h;
00719         int unused;
00720         int memlog;
00721         int mem_summary;
00722 
00723         memlog=cfg_get(core, core_cfg, memlog);
00724         mem_summary=cfg_get(core, core_cfg, mem_summary);
00725         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "(%p):\n", qm);
00726         if (!qm) return;
00727 
00728         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ", "heap size= %lu\n",
00729                         qm->size);
00730         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00731                         "used= %lu, used+overhead=%lu, free=%lu\n",
00732                         qm->used, qm->real_used, qm->size-qm->real_used);
00733         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00734                         "max used (+overhead)= %lu\n", qm->max_real_used);
00735         
00736         if (mem_summary & 16) return;
00737 
00738         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00739                         "dumping all alloc'ed. fragments:\n");
00740         for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;f=FRAG_NEXT(f)
00741                         ,i++){
00742                 if (! f->u.is_free){
00743                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00744                                         "   %3d. %c  address=%p frag=%p size=%lu used=%d\n",
00745                                 i,
00746                                 (f->u.is_free)?'a':'N',
00747                                 (char*)f+sizeof(struct qm_frag), f, f->size, FRAG_WAS_USED(f));
00748 #ifdef DBG_QM_MALLOC
00749                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00750                                         "          %s from %s: %s(%ld)\n",
00751                                 (f->u.is_free)?"freed":"alloc'd", f->file, f->func, f->line);
00752                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00753                                         "         start check=%lx, end check= %lx, %lx\n",
00754                                 f->check, FRAG_END(f)->check1, FRAG_END(f)->check2);
00755 #endif
00756                 }
00757         }
00758         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00759                         "dumping free list stats :\n");
00760         for(h=0,i=0;h<QM_HASH_SIZE;h++){
00761                 unused=0;
00762                 for (f=qm->free_hash[h].head.u.nxt_free,j=0; 
00763                                 f!=&(qm->free_hash[h].head); f=f->u.nxt_free, i++, j++){
00764                                 if (!FRAG_WAS_USED(f)){
00765                                         unused++;
00766 #ifdef DBG_QM_MALLOC
00767                                         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00768                                                 "unused fragm.: hash = %3d, fragment %p,"
00769                                                 " address %p size %lu, created from %s: %s(%lu)\n",
00770                                             h, f, (char*)f+sizeof(struct qm_frag), f->size,
00771                                                 f->file, f->func, f->line);
00772 #endif
00773                                 }
00774                 }
00775 
00776                 if (j) LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00777                                 "hash= %3d. fragments no.: %5d, unused: %5d\n"
00778                                         "\t\t bucket size: %9lu - %9ld (first %9lu)\n",
00779                                         h, j, unused, UN_HASH(h),
00780                                         ((h<=QM_MALLOC_OPTIMIZE/ROUNDTO)?1:2)*UN_HASH(h),
00781                                         qm->free_hash[h].head.u.nxt_free->size
00782                                 );
00783                 if (j!=qm->free_hash[h].no){
00784                         LOG(L_CRIT, "BUG: qm_status: different free frag. count: %d!=%lu"
00785                                 " for hash %3d\n", j, qm->free_hash[h].no, h);
00786                 }
00787 
00788         }
00789         LOG_(DEFAULT_FACILITY, memlog, "qm_status: ",
00790                         "-----------------------------\n");
00791 }
00792 
00793 
00794 /* fills a malloc info structure with info about the block
00795  * if a parameter is not supported, it will be filled with 0 */
00796 void qm_info(struct qm_block* qm, struct mem_info* info)
00797 {
00798         int r;
00799         long total_frags;
00800         
00801         total_frags=0;
00802         memset(info,0, sizeof(*info));
00803         info->total_size=qm->size;
00804         info->min_frag=MIN_FRAG_SIZE;
00805         info->free=qm->size-qm->real_used;
00806         info->used=qm->used;
00807         info->real_used=qm->real_used;
00808         info->max_used=qm->max_real_used;
00809         for(r=0;r<QM_HASH_SIZE; r++){
00810                 total_frags+=qm->free_hash[r].no;
00811         }
00812         info->total_frags=total_frags;
00813 }
00814 
00815 
00816 /* returns how much free memory is available
00817  * it never returns an error (unlike fm_available) */
00818 unsigned long qm_available(struct qm_block* qm)
00819 {
00820         return qm->size-qm->real_used;
00821 }
00822 
00823 
00824 
00825 #ifdef DBG_QM_MALLOC
00826 
00827 typedef struct _mem_counter{
00828         const char *file;
00829         const char *func;
00830         unsigned long line;
00831         
00832         unsigned long size;
00833         int count;
00834         
00835         struct _mem_counter *next;
00836 } mem_counter;
00837 
00838 static mem_counter* get_mem_counter(mem_counter **root, struct qm_frag* f)
00839 {
00840         mem_counter *x;
00841         if (!*root) goto make_new;
00842         for(x=*root;x;x=x->next)
00843                 if (x->file == f->file && x->func == f->func && x->line == f->line)
00844                         return x;
00845 make_new:       
00846         x = malloc(sizeof(mem_counter));
00847         x->file = f->file;
00848         x->func = f->func;
00849         x->line = f->line;
00850         x->count = 0;
00851         x->size = 0;
00852         x->next = *root;
00853         *root = x;
00854         return x;
00855 }
00856 
00857 
00858 
00859 void qm_sums(struct qm_block* qm)
00860 {
00861         struct qm_frag* f;
00862         int i;
00863         mem_counter *root, *x;
00864         int memlog;
00865         
00866         root=0;
00867         if (!qm) return;
00868         
00869         memlog=cfg_get(core, core_cfg, memlog);
00870         LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
00871                         "summarizing all alloc'ed. fragments:\n");
00872         
00873         for (f=qm->first_frag, i=0;(char*)f<(char*)qm->last_frag_end;
00874                         f=FRAG_NEXT(f),i++){
00875                 if (! f->u.is_free){
00876                         x = get_mem_counter(&root,f);
00877                         x->count++;
00878                         x->size+=f->size;
00879                 }
00880         }
00881         x = root;
00882         while(x){
00883                 LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
00884                                 " count=%6d size=%10lu bytes from %s: %s(%ld)\n",
00885                         x->count,x->size,
00886                         x->file, x->func, x->line
00887                         );
00888                 root = x->next;
00889                 free(x);
00890                 x = root;
00891         }
00892         LOG_(DEFAULT_FACILITY, memlog, "qm_sums: ",
00893                         "-----------------------------\n");
00894 }
00895 #endif /* DBG_QM_MALLOC */
00896 
00897 
00898 #endif