00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #ifdef HAVE_CONFIG_H
00026 # include <config.h>
00027 #endif
00028
00029
00030
00031
00032
00033 #include "memory_p.h"
00034 #include <gwenhywfar/gwenhywfarapi.h>
00035 #include <gwenhywfar/types.h>
00036 #include <gwenhywfar/stringlist.h>
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 #ifdef HAVE_STRINGS_H
00041 # include <strings.h>
00042 #endif
00043 #include <assert.h>
00044
00045 #ifdef HAVE_UNISTD_H
00046 # include <unistd.h>
00047 #endif
00048
00049
00050 static GWEN_MEMORY_TABLE *gwen_memory__first_table=0;
00051 static int gwen_memory__debug=0;
00052 static int gwen_memory__nofree=0;
00053 static int gwen_memory__verbous=0;
00054 static size_t gwen_memory__allocated_bytes=0;
00055 static size_t gwen_memory__allocated_calls=0;
00056 static size_t gwen_memory__allocated_reused=0;
00057
00058 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00059 static size_t gwen_memory__released_since_collect=0;
00060 #endif
00061
00062
00063
00064 int GWEN_Memory_ModuleInit(void){
00065 const char *s;
00066
00067 s=getenv(GWEN_MEMORY_ENV_DEBUG);
00068 if (s) {
00069 fprintf(stderr, "Memory debugging is enabled\n");
00070 gwen_memory__debug=1;
00071 gwen_memory__nofree=(getenv(GWEN_MEMORY_ENV_NO_FREE)!=0);
00072 gwen_memory__verbous=(getenv(GWEN_MEMORY_ENV_VERBOUS)!=0);
00073 }
00074 return 0;
00075 }
00076
00077
00078
00079 int GWEN_Memory_ModuleFini(void){
00080 GWEN_MEMORY_TABLE *mt;
00081
00082 mt=gwen_memory__first_table;
00083 while(mt) {
00084 GWEN_MEMORY_TABLE *next;
00085
00086 next=mt->next;
00087 GWEN_Memory_Table_free(mt);
00088 mt=next;
00089 }
00090
00091 if (gwen_memory__verbous) {
00092 size_t avg=0;
00093 size_t bytes;
00094 const char *suffix;
00095
00096 if (gwen_memory__allocated_calls)
00097 avg=gwen_memory__allocated_bytes/gwen_memory__allocated_calls;
00098
00099 if (gwen_memory__allocated_bytes>(1024*1024)) {
00100 bytes=gwen_memory__allocated_bytes/(1024*1024);
00101 suffix="mb";
00102 }
00103 else if (gwen_memory__allocated_bytes>1024) {
00104 bytes=gwen_memory__allocated_bytes/1024;
00105 suffix="kb";
00106 }
00107 else {
00108 bytes=gwen_memory__allocated_bytes;
00109 suffix="bytes";
00110 }
00111
00112 fprintf(stderr,
00113 "GWEN info: %zu %s allocated in %zu calls "
00114 "(%zu times reused, average %zu bytes)\n",
00115 bytes, suffix,
00116 gwen_memory__allocated_calls,
00117 gwen_memory__allocated_reused,
00118 avg);
00119 }
00120
00121 return 0;
00122 }
00123
00124
00125
00126 void GWEN_Memory_Report(void){
00127 return;
00128 }
00129
00130
00131
00132
00133
00134 GWEN_MEMORY_TABLE *GWEN_Memory_Table_new(void) {
00135 GWEN_MEMORY_TABLE *mt;
00136 unsigned char *p;
00137 unsigned short dsize;
00138
00139 if (gwen_memory__verbous)
00140 fprintf(stderr, "GWEN info: allocating memory table\n");
00141 mt=(GWEN_MEMORY_TABLE*)malloc(sizeof(GWEN_MEMORY_TABLE));
00142 assert(mt);
00143 memset(mt, 0, sizeof(GWEN_MEMORY_TABLE));
00144 dsize=GWEN_MEMORY_MAXBLOCK;
00145 p=mt->data;
00146 GWEN_MEMORY_WRITESIZE(p, dsize);
00147
00148 return mt;
00149 }
00150
00151
00152
00153 void GWEN_Memory_Table_free(GWEN_MEMORY_TABLE *mt) {
00154 if (mt) {
00155 if (gwen_memory__debug) {
00156 unsigned char *p;
00157 unsigned char *end;
00158
00159 p=mt->data;
00160 end=p+GWEN_MEMORY_TABLE_LEN;
00161 while(p<end) {
00162 unsigned short bsize;
00163 unsigned short rsize;
00164
00165 bsize=GWEN_MEMORY_READSIZE(p);
00166 rsize=bsize & GWEN_MEMORY_MASK_LEN;
00167 if (bsize & GWEN_MEMORY_MASK_MALLOCED) {
00168 fprintf(stderr,
00169 "GWEN warning: Block %p still allocated (%d bytes)\n",
00170 GWEN_MEMORY_GETDATA(p),
00171 rsize);
00172 }
00173 p+=rsize+GWEN_MEMORY_SIZELEN;
00174 }
00175 }
00176 free(mt);
00177 }
00178 }
00179
00180
00181
00182 void GWEN_Memory_Table_Append(GWEN_MEMORY_TABLE *head, GWEN_MEMORY_TABLE *mt){
00183 GWEN_MEMORY_TABLE *last;
00184
00185 assert(head);
00186 assert(mt);
00187
00188 last=head;
00189 while(last->next)
00190 last=last->next;
00191 last->next=mt;
00192 }
00193
00194
00195
00196 void GWEN_Memory_Table_Insert(GWEN_MEMORY_TABLE *mt){
00197 mt->next=gwen_memory__first_table;
00198 gwen_memory__first_table=mt;
00199 }
00200
00201
00202
00203 unsigned char *GWEN_Memory_Table__FindFreeBlock(GWEN_MEMORY_TABLE *mt,
00204 unsigned short dsize) {
00205 unsigned char *end;
00206 unsigned char *p;
00207
00208 end=mt->data+GWEN_MEMORY_TABLE_LEN;
00209 p=mt->data;
00210 while(p<end) {
00211 unsigned short bsize;
00212 unsigned short rsize;
00213
00214 bsize=GWEN_MEMORY_READSIZE(p);
00215 rsize=bsize & GWEN_MEMORY_MASK_LEN;
00216
00217
00218
00219
00220 if (rsize && !(bsize & GWEN_MEMORY_MASK_INUSE)) {
00221
00222 if (rsize==dsize ||
00223 rsize>=(dsize+GWEN_MEMORY_SIZELEN+GWEN_MEMORY_MINREMAIN)) {
00224 return p;
00225 }
00226 }
00227 p+=rsize+GWEN_MEMORY_SIZELEN;
00228 }
00229
00230 return 0;
00231 }
00232
00233
00234
00235 void GWEN_Memory_Table__CollectAt(GWEN_MEMORY_TABLE *mt,
00236 unsigned char *p) {
00237 unsigned char *end;
00238 unsigned short nsize=0;
00239 unsigned char *np;
00240 int cnt=0;
00241
00242 np=p;
00243 end=mt->data+GWEN_MEMORY_TABLE_LEN;
00244
00245 while(np<end) {
00246 unsigned short bsize;
00247 unsigned short rsize;
00248
00249 bsize=GWEN_MEMORY_READSIZE(np);
00250 rsize=bsize & GWEN_MEMORY_MASK_LEN;
00251 if (rsize && !(bsize & GWEN_MEMORY_MASK_INUSE)) {
00252 nsize+=rsize;
00253 if (cnt)
00254 nsize+=GWEN_MEMORY_SIZELEN;
00255 cnt++;
00256 }
00257 else
00258 break;
00259
00260 np+=rsize+GWEN_MEMORY_SIZELEN;
00261 }
00262
00263 if (cnt>1) {
00264 fprintf(stderr, "GWEN info: collected %u bytes\n", nsize);
00265 GWEN_MEMORY_WRITESIZE(p, nsize);
00266 }
00267
00268
00269 }
00270
00271
00272
00273 void GWEN_Memory_Table__Collect(GWEN_MEMORY_TABLE *mt) {
00274 unsigned char *p;
00275 unsigned char *end;
00276
00277 end=mt->data+GWEN_MEMORY_TABLE_LEN;
00278 p=mt->data;
00279 while(p<end) {
00280 unsigned short bsize;
00281 unsigned short rsize;
00282
00283 GWEN_Memory_Table__CollectAt(mt, p);
00284 bsize=GWEN_MEMORY_READSIZE(p);
00285 rsize=bsize & GWEN_MEMORY_MASK_LEN;
00286 p+=rsize+GWEN_MEMORY_SIZELEN;
00287 }
00288 }
00289
00290
00291
00292 void GWEN_Memory_Table__Dump(GWEN_MEMORY_TABLE *mt) {
00293 unsigned char *p;
00294 unsigned char *end;
00295
00296 p=mt->data;
00297 end=p+GWEN_MEMORY_TABLE_LEN;
00298 while(p<end) {
00299 unsigned short bsize;
00300 unsigned short rsize;
00301
00302 bsize=GWEN_MEMORY_READSIZE(p);
00303 rsize=bsize & GWEN_MEMORY_MASK_LEN;
00304 fprintf(stderr,
00305 "GWEN debug: at %5zu: found block with %5u bytes [%p] (%s)\n",
00306 p-mt->data,
00307 rsize,
00308 p,
00309 (bsize & GWEN_MEMORY_MASK_INUSE)?"used":"free");
00310 p+=rsize+GWEN_MEMORY_SIZELEN;
00311 }
00312 }
00313
00314
00315
00316 unsigned char *GWEN_Memory__FindFreeBlock(unsigned short dsize) {
00317 GWEN_MEMORY_TABLE *mt;
00318 unsigned char *p=0;
00319
00320 if (dsize>GWEN_MEMORY_MAXBLOCK) {
00321 fprintf(stderr, "GWEN error: Memory block too big (%d>%d)\n",
00322 dsize, GWEN_MEMORY_MAXBLOCK);
00323 abort();
00324 }
00325 if (gwen_memory__first_table==0)
00326 gwen_memory__first_table=GWEN_Memory_Table_new();
00327
00328 mt=gwen_memory__first_table;
00329 assert(mt);
00330
00331 while(mt) {
00332 p=GWEN_Memory_Table__FindFreeBlock(mt, dsize);
00333 if (p)
00334 return p;
00335 mt=mt->next;
00336 }
00337
00338 mt=GWEN_Memory_Table_new();
00339
00340 GWEN_Memory_Table_Insert(mt);
00341 p=GWEN_Memory_Table__FindFreeBlock(mt, dsize);
00342 assert(p);
00343
00344 return p;
00345 }
00346
00347
00348
00349 void *GWEN_Memory__Malloc(unsigned short dsize) {
00350 unsigned char *p;
00351 unsigned short bsize;
00352 unsigned short rsize;
00353
00354 p=GWEN_Memory__FindFreeBlock(dsize);
00355 assert(p);
00356
00357 bsize=GWEN_MEMORY_READSIZE(p);
00358 rsize=bsize & GWEN_MEMORY_MASK_LEN;
00359
00360 if (rsize>dsize) {
00361 unsigned char *np;
00362 unsigned short nsize;
00363
00364
00365 nsize=rsize-dsize-GWEN_MEMORY_SIZELEN;
00366 np=p+GWEN_MEMORY_SIZELEN+dsize;
00367
00368
00369
00370 GWEN_MEMORY_WRITESIZE(np, (nsize & GWEN_MEMORY_MASK_LEN));
00371 }
00372 else
00373 gwen_memory__allocated_reused++;
00374
00375 GWEN_MEMORY_WRITESIZE(p, (dsize |
00376 GWEN_MEMORY_MASK_INUSE |
00377 GWEN_MEMORY_MASK_MALLOCED));
00378
00379
00380 return (void*)GWEN_MEMORY_GETDATA(p);
00381 }
00382
00383
00384
00385 void *GWEN_Memory_malloc(size_t wsize) {
00386 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00387 void *p;
00388 size_t dsize;
00389 #endif
00390
00391 if (GWEN_UNLIKELY(wsize==0)) {
00392 fprintf(stderr,
00393 "GWEN error: allocating 0 bytes, maybe a program error\n");
00394 abort();
00395 }
00396
00397 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00398 dsize=(wsize+GWEN_MEMORY_GRANULARITY-1) & ~(GWEN_MEMORY_GRANULARITY-1);
00399
00400 if (dsize<GWEN_MEMORY_MAXBLOCK) {
00401
00402
00403
00404
00405 p=GWEN_Memory__Malloc(dsize & GWEN_MEMORY_MASK_LEN);
00406 }
00407 else {
00408 unsigned char *pc;
00409
00410
00411 if (gwen_memory__verbous)
00412 fprintf(stderr, "GWEN info: Allocating %u bytes externally\n",
00413 dsize);
00414 pc=(unsigned char*)malloc(dsize+GWEN_MEMORY_SIZELEN);
00415 assert(pc);
00416 GWEN_MEMORY_WRITESIZE(pc, GWEN_MEMORY_EXTERNAL);
00417 p=GWEN_MEMORY_GETDATA(pc);
00418 }
00419
00420 gwen_memory__allocated_bytes+=dsize;
00421 gwen_memory__allocated_calls++;
00422
00423 return p;
00424 #else
00425 return malloc(wsize);
00426 #endif
00427 }
00428
00429
00430
00431 void *GWEN_Memory_realloc(void *oldp, size_t nsize) {
00432 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00433 void *p;
00434 unsigned char *pc;
00435 unsigned short dsize;
00436 unsigned short rsize;
00437 #endif
00438
00439 assert(oldp);
00440 assert(nsize);
00441
00442 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00443 pc=GWEN_MEMORY_GETSTART(oldp);
00444 dsize=GWEN_MEMORY_READSIZE(pc);
00445 rsize=dsize & GWEN_MEMORY_MASK_LEN;
00446
00447 if (!(dsize & GWEN_MEMORY_MASK_MALLOCED)) {
00448 fprintf(stderr, "GWEN error: Block %p already free'd\n", oldp);
00449 abort();
00450 }
00451
00452 if (!(dsize & GWEN_MEMORY_MASK_INUSE)) {
00453 fprintf(stderr, "GWEN error: Block %p not in use\n", oldp);
00454 abort();
00455 }
00456
00457 p=GWEN_Memory_malloc(nsize);
00458 memmove(p, oldp, rsize);
00459 GWEN_Memory_dealloc(oldp);
00460 return p;
00461 #else
00462 return realloc(oldp, nsize);
00463 #endif
00464 }
00465
00466
00467
00468 void GWEN_Memory_dealloc(void *p) {
00469 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00470 if (p) {
00471 unsigned char *pc;
00472 unsigned short dsize;
00473
00474 pc=GWEN_MEMORY_GETSTART(p);
00475 dsize=GWEN_MEMORY_READSIZE(pc);
00476
00477 if (!(dsize & GWEN_MEMORY_MASK_MALLOCED)) {
00478 fprintf(stderr, "GWEN error: Block %p already free'd\n", p);
00479 abort();
00480 }
00481
00482 if (!(dsize & GWEN_MEMORY_MASK_INUSE)) {
00483 fprintf(stderr, "GWEN error: Block %p not in use\n", p);
00484 abort();
00485 }
00486
00487 if (gwen_memory__nofree==0) {
00488 GWEN_MEMORY_WRITESIZE(pc,
00489 (dsize &
00490 ~GWEN_MEMORY_MASK_MALLOCED &
00491 ~GWEN_MEMORY_MASK_INUSE));
00492 }
00493 else {
00494 GWEN_MEMORY_WRITESIZE(pc,
00495 (dsize &
00496 ~GWEN_MEMORY_MASK_MALLOCED));
00497 }
00498
00499 if (dsize==GWEN_MEMORY_EXTERNAL) {
00500
00501
00502
00503 if (gwen_memory__nofree==0)
00504 free((void*)pc);
00505 }
00506 else {
00507
00508
00509
00510
00511 if (gwen_memory__released_since_collect>GWEN_MEMORY_COLLECT_AFTER){
00512 fprintf(stderr, "GWEN info: collecting free blocks\n");
00513 GWEN_Memory_Collect();
00514 gwen_memory__released_since_collect=0;
00515 }
00516 }
00517 }
00518 #else
00519 if (GWEN_LIKELY(gwen_memory__nofree==0))
00520 free(p);
00521 #endif
00522 }
00523
00524
00525
00526 char *GWEN_Memory_strdup(const char *s) {
00527 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00528 unsigned int dsize;
00529 char *p;
00530 #endif
00531
00532 assert(s);
00533 #ifdef ENABLE_MY_SMALL_BLOCK_ALLOC
00534 dsize=strlen(s);
00535
00536 p=(char*)GWEN_Memory_malloc(dsize+1);
00537 assert(p);
00538 memmove(p, s, dsize+1);
00539 return p;
00540 #else
00541 return strdup(s);
00542 #endif
00543 }
00544
00545
00546
00547 void GWEN_Memory_Dump(void) {
00548 GWEN_MEMORY_TABLE *mt;
00549
00550 mt=gwen_memory__first_table;
00551 while(mt) {
00552 GWEN_Memory_Table__Dump(mt);
00553 mt=mt->next;
00554 }
00555 }
00556
00557
00558
00559 void GWEN_Memory_Collect(void) {
00560 GWEN_MEMORY_TABLE *mt;
00561
00562 mt=gwen_memory__first_table;
00563 while(mt) {
00564 GWEN_Memory_Table__Collect(mt);
00565 mt=mt->next;
00566 }
00567 }
00568
00569
00570