pebble/tests/fakes/fake_pbl_malloc.h
2025-01-27 11:38:16 -08:00

238 lines
5.7 KiB
C

/*
* 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 <util/heap.h>
#include "util/list.h"
#include "util/math.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
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);
}