/* * 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 #include #include #include #include #include #include #include #include #include #include #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 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 #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 #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 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 [alignment]\n", argv[0].str); printf("\t%s realloc \n", argv[0].str); printf("\t%s free
\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