Merge "Implement malloc_usable_size for debug impls."
This commit is contained in:
commit
9647f797d5
@ -348,7 +348,7 @@ static void out_vformat(Out& o, const char* format, va_list args) {
|
|||||||
buffer[0] = '0';
|
buffer[0] = '0';
|
||||||
buffer[1] = 'x';
|
buffer[1] = 'x';
|
||||||
format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
|
format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
|
||||||
} else if (c == 'd' || c == 'i' || c == 'o' || c == 'x' || c == 'X') {
|
} else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
|
||||||
/* integers - first read value from stack */
|
/* integers - first read value from stack */
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
int is_signed = (c == 'd' || c == 'i' || c == 'o');
|
int is_signed = (c == 'd' || c == 'i' || c == 'o');
|
||||||
|
@ -74,6 +74,10 @@ static void log_message(const char* format, ...) {
|
|||||||
|
|
||||||
struct hdr_t {
|
struct hdr_t {
|
||||||
uint32_t tag;
|
uint32_t tag;
|
||||||
|
void* base; // Always points to the memory allocated using dlmalloc.
|
||||||
|
// For memory allocated in chk_memalign, this value will
|
||||||
|
// not be the same as the location of the start of this
|
||||||
|
// structure.
|
||||||
hdr_t* prev;
|
hdr_t* prev;
|
||||||
hdr_t* next;
|
hdr_t* next;
|
||||||
uintptr_t bt[MAX_BACKTRACE_DEPTH];
|
uintptr_t bt[MAX_BACKTRACE_DEPTH];
|
||||||
@ -82,7 +86,7 @@ struct hdr_t {
|
|||||||
int freed_bt_depth;
|
int freed_bt_depth;
|
||||||
size_t size;
|
size_t size;
|
||||||
char front_guard[FRONT_GUARD_LEN];
|
char front_guard[FRONT_GUARD_LEN];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed, aligned(MALLOC_ALIGNMENT)));
|
||||||
|
|
||||||
struct ftr_t {
|
struct ftr_t {
|
||||||
char rear_guard[REAR_GUARD_LEN];
|
char rear_guard[REAR_GUARD_LEN];
|
||||||
@ -100,6 +104,11 @@ static inline hdr_t* meta(void* user) {
|
|||||||
return reinterpret_cast<hdr_t*>(user) - 1;
|
return reinterpret_cast<hdr_t*>(user) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const hdr_t* const_meta(const void* user) {
|
||||||
|
return reinterpret_cast<const hdr_t*>(user) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static unsigned gAllocatedBlockCount;
|
static unsigned gAllocatedBlockCount;
|
||||||
static hdr_t* tail;
|
static hdr_t* tail;
|
||||||
static hdr_t* head;
|
static hdr_t* head;
|
||||||
@ -200,7 +209,7 @@ static inline void poison(hdr_t *hdr) {
|
|||||||
|
|
||||||
static int was_used_after_free(hdr_t* hdr) {
|
static int was_used_after_free(hdr_t* hdr) {
|
||||||
unsigned i;
|
unsigned i;
|
||||||
const char *data = (const char *)user(hdr);
|
const char* data = reinterpret_cast<const char *>(user(hdr));
|
||||||
for (i = 0; i < hdr->size; i++)
|
for (i = 0; i < hdr->size; i++)
|
||||||
if (data[i] != FREE_POISON)
|
if (data[i] != FREE_POISON)
|
||||||
return 1;
|
return 1;
|
||||||
@ -309,7 +318,7 @@ static inline void add_to_backlog(hdr_t *hdr) {
|
|||||||
while (backlog_num > gMallocDebugBacklog) {
|
while (backlog_num > gMallocDebugBacklog) {
|
||||||
hdr_t* gone = backlog_tail;
|
hdr_t* gone = backlog_tail;
|
||||||
del_from_backlog_locked(gone);
|
del_from_backlog_locked(gone);
|
||||||
dlfree(gone);
|
dlfree(gone->base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +327,7 @@ extern "C" void* chk_malloc(size_t size) {
|
|||||||
|
|
||||||
hdr_t* hdr = static_cast<hdr_t*>(dlmalloc(sizeof(hdr_t) + size + sizeof(ftr_t)));
|
hdr_t* hdr = static_cast<hdr_t*>(dlmalloc(sizeof(hdr_t) + size + sizeof(ftr_t)));
|
||||||
if (hdr) {
|
if (hdr) {
|
||||||
|
hdr->base = hdr;
|
||||||
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
||||||
add(hdr, size);
|
add(hdr, size);
|
||||||
return user(hdr);
|
return user(hdr);
|
||||||
@ -325,12 +335,43 @@ extern "C" void* chk_malloc(size_t size) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void* chk_memalign(size_t, size_t bytes) {
|
extern "C" void* chk_memalign(size_t alignment, size_t bytes) {
|
||||||
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
|
if (alignment <= MALLOC_ALIGNMENT) {
|
||||||
// XXX: it's better to use malloc, than being wrong
|
|
||||||
return chk_malloc(bytes);
|
return chk_malloc(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make the alignment a power of two.
|
||||||
|
if (alignment & (alignment-1)) {
|
||||||
|
alignment = 1L << (31 - __builtin_clz(alignment));
|
||||||
|
}
|
||||||
|
|
||||||
|
// here, alignment is at least MALLOC_ALIGNMENT<<1 bytes
|
||||||
|
// we will align by at least MALLOC_ALIGNMENT bytes
|
||||||
|
// and at most alignment-MALLOC_ALIGNMENT bytes
|
||||||
|
size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;
|
||||||
|
if (size < bytes) { // Overflow.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* base = dlmalloc(sizeof(hdr_t) + size + sizeof(ftr_t));
|
||||||
|
if (base != NULL) {
|
||||||
|
// Check that the actual pointer that will be returned is aligned
|
||||||
|
// properly.
|
||||||
|
uintptr_t ptr = reinterpret_cast<uintptr_t>(user(reinterpret_cast<hdr_t*>(base)));
|
||||||
|
if ((ptr % alignment) != 0) {
|
||||||
|
// Align the pointer.
|
||||||
|
ptr += ((-ptr) % alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr_t* hdr = meta(reinterpret_cast<void*>(ptr));
|
||||||
|
hdr->base = base;
|
||||||
|
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
||||||
|
add(hdr, bytes);
|
||||||
|
return user(hdr);
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void chk_free(void* ptr) {
|
extern "C" void chk_free(void* ptr) {
|
||||||
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
|
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
|
||||||
|
|
||||||
@ -414,8 +455,23 @@ extern "C" void *chk_realloc(void *ptr, size_t size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hdr->base != hdr) {
|
||||||
|
// An allocation from memalign, so create another allocation and
|
||||||
|
// copy the data out.
|
||||||
|
void* newMem = dlmalloc(sizeof(hdr_t) + size + sizeof(ftr_t));
|
||||||
|
if (newMem) {
|
||||||
|
memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size);
|
||||||
|
dlfree(hdr->base);
|
||||||
|
hdr = static_cast<hdr_t*>(newMem);
|
||||||
|
} else {
|
||||||
|
dlfree(hdr->base);
|
||||||
|
hdr = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
hdr = static_cast<hdr_t*>(dlrealloc(hdr, sizeof(hdr_t) + size + sizeof(ftr_t)));
|
hdr = static_cast<hdr_t*>(dlrealloc(hdr, sizeof(hdr_t) + size + sizeof(ftr_t)));
|
||||||
|
}
|
||||||
if (hdr) {
|
if (hdr) {
|
||||||
|
hdr->base = hdr;
|
||||||
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
||||||
add(hdr, size);
|
add(hdr, size);
|
||||||
return user(hdr);
|
return user(hdr);
|
||||||
@ -429,6 +485,7 @@ extern "C" void *chk_calloc(int nmemb, size_t size) {
|
|||||||
size_t total_size = nmemb * size;
|
size_t total_size = nmemb * size;
|
||||||
hdr_t* hdr = static_cast<hdr_t*>(dlcalloc(1, sizeof(hdr_t) + total_size + sizeof(ftr_t)));
|
hdr_t* hdr = static_cast<hdr_t*>(dlcalloc(1, sizeof(hdr_t) + total_size + sizeof(ftr_t)));
|
||||||
if (hdr) {
|
if (hdr) {
|
||||||
|
hdr->base = hdr;
|
||||||
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
|
||||||
add(hdr, total_size);
|
add(hdr, total_size);
|
||||||
return user(hdr);
|
return user(hdr);
|
||||||
@ -436,6 +493,18 @@ extern "C" void *chk_calloc(int nmemb, size_t size) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" size_t chk_malloc_usable_size(const void* ptr) {
|
||||||
|
// dlmalloc_usable_size returns 0 for NULL and unknown blocks.
|
||||||
|
if (ptr == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const hdr_t* hdr = const_meta(ptr);
|
||||||
|
|
||||||
|
// The sentinel tail is written just after the request block bytes
|
||||||
|
// so there is no extra room we can report here.
|
||||||
|
return hdr->size;
|
||||||
|
}
|
||||||
|
|
||||||
static void ReportMemoryLeaks() {
|
static void ReportMemoryLeaks() {
|
||||||
// We only track leaks at level 10.
|
// We only track leaks at level 10.
|
||||||
if (gMallocDebugLevel != 10) {
|
if (gMallocDebugLevel != 10) {
|
||||||
|
@ -190,10 +190,6 @@ extern "C" struct mallinfo mallinfo() {
|
|||||||
return dlmallinfo();
|
return dlmallinfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" size_t malloc_usable_size(const void* mem) {
|
|
||||||
return dlmalloc_usable_size(mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void* valloc(size_t bytes) {
|
extern "C" void* valloc(size_t bytes) {
|
||||||
return dlvalloc(bytes);
|
return dlvalloc(bytes);
|
||||||
}
|
}
|
||||||
@ -215,8 +211,9 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
|
|||||||
|
|
||||||
/* Table for dispatching malloc calls, initialized with default dispatchers. */
|
/* Table for dispatching malloc calls, initialized with default dispatchers. */
|
||||||
extern const MallocDebug __libc_malloc_default_dispatch;
|
extern const MallocDebug __libc_malloc_default_dispatch;
|
||||||
const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) = {
|
const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) =
|
||||||
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
|
{
|
||||||
|
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign, dlmalloc_usable_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Selector of dispatch table to use for dispatching malloc calls. */
|
/* Selector of dispatch table to use for dispatching malloc calls. */
|
||||||
@ -242,6 +239,10 @@ extern "C" void* memalign(size_t alignment, size_t bytes) {
|
|||||||
return __libc_malloc_dispatch->memalign(alignment, bytes);
|
return __libc_malloc_dispatch->memalign(alignment, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" size_t malloc_usable_size(const void* mem) {
|
||||||
|
return __libc_malloc_dispatch->malloc_usable_size(mem);
|
||||||
|
}
|
||||||
|
|
||||||
/* We implement malloc debugging only in libc.so, so code below
|
/* We implement malloc debugging only in libc.so, so code below
|
||||||
* must be excluded if we compile this file for static libc.a
|
* must be excluded if we compile this file for static libc.a
|
||||||
*/
|
*/
|
||||||
@ -253,7 +254,7 @@ extern "C" void* memalign(size_t alignment, size_t bytes) {
|
|||||||
|
|
||||||
/* Table for dispatching malloc calls, depending on environment. */
|
/* Table for dispatching malloc calls, depending on environment. */
|
||||||
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
|
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
|
||||||
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
|
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign, dlmalloc_usable_size
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const char* __progname;
|
extern const char* __progname;
|
||||||
@ -280,11 +281,6 @@ extern const char* __progname;
|
|||||||
*/
|
*/
|
||||||
static void* libc_malloc_impl_handle = NULL;
|
static void* libc_malloc_impl_handle = NULL;
|
||||||
|
|
||||||
// This must match the alignment used by dlmalloc.
|
|
||||||
#ifndef MALLOC_ALIGNMENT
|
|
||||||
#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This variable is set to the value of property libc.debug.malloc.backlog,
|
/* This variable is set to the value of property libc.debug.malloc.backlog,
|
||||||
* when the value of libc.debug.malloc = 10. It determines the size of the
|
* when the value of libc.debug.malloc = 10. It determines the size of the
|
||||||
* backlog we use to detect multiple frees. If the property is not set, the
|
* backlog we use to detect multiple frees. If the property is not set, the
|
||||||
@ -296,41 +292,26 @@ unsigned int gMallocDebugBacklog;
|
|||||||
/* The value of libc.debug.malloc. */
|
/* The value of libc.debug.malloc. */
|
||||||
int gMallocDebugLevel;
|
int gMallocDebugLevel;
|
||||||
|
|
||||||
static void InitMalloc(MallocDebug* table, const char* prefix) {
|
template<typename FunctionType>
|
||||||
|
void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
|
||||||
|
char symbol[128];
|
||||||
|
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
|
||||||
|
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
|
||||||
|
if (*func == NULL) {
|
||||||
|
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitMalloc(void* malloc_impl_handler, MallocDebug* table, const char* prefix) {
|
||||||
__libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n",
|
__libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n",
|
||||||
__progname, gMallocDebugLevel, prefix);
|
__progname, gMallocDebugLevel, prefix);
|
||||||
|
|
||||||
char symbol[128];
|
InitMallocFunction<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc");
|
||||||
|
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
|
||||||
snprintf(symbol, sizeof(symbol), "%s_malloc", prefix);
|
InitMallocFunction<MallocDebugCalloc>(malloc_impl_handler, &table->calloc, prefix, "calloc");
|
||||||
table->malloc = reinterpret_cast<MallocDebugMalloc>(dlsym(libc_malloc_impl_handle, symbol));
|
InitMallocFunction<MallocDebugRealloc>(malloc_impl_handler, &table->realloc, prefix, "realloc");
|
||||||
if (table->malloc == NULL) {
|
InitMallocFunction<MallocDebugMemalign>(malloc_impl_handler, &table->memalign, prefix, "memalign");
|
||||||
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
InitMallocFunction<MallocDebugMallocUsableSize>(malloc_impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size");
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(symbol, sizeof(symbol), "%s_free", prefix);
|
|
||||||
table->free = reinterpret_cast<MallocDebugFree>(dlsym(libc_malloc_impl_handle, symbol));
|
|
||||||
if (table->free == NULL) {
|
|
||||||
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(symbol, sizeof(symbol), "%s_calloc", prefix);
|
|
||||||
table->calloc = reinterpret_cast<MallocDebugCalloc>(dlsym(libc_malloc_impl_handle, symbol));
|
|
||||||
if (table->calloc == NULL) {
|
|
||||||
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(symbol, sizeof(symbol), "%s_realloc", prefix);
|
|
||||||
table->realloc = reinterpret_cast<MallocDebugRealloc>(dlsym(libc_malloc_impl_handle, symbol));
|
|
||||||
if (table->realloc == NULL) {
|
|
||||||
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(symbol, sizeof(symbol), "%s_memalign", prefix);
|
|
||||||
table->memalign = reinterpret_cast<MallocDebugMemalign>(dlsym(libc_malloc_impl_handle, symbol));
|
|
||||||
if (table->memalign == NULL) {
|
|
||||||
error_log("%s: dlsym(\"%s\") failed", __progname, symbol);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initializes memory allocation framework once per process. */
|
/* Initializes memory allocation framework once per process. */
|
||||||
@ -422,24 +403,24 @@ static void malloc_init_impl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load .so that implements the required malloc debugging functionality.
|
// Load .so that implements the required malloc debugging functionality.
|
||||||
libc_malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
|
void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
|
||||||
if (libc_malloc_impl_handle == NULL) {
|
if (malloc_impl_handle == NULL) {
|
||||||
error_log("%s: Missing module %s required for malloc debug level %d: %s",
|
error_log("%s: Missing module %s required for malloc debug level %d: %s",
|
||||||
__progname, so_name, gMallocDebugLevel, dlerror());
|
__progname, so_name, gMallocDebugLevel, dlerror());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize malloc debugging in the loaded module.
|
// Initialize malloc debugging in the loaded module.
|
||||||
malloc_debug_initialize = reinterpret_cast<MallocDebugInit>(dlsym(libc_malloc_impl_handle,
|
malloc_debug_initialize = reinterpret_cast<MallocDebugInit>(dlsym(malloc_impl_handle,
|
||||||
"malloc_debug_initialize"));
|
"malloc_debug_initialize"));
|
||||||
if (malloc_debug_initialize == NULL) {
|
if (malloc_debug_initialize == NULL) {
|
||||||
error_log("%s: Initialization routine is not found in %s\n",
|
error_log("%s: Initialization routine is not found in %s\n",
|
||||||
__progname, so_name);
|
__progname, so_name);
|
||||||
dlclose(libc_malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (malloc_debug_initialize() == -1) {
|
if (malloc_debug_initialize() == -1) {
|
||||||
dlclose(libc_malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,34 +428,35 @@ static void malloc_init_impl() {
|
|||||||
// For memory checker we need to do extra initialization.
|
// For memory checker we need to do extra initialization.
|
||||||
typedef int (*MemCheckInit)(int, const char*);
|
typedef int (*MemCheckInit)(int, const char*);
|
||||||
MemCheckInit memcheck_initialize =
|
MemCheckInit memcheck_initialize =
|
||||||
reinterpret_cast<MemCheckInit>(dlsym(libc_malloc_impl_handle,
|
reinterpret_cast<MemCheckInit>(dlsym(malloc_impl_handle,
|
||||||
"memcheck_initialize"));
|
"memcheck_initialize"));
|
||||||
if (memcheck_initialize == NULL) {
|
if (memcheck_initialize == NULL) {
|
||||||
error_log("%s: memcheck_initialize routine is not found in %s\n",
|
error_log("%s: memcheck_initialize routine is not found in %s\n",
|
||||||
__progname, so_name);
|
__progname, so_name);
|
||||||
dlclose(libc_malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcheck_initialize(MALLOC_ALIGNMENT, memcheck_tracing)) {
|
if (memcheck_initialize(MALLOC_ALIGNMENT, memcheck_tracing)) {
|
||||||
dlclose(libc_malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize malloc dispatch table with appropriate routines.
|
// Initialize malloc dispatch table with appropriate routines.
|
||||||
switch (gMallocDebugLevel) {
|
switch (gMallocDebugLevel) {
|
||||||
case 1:
|
case 1:
|
||||||
InitMalloc(&gMallocUse, "leak");
|
InitMalloc(malloc_impl_handle, &gMallocUse, "leak");
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
InitMalloc(&gMallocUse, "fill");
|
InitMalloc(malloc_impl_handle, &gMallocUse, "fill");
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
InitMalloc(&gMallocUse, "chk");
|
InitMalloc(malloc_impl_handle, &gMallocUse, "chk");
|
||||||
break;
|
break;
|
||||||
case 20:
|
case 20:
|
||||||
InitMalloc(&gMallocUse, "qemu_instrumented");
|
InitMalloc(malloc_impl_handle, &gMallocUse, "qemu_instrumented");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -485,13 +467,14 @@ static void malloc_init_impl() {
|
|||||||
(gMallocUse.free == NULL) ||
|
(gMallocUse.free == NULL) ||
|
||||||
(gMallocUse.calloc == NULL) ||
|
(gMallocUse.calloc == NULL) ||
|
||||||
(gMallocUse.realloc == NULL) ||
|
(gMallocUse.realloc == NULL) ||
|
||||||
(gMallocUse.memalign == NULL)) {
|
(gMallocUse.memalign == NULL) ||
|
||||||
|
(gMallocUse.malloc_usable_size == NULL)) {
|
||||||
error_log("%s: some symbols for libc.debug.malloc level %d were not found (see above)",
|
error_log("%s: some symbols for libc.debug.malloc level %d were not found (see above)",
|
||||||
__progname, gMallocDebugLevel);
|
__progname, gMallocDebugLevel);
|
||||||
dlclose(libc_malloc_impl_handle);
|
dlclose(malloc_impl_handle);
|
||||||
libc_malloc_impl_handle = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
__libc_malloc_dispatch = &gMallocUse;
|
__libc_malloc_dispatch = &gMallocUse;
|
||||||
|
libc_malloc_impl_handle = malloc_impl_handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,11 @@
|
|||||||
|
|
||||||
#define MAX_SIZE_T (~(size_t)0)
|
#define MAX_SIZE_T (~(size_t)0)
|
||||||
|
|
||||||
|
// This must match the alignment used by dlmalloc.
|
||||||
|
#ifndef MALLOC_ALIGNMENT
|
||||||
|
#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
|
||||||
|
#endif
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Structures
|
// Structures
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@ -71,12 +76,14 @@ typedef void (*MallocDebugFree)(void*);
|
|||||||
typedef void* (*MallocDebugCalloc)(size_t, size_t);
|
typedef void* (*MallocDebugCalloc)(size_t, size_t);
|
||||||
typedef void* (*MallocDebugRealloc)(void*, size_t);
|
typedef void* (*MallocDebugRealloc)(void*, size_t);
|
||||||
typedef void* (*MallocDebugMemalign)(size_t, size_t);
|
typedef void* (*MallocDebugMemalign)(size_t, size_t);
|
||||||
|
typedef size_t (*MallocDebugMallocUsableSize)(const void*);
|
||||||
struct MallocDebug {
|
struct MallocDebug {
|
||||||
MallocDebugMalloc malloc;
|
MallocDebugMalloc malloc;
|
||||||
MallocDebugFree free;
|
MallocDebugFree free;
|
||||||
MallocDebugCalloc calloc;
|
MallocDebugCalloc calloc;
|
||||||
MallocDebugRealloc realloc;
|
MallocDebugRealloc realloc;
|
||||||
MallocDebugMemalign memalign;
|
MallocDebugMemalign memalign;
|
||||||
|
MallocDebugMallocUsableSize malloc_usable_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Malloc debugging initialization and finalization routines.
|
/* Malloc debugging initialization and finalization routines.
|
||||||
|
@ -67,9 +67,6 @@ extern HashTable gHashTable;
|
|||||||
// stack trace functions
|
// stack trace functions
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
#ifndef MALLOC_ALIGNMENT
|
|
||||||
#define MALLOC_ALIGNMENT ((size_t)8U)
|
|
||||||
#endif
|
|
||||||
#define GUARD 0x48151642
|
#define GUARD 0x48151642
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
|
|
||||||
@ -80,12 +77,16 @@ extern HashTable gHashTable;
|
|||||||
struct AllocationEntry {
|
struct AllocationEntry {
|
||||||
HashEntry* entry;
|
HashEntry* entry;
|
||||||
uint32_t guard;
|
uint32_t guard;
|
||||||
};
|
} __attribute__((aligned(MALLOC_ALIGNMENT)));
|
||||||
|
|
||||||
static AllocationEntry* to_header(void* mem) {
|
static inline AllocationEntry* to_header(void* mem) {
|
||||||
return reinterpret_cast<AllocationEntry*>(mem) - 1;
|
return reinterpret_cast<AllocationEntry*>(mem) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const AllocationEntry* const_to_header(const void* mem) {
|
||||||
|
return reinterpret_cast<const AllocationEntry*>(mem) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Hash Table functions
|
// Hash Table functions
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@ -229,17 +230,16 @@ extern "C" void fill_free(void* mem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void* fill_realloc(void* mem, size_t bytes) {
|
extern "C" void* fill_realloc(void* mem, size_t bytes) {
|
||||||
void* buffer = fill_malloc(bytes);
|
size_t oldSize = dlmalloc_usable_size(mem);
|
||||||
if (mem == NULL) {
|
void* newMem = dlrealloc(mem, bytes);
|
||||||
return buffer;
|
if (newMem) {
|
||||||
|
// If this is larger than before, fill the extra with our pattern.
|
||||||
|
size_t newSize = dlmalloc_usable_size(newMem);
|
||||||
|
if (newSize > oldSize) {
|
||||||
|
memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(newMem)+oldSize), CHK_FILL_FREE, newSize-oldSize);
|
||||||
}
|
}
|
||||||
if (buffer) {
|
|
||||||
size_t old_size = dlmalloc_usable_size(mem);
|
|
||||||
size_t size = (bytes < old_size)?(bytes):(old_size);
|
|
||||||
memcpy(buffer, mem, size);
|
|
||||||
fill_free(mem);
|
|
||||||
}
|
}
|
||||||
return buffer;
|
return newMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void* fill_memalign(size_t alignment, size_t bytes) {
|
extern "C" void* fill_memalign(size_t alignment, size_t bytes) {
|
||||||
@ -250,11 +250,17 @@ extern "C" void* fill_memalign(size_t alignment, size_t bytes) {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" size_t fill_malloc_usable_size(const void* mem) {
|
||||||
|
// Since we didn't allocate extra bytes before or after, we can
|
||||||
|
// report the normal usable size here.
|
||||||
|
return dlmalloc_usable_size(mem);
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// malloc leak functions
|
// malloc leak functions
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
static void* MEMALIGN_GUARD = reinterpret_cast<void*>(0xA1A41520);
|
static uint32_t MEMALIGN_GUARD = 0xA1A41520;
|
||||||
|
|
||||||
extern "C" void* leak_malloc(size_t bytes) {
|
extern "C" void* leak_malloc(size_t bytes) {
|
||||||
// allocate enough space infront of the allocation to store the pointer for
|
// allocate enough space infront of the allocation to store the pointer for
|
||||||
@ -296,9 +302,10 @@ extern "C" void leak_free(void* mem) {
|
|||||||
|
|
||||||
if (header->guard != GUARD) {
|
if (header->guard != GUARD) {
|
||||||
// could be a memaligned block
|
// could be a memaligned block
|
||||||
if (reinterpret_cast<void**>(mem)[-1] == MEMALIGN_GUARD) {
|
if (header->guard == MEMALIGN_GUARD) {
|
||||||
mem = reinterpret_cast<void**>(mem)[-2];
|
// For memaligned blocks, header->entry points to the memory
|
||||||
header = to_header(mem);
|
// allocated through leak_malloc.
|
||||||
|
header = to_header(header->entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,19 +345,26 @@ extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
|
|||||||
if (oldMem == NULL) {
|
if (oldMem == NULL) {
|
||||||
return leak_malloc(bytes);
|
return leak_malloc(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* newMem = NULL;
|
void* newMem = NULL;
|
||||||
AllocationEntry* header = to_header(oldMem);
|
AllocationEntry* header = to_header(oldMem);
|
||||||
if (header && header->guard == GUARD) {
|
if (header->guard == MEMALIGN_GUARD) {
|
||||||
size_t oldSize = header->entry->size & ~SIZE_FLAG_MASK;
|
// Get the real header.
|
||||||
|
header = to_header(header->entry);
|
||||||
|
} else if (header->guard != GUARD) {
|
||||||
|
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
|
||||||
|
header->guard, header->entry);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
newMem = leak_malloc(bytes);
|
newMem = leak_malloc(bytes);
|
||||||
if (newMem != NULL) {
|
if (newMem != NULL) {
|
||||||
|
size_t oldSize = header->entry->size & ~SIZE_FLAG_MASK;
|
||||||
size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
|
size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
|
||||||
memcpy(newMem, oldMem, copySize);
|
memcpy(newMem, oldMem, copySize);
|
||||||
|
}
|
||||||
leak_free(oldMem);
|
leak_free(oldMem);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newMem = dlrealloc(oldMem, bytes);
|
|
||||||
}
|
|
||||||
return newMem;
|
return newMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +389,7 @@ extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
|
|||||||
|
|
||||||
void* base = leak_malloc(size);
|
void* base = leak_malloc(size);
|
||||||
if (base != NULL) {
|
if (base != NULL) {
|
||||||
intptr_t ptr = reinterpret_cast<intptr_t>(base);
|
uintptr_t ptr = reinterpret_cast<uintptr_t>(base);
|
||||||
if ((ptr % alignment) == 0) {
|
if ((ptr % alignment) == 0) {
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
@ -383,11 +397,38 @@ extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
|
|||||||
// align the pointer
|
// align the pointer
|
||||||
ptr += ((-ptr) % alignment);
|
ptr += ((-ptr) % alignment);
|
||||||
|
|
||||||
// there is always enough space for the base pointer and the guard
|
// Already allocated enough space for the header. This assumes
|
||||||
reinterpret_cast<void**>(ptr)[-1] = MEMALIGN_GUARD;
|
// that the malloc alignment is at least 8, otherwise, this is
|
||||||
reinterpret_cast<void**>(ptr)[-2] = base;
|
// not guaranteed to have the space for the header.
|
||||||
|
AllocationEntry* header = to_header(reinterpret_cast<void*>(ptr));
|
||||||
|
header->guard = MEMALIGN_GUARD;
|
||||||
|
header->entry = reinterpret_cast<HashEntry*>(base);
|
||||||
|
|
||||||
return reinterpret_cast<void*>(ptr);
|
return reinterpret_cast<void*>(ptr);
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" size_t leak_malloc_usable_size(const void* mem) {
|
||||||
|
if (mem != NULL) {
|
||||||
|
// Check the guard to make sure it is valid.
|
||||||
|
const AllocationEntry* header = const_to_header((void*)mem);
|
||||||
|
|
||||||
|
if (header->guard == MEMALIGN_GUARD) {
|
||||||
|
// If this is a memalign'd pointer, then grab the header from
|
||||||
|
// entry.
|
||||||
|
header = const_to_header(header->entry);
|
||||||
|
} else if (header->guard != GUARD) {
|
||||||
|
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
|
||||||
|
header->guard, header->entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ret = dlmalloc_usable_size(header);
|
||||||
|
if (ret != 0) {
|
||||||
|
// The usable area starts at 'mem' and stops at 'header+ret'.
|
||||||
|
return reinterpret_cast<uintptr_t>(header) + ret - reinterpret_cast<uintptr_t>(mem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -137,7 +137,7 @@ struct MallocDescQuery {
|
|||||||
* will respond with information about allocated block that contains this
|
* will respond with information about allocated block that contains this
|
||||||
* pointer.
|
* pointer.
|
||||||
*/
|
*/
|
||||||
void* ptr;
|
const void* ptr;
|
||||||
|
|
||||||
/* Id of the process that initialized libc instance, in which this query
|
/* Id of the process that initialized libc instance, in which this query
|
||||||
* is called. This field is used by the emulator to report errors in
|
* is called. This field is used by the emulator to report errors in
|
||||||
@ -469,7 +469,7 @@ static inline int notify_qemu_free(void* ptr_to_free) {
|
|||||||
* Return:
|
* Return:
|
||||||
* Zero on success, or -1 on failure.
|
* Zero on success, or -1 on failure.
|
||||||
*/
|
*/
|
||||||
static inline int query_qemu_malloc_info(void* ptr, MallocDesc* desc, uint32_t routine) {
|
static inline int query_qemu_malloc_info(const void* ptr, MallocDesc* desc, uint32_t routine) {
|
||||||
volatile MallocDescQuery query;
|
volatile MallocDescQuery query;
|
||||||
|
|
||||||
query.ptr = ptr;
|
query.ptr = ptr;
|
||||||
@ -574,11 +574,12 @@ static void test_access_violation(const MallocDesc* desc) {
|
|||||||
// API routines
|
// API routines
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
void* qemu_instrumented_malloc(size_t bytes);
|
extern "C" void* qemu_instrumented_malloc(size_t bytes);
|
||||||
void qemu_instrumented_free(void* mem);
|
extern "C" void qemu_instrumented_free(void* mem);
|
||||||
void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size);
|
extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size);
|
||||||
void* qemu_instrumented_realloc(void* mem, size_t bytes);
|
extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes);
|
||||||
void* qemu_instrumented_memalign(size_t alignment, size_t bytes);
|
extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes);
|
||||||
|
extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem);
|
||||||
|
|
||||||
/* Initializes malloc debugging instrumentation for the emulator.
|
/* Initializes malloc debugging instrumentation for the emulator.
|
||||||
* This routine is called from malloc_init_impl routine implemented in
|
* This routine is called from malloc_init_impl routine implemented in
|
||||||
@ -589,7 +590,7 @@ void* qemu_instrumented_memalign(size_t alignment, size_t bytes);
|
|||||||
* Return:
|
* Return:
|
||||||
* 0 on success, or -1 on failure.
|
* 0 on success, or -1 on failure.
|
||||||
*/
|
*/
|
||||||
int malloc_debug_initialize() {
|
extern "C" int malloc_debug_initialize() {
|
||||||
/* We will be using emulator's magic page to report memory allocation
|
/* We will be using emulator's magic page to report memory allocation
|
||||||
* activities. In essence, what magic page does, it translates writes to
|
* activities. In essence, what magic page does, it translates writes to
|
||||||
* the memory mapped spaces into writes to an I/O port that emulator
|
* the memory mapped spaces into writes to an I/O port that emulator
|
||||||
@ -627,7 +628,7 @@ int malloc_debug_initialize() {
|
|||||||
* Return:
|
* Return:
|
||||||
* 0 on success, or -1 on failure.
|
* 0 on success, or -1 on failure.
|
||||||
*/
|
*/
|
||||||
int memcheck_initialize(int alignment, const char* memcheck_param) {
|
extern "C" int memcheck_initialize(int alignment, const char* memcheck_param) {
|
||||||
malloc_alignment = alignment;
|
malloc_alignment = alignment;
|
||||||
|
|
||||||
/* Parse -memcheck parameter for the guest tracing flags. */
|
/* Parse -memcheck parameter for the guest tracing flags. */
|
||||||
@ -673,7 +674,7 @@ int memcheck_initialize(int alignment, const char* memcheck_param) {
|
|||||||
* bytes (plus prefix, and suffix guards), and report allocation to the
|
* bytes (plus prefix, and suffix guards), and report allocation to the
|
||||||
* emulator.
|
* emulator.
|
||||||
*/
|
*/
|
||||||
void* qemu_instrumented_malloc(size_t bytes) {
|
extern "C" void* qemu_instrumented_malloc(size_t bytes) {
|
||||||
MallocDesc desc;
|
MallocDesc desc;
|
||||||
|
|
||||||
/* Initialize block descriptor and allocate memory. Note that dlmalloc
|
/* Initialize block descriptor and allocate memory. Note that dlmalloc
|
||||||
@ -708,7 +709,7 @@ void* qemu_instrumented_malloc(size_t bytes) {
|
|||||||
* Primary responsibility of this routine is to free requested memory, and
|
* Primary responsibility of this routine is to free requested memory, and
|
||||||
* report free block to the emulator.
|
* report free block to the emulator.
|
||||||
*/
|
*/
|
||||||
void qemu_instrumented_free(void* mem) {
|
extern "C" void qemu_instrumented_free(void* mem) {
|
||||||
MallocDesc desc;
|
MallocDesc desc;
|
||||||
|
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
@ -751,7 +752,7 @@ void qemu_instrumented_free(void* mem) {
|
|||||||
/* This routine serves as entry point for 'calloc'.
|
/* This routine serves as entry point for 'calloc'.
|
||||||
* This routine behaves similarly to qemu_instrumented_malloc.
|
* This routine behaves similarly to qemu_instrumented_malloc.
|
||||||
*/
|
*/
|
||||||
void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
|
extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
|
||||||
if (n_elements == 0 || elem_size == 0) {
|
if (n_elements == 0 || elem_size == 0) {
|
||||||
// Just let go zero bytes allocation.
|
// Just let go zero bytes allocation.
|
||||||
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
|
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
|
||||||
@ -823,7 +824,7 @@ void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
|
|||||||
* allocation, but overall it doesn't seem to matter, as caller of realloc
|
* allocation, but overall it doesn't seem to matter, as caller of realloc
|
||||||
* should not expect that pointer returned after shrinking will remain the same.
|
* should not expect that pointer returned after shrinking will remain the same.
|
||||||
*/
|
*/
|
||||||
void* qemu_instrumented_realloc(void* mem, size_t bytes) {
|
extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
|
||||||
MallocDesc new_desc;
|
MallocDesc new_desc;
|
||||||
MallocDesc cur_desc;
|
MallocDesc cur_desc;
|
||||||
size_t to_copy;
|
size_t to_copy;
|
||||||
@ -927,7 +928,7 @@ void* qemu_instrumented_realloc(void* mem, size_t bytes) {
|
|||||||
/* This routine serves as entry point for 'memalign'.
|
/* This routine serves as entry point for 'memalign'.
|
||||||
* This routine behaves similarly to qemu_instrumented_malloc.
|
* This routine behaves similarly to qemu_instrumented_malloc.
|
||||||
*/
|
*/
|
||||||
void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
|
extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
|
||||||
MallocDesc desc;
|
MallocDesc desc;
|
||||||
|
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
@ -967,3 +968,27 @@ void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
|
|||||||
malloc_pid, getpid(), alignment, bytes);
|
malloc_pid, getpid(), alignment, bytes);
|
||||||
return mallocdesc_user_ptr(&desc);
|
return mallocdesc_user_ptr(&desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem) {
|
||||||
|
MallocDesc cur_desc;
|
||||||
|
|
||||||
|
// Query emulator for the reallocating block information.
|
||||||
|
if (query_qemu_malloc_info(mem, &cur_desc, 2)) {
|
||||||
|
// Note that this violation should be already caught in the emulator.
|
||||||
|
error_log("<libc_pid=%03u, pid=%03u>: malloc_usable_size(%p) query_info failed.",
|
||||||
|
malloc_pid, getpid(), mem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure that reallocating pointer value is what we would expect
|
||||||
|
* for this memory block. Note that this violation should be already caught
|
||||||
|
* in the emulator.*/
|
||||||
|
if (mem != mallocdesc_user_ptr(&cur_desc)) {
|
||||||
|
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: malloc_usable_size(%p) is invalid for ",
|
||||||
|
malloc_pid, getpid(), mem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* during instrumentation, we can't really report anything more than requested_bytes */
|
||||||
|
return cur_desc.requested_bytes;
|
||||||
|
}
|
||||||
|
@ -66,6 +66,7 @@ test_src_files = \
|
|||||||
getcwd_test.cpp \
|
getcwd_test.cpp \
|
||||||
libc_logging_test.cpp \
|
libc_logging_test.cpp \
|
||||||
libgen_test.cpp \
|
libgen_test.cpp \
|
||||||
|
malloc_test.cpp \
|
||||||
math_test.cpp \
|
math_test.cpp \
|
||||||
netdb_test.cpp \
|
netdb_test.cpp \
|
||||||
pthread_test.cpp \
|
pthread_test.cpp \
|
||||||
|
235
tests/malloc_test.cpp
Normal file
235
tests/malloc_test.cpp
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
TEST(malloc, malloc_std) {
|
||||||
|
// Simple malloc test.
|
||||||
|
void *ptr = malloc(100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, calloc_std) {
|
||||||
|
// Simple calloc test.
|
||||||
|
size_t alloc_len = 100;
|
||||||
|
char *ptr = (char *)calloc(1, alloc_len);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(alloc_len, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < alloc_len; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, memalign_multiple) {
|
||||||
|
// Memalign test where the alignment is any value.
|
||||||
|
for (size_t i = 0; i <= 12; i++) {
|
||||||
|
for (size_t alignment = 1 << i; alignment < (1U << (i+1)); alignment++) {
|
||||||
|
char *ptr = (char*)memalign(alignment, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
ASSERT_EQ(0, (intptr_t)ptr % (1 << i));
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, memalign_realloc) {
|
||||||
|
// Memalign and then realloc the pointer a couple of times.
|
||||||
|
for (size_t alignment = 1; alignment <= 4096; alignment <<= 1) {
|
||||||
|
char *ptr = (char*)memalign(alignment, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
ASSERT_EQ(0U, (intptr_t)ptr % alignment);
|
||||||
|
memset(ptr, 0x23, 100);
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(0x23, ptr[i]);
|
||||||
|
}
|
||||||
|
memset(ptr, 0x45, 200);
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 300);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(300U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 200; i++) {
|
||||||
|
ASSERT_EQ(0x45, ptr[i]);
|
||||||
|
}
|
||||||
|
memset(ptr, 0x67, 300);
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 250);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(250U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 250; i++) {
|
||||||
|
ASSERT_EQ(0x67, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, malloc_realloc_larger) {
|
||||||
|
// Realloc to a larger size, malloc is used for the original allocation.
|
||||||
|
char *ptr = (char *)malloc(100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
memset(ptr, 67, 100);
|
||||||
|
|
||||||
|
ptr = (char *)realloc(ptr, 200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(67, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, malloc_realloc_smaller) {
|
||||||
|
// Realloc to a smaller size, malloc is used for the original allocation.
|
||||||
|
char *ptr = (char *)malloc(200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
memset(ptr, 67, 200);
|
||||||
|
|
||||||
|
ptr = (char *)realloc(ptr, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(67, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, malloc_multiple_realloc) {
|
||||||
|
// Multiple reallocs, malloc is used for the original allocation.
|
||||||
|
char *ptr = (char *)malloc(200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
memset(ptr, 0x23, 200);
|
||||||
|
|
||||||
|
ptr = (char *)realloc(ptr, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(0x23, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 50);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(50U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 50; i++) {
|
||||||
|
ASSERT_EQ(0x23, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 150);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(150U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 50; i++) {
|
||||||
|
ASSERT_EQ(0x23, ptr[i]);
|
||||||
|
}
|
||||||
|
memset(ptr, 0x23, 150);
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 425);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(425U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 150; i++) {
|
||||||
|
ASSERT_EQ(0x23, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
TEST(malloc, calloc_realloc_larger) {
|
||||||
|
// Realloc to a larger size, calloc is used for the original allocation.
|
||||||
|
char *ptr = (char *)calloc(1, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
|
||||||
|
ptr = (char *)realloc(ptr, 200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, calloc_realloc_smaller) {
|
||||||
|
// Realloc to a smaller size, calloc is used for the original allocation.
|
||||||
|
char *ptr = (char *)calloc(1, 200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
|
||||||
|
ptr = (char *)realloc(ptr, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(malloc, calloc_multiple_realloc) {
|
||||||
|
// Multiple reallocs, calloc is used for the original allocation.
|
||||||
|
char *ptr = (char *)calloc(1, 200);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(200U, malloc_usable_size(ptr));
|
||||||
|
|
||||||
|
ptr = (char *)realloc(ptr, 100);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(100U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 50);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(50U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 50; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 150);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(150U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 50; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
memset(ptr, 0, 150);
|
||||||
|
|
||||||
|
ptr = (char*)realloc(ptr, 425);
|
||||||
|
ASSERT_TRUE(ptr != NULL);
|
||||||
|
ASSERT_LE(425U, malloc_usable_size(ptr));
|
||||||
|
for (size_t i = 0; i < 150; i++) {
|
||||||
|
ASSERT_EQ(0, ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user