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
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091 #include "gw-config.h"
00092
00093 #include <stdlib.h>
00094 #include <errno.h>
00095 #include <string.h>
00096 #if HAVE_BACKTRACE
00097 #include <execinfo.h>
00098 #endif
00099
00100 #include "gwlib.h"
00101
00102
00103
00104 #undef malloc
00105 #undef realloc
00106 #undef free
00107
00108
00109
00110 #define NEW_AREA_PATTERN 0xcafebabe
00111
00112
00113
00114 #define FREE_AREA_PATTERN 0xdeadbeef
00115
00116
00117
00118 #define START_MARK_PATTERN 0xdadaface
00119
00120
00121 #define END_MARK_PATTERN 0xadadafec
00122
00123
00124 #define MAX_DUMP 16
00125
00126 static int initialized = 0;
00127
00128
00129 static int slow = 0;
00130
00131
00132
00133
00134 static Mutex gwmem_lock;
00135
00136 struct location
00137 {
00138 const char *filename;
00139 long lineno;
00140 const char *function;
00141 };
00142
00143
00144
00145
00146 struct area
00147 {
00148 void *area;
00149 size_t area_size;
00150 size_t max_size;
00151 struct location allocator;
00152 struct location reallocator;
00153 struct location claimer;
00154 #if HAVE_BACKTRACE
00155 void *frames[10];
00156 size_t frame_size;
00157 #endif
00158 };
00159
00160
00161
00162
00163 #define MARKER_SIZE 16
00164
00165
00166 #define MAX_TAB_SIZE (100*1024*1024L)
00167 #define MAX_ALLOCATIONS ((long) (MAX_TAB_SIZE/sizeof(struct area)))
00168
00169
00170
00171
00172
00173
00174 #define FREE_RING_SIZE 1024
00175
00176 static struct area allocated[MAX_ALLOCATIONS];
00177 static struct area free_ring[FREE_RING_SIZE];
00178
00179
00180
00181 static long num_allocations;
00182
00183
00184 static long free_ring_start;
00185 static long free_ring_len;
00186
00187
00188
00189 static long highest_num_allocations;
00190
00191 static long highest_total_size;
00192
00193 static long total_size;
00194
00195
00196
00197 static inline void lock(void)
00198 {
00199 mutex_lock(&gwmem_lock);
00200 }
00201
00202 static inline void unlock(void)
00203 {
00204 mutex_unlock(&gwmem_lock);
00205 }
00206
00207 static unsigned long round_pow2(unsigned long num)
00208 {
00209 unsigned long i;
00210
00211 if (num <= 16)
00212 return 16;
00213
00214 for (i = 32; i < 0x80000000L; i <<= 1) {
00215 if (num <= i)
00216 return i;
00217 }
00218
00219
00220
00221 if (num <= 0x80000000L)
00222 return 0x80000000L;
00223
00224 return 0xffffffffL;
00225 }
00226
00227
00228 static void fill(unsigned char *p, size_t bytes, long pattern)
00229 {
00230 while (bytes > sizeof(pattern)) {
00231 memcpy(p, &pattern, sizeof(pattern));
00232 p += sizeof(pattern);
00233 bytes -= sizeof(pattern);
00234 }
00235 if (bytes > 0)
00236 memcpy(p, &pattern, bytes);
00237 }
00238
00239
00240 static int untouched(unsigned char *p, size_t bytes, long pattern)
00241 {
00242 while (bytes > sizeof(pattern)) {
00243 if (memcmp(p, &pattern, sizeof(pattern)) != 0)
00244 return 0;
00245 p += sizeof(pattern);
00246 bytes -= sizeof(pattern);
00247 }
00248 if (bytes > 0 && memcmp(p, &pattern, bytes) != 0)
00249 return 0;
00250 return 1;
00251 }
00252
00253
00254 static inline void endmark(unsigned char *p, size_t size)
00255 {
00256 fill(p + size, MARKER_SIZE, END_MARK_PATTERN);
00257 }
00258
00259
00260
00261
00262 static void startmark(unsigned char *p, long number)
00263 {
00264 gw_assert(MARKER_SIZE >= sizeof(long));
00265 gw_assert(number >= 0);
00266
00267 fill(p - MARKER_SIZE, sizeof(long), number);
00268 fill(p - MARKER_SIZE + sizeof(long),
00269 MARKER_SIZE - sizeof(long), START_MARK_PATTERN);
00270 }
00271
00272
00273
00274
00275 static long check_startmark(unsigned char *p)
00276 {
00277 long number;
00278 if (!untouched(p - MARKER_SIZE + sizeof(long),
00279 MARKER_SIZE - sizeof(long), START_MARK_PATTERN))
00280 return -1;
00281 memcpy(&number, p - MARKER_SIZE, sizeof(number));
00282 return number;
00283 }
00284
00285 static int check_endmark(unsigned char *p, size_t size)
00286 {
00287 if (!untouched(p + size, MARKER_SIZE, END_MARK_PATTERN))
00288 return -1;
00289 return 0;
00290 }
00291
00292 static int check_marks(struct area *area, long index)
00293 {
00294 int result = 0;
00295
00296 if (check_startmark(area->area) != index) {
00297 error(0, "Start marker was damaged for area %ld", index);
00298 result = -1;
00299 }
00300 if (check_endmark(area->area, area->area_size) < 0) {
00301 error(0, "End marker was damaged for area %ld", index);
00302 result = -1;
00303 }
00304
00305 return result;
00306 }
00307
00308 static void dump_area(struct area *area)
00309 {
00310 debug("gwlib.gwmem", 0, "Area %p, size %ld, max_size %ld",
00311 area->area, (long) area->area_size, (long) area->max_size);
00312 debug("gwlib.gwmem", 0, "Allocated by %s() at %s:%ld",
00313 area->allocator.function,
00314 area->allocator.filename,
00315 area->allocator.lineno);
00316 if (area->reallocator.function) {
00317 debug("gwlib.gwmem", 0, "Re-allocated by %s() at %s:%ld",
00318 area->reallocator.function,
00319 area->reallocator.filename,
00320 area->reallocator.lineno);
00321 }
00322 if (area->claimer.function) {
00323 debug("gwlib.gwmem", 0, "Claimed by %s() at %s:%ld",
00324 area->claimer.function,
00325 area->claimer.filename,
00326 area->claimer.lineno);
00327 }
00328 if (area->area_size > 0) {
00329 size_t i;
00330 unsigned char *p;
00331 char buf[MAX_DUMP * 3 + 1];
00332
00333 p = area->area;
00334 buf[0] = '\0';
00335 for (i = 0; i < area->area_size && i < MAX_DUMP; ++i)
00336 sprintf(strchr(buf, '\0'), "%02x ", p[i]);
00337
00338 debug("gwlib.gwmem", 0, "Contents of area (first %d bytes):", MAX_DUMP);
00339 debug("gwlib.gwmem", 0, " %s", buf);
00340 }
00341 #if HAVE_BACKTRACE
00342 {
00343 size_t i;
00344 char **strings = backtrace_symbols(area->frames, area->frame_size);
00345 debug("gwlib.gwmem", 0, "Backtrace of last malloc/realloc:");
00346 for (i = 0; i < area->frame_size; i++) {
00347 if (strings != NULL)
00348 debug("gwlib.gwmem", 0, "%s", strings[i]);
00349 else
00350 debug("gwlib.gwmem", 0, "%p", area->frames[i]);
00351 }
00352 free(strings);
00353 }
00354 #endif
00355 }
00356
00357 static struct area *find_area(unsigned char *p)
00358 {
00359 long index;
00360 struct area *area;
00361 long suspicious_pointer;
00362 unsigned long p_ul;
00363
00364 gw_assert(p != NULL);
00365
00366 p_ul = (unsigned long) p;
00367 suspicious_pointer =
00368 (sizeof(p) == sizeof(long) &&
00369 (p_ul == NEW_AREA_PATTERN || p_ul == FREE_AREA_PATTERN ||
00370 p_ul == START_MARK_PATTERN || p_ul == END_MARK_PATTERN));
00371
00372 if (slow || suspicious_pointer) {
00373
00374
00375
00376 for (index = 0; index < num_allocations; index++) {
00377 if (allocated[index].area == p)
00378 break;
00379 }
00380 if (index == num_allocations) {
00381 error(0, "Area %p not found in allocation table.", p);
00382 return NULL;
00383 }
00384 }
00385
00386 index = check_startmark(p);
00387 if (index >= 0 && index < num_allocations &&
00388 allocated[index].area == p) {
00389 area = &allocated[index];
00390 if (check_endmark(p, area->area_size) < 0) {
00391 error(0, "End marker was damaged for area %p", p);
00392 dump_area(area);
00393 }
00394 return area;
00395 }
00396
00397 error(0, "Start marker was damaged for area %p", p);
00398 for (index = 0; index < num_allocations; index++) {
00399 if (allocated[index].area == p) {
00400 area = &allocated[index];
00401 dump_area(area);
00402 return area;
00403 }
00404 }
00405
00406 error(0, "Could not find area information.");
00407 return NULL;
00408 }
00409
00410 static void change_total_size(long change)
00411 {
00412 total_size += change;
00413 if (total_size > highest_total_size)
00414 highest_total_size = total_size;
00415 }
00416
00417 static struct area *record_allocation(unsigned char *p, size_t size,
00418 const char *filename, long lineno, const char *function)
00419 {
00420 struct area *area;
00421 static struct area empty_area;
00422
00423 if (num_allocations == MAX_ALLOCATIONS) {
00424 panic(0, "Too many concurrent allocations.");
00425 }
00426
00427 area = &allocated[num_allocations];
00428 *area = empty_area;
00429 area->area = p;
00430 area->area_size = size;
00431 area->max_size = size;
00432 area->allocator.filename = filename;
00433 area->allocator.lineno = lineno;
00434 area->allocator.function = function;
00435 #if HAVE_BACKTRACE
00436 area->frame_size = backtrace(area->frames, sizeof(area->frames) / sizeof(void*));
00437 #endif
00438
00439 startmark(area->area, num_allocations);
00440 endmark(area->area, area->area_size);
00441
00442 num_allocations++;
00443 if (num_allocations > highest_num_allocations)
00444 highest_num_allocations = num_allocations;
00445 change_total_size(size);
00446
00447 return area;
00448 }
00449
00450 static void remove_allocation(struct area *area)
00451 {
00452 change_total_size(-1*area->area_size);
00453 num_allocations--;
00454 if (area == &allocated[num_allocations])
00455 return;
00456 check_marks(&allocated[num_allocations], num_allocations);
00457 *area = allocated[num_allocations];
00458 startmark(area->area, area - allocated);
00459 }
00460
00461 static void drop_from_free_ring(long index)
00462 {
00463 struct area *area;
00464
00465 area = &free_ring[index];
00466 if (check_marks(area, index) < 0 ||
00467 !untouched(area->area, area->area_size, FREE_AREA_PATTERN)) {
00468 error(0, "Freed area %p has been tampered with.", area->area);
00469 dump_area(area);
00470 }
00471 free((unsigned char *)area->area - MARKER_SIZE);
00472 }
00473
00474 static void put_on_free_ring(struct area *area)
00475 {
00476
00477 if (free_ring_len < FREE_RING_SIZE) {
00478 free_ring[free_ring_len] = *area;
00479 startmark(area->area, free_ring_len);
00480 free_ring_len++;
00481 return;
00482 }
00483
00484
00485
00486
00487 drop_from_free_ring(free_ring_start);
00488 free_ring[free_ring_start] = *area;
00489 startmark(area->area, free_ring_start);
00490 free_ring_start = (free_ring_start + 1) % FREE_RING_SIZE;
00491 }
00492
00493 static void free_area(struct area *area)
00494 {
00495 fill(area->area, area->area_size, FREE_AREA_PATTERN);
00496 put_on_free_ring(area);
00497 remove_allocation(area);
00498 }
00499
00500 void gw_check_init_mem(int slow_flag)
00501 {
00502 mutex_init_static(&gwmem_lock);
00503 slow = slow_flag;
00504 initialized = 1;
00505 }
00506
00507 void gw_check_shutdown(void)
00508 {
00509 mutex_destroy(&gwmem_lock);
00510 initialized = 0;
00511 }
00512
00513 void *gw_check_malloc(size_t size, const char *filename, long lineno,
00514 const char *function)
00515 {
00516 unsigned char *p;
00517
00518 gw_assert(initialized);
00519
00520
00521 gw_assert(size > 0);
00522
00523 p = malloc(size + 2 * MARKER_SIZE);
00524 if (p == NULL)
00525 panic(errno, "Memory allocation of %ld bytes failed.", (long)size);
00526 p += MARKER_SIZE;
00527
00528 lock();
00529 fill(p, size, NEW_AREA_PATTERN);
00530 record_allocation(p, size, filename, lineno, function);
00531 unlock();
00532
00533 return p;
00534 }
00535
00536 void *gw_check_realloc(void *p, size_t size, const char *filename,
00537 long lineno, const char *function)
00538 {
00539 struct area *area;
00540
00541 if (p == NULL)
00542 return gw_check_malloc(size, filename, lineno, function);
00543
00544 gw_assert(initialized);
00545 gw_assert(size > 0);
00546
00547 lock();
00548 area = find_area(p);
00549 if (!area) {
00550 unlock();
00551 panic(0, "Realloc called on non-allocated area");
00552 }
00553
00554 if (size == area->area_size) {
00555
00556 } else if (size <= area->max_size) {
00557 change_total_size(size - area->area_size);
00558 area->area_size = size;
00559 endmark(p, size);
00560 } else if (size > area->max_size) {
00561
00562
00563
00564
00565 struct area *new_area;
00566 size_t new_size;
00567 unsigned char *new_p;
00568
00569 new_size = round_pow2(size + 2 * MARKER_SIZE);
00570 new_p = malloc(new_size);
00571 new_size -= 2 * MARKER_SIZE;
00572 new_p += MARKER_SIZE;
00573 memcpy(new_p, p, area->area_size);
00574 fill(new_p + area->area_size, size - area->area_size,
00575 NEW_AREA_PATTERN);
00576 new_area = record_allocation(new_p, size,
00577 area->allocator.filename,
00578 area->allocator.lineno,
00579 area->allocator.function);
00580 new_area->max_size = new_size;
00581 free_area(area);
00582
00583 p = new_p;
00584 area = new_area;
00585 }
00586
00587 area->reallocator.filename = filename;
00588 area->reallocator.lineno = lineno;
00589 area->reallocator.function = function;
00590 unlock();
00591 return p;
00592 }
00593
00594 void gw_check_free(void *p, const char *filename, long lineno,
00595 const char *function)
00596 {
00597 struct area *area;
00598 gw_assert(initialized);
00599
00600 if (p == NULL)
00601 return;
00602
00603 lock();
00604 area = find_area(p);
00605 if (!area) {
00606 unlock();
00607 panic(0, "Free called on non-allocated area");
00608 }
00609
00610 free_area(area);
00611 unlock();
00612 }
00613
00614 char *gw_check_strdup(const char *str, const char *filename, long lineno,
00615 const char *function)
00616 {
00617 char *copy;
00618
00619 gw_assert(initialized);
00620 gw_assert(str != NULL);
00621
00622 copy = gw_check_malloc(strlen(str) + 1, filename, lineno, function);
00623 strcpy(copy, str);
00624 return copy;
00625 }
00626
00627 void *gw_check_claim_area(void *p, const char *filename, long lineno,
00628 const char *function)
00629 {
00630 struct area *area;
00631
00632
00633 if (p == NULL)
00634 return NULL;
00635
00636 lock();
00637 area = find_area(p);
00638 if (!area) {
00639 unlock();
00640 panic(0, "Claim_area called on non-allocated area");
00641 }
00642
00643 area->claimer.filename = filename;
00644 area->claimer.lineno = lineno;
00645 area->claimer.function = function;
00646 unlock();
00647
00648
00649 return p;
00650 }
00651
00652 void gw_check_check_leaks(void)
00653 {
00654 long calculated_size;
00655 long index;
00656
00657 gw_assert(initialized);
00658 lock();
00659
00660 for (index = 0; index < free_ring_len; index++) {
00661 drop_from_free_ring(index);
00662 }
00663 free_ring_len = 0;
00664
00665 calculated_size = 0;
00666 for (index = 0; index < num_allocations; index++) {
00667 calculated_size += allocated[index].area_size;
00668 }
00669 gw_assert(calculated_size == total_size);
00670
00671 debug("gwlib.gwmem", 0, "----------------------------------------");
00672 debug("gwlib.gwmem", 0, "Current allocations: %ld areas, %ld bytes",
00673 num_allocations, total_size);
00674 debug("gwlib.gwmem", 0, "Highest number of allocations: %ld areas",
00675 highest_num_allocations);
00676 debug("gwlib.gwmem", 0, "Highest memory usage: %ld bytes",
00677 highest_total_size);
00678 for (index = 0; index < num_allocations; index++) {
00679 check_marks(&allocated[index], index);
00680 dump_area(&allocated[index]);
00681 }
00682
00683 unlock();
00684 }
00685
00686 int gw_check_is_allocated(void *p)
00687 {
00688 struct area *area;
00689
00690 lock();
00691 area = find_area(p);
00692 unlock();
00693 return area != NULL;
00694 }
00695
00696 long gw_check_area_size(void *p)
00697 {
00698 struct area *area;
00699 size_t size;
00700
00701 lock();
00702 area = find_area(p);
00703 if (!area) {
00704 unlock();
00705 warning(0, "Area_size called on non-allocated area %p", p);
00706 return -1;
00707 }
00708 size = area->area_size;
00709 unlock();
00710 return size;
00711 }
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.