1
/* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* Original author: Paul McCullagh (H&G2JCtL)
20
* Continued development: Barry Leslie
27
* For debugging memory leaks search for "DEBUG-BREAK-POINT" and watch mm_tracking_id
38
#include "CSException.h"
41
#include "CSStrUtil.h"
52
void *cs_malloc(size_t size)
56
if (!(ptr = malloc(size)))
57
CSException::throwOSError(CS_CONTEXT, ENOMEM);
61
void *cs_calloc(size_t size)
65
if (!(ptr = malloc(size)))
66
CSException::throwOSError(CS_CONTEXT, ENOMEM);
71
void cs_realloc(void **ptr, size_t size)
75
if (!(new_ptr = realloc(*ptr, size)))
76
CSException::throwOSError(CS_CONTEXT, ENOMEM);
80
void cs_free(void *ptr)
86
* -----------------------------------------------------------------------
87
* DEBUG MEMORY ALLOCATION AND HEAP CHECKING
94
#define ADD_TOTAL_ALLOCS 4000
96
#define SHIFT_RIGHT(ptr, n) memmove(((char *) (ptr)) + sizeof(MissingMemoryRec), (ptr), (long) (n) * sizeof(MissingMemoryRec))
97
#define SHIFT_LEFT(ptr, n) memmove((ptr), ((char *) (ptr)) + sizeof(MissingMemoryRec), (long) (n) * sizeof(MissingMemoryRec))
99
typedef struct MissingMemory {
102
const char *func_name;
103
const char *file_name;
105
} MissingMemoryRec, *MissingMemoryPtr;
107
static MissingMemoryRec *mm_addresses = NULL;
108
static uint32_t mm_nr_in_use = 0L;
109
static uint32_t mm_total_allocated = 0L;
110
static uint32_t mm_alloc_count = 0;
111
static pthread_mutex_t mm_mutex;
113
/* Set this variable to the ID of the memory you want
116
static uint32_t mm_tracking_id = 0;
118
static void mm_println(const char *str)
123
static long mm_find_pointer(void *ptr)
125
register long i, n, guess;
130
guess = (i + n - 1) >> 1;
131
if (ptr == mm_addresses[guess].ptr)
133
if (ptr < mm_addresses[guess].ptr)
141
static long mm_add_pointer(void *ptr)
143
register int i, n, guess;
145
if (mm_nr_in_use == mm_total_allocated) {
146
/* Not enough space, add more: */
147
MissingMemoryRec *new_addresses;
149
new_addresses = (MissingMemoryRec *) malloc(sizeof(MissingMemoryRec) * (mm_total_allocated + ADD_TOTAL_ALLOCS));
154
memcpy(new_addresses, mm_addresses, sizeof(MissingMemoryRec) * mm_total_allocated);
158
mm_addresses = new_addresses;
159
mm_total_allocated += ADD_TOTAL_ALLOCS;
165
guess = (i + n - 1) >> 1;
166
if (ptr < mm_addresses[guess].ptr)
172
SHIFT_RIGHT(&mm_addresses[i], mm_nr_in_use - i);
174
mm_addresses[i].ptr = ptr;
178
static char *cs_mm_watch_point = 0;
180
static long mm_remove_pointer(void *ptr)
182
register int i, n, guess;
184
if (cs_mm_watch_point == ptr)
185
printf("Hit watch point\n");
190
guess = (i + n - 1) >> 1;
191
if (ptr == mm_addresses[guess].ptr)
193
if (ptr < mm_addresses[guess].ptr)
201
/* Decrease the number of sets, and shift left: */
203
SHIFT_LEFT(&mm_addresses[guess], mm_nr_in_use - guess);
207
static void mm_throw_assertion(MissingMemoryPtr mm_ptr, void *p, const char *message)
212
snprintf(str, 200, "MM ERROR: %8p (#%"PRId32") %s:%"PRId32" %s",
215
cs_last_name_of_path(mm_ptr->file_name),
220
snprintf(str, 200, "MM ERROR: %8p %s", p, message);
224
static uint32_t mm_add_core_ptr(void *ptr, const char *func, const char *file, int line)
229
mm = mm_add_pointer(ptr);
231
mm_println("MM ERROR: Cannot allocate table big enough!");
235
/* Record the pointer: */
236
if (mm_alloc_count == mm_tracking_id) {
237
/* Enable you to set a breakpoint: */
238
id = mm_alloc_count++; // DEBUG-BREAK-POINT
241
id = mm_alloc_count++;
243
mm_addresses[mm].ptr = ptr;
244
mm_addresses[mm].id = id;
245
mm_addresses[mm].func_name = func;
247
mm_addresses[mm].file_name = file;
249
mm_addresses[mm].file_name = "?";
250
mm_addresses[mm].line_nr = line;
254
static void mm_remove_core_ptr(void *ptr)
258
mm = mm_remove_pointer(ptr);
260
mm_println("MM ERROR: Pointer not allocated");
265
static long mm_find_core_ptr(void *ptr)
269
mm = mm_find_pointer(ptr);
271
mm_throw_assertion(NULL, ptr, "Pointer not allocated");
275
static void mm_replace_core_ptr(long i, void *ptr)
277
MissingMemoryRec tmp = mm_addresses[i];
280
mm_remove_pointer(mm_addresses[i].ptr);
281
mm = mm_add_pointer(ptr);
283
mm_println("MM ERROR: Cannot allocate table big enough!");
286
mm_addresses[mm] = tmp;
287
mm_addresses[mm].ptr = ptr;
291
* -----------------------------------------------------------------------
292
* MISSING MEMORY PUBLIC ROUTINES
295
#define MEM_DEBUG_HDR_SIZE offsetof(MemoryDebugRec, data)
296
#define MEM_TRAILER_SIZE 2
297
#define MEM_HEADER 0x01010101
298
#define MEM_FREED 0x03030303
299
#define MEM_TRAILER_BYTE 0x02
300
#define MEM_FREED_BYTE 0x03
302
typedef struct MemoryDebug {
304
uint32_t md_id; /* The memory ID! */
307
} MemoryDebugRec, *MemoryDebugPtr;
309
static size_t mm_check_and_free(MissingMemoryPtr mm_ptr, void *p, bool freeme)
311
unsigned char *ptr = (unsigned char *) p - MEM_DEBUG_HDR_SIZE;
312
MemoryDebugPtr debug_ptr = (MemoryDebugPtr) ptr;
313
size_t size = debug_ptr->size;
317
if (!ASSERT(((size_t) p & 1L) == 0))
320
if (debug_ptr->check == MEM_FREED) {
321
mm_println("MM ERROR: Pointer already freed 'debug_ptr->check != MEM_FREED'");
325
if (debug_ptr->check != MEM_HEADER) {
326
mm_println("MM ERROR: Header not valid 'debug_ptr->check != MEM_HEADER'");
330
if (!(*((unsigned char *) ptr + size + MEM_DEBUG_HDR_SIZE) == MEM_TRAILER_BYTE &&
331
*((unsigned char *) ptr + size + MEM_DEBUG_HDR_SIZE + 1L) == MEM_TRAILER_BYTE)) {
332
mm_throw_assertion(mm_ptr, p, "Trailer overwritten");
337
debug_ptr->check = MEM_FREED;
338
*((unsigned char *) ptr + size + MEM_DEBUG_HDR_SIZE) = MEM_FREED_BYTE;
339
*((unsigned char *) ptr + size + MEM_DEBUG_HDR_SIZE + 1L) = MEM_FREED_BYTE;
341
memset(((unsigned char *) ptr) + MEM_DEBUG_HDR_SIZE, 0xF5, size);
348
bool cs_mm_scan_core(void)
356
pthread_mutex_lock(&mm_mutex);
358
for (mm=0; mm<mm_nr_in_use; mm++) {
359
if (!mm_check_and_free(&mm_addresses[mm], mm_addresses[mm].ptr, false))
363
pthread_mutex_unlock(&mm_mutex);
367
void cs_mm_memmove(void *block, void *dest, void *source, size_t size)
370
MemoryDebugPtr debug_ptr = (MemoryDebugPtr) ((char *) block - MEM_DEBUG_HDR_SIZE);
373
pthread_mutex_lock(&mm_mutex);
374
mm_find_core_ptr(block);
375
pthread_mutex_unlock(&mm_mutex);
377
mm_check_and_free(NULL, block, false);
379
if (dest < block || (char *) dest > (char *) block + debug_ptr->size) {
380
mm_println("MM ERROR: Destination not in block");
383
if ((char *) dest + size > (char *) block + debug_ptr->size) {
384
mm_println("MM ERROR: Copy will overwrite memory");
389
memmove(dest, source, size);
392
void cs_mm_memcpy(void *block, void *dest, void *source, size_t size)
395
MemoryDebugPtr debug_ptr = (MemoryDebugPtr) ((char *) block - MEM_DEBUG_HDR_SIZE);
398
pthread_mutex_lock(&mm_mutex);
399
mm_find_core_ptr(block);
400
pthread_mutex_unlock(&mm_mutex);
402
mm_check_and_free(NULL, block, false);
404
if (dest < block || (char *) dest > (char *) block + debug_ptr->size)
405
mm_throw_assertion(NULL, block, "Destination not in block");
406
if ((char *) dest + size > (char *) block + debug_ptr->size)
407
mm_throw_assertion(NULL, block, "Copy will overwrite memory");
410
memcpy(dest, source, size);
413
void cs_mm_memset(void *block, void *dest, int value, size_t size)
416
MemoryDebugPtr debug_ptr = (MemoryDebugPtr) ((char *) block - MEM_DEBUG_HDR_SIZE);
419
pthread_mutex_lock(&mm_mutex);
420
mm_find_core_ptr(block);
421
pthread_mutex_unlock(&mm_mutex);
423
mm_check_and_free(NULL, block, false);
425
if (dest < block || (char *) dest > (char *) block + debug_ptr->size)
426
mm_throw_assertion(NULL, block, "Destination not in block");
427
if ((char *) dest + size > (char *) block + debug_ptr->size)
428
mm_throw_assertion(NULL, block, "Copy will overwrite memory");
431
memset(dest, value, size);
434
void *cs_mm_malloc(const char *func, const char *file, int line, size_t size)
436
unsigned char *p = (unsigned char *) cs_malloc(size + MEM_DEBUG_HDR_SIZE + MEM_TRAILER_SIZE);
441
memset(p, 0x55, size + MEM_DEBUG_HDR_SIZE + MEM_TRAILER_SIZE);
443
((MemoryDebugPtr) p)->check = MEM_HEADER;
444
((MemoryDebugPtr) p)->md_id = 0;
445
((MemoryDebugPtr) p)->size = (uint32_t) size;
446
*(p + size + MEM_DEBUG_HDR_SIZE) = MEM_TRAILER_BYTE;
447
*(p + size + MEM_DEBUG_HDR_SIZE + 1L) = MEM_TRAILER_BYTE;
450
pthread_mutex_lock(&mm_mutex);
451
((MemoryDebugPtr) p)->md_id = mm_add_core_ptr(p + MEM_DEBUG_HDR_SIZE, func, file, line);
452
pthread_mutex_unlock(&mm_mutex);
455
return p + MEM_DEBUG_HDR_SIZE;
458
void *cs_mm_calloc(const char *func, const char *file, int line, size_t size)
460
unsigned char *p = (unsigned char *) cs_calloc(size + MEM_DEBUG_HDR_SIZE + MEM_TRAILER_SIZE);
465
((MemoryDebugPtr) p)->check = MEM_HEADER;
466
((MemoryDebugPtr) p)->md_id = 0;
467
((MemoryDebugPtr) p)->size = (uint32_t) size;
468
*(p + size + MEM_DEBUG_HDR_SIZE) = MEM_TRAILER_BYTE;
469
*(p + size + MEM_DEBUG_HDR_SIZE + 1L) = MEM_TRAILER_BYTE;
472
pthread_mutex_lock(&mm_mutex);
473
((MemoryDebugPtr) p)->md_id = mm_add_core_ptr(p + MEM_DEBUG_HDR_SIZE, func, file, line);
474
pthread_mutex_unlock(&mm_mutex);
477
return p + MEM_DEBUG_HDR_SIZE;
480
void cs_mm_realloc(const char *func, const char *file, int line, void **ptr, size_t newsize)
482
unsigned char *oldptr = (unsigned char *) *ptr;
488
*ptr = cs_mm_malloc(func, file, line, newsize);
493
// The lock must be held until the realloc has completed otherwise
494
// a scan of the memory may report a bad memory header.
495
pthread_mutex_lock(&mm_mutex);
496
if ((mm = mm_find_core_ptr(oldptr)) < 0) {
497
pthread_mutex_unlock(&mm_mutex);
498
CSException::throwOSError(CS_CONTEXT, ENOMEM);
500
// pthread_mutex_unlock(&mm_mutex); It will be unlocked below
503
oldptr = oldptr - MEM_DEBUG_HDR_SIZE;
504
size = ((MemoryDebugPtr) oldptr)->size;
506
ASSERT(((MemoryDebugPtr) oldptr)->check == MEM_HEADER);
507
ASSERT(*((unsigned char *) oldptr + size + MEM_DEBUG_HDR_SIZE) == MEM_TRAILER_BYTE &&
508
*((unsigned char *) oldptr + size + MEM_DEBUG_HDR_SIZE + 1L) == MEM_TRAILER_BYTE);
510
/* Grow allways moves! */
511
pnew = (unsigned char *) cs_malloc(newsize + MEM_DEBUG_HDR_SIZE + MEM_TRAILER_SIZE);
514
pthread_mutex_unlock(&mm_mutex);
516
CSException::throwOSError(CS_CONTEXT, ENOMEM);
520
if (newsize > size) {
521
memcpy(((MemoryDebugPtr) pnew)->data, ((MemoryDebugPtr) oldptr)->data, size);
522
memset(((MemoryDebugPtr) pnew)->data + size, 0x55, newsize - size);
525
memcpy(((MemoryDebugPtr) pnew)->data, ((MemoryDebugPtr) oldptr)->data, newsize);
526
memset(oldptr, 0x55, size + MEM_DEBUG_HDR_SIZE + MEM_TRAILER_SIZE);
529
// pthread_mutex_lock(&mm_mutex); It was locked above
530
if ((mm = mm_find_core_ptr(oldptr + MEM_DEBUG_HDR_SIZE)) < 0) {
531
pthread_mutex_unlock(&mm_mutex);
532
CSException::throwOSError(CS_CONTEXT, ENOMEM);
535
mm_replace_core_ptr(mm, pnew + MEM_DEBUG_HDR_SIZE);
536
pthread_mutex_unlock(&mm_mutex);
541
((MemoryDebugPtr) pnew)->check = MEM_HEADER;
542
((MemoryDebugPtr) pnew)->size = (uint32_t) newsize;
543
*(pnew + newsize + MEM_DEBUG_HDR_SIZE) = MEM_TRAILER_BYTE;
544
*(pnew + newsize + MEM_DEBUG_HDR_SIZE + 1L) = MEM_TRAILER_BYTE;
546
*ptr = pnew + MEM_DEBUG_HDR_SIZE;
549
void cs_mm_free(void *ptr)
551
bool my_pointer = false;
554
pthread_mutex_lock(&mm_mutex);
555
if (mm_find_pointer(ptr) >= 0) {
557
mm_remove_core_ptr(ptr);
559
pthread_mutex_unlock(&mm_mutex);
562
mm_check_and_free(NULL, ptr, true);
567
void cs_mm_pfree(void **ptr)
577
size_t cs_mm_malloc_size(void *ptr)
582
pthread_mutex_lock(&mm_mutex);
583
mm_find_core_ptr(ptr);
584
pthread_mutex_unlock(&mm_mutex);
586
size = mm_check_and_free(NULL, ptr, false);
590
void cs_mm_print_track(const char *func, const char *file, uint32_t line, void *p, bool inc, uint32_t ref_cnt, int track_me)
592
unsigned char *ptr = (unsigned char *) p - MEM_DEBUG_HDR_SIZE;
593
MemoryDebugPtr debug_ptr = (MemoryDebugPtr) ptr;
594
CSThread *self = CSThread::getSelf();
598
if (!track_me && !mm_tracking_id)
602
cs_format_context(300, buffer, func, file, line);
603
fprintf(stderr, "TRACKING (%"PRIu32"): %s %2"PRIu32" %s", debug_ptr->md_id, inc ? "INC" : "DEC", ref_cnt, buffer);
606
fprintf(stderr, "TRACKING (%"PRIu32"): %s %2"PRIu32"", debug_ptr->md_id, inc ? "INC" : "DEC", ref_cnt);
608
for (int i = self->callTop-1; i>=0 && cnt < 4; i--) {
609
cs_format_context(300, buffer, self->callStack[i].cs_func, self->callStack[i].cs_file, self->callStack[i].cs_line);
610
fprintf(stderr," %s", buffer);
613
fprintf(stderr,"\n");
616
void cs_mm_track_memory(const char *func, const char *file, uint32_t line, void *p, bool inc, uint32_t ref_cnt, int track_me)
618
unsigned char *ptr = (unsigned char *) p - MEM_DEBUG_HDR_SIZE;
619
MemoryDebugPtr debug_ptr = (MemoryDebugPtr) ptr;
621
if (track_me || (mm_tracking_id && debug_ptr->md_id == mm_tracking_id))
622
cs_mm_print_track(func, file, line, p, inc, ref_cnt, track_me);
628
* -----------------------------------------------------------------------
632
bool cs_init_memory(void)
635
pthread_mutex_init(&mm_mutex, NULL);
636
mm_addresses = (MissingMemoryRec *) malloc(sizeof(MissingMemoryRec) * ADD_TOTAL_ALLOCS);
638
mm_println("MM ERROR: Insuffient memory to allocate MM table");
639
pthread_mutex_destroy(&mm_mutex);
643
memset(mm_addresses, 0, sizeof(MissingMemoryRec) * ADD_TOTAL_ALLOCS);
644
mm_total_allocated = ADD_TOTAL_ALLOCS;
651
void cs_exit_memory(void)
659
pthread_mutex_lock(&mm_mutex);
660
for (mm=0; mm<mm_nr_in_use; mm++)
661
mm_throw_assertion(&mm_addresses[mm], mm_addresses[mm].ptr, "Not freed");
665
mm_total_allocated = 0L;
667
pthread_mutex_unlock(&mm_mutex);
669
pthread_mutex_destroy(&mm_mutex);
674
uint32_t cs_mm_get_check_point()
676
return mm_alloc_count;
679
// Reports any memory allocated after the check_point
680
// but has not been freed.
681
void cs_mm_assert_check_point(uint32_t check_point)
688
pthread_mutex_lock(&mm_mutex);
689
for (mm=0; mm<mm_nr_in_use; mm++) {
690
if (mm_addresses[mm].id >= check_point)
691
mm_throw_assertion(&mm_addresses[mm], mm_addresses[mm].ptr, "Not freed");
694
pthread_mutex_unlock(&mm_mutex);