blob: 1aa9ac37c82cebbbae048a23545ffff19754155b [file] [log] [blame] [edit]
/*
* Copyright (c) 2008-2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <lib/heap.h>
#include <trace.h>
#include <debug.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <list.h>
#include <kernel/spinlock.h>
#include <lib/console.h>
#include <lib/page_alloc.h>
#define LOCAL_TRACE 0
/* heap tracing */
#if LK_DEBUGLEVEL > 0
static bool heap_trace = false;
#else
#define heap_trace (false)
#endif
#if WITH_LIB_HEAP_MINIHEAP
/* miniheap implementation */
#include <lib/miniheap.h>
static inline void *HEAP_MALLOC(size_t s) { return miniheap_malloc(s); }
static inline void *HEAP_REALLOC(void *ptr, size_t s) { return miniheap_realloc(ptr, s); }
static inline void *HEAP_MEMALIGN(size_t boundary, size_t s) { return miniheap_memalign(boundary, s); }
#define HEAP_FREE miniheap_free
static inline void *HEAP_CALLOC(size_t n, size_t s)
{
size_t realsize = n * s;
void *ptr = miniheap_malloc(realsize);
if (likely(ptr))
memset(ptr, 0, realsize);
return ptr;
}
static inline void HEAP_INIT(void)
{
/* start the heap off with some spare memory in the page allocator */
size_t len;
void *ptr = page_first_alloc(&len);
if (!ptr) {
panic("heap init failed");
}
miniheap_init(ptr, len);
}
#define HEAP_DUMP miniheap_dump
#define HEAP_TRIM miniheap_trim
/* end miniheap implementation */
#elif WITH_LIB_HEAP_CMPCTMALLOC
/* cmpctmalloc implementation */
#include <lib/cmpctmalloc.h>
#define HEAP_MEMALIGN(boundary, s) cmpct_memalign(s, boundary)
#define HEAP_MALLOC cmpct_alloc
#define HEAP_REALLOC cmpct_realloc
#define HEAP_FREE cmpct_free
#define HEAP_INIT cmpct_init
#define HEAP_DUMP cmpct_dump
#define HEAP_TRIM cmpct_trim
static inline void *HEAP_CALLOC(size_t n, size_t s)
{
size_t realsize = n * s;
void *ptr = cmpct_alloc(realsize);
if (likely(ptr))
memset(ptr, 0, realsize);
return ptr;
}
/* end cmpctmalloc implementation */
#elif WITH_LIB_HEAP_DLMALLOC
/* dlmalloc implementation */
#include <lib/dlmalloc.h>
#define HEAP_MALLOC(s) dlmalloc(s)
#define HEAP_CALLOC(n, s) dlcalloc(n, s)
#define HEAP_MEMALIGN(b, s) dlmemalign(b, s)
#define HEAP_REALLOC(p, s) dlrealloc(p, s)
#define HEAP_FREE(p) dlfree(p)
static inline void HEAP_INIT(void) {}
static void dump_callback(void *start, void *end, size_t used_bytes, void *arg) {
printf("\t\tstart %p end %p used_bytes %zu\n", start, end, used_bytes);
}
static inline void HEAP_DUMP(void)
{
struct mallinfo minfo = dlmallinfo();
printf("\tmallinfo (dlmalloc):\n");
printf("\t\tarena space 0x%zx\n", minfo.arena);
printf("\t\tfree chunks 0x%zx\n", minfo.ordblks);
printf("\t\tspace in mapped regions 0x%zx\n", minfo.hblkhd);
printf("\t\tmax total allocated 0x%zx\n", minfo.usmblks);
printf("\t\ttotal allocated 0x%zx\n", minfo.uordblks);
printf("\t\tfree 0x%zx\n", minfo.fordblks);
printf("\t\treleasable space 0x%zx\n", minfo.keepcost);
printf("\theap block list:\n");
dlmalloc_inspect_all(&dump_callback, NULL);
}
static inline void HEAP_TRIM(void) { dlmalloc_trim(0); }
/* end dlmalloc implementation */
#else
#error need to select valid heap implementation or provide wrapper
#endif
static inline int HEAP_POSIX_MEMALIGN(void **res, size_t align, size_t size)
{
if (align < sizeof(void *)) {
LTRACEF("Invalid alignment requested\n");
return ERR_INVALID_ARGS;
}
if (res == NULL) {
LTRACEF("Invalid result pointer\n");
return ERR_INVALID_ARGS;
}
void *mem = HEAP_MEMALIGN(align, size);
if (mem == NULL) {
LTRACEF("Insufficient memory\n");
return ERR_NO_MEMORY;
}
*res = mem;
return NO_ERROR;
}
void heap_init(void)
{
HEAP_INIT();
}
void heap_trim(void)
{
HEAP_TRIM();
}
void *malloc(size_t size)
{
LTRACEF("size %zd\n", size);
void *ptr = HEAP_MALLOC(size);
if (heap_trace)
printf("caller %p malloc %zu -> %p\n", __GET_CALLER(), size, ptr);
return ptr;
}
void *memalign(size_t boundary, size_t size)
{
LTRACEF("boundary %zu, size %zd\n", boundary, size);
void *ptr = HEAP_MEMALIGN(boundary, size);
if (heap_trace)
printf("caller %p memalign %zu, %zu -> %p\n", __GET_CALLER(), boundary, size, ptr);
return ptr;
}
int posix_memalign(void **res, size_t align, size_t size)
{
LTRACEF("res %p, align %zu, size %zd\n", res, align, size);
int rc = HEAP_POSIX_MEMALIGN(res, align, size);
if (heap_trace) {
printf("caller %p posix_memalign %p, %zu, %zu -> %d\n", __GET_CALLER(),
res, align, size, rc);
}
return rc;
}
void *calloc(size_t count, size_t size)
{
LTRACEF("count %zu, size %zd\n", count, size);
void *ptr = HEAP_CALLOC(count, size);
if (heap_trace)
printf("caller %p calloc %zu, %zu -> %p\n", __GET_CALLER(), count, size, ptr);
return ptr;
}
void *realloc(void *ptr, size_t size)
{
LTRACEF("ptr %p, size %zd\n", ptr, size);
void *ptr2 = HEAP_REALLOC(ptr, size);
if (heap_trace)
printf("caller %p realloc %p, %zu -> %p\n", __GET_CALLER(), ptr, size, ptr2);
return ptr2;
}
void free(void *ptr)
{
LTRACEF("ptr %p\n", ptr);
if (heap_trace)
printf("caller %p free %p\n", __GET_CALLER(), ptr);
HEAP_FREE(ptr);
}
static void heap_dump(void)
{
HEAP_DUMP();
}
static void heap_test(void)
{
#if WITH_LIB_HEAP_CMPCTMALLOC
cmpct_test();
#else
void *ptr[16];
ptr[0] = HEAP_MALLOC(8);
ptr[1] = HEAP_MALLOC(32);
ptr[2] = HEAP_MALLOC(7);
ptr[3] = HEAP_MALLOC(0);
ptr[4] = HEAP_MALLOC(98713);
ptr[5] = HEAP_MALLOC(16);
HEAP_FREE(ptr[5]);
HEAP_FREE(ptr[1]);
HEAP_FREE(ptr[3]);
HEAP_FREE(ptr[0]);
HEAP_FREE(ptr[4]);
HEAP_FREE(ptr[2]);
HEAP_DUMP();
int i;
for (i=0; i < 16; i++)
ptr[i] = 0;
for (i=0; i < 32768; i++) {
unsigned int index = (unsigned int)rand() % 16;
if ((i % (16*1024)) == 0)
printf("pass %d\n", i);
// printf("index 0x%x\n", index);
if (ptr[index]) {
// printf("freeing ptr[0x%x] = %p\n", index, ptr[index]);
HEAP_FREE(ptr[index]);
ptr[index] = 0;
}
unsigned int align = 1 << ((unsigned int)rand() % 8);
ptr[index] = HEAP_MEMALIGN(align, (unsigned int)rand() % 32768);
// printf("ptr[0x%x] = %p, align 0x%x\n", index, ptr[index], align);
DEBUG_ASSERT(((addr_t)ptr[index] % align) == 0);
// heap_dump();
}
for (i=0; i < 16; i++) {
if (ptr[i])
HEAP_FREE(ptr[i]);
}
HEAP_DUMP();
#endif
}
#if LK_DEBUGLEVEL > 1
#if WITH_LIB_CONSOLE
#include <lib/console.h>
static int cmd_heap(int argc, const cmd_args *argv);
STATIC_COMMAND_START
STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
STATIC_COMMAND_END(heap);
static int cmd_heap(int argc, const cmd_args *argv)
{
if (argc < 2) {
notenoughargs:
printf("not enough arguments\n");
usage:
printf("usage:\n");
printf("\t%s info\n", argv[0].str);
printf("\t%s trace\n", argv[0].str);
printf("\t%s trim\n", argv[0].str);
printf("\t%s alloc <size> [alignment]\n", argv[0].str);
printf("\t%s realloc <ptr> <size>\n", argv[0].str);
printf("\t%s free <address>\n", argv[0].str);
return -1;
}
if (strcmp(argv[1].str, "info") == 0) {
heap_dump();
} else if (strcmp(argv[1].str, "test") == 0) {
heap_test();
} else if (strcmp(argv[1].str, "trace") == 0) {
heap_trace = !heap_trace;
printf("heap trace is now %s\n", heap_trace ? "on" : "off");
} else if (strcmp(argv[1].str, "trim") == 0) {
heap_trim();
} else if (strcmp(argv[1].str, "alloc") == 0) {
if (argc < 3) goto notenoughargs;
void *ptr = memalign((argc >= 4) ? argv[3].u : 0, argv[2].u);
printf("memalign returns %p\n", ptr);
} else if (strcmp(argv[1].str, "realloc") == 0) {
if (argc < 4) goto notenoughargs;
void *ptr = realloc(argv[2].p, argv[3].u);
printf("realloc returns %p\n", ptr);
} else if (strcmp(argv[1].str, "free") == 0) {
if (argc < 2) goto notenoughargs;
free(argv[2].p);
} else {
printf("unrecognized command\n");
goto usage;
}
return 0;
}
#endif
#endif