19 #ifdef ENABLE_MEMORY_HOOK
31 const int MAXIMUM_HASH_SLOTS = 256 *
KILOBYTE;
35 const int SINGLE_LINE_SIZE_ESTIMATE = 200;
40 const int RESERVED_AREA = 1000;
44 #define CLEAR_ALLOCATED_MEMORY
49 #define MEMORY_CHECKER_STATISTICS
62 #include <basis/trap_new.addin>
63 void *
operator new(
size_t size,
char *file,
int line)
throw (std::bad_alloc)
65 #include <basis/untrap_new.addin>
67 void operator delete(
void *ptr)
throw ()
86 #ifdef ENABLE_CALLSTACK_TRACKING
90 void construct(
void *ptr,
int size,
char *where,
int line) {
94 _where = strdup(where);
95 if (strlen(_where) > SINGLE_LINE_SIZE_ESTIMATE - 40) {
97 _where[SINGLE_LINE_SIZE_ESTIMATE - 40] =
'\0';
100 #ifdef ENABLE_CALLSTACK_TRACKING
112 #ifdef ENABLE_CALLSTACK_TRACKING
121 #ifdef MEMORY_CHECKER_STATISTICS
128 double _stat_new_allocations = 0;
129 double _stat_freed_allocations = 0;
131 double _stat_new_allocations_size = 0;
132 double _stat_freed_allocations_size = 0;
145 _lock = (mutex_base *)malloc(
sizeof(mutex_base));
153 int count()
const {
return _count; }
155 int record_memory(
void *ptr,
int size,
char *where,
int line) {
156 memlink *new_guy = (memlink *)malloc(
sizeof(memlink));
157 new_guy->construct(ptr, size, where, line);
161 new_guy->_next = _head;
168 int release_memory(
void *to_release) {
171 memlink *current = _head;
174 if (current->_chunk == to_release) {
175 #ifdef MEMORY_CHECKER_STATISTICS
177 _stat_freed_allocations += 1.0;
178 _stat_freed_allocations_size += current->_size;
180 #ifdef DEBUG_MEMORY_CHECKER
181 printf(
"found %p listed, removing for %s[%d]\n", to_release,
182 current->_where, current->_line);
187 _head = current->_next;
190 previous->_next = current->_next;
201 current = current->_next;
203 #ifdef DEBUG_MEMORY_CHECKER
204 printf(
"failed to find %p listed.\n", to_release);
207 return common::NOT_FOUND;
210 void dump_list(
char *add_to,
int &curr_size,
int max_size) {
211 int size_alloc = 2 * SINGLE_LINE_SIZE_ESTIMATE;
212 char *temp_str = (
char *)malloc(size_alloc);
213 memlink *current = _head;
216 sprintf(temp_str,
"\n\"%s[%d]\", \"size %d\", \"addr %p\"\n",
217 current->_where, current->_line, current->_size, current->_chunk);
218 int len_add = strlen(temp_str);
219 if (curr_size + len_add < max_size) {
220 strcat(add_to, temp_str);
221 curr_size += len_add;
223 #ifdef ENABLE_CALLSTACK_TRACKING
224 len_add = strlen(current->_stack);
225 if (curr_size + len_add < max_size) {
226 strcat(add_to, current->_stack);
227 curr_size += len_add;
230 current = current->_next;
243 class allocation_memories
246 void construct(
int num_slots) {
247 _num_slots = num_slots;
248 _bins = (memory_bin *)malloc(num_slots *
sizeof(memory_bin));
249 for (
int i = 0; i < num_slots; i++)
250 _bins[i].construct();
255 for (
int i = 0; i < _num_slots; i++) {
262 int compute_slot(
void *ptr) {
263 return utility::hash_bytes(&ptr,
sizeof(
void *)) % _num_slots;
266 void *provide_memory(
int size_needed,
char *file,
int line) {
267 void *new_allocation = malloc(size_needed);
269 int slot = compute_slot(new_allocation);
270 #ifdef DEBUG_MEMORY_CHECKER
271 printf(
"using slot %d for %p\n", slot, new_allocation);
273 _bins[slot].record_memory(new_allocation, size_needed, file, line);
274 #ifdef MEMORY_CHECKER_STATISTICS
275 _stat_new_allocations += 1.0;
276 _stat_new_allocations_size += size_needed;
278 return new_allocation;
281 int release_memory(
void *to_drop) {
282 int slot = compute_slot(to_drop);
283 #ifdef DEBUG_MEMORY_CHECKER
284 printf(
"removing mem %p from slot %d.\n", to_drop, slot);
286 return _bins[slot].release_memory(to_drop);
294 char *report_allocations() {
297 for (
int i = 0; i < _num_slots; i++) {
299 full_count += _bins[i].count();
303 int alloc_size = full_count * SINGLE_LINE_SIZE_ESTIMATE + RESERVED_AREA;
304 char *to_return = (
char *)malloc(alloc_size);
307 strcat(to_return,
"===================\n");
308 strcat(to_return,
"Unfreed Allocations\n");
309 strcat(to_return,
"===================\n");
311 int curr_size = strlen(to_return);
312 for (
int i = 0; i < _num_slots; i++) {
313 _bins[i].dump_list(to_return, curr_size, alloc_size - RESERVED_AREA);
319 char *text_form(
bool show_outstanding) {
321 if (show_outstanding) {
322 to_return = report_allocations();
324 to_return = (
char *)malloc(RESERVED_AREA);
327 #ifdef MEMORY_CHECKER_STATISTICS
328 char *temp_str = (
char *)malloc(4 * SINGLE_LINE_SIZE_ESTIMATE);
330 sprintf(temp_str,
"=================\n");
331 strcat(to_return, temp_str);
332 sprintf(temp_str,
"Memory Statistics\n");
333 strcat(to_return, temp_str);
334 sprintf(temp_str,
"=================\n");
335 strcat(to_return, temp_str);
336 sprintf(temp_str,
"Measurements taken across entire program runtime:\n");
337 strcat(to_return, temp_str);
338 sprintf(temp_str,
" %.0f new allocations.\n", _stat_new_allocations);
339 strcat(to_return, temp_str);
340 sprintf(temp_str,
" %.4f new Mbytes.\n",
341 _stat_new_allocations_size /
MEGABYTE);
342 strcat(to_return, temp_str);
343 sprintf(temp_str,
" %.0f freed deallocations.\n",
344 _stat_freed_allocations);
345 strcat(to_return, temp_str);
346 sprintf(temp_str,
" %.4f freed Mbytes.\n",
347 _stat_freed_allocations_size /
MEGABYTE);
348 strcat(to_return, temp_str);
362 void memory_checker::construct()
364 _mems = (allocation_memories *)malloc(
sizeof(allocation_memories));
365 _mems->construct(MAXIMUM_HASH_SLOTS);
370 void memory_checker::destruct()
372 if (_unusable)
return;
373 if (!_mems) printf(
"memory_checker::destruct being invoked twice!\n");
376 char *mem_state = text_form(
true);
377 printf(
"%s", mem_state);
388 void *memory_checker::provide_memory(
size_t size,
char *file,
int line)
390 if (_unusable || !_enabled)
return malloc(size);
391 return _mems->provide_memory(size, file, line);
394 int memory_checker::release_memory(
void *ptr)
396 if (_unusable || !_enabled) {
400 return _mems->release_memory(ptr);
403 char *memory_checker::text_form(
bool show_outstanding)
405 if (_unusable)
return strdup(
"already destroyed memory_checker!\n");
406 return _mems->text_form(show_outstanding);
char * full_trace() const
provides the current stack trace in a newly malloc'd string.
Constants and objects used throughout HOOPLE.
#define NULL_POINTER
The value representing a pointer to nothing.
#define program_wide_memories()
basis::astring dump_list(type v[], int size)
dumps the contents of the list out, assuming that the type can be turned into an int.
Implements an application lock to ensure only one is running at once.
callstack_tracker & thread_wide_stack_trace()
the single instance of callstack_tracker.
const int MEGABYTE
Number of bytes in a megabyte.
const int KILOBYTE
Number of bytes in a kilobyte.