/* * Copyright 2024 Google LLC * * 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. */ #pragma once #include #include "util/list.h" #include "util/math.h" #include #include #include typedef struct { ListNode list_node; size_t bytes; void *ptr; void *lr; } PointerListNode; static PointerListNode *s_pointer_list = NULL; static bool prv_pointer_list_filter(ListNode *node, void *ptr) { return ((PointerListNode *)node)->ptr == ptr; } static void prv_pointer_list_add(void *ptr, size_t bytes, void *lr) { PointerListNode *node = malloc(sizeof(PointerListNode)); list_init(&node->list_node); node->ptr = ptr; node->bytes = bytes; node->lr = lr; s_pointer_list = (PointerListNode *)list_prepend((ListNode *)s_pointer_list, &node->list_node); } static void prv_pointer_list_remove(void *ptr) { ListNode *node = list_find((ListNode *)s_pointer_list, prv_pointer_list_filter, ptr); if (!node && ptr) { printf("*** INVALID FREE: %p\n", ptr); cl_fail("Pointer has not been alloc'd (maybe a double free?)"); } list_remove(node, (ListNode **)&s_pointer_list, NULL); free(node); } static size_t s_max_size_allowed = ~0; static Heap s_heap; Heap *task_heap_get_for_current_task(void) { return &s_heap; } static void *malloc_and_track(size_t bytes, void *lr) { if (bytes >= s_max_size_allowed) { return NULL; } void *rt = malloc(bytes); prv_pointer_list_add(rt, bytes, lr); return rt; } static void *calloc_and_track(int n, size_t bytes, void *lr) { if ((bytes * n) >= s_max_size_allowed) { return NULL; } void *rt = calloc(n, bytes); prv_pointer_list_add(rt, bytes, lr); return rt; } void fake_malloc_set_largest_free_block(size_t bytes) { s_max_size_allowed = bytes; } static void free_and_track(void *ptr) { prv_pointer_list_remove(ptr); free(ptr); } void *realloc_and_track(void *ptr, size_t bytes, void *lr) { void *new_ptr = malloc_and_track(bytes, lr); if (new_ptr && ptr) { ListNode *node = list_find((ListNode *)s_pointer_list, prv_pointer_list_filter, ptr); cl_assert(node); memcpy(new_ptr, ptr, MIN(((PointerListNode*)node)->bytes, bytes)); free_and_track(ptr); } return new_ptr; } int fake_pbl_malloc_num_net_allocs(void) { return list_count((ListNode *)s_pointer_list); } void fake_pbl_malloc_check_net_allocs(void) { if (fake_pbl_malloc_num_net_allocs() > 0) { ListNode *node = (ListNode *)s_pointer_list; while (node) { PointerListNode *ptr_node = (PointerListNode *)node; printf("Still allocated: %p (%zu bytes, lr %p)\n", ptr_node->ptr, ptr_node->bytes, ptr_node->lr); node = list_get_next(node); } } cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0); } void fake_pbl_malloc_clear_tracking(void) { while (s_pointer_list) { ListNode *new_head = list_pop_head((ListNode *)s_pointer_list); free(s_pointer_list); s_pointer_list = (PointerListNode *)new_head; } s_max_size_allowed = ~0; } void *task_malloc(size_t bytes) { return malloc_and_track(bytes, __builtin_return_address(0)); } void *task_malloc_check(size_t bytes) { return malloc_and_track(bytes, __builtin_return_address(0)); } void *task_realloc(void *ptr, size_t bytes) { return realloc_and_track(ptr, bytes, __builtin_return_address(0)); } void *task_zalloc(size_t bytes) { void *ptr = task_malloc(bytes); if (ptr) { memset(ptr, 0, bytes); } return ptr; } void *task_zalloc_check(size_t bytes) { void *ptr = task_malloc_check(bytes); memset(ptr, 0, bytes); return ptr; } void *task_calloc(size_t count, size_t size) { return calloc_and_track(count, size, __builtin_return_address(0)); } void *task_calloc_check(size_t count, size_t size) { return calloc_and_track(count, size, __builtin_return_address(0)); } void task_free(void *ptr) { free_and_track(ptr); } void *applib_zalloc(size_t bytes) { return calloc_and_track(1, bytes, __builtin_return_address(0)); } void applib_free(void *ptr) { free_and_track(ptr); } void *app_malloc(size_t bytes) { return malloc_and_track(bytes, __builtin_return_address(0)); } void *app_malloc_check(size_t bytes) { return malloc_and_track(bytes, __builtin_return_address(0)); } void app_free(void *ptr) { free_and_track(ptr); } void *kernel_malloc(size_t bytes) { return malloc_and_track(bytes, __builtin_return_address(0)); } void *kernel_zalloc(size_t bytes) { return calloc_and_track(1, bytes, __builtin_return_address(0)); } void *kernel_zalloc_check(size_t bytes) { return kernel_zalloc(bytes); } void *kernel_malloc_check(size_t bytes) { return malloc_and_track(bytes, __builtin_return_address(0)); } void *kernel_realloc(void *ptr, size_t bytes) { return realloc_and_track(ptr, bytes, __builtin_return_address(0)); } void kernel_free(void *ptr) { free_and_track(ptr); } void* kernel_calloc(size_t count, size_t size) { return calloc_and_track(count, size, __builtin_return_address(0)); } char* kernel_strdup(const char* s) { char *r = malloc_and_track(strlen(s) + 1, __builtin_return_address(0)); if (!r) { return NULL; } strcpy(r, s); return r; } char* kernel_strdup_check(const char* s) { return kernel_strdup(s); } char* task_strdup(const char* s) { return kernel_strdup(s); } void smart_free(void *ptr) { free_and_track(ptr); }