00001 #ifndef MEMORY_CHECKER_IMPLEMENTATION_FILE
00002 #define MEMORY_CHECKER_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef ENABLE_MEMORY_HOOK
00022
00023 #include "definitions.h"
00024 #include "log_base.h"
00025 #include "memory_checker.h"
00026 #include "mutex.h"
00027 #include "utility.h"
00028
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032
00033 const int MAXIMUM_HASH_SLOTS = 256 * KILOBYTE;
00034
00035
00036
00037 const int SINGLE_LINE_SIZE_ESTIMATE = 200;
00038
00039
00040
00041
00042 const int RESERVED_AREA = 1000;
00043
00044
00045
00046 #define CLEAR_ALLOCATED_MEMORY
00047
00048
00049
00050
00051 #define MEMORY_CHECKER_STATISTICS
00052
00053
00054
00055
00056
00057
00059
00060
00061
00062 #include <basis/trap_new.addin>
00063 void *operator new(size_t size, char *file, int line) throw (std::bad_alloc)
00064 { return program_wide_memories().provide_memory(size, file, line); }
00065 #include <basis/untrap_new.addin>
00066
00067 void operator delete(void *ptr) throw ()
00068 { program_wide_memories().release_memory(ptr); }
00069
00071
00072
00073
00074
00075 class memlink
00076 {
00077 public:
00078 void *_chunk;
00079
00082 memlink *_next;
00083 int _size;
00084 char *_where;
00085 int _line;
00086 #ifdef ENABLE_CALLSTACK_TRACKING
00087 char *_stack;
00088 #endif
00089
00090 void construct(void *ptr, int size, char *where, int line) {
00091 _next = NIL;
00092 _chunk = ptr;
00093 _size = size;
00094 _where = strdup(where);
00095 if (strlen(_where) > SINGLE_LINE_SIZE_ESTIMATE - 40) {
00096
00097 _where[SINGLE_LINE_SIZE_ESTIMATE - 40] = '\0';
00098 }
00099 _line = line;
00100 #ifdef ENABLE_CALLSTACK_TRACKING
00101 _stack = program_wide_stack_trace().full_trace();
00103 #endif
00104 }
00105
00106 void destruct() {
00107 free(_chunk); _chunk = NIL;
00108 free(_where); _where = NIL;
00109 _next = NIL;
00110 _size = 0;
00111 _line = 0;
00112 #ifdef ENABLE_CALLSTACK_TRACKING
00113 free(_stack); _stack = NIL;
00114 #endif
00115 }
00116 };
00117
00119
00120
00121 #ifdef MEMORY_CHECKER_STATISTICS
00122
00123
00124
00125
00126
00127
00128 double _stat_new_allocations = 0;
00129 double _stat_freed_allocations = 0;
00130
00131 double _stat_new_allocations_size = 0;
00132 double _stat_freed_allocations_size = 0;
00133 #endif
00134
00136
00138
00139 class memory_bin
00140 {
00141 public:
00142 void construct() {
00143 _head = NIL;
00144 _count = 0;
00145 _lock = (mutex_base *)malloc(sizeof(mutex_base));
00146 _lock->construct();
00147 }
00148 void destruct() {
00149 _lock->destruct();
00150 free(_lock);
00151 }
00152
00153 inline int count() const { return _count; }
00154
00155 int record_memory(void *ptr, int size, char *where, int line) {
00156 memlink *new_guy = (memlink *)malloc(sizeof(memlink));
00157 new_guy->construct(ptr, size, where, line);
00158 _lock->lock();
00159
00160
00161 new_guy->_next = _head;
00162 _head = new_guy;
00163 _count++;
00164 _lock->unlock();
00165 return common::OKAY;
00166 }
00167
00168 int release_memory(void *to_release) {
00169 _lock->lock();
00170
00171 memlink *current = _head;
00172 memlink *previous = NIL;
00173 while (current) {
00174 if (current->_chunk == to_release) {
00175 #ifdef MEMORY_CHECKER_STATISTICS
00176
00177 _stat_freed_allocations += 1.0;
00178 _stat_freed_allocations_size += current->_size;
00179 #endif
00180 #ifdef DEBUG_MEMORY_CHECKER
00181 printf("found %p listed, removing for %s[%d]\n", to_release,
00182 current->_where, current->_line);
00183 #endif
00184
00185 if (!previous) {
00186
00187 _head = current->_next;
00188 } else {
00189
00190 previous->_next = current->_next;
00191 }
00192
00193 current->destruct();
00194 free(current);
00195 _count--;
00196 _lock->unlock();
00197 return common::OKAY;
00198 }
00199
00200 previous = current;
00201 current = current->_next;
00202 }
00203 #ifdef DEBUG_MEMORY_CHECKER
00204 printf("failed to find %p listed.\n", to_release);
00205 #endif
00206 _lock->unlock();
00207 return common::NOT_FOUND;
00208 }
00209
00210 void dump_list(char *add_to, int &curr_size, int max_size) {
00211 int size_alloc = 2 * SINGLE_LINE_SIZE_ESTIMATE;
00212 char *temp_str = (char *)malloc(size_alloc);
00213 memlink *current = _head;
00214 while (current) {
00215 temp_str[0] = '\0';
00216 sprintf(temp_str, "\n\"%s[%d]\", \"size %d\", \"addr %p\"\n",
00217 current->_where, current->_line, current->_size, current->_chunk);
00218 int len_add = strlen(temp_str);
00219 if (curr_size + len_add < max_size) {
00220 strcat(add_to, temp_str);
00221 curr_size += len_add;
00222 }
00223 #ifdef ENABLE_CALLSTACK_TRACKING
00224 len_add = strlen(current->_stack);
00225 if (curr_size + len_add < max_size) {
00226 strcat(add_to, current->_stack);
00227 curr_size += len_add;
00228 }
00229 #endif
00230 current = current->_next;
00231 }
00232 free(temp_str);
00233 }
00234
00235 private:
00236 memlink *_head;
00237 mutex_base *_lock;
00238 int _count;
00239 };
00240
00242
00243 class allocation_memories
00244 {
00245 public:
00246 void construct(int num_slots) {
00247 _num_slots = num_slots;
00248 _bins = (memory_bin *)malloc(num_slots * sizeof(memory_bin));
00249 for (int i = 0; i < num_slots; i++)
00250 _bins[i].construct();
00251 }
00252
00253 void destruct() {
00254
00255 for (int i = 0; i < _num_slots; i++) {
00256 _bins[i].destruct();
00257 }
00258 free(_bins);
00259 _bins = NIL;
00260 }
00261
00262 int compute_slot(void *ptr) {
00263 return utility::hash_bytes(&ptr, sizeof(void *)) % _num_slots;
00264 }
00265
00266 void *provide_memory(int size_needed, char *file, int line) {
00267 void *new_allocation = malloc(size_needed);
00268
00269 int slot = compute_slot(new_allocation);
00270 #ifdef DEBUG_MEMORY_CHECKER
00271 printf("using slot %d for %p\n", slot, new_allocation);
00272 #endif
00273 _bins[slot].record_memory(new_allocation, size_needed, file, line);
00274 #ifdef MEMORY_CHECKER_STATISTICS
00275 _stat_new_allocations += 1.0;
00276 _stat_new_allocations_size += size_needed;
00277 #endif
00278 return new_allocation;
00279 }
00280
00281 int release_memory(void *to_drop) {
00282 int slot = compute_slot(to_drop);
00283 #ifdef DEBUG_MEMORY_CHECKER
00284 printf("removing mem %p from slot %d.\n", to_drop, slot);
00285 #endif
00286 return _bins[slot].release_memory(to_drop);
00287 }
00288
00290
00294 char *report_allocations() {
00295
00296 int full_count = 0;
00297 for (int i = 0; i < _num_slots; i++) {
00299 full_count += _bins[i].count();
00300 }
00302
00303 int alloc_size = full_count * SINGLE_LINE_SIZE_ESTIMATE + RESERVED_AREA;
00304 char *to_return = (char *)malloc(alloc_size);
00305 to_return[0] = '\0';
00306 if (full_count) {
00307 strcat(to_return, "===================\n");
00308 strcat(to_return, "Unfreed Allocations\n");
00309 strcat(to_return, "===================\n");
00310 }
00311 int curr_size = strlen(to_return);
00312 for (int i = 0; i < _num_slots; i++) {
00313 _bins[i].dump_list(to_return, curr_size, alloc_size - RESERVED_AREA);
00314 }
00315 return to_return;
00316 }
00317
00318
00319 char *text_form(bool show_outstanding) {
00320 char *to_return = NIL;
00321 if (show_outstanding) {
00322 to_return = report_allocations();
00323 } else {
00324 to_return = (char *)malloc(RESERVED_AREA);
00325 to_return[0] = '\0';
00326 }
00327 #ifdef MEMORY_CHECKER_STATISTICS
00328 char *temp_str = (char *)malloc(4 * SINGLE_LINE_SIZE_ESTIMATE);
00329
00330 sprintf(temp_str, "=================\n");
00331 strcat(to_return, temp_str);
00332 sprintf(temp_str, "Memory Statistics\n");
00333 strcat(to_return, temp_str);
00334 sprintf(temp_str, "=================\n");
00335 strcat(to_return, temp_str);
00336 sprintf(temp_str, "Measurements taken across entire program runtime:\n");
00337 strcat(to_return, temp_str);
00338 sprintf(temp_str, " %.0f new allocations.\n", _stat_new_allocations);
00339 strcat(to_return, temp_str);
00340 sprintf(temp_str, " %.4f new Mbytes.\n",
00341 _stat_new_allocations_size / MEGABYTE);
00342 strcat(to_return, temp_str);
00343 sprintf(temp_str, " %.0f freed deallocations.\n",
00344 _stat_freed_allocations);
00345 strcat(to_return, temp_str);
00346 sprintf(temp_str, " %.4f freed Mbytes.\n",
00347 _stat_freed_allocations_size / MEGABYTE);
00348 strcat(to_return, temp_str);
00349
00350 free(temp_str);
00351 #endif
00352 return to_return;
00353 }
00354
00355 private:
00356 memory_bin *_bins;
00357 int _num_slots;
00358 };
00359
00361
00362 void memory_checker::construct()
00363 {
00364 _mems = (allocation_memories *)malloc(sizeof(allocation_memories));
00365 _mems->construct(MAXIMUM_HASH_SLOTS);
00366 _unusable = false;
00367 _enabled = true;
00368 }
00369
00370 void memory_checker::destruct()
00371 {
00372 if (_unusable) return;
00373 if (!_mems) printf("memory_checker::destruct being invoked twice!\n");
00374
00375
00376 char *mem_state = text_form(true);
00377 printf("%s", mem_state);
00379
00380
00381 _unusable = true;
00382
00383 _mems->destruct();
00384 free(_mems);
00385 _mems = NIL;
00386 }
00387
00388 void *memory_checker::provide_memory(size_t size, char *file, int line)
00389 {
00390 if (_unusable || !_enabled) return malloc(size);
00391 return _mems->provide_memory(size, file, line);
00392 }
00393
00394 int memory_checker::release_memory(void *ptr)
00395 {
00396 if (_unusable || !_enabled) {
00397 free(ptr);
00398 return common::OKAY;
00399 }
00400 return _mems->release_memory(ptr);
00401 }
00402
00403 char *memory_checker::text_form(bool show_outstanding)
00404 {
00405 if (_unusable) return strdup("already destroyed memory_checker!\n");
00406 return _mems->text_form(show_outstanding);
00407 }
00408
00410
00411 #endif // enable memory hook
00412
00413 #endif //MEMORY_CHECKER_IMPLEMENTATION_FILE
00414