/* -*- mode: C; c-basic-offset: 4 -*- */ #ident "Copyright (c) 2007 Tokutek Inc. All rights reserved." #include "memory.h" #include <assert.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <stdio.h> int toku_memory_check=0; #define WHEN_MEM_DEBUG(x) ({if (toku_memory_check) ({x});}) static long long n_items_malloced=0; /* Memory checking */ enum { items_limit = 1000 }; static int overflowed=0; static void *items[items_limit]; static long sizes[items_limit]; static void note_did_malloc (void *p, long size) { static long long count=0; WHEN_MEM_DEBUG( if (n_items_malloced<items_limit) { items[n_items_malloced]=p; sizes[n_items_malloced]=size; } else overflowed=1; //printf("%s:%d %p=malloc(%ld)\n", __FILE__, __LINE__, p, size); ); n_items_malloced++; count++; } static void note_did_free(void *p) { WHEN_MEM_DEBUG( if (!overflowed) { int i; //printf("not overflowed\n"); for (i=0; i<n_items_malloced; i++) { if (items[i]==p) { items[i]=items[n_items_malloced-1]; sizes[i]=sizes[n_items_malloced-1]; // printf("items[%d] replaced, now %p\n", i, items[i]); goto ok; } } printf("%s:%d freed something (%p) not alloced\n", __FILE__, __LINE__, p); abort(); ok:; } //printf("%s:%d free(%p)\n", __FILE__, __LINE__, p); ); n_items_malloced--; } //#define BUFFERED_MALLOC #ifdef BUFFERED_MALLOC #error "Turning on BUFFERED_MALLOC will probably break things. For example, DBT's are DB_DBT_MALLOC'd using toku_malloc(), but then the user calls free() (rather than toku_free)." enum { BUFFERING = 4096 }; void mark_buffer (char *p, int size) { unsigned int *pl = (unsigned int*)p; int i; for (i=0; i<BUFFERING/4; i++) { pl[i] = 0xdeadbeef; } pl[BUFFERING/8] = size; } int check_buffer (char *p) { unsigned int *pl = (unsigned int*)p; int i; for (i=0; i<BUFFERING/4; i++) { if (i!=BUFFERING/8) { assert(pl[i] == 0xdeadbeef); } } return pl[BUFFERING/8]; } void check_all_buffers (void) { int i; if (!overflowed) { for (i=0; i<n_items_malloced; i++) { int size = check_buffer(((char*)items[i])-BUFFERING); check_buffer(((char*)items[i])+size); } } } void *actual_malloc(long size) { char *r = malloc(size+BUFFERING*2); mark_buffer(r, size); mark_buffer(r+size+BUFFERING, size); check_all_buffers(); return r+BUFFERING; } void actual_free(void *pv) { char *p = pv; int size=check_buffer(p-BUFFERING); check_buffer(p+size); check_all_buffers(); //free(p-BUFFERING); } void *actual_realloc(void *pv, long size) { check_all_buffers(); { char *p = pv; char *r = realloc(p-BUFFERING, size+BUFFERING*2); mark_buffer(r, size); mark_buffer(r+size+BUFFERING, size); return r+BUFFERING; } } void *actual_calloc (long nmemb, long size) { return actual_malloc(nmemb*size); } void do_memory_check (void) { check_all_buffers(); } #else #define actual_malloc malloc #define actual_free free #define actual_realloc realloc #define actual_calloc calloc #endif void *toku_calloc(long nmemb, long size) { void *r; errno=0; r = actual_calloc(nmemb, size); //printf("%s:%d calloc(%ld,%ld)->%p\n", __FILE__, __LINE__, nmemb, size, r); note_did_malloc(r, nmemb*size); //if ((long)r==0x80523f8) { printf("%s:%d %p\n", __FILE__, __LINE__, r); } return r; } #define FREELIST_LIMIT (64+1) static void *freelist[FREELIST_LIMIT]; #define MALLOC_SIZE_COUNTING_LIMIT 512 static int malloc_counts[MALLOC_SIZE_COUNTING_LIMIT]; // We rely on static variables being initialized to 0. static int fresh_malloc_counts[MALLOC_SIZE_COUNTING_LIMIT]; // We rely on static variables being initialized to 0. static int other_malloc_count=0, fresh_other_malloc_count=0; void *toku_malloc(unsigned long size) { void * r; errno=0; if (size<MALLOC_SIZE_COUNTING_LIMIT) malloc_counts[size]++; else other_malloc_count++; if (size>=sizeof(void*) && size<FREELIST_LIMIT) { if (freelist[size]) { r = freelist[size]; freelist[size] = *(void**)r; note_did_malloc(r, size); return r; } } if (size<MALLOC_SIZE_COUNTING_LIMIT) fresh_malloc_counts[size]++; else fresh_other_malloc_count++; r=actual_malloc(size); //printf("%s:%d malloc(%ld)->%p\n", __FILE__, __LINE__, size,r); note_did_malloc(r, size); //if ((long)r==0x80523f8) { printf("%s:%d %p size=%ld\n", __FILE__, __LINE__, r, size); } return r; } void *toku_tagmalloc(unsigned long size, int typtag) { //printf("%s:%d tagmalloc\n", __FILE__, __LINE__); void *r = toku_malloc(size); assert(size>sizeof(int)); ((int*)r)[0] = typtag; return r; } void *toku_realloc(void *p, long size) { void *newp; note_did_free(p); errno=0; newp = actual_realloc(p, size); //printf("%s:%d realloc(%p,%ld)-->%p\n", __FILE__, __LINE__, p, size, newp); note_did_malloc(newp, size); return newp; } void toku_free(void* p) { //printf("%s:%d free(%p)\n", __FILE__, __LINE__, p); note_did_free(p); actual_free(p); } void toku_free_n(void* p, unsigned long size) { //printf("%s:%d free(%p)\n", __FILE__, __LINE__, p); note_did_free(p); if (size>=sizeof(void*) && size<FREELIST_LIMIT) { //printf("freelist[%lu] ||= %p\n", size, p); *(void**)p = freelist[size]; freelist[size]=p; } else { actual_free(p); } } void *toku_memdup (const void *v, unsigned int len) { void *r=toku_malloc(len); memcpy(r,v,len); return r; } char *toku_strdup (const char *s) { return toku_memdup(s, strlen(s)+1); } void toku_memory_check_all_free (void) { if (n_items_malloced>0) { printf("n_items_malloced=%lld\n", n_items_malloced); if (toku_memory_check) printf(" one item is %p size=%ld\n", items[0], sizes[0]); } assert(n_items_malloced==0); } int toku_get_n_items_malloced (void) { return n_items_malloced; } void toku_print_malloced_items (void) { int i; for (i=0; i<n_items_malloced; i++) { printf(" %p size=%ld\n", items[i], sizes[i]); } } void toku_malloc_report (void) { int i; printf("malloc report:\n"); for (i=0; i<MALLOC_SIZE_COUNTING_LIMIT; i++) { if (malloc_counts[i] || fresh_malloc_counts[i]) printf("%d: %d (%d fresh)\n", i, malloc_counts[i], fresh_malloc_counts[i]); } printf("Other: %d (%d fresh)\n", other_malloc_count, fresh_other_malloc_count); } void toku_malloc_cleanup (void) { int i; for (i=0; i<FREELIST_LIMIT; i++) { void *p; while ((p = freelist[i])) { freelist[i] = *(void**)p; actual_free(p); } } }