From 92565e9246f3a47b90fea4a436ecfd8e7a1198b8 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Mon, 2 Jan 2023 22:48:07 +0000 Subject: [PATCH] buflib: Add malloc-backed buflib This is intended for improving the effectiveness of tools like ASAN when debugging memory errors in the sim. It's not meant to be a serious allocator for hosted targets. Enable it by changing the buflib backend in config.h. Change-Id: I0cf23cefa47ee35dede7b49e0e5b72dac60e8d3e --- firmware/SOURCES | 2 + firmware/buflib_malloc.c | 251 +++++++++++++++++++++++++++++++ firmware/export/config.h | 1 + firmware/include/buflib.h | 2 + firmware/include/buflib_malloc.h | 53 +++++++ 5 files changed, 309 insertions(+) create mode 100644 firmware/buflib_malloc.c create mode 100644 firmware/include/buflib_malloc.h diff --git a/firmware/SOURCES b/firmware/SOURCES index 02e962c0c3..7e2ffb323e 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -6,6 +6,8 @@ events.c backlight.c #if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL buflib_mempool.c +#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC +buflib_malloc.c #endif core_alloc.c general.c diff --git a/firmware/buflib_malloc.c b/firmware/buflib_malloc.c new file mode 100644 index 0000000000..fdc2b5b925 --- /dev/null +++ b/firmware/buflib_malloc.c @@ -0,0 +1,251 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2023 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + * Malloc backed buflib. This is intended for debugging rather than for + * serious use - the buffer passed to the context is wasted, and memory + * is acquired from malloc() instead. The main point is to make ASAN more + * effective by isolating buflib allocations from each other. + * + * Currently this is a bare-minimum implementation, it doesn't even run + * buflib callbacks since it never moves anything. It could later be + * extended with stress-testing options, for example by randomly moving + * allocations around. + */ + +#include "buflib.h" +#include "panic.h" +#include + +static struct buflib_malloc_handle *get_free_handle(struct buflib_context *ctx) +{ + struct buflib_malloc_handle *h; + for (size_t i = 0; i < ctx->num_allocs; ++i) + { + h = &ctx->allocs[i]; + if (h->size == 0) + return h; + } + + ctx->num_allocs++; + ctx->allocs = realloc(ctx->allocs, ctx->num_allocs * sizeof(*ctx->allocs)); + if (!ctx->allocs) + panicf("buflib %p handle OOM", ctx); + + h = &ctx->allocs[ctx->num_allocs - 1]; + h->size = 0; + return h; +} + +static int get_handle_num(struct buflib_context *ctx, + struct buflib_malloc_handle *handle) +{ + return (handle - ctx->allocs) + 1; +} + +static struct buflib_malloc_handle *get_handle(struct buflib_context *ctx, + int handle) +{ + return &ctx->allocs[handle - 1]; +} + +struct buflib_callbacks buflib_ops_locked = { + .move_callback = NULL, + .shrink_callback = NULL, + .sync_callback = NULL, +}; + +void buflib_init(struct buflib_context *ctx, void *buf, size_t size) +{ + ctx->allocs = NULL; + ctx->num_allocs = 0; + ctx->buf = buf; + ctx->bufsize = size; +} + +size_t buflib_available(struct buflib_context *ctx) +{ + return ctx->bufsize; +} + +size_t buflib_allocatable(struct buflib_context *ctx) +{ + return ctx->bufsize; +} + +bool buflib_context_relocate(struct buflib_context *ctx, void *buf) +{ + ctx->buf = buf; + return true; +} + +int buflib_alloc(struct buflib_context *ctx, size_t size) +{ + return buflib_alloc_ex(ctx, size, NULL); +} + +int buflib_alloc_ex(struct buflib_context *ctx, size_t size, + struct buflib_callbacks *ops) +{ + struct buflib_malloc_handle *handle = get_free_handle(ctx); + + handle->data = malloc(size); + handle->user = handle->data; + handle->size = size; + handle->pin_count = 0; + handle->ops = ops; + + if (!handle->data) + panicf("buflib %p data OOM", ctx); + + return get_handle_num(ctx, handle); +} + +int buflib_alloc_maximum(struct buflib_context* ctx, + size_t *size, struct buflib_callbacks *ops) +{ + *size = ctx->bufsize; + + return buflib_alloc_ex(ctx, *size, ops); +} + +bool buflib_shrink(struct buflib_context *ctx, int handle, + void *newstart, size_t new_size) +{ + struct buflib_malloc_handle *h = get_handle(ctx, handle); + if (newstart < h->user || new_size > h->size - (newstart - h->user)) + return false; + + /* XXX: this might be allowed, but what would be the point... */ + if (new_size == 0) + { + panicf("weird shrink"); + return false; + } + + /* due to buflib semantics we must not realloc */ + h->user = newstart; + h->size = new_size; + return true; +} + +void buflib_pin(struct buflib_context *ctx, int handle) +{ + struct buflib_malloc_handle *h = get_handle(ctx, handle); + + h->pin_count++; +} + +void buflib_unpin(struct buflib_context *ctx, int handle) +{ + struct buflib_malloc_handle *h = get_handle(ctx, handle); + + h->pin_count--; +} + +unsigned buflib_pin_count(struct buflib_context *ctx, int handle) +{ + struct buflib_malloc_handle *h = get_handle(ctx, handle); + + return h->pin_count; +} + +int buflib_free(struct buflib_context *ctx, int handle) +{ + if (handle <= 0) + return 0; + + struct buflib_malloc_handle *h = get_handle(ctx, handle); + + free(h->data); + h->size = 0; + + return 0; +} + +#ifdef BUFLIB_DEBUG_GET_DATA +void *buflib_get_data(struct buflib_context *ctx, int handle) +{ + /* kind of silly since it's better for ASAN to catch this but... */ + if (handle <= 0 || handle > ctx->num_allocs) + panicf("buflib %p: invalid handle %d", ctx, handle); + + struct buflib_malloc_handle *h = get_handle(ctx, handle); + if (h->user == NULL) + panicf("buflib %p: handle %d use after free", ctx, handle); + + return h->user; +} +#endif + +void *buflib_buffer_out(struct buflib_context *ctx, size_t *size) +{ + if (*size == 0) + *size = ctx->bufsize; + + void *ret = ctx->buf; + + ctx->buf += *size; + return ret; +} + +void buflib_buffer_in(struct buflib_context *ctx, int size) +{ + ctx->buf -= size; +} + +#ifdef BUFLIB_DEBUG_PRINT +int buflib_get_num_blocks(struct buflib_context *ctx) +{ + return ctx->num_allocs; +} + +bool buflib_print_block_at(struct buflib_context *ctx, int block_num, + char *buf, size_t bufsize) +{ + if (block_num >= ctx->num_allocs) + { + if (bufsize > 0) + *buf = '\0'; + return false; + } + + struct buflib_malloc_handle *handle = &ctx->allocs[block_num]; + if (handle->data) + { + snprintf(buf, bufsize, "%03d addr:%8p length:%zu", + block_num, handle->data, handle->size); + } + else + { + snprintf(buf, bufsize, "%03d (unallocated)", block_num); + } + + return true; +} +#endif + +#ifdef BUFLIB_DEBUG_CHECK_VALID +void buflib_check_valid(struct buflib_context *ctx) +{ + (void)ctx; +} +#endif diff --git a/firmware/export/config.h b/firmware/export/config.h index d8674c40f2..2ec0b7878f 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -382,6 +382,7 @@ Lyre prototype 1 */ /* CONFIG_BUFLIB_BACKEND */ #define BUFLIB_BACKEND_MEMPOOL 0 /* Default memory pool backed buflib */ +#define BUFLIB_BACKEND_MALLOC 1 /* malloc() buflib (for debugging) */ /* now go and pick yours */ #if defined(IRIVER_H100) diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h index 32a5a6abe0..c4865b6d9b 100644 --- a/firmware/include/buflib.h +++ b/firmware/include/buflib.h @@ -380,6 +380,8 @@ void buflib_check_valid(struct buflib_context *ctx); #if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL #include "buflib_mempool.h" +#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC +#include "buflib_malloc.h" #endif #ifndef BUFLIB_ALLOC_OVERHEAD diff --git a/firmware/include/buflib_malloc.h b/firmware/include/buflib_malloc.h new file mode 100644 index 0000000000..32c837e7b7 --- /dev/null +++ b/firmware/include/buflib_malloc.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2023 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _BUFLIB_MALLOC_H_ +#define _BUFLIB_MALLOC_H_ + +#ifndef _BUFLIB_H_ +# error "include buflib.h instead" +#endif + +struct buflib_malloc_handle +{ + void *data; + void *user; + size_t size; + unsigned int pin_count; + struct buflib_callbacks *ops; +}; + +struct buflib_context +{ + struct buflib_malloc_handle *allocs; + size_t num_allocs; + + void *buf; + size_t bufsize; +}; + +#ifndef BUFLIB_DEBUG_GET_DATA +static inline void *buflib_get_data(struct buflib_context *ctx, int handle) +{ + return ctx->allocs[handle - 1].user; +} +#endif + +#endif /* _BUFLIB_MALLOC_H_ */