From d7bdc0722f99320abeffffb53c2bb6dd4278c9aa Mon Sep 17 00:00:00 2001 From: yukkop Date: Fri, 11 Apr 2025 22:34:43 +0000 Subject: [PATCH] feat: `hectic` C: debug constructor wroks!!! --- package/c/hectic/hectic.c | 124 ++++++++++++++++++++++++++----- package/c/hectic/hectic.h | 47 +++++++++++- package/c/hectic/test/01-debug.c | 52 ++----------- 3 files changed, 155 insertions(+), 68 deletions(-) diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index 0c9a972..0fa3252 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -64,8 +64,8 @@ void set_output_color_mode(ColorMode mode) { #define POSITION_INFO_DECLARATION const char *file, const char *func, int line #define POSITION_INFO file, func, line -#define COLORING_DECLARATION POSITION_INFO_DECLARATION Arena *arena - +#define CTX_DECLARATION POSITION_INFO_DECLARATION, Arena *arena +#define CTX(lifetimed_arena) POSITION_INFO, arena = (lifetimed_arena) // ------------ // -- Logger -- @@ -198,6 +198,88 @@ char* raise_message( // -- debug -- // ----------- +PtrSet *ptrset_init(Arena *arena) { + PtrSet *set = arena_alloc(arena, sizeof(PtrSet)); + set->data = arena_alloc(arena, 4 * sizeof(void*)); + set->size = 0; + set->capacity = 4; + return set; +} + +bool debug_ptrset_contains__(PtrSet *set, void *ptr) { + for (size_t i = 0; i < set->size; i++) { + if (set->data[i] == ptr) + return true; + } + return false; +} + +void debug_ptrset_add__(CTX_DECLARATION, PtrSet *set, void *ptr) { + if (set->size == set->capacity) { + set->capacity = set->capacity ? set->capacity * 2 : 4; + set->data = arena_realloc__(CTX(arena), set->data, set->capacity, set->capacity * sizeof(void*)); + } + set->data[set->size++] = ptr; +} + +char *string_to_debug_str__(CTX_DECLARATION, const char *name, const char *string) { + return arena_strdup_fmt__(CTX(arena), "%s = %p \"%s\"", name, string, string); +} + +char *number_to_debug_str__(CTX_DECLARATION, const char *name, int number) { + return arena_strdup_fmt__(CTX(arena), "%s = %d", name, number); +} + +/* Private function */ +char *debug_join_debug_strings_v(CTX_DECLARATION, int count, va_list args) { + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "DEBUG JOIN: Joining %d strings", count); + int total_len = 1; + + va_list args_copy; + va_copy(args_copy, args); + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "DEBUG JOIN: Starting first pass"); + for (int i = 0; i < count; i++) { + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "iter1"); + char *s = va_arg(args_copy, char*); + int len = strlen(s); + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "DEBUG JOIN: String %d: [%s] %p len: %d", i, s, s, len); + total_len += len; + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "iter2"); + } + va_end(args_copy); + + char *joined = arena_alloc__(CTX(arena), total_len); + joined[0] = '\0'; + + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "DEBUG JOIN: concatenating strings"); + va_copy(args_copy, args); + for (int i = 0; i < count; i++) { + char *s = va_arg(args_copy, char*); + strcat(joined, s); + if (i < count - 1) { + strcat(joined, ", "); + } + } + va_end(args_copy); + + return joined; +} + +char *struct_to_debug_str__(CTX_DECLARATION, const char *type, const char *name, void *ptr, int count, ...) { + raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "DEBUG STR: type: %s, name: %s, ptr: %p, count: %d", type, name, ptr, count); + char *result; + if ((ptr) == NULL) { + result = arena_strdup_fmt__(CTX(arena), "%s %s = NULL", type, name); + } else { + va_list args; + va_start(args, count); + char *joined = debug_join_debug_strings_v(CTX(arena), count, args); + va_end(args); + result = arena_strdup_fmt__(CTX(arena), "%s %s = {%s} %p", type, name, joined, ptr); + } + return result; +} + // ------------ // -- arena -- // ------------ @@ -287,7 +369,7 @@ void* arena_alloc__(POSITION_INFO_DECLARATION, Arena *arena, size_t size) { raise_message(LOG_LEVEL_DEBUG, POSITION_INFO, "ARENA ALLOC: Allocating memory (arena: %p, size: %zu bytes)", arena, size); - void *mem = arena_alloc_or_null__(POSITION_INFO, arena, size, true); + void *mem = arena_alloc_or_null__(POSITION_INFO, arena, size, false); if (!mem) { raise_message(LOG_LEVEL_DEBUG, POSITION_INFO, "ARENA ALLOC: Allocation failed (arena: %p, requested: %zu bytes)", arena, size); @@ -302,6 +384,26 @@ void* arena_alloc__(POSITION_INFO_DECLARATION, Arena *arena, size_t size) { return mem; } +/* + * Reallocates a memory block and copies the contents of the old block to the new one. + * NOTE(yukkop): We need to provide the old size to avoid copying more than needed. + */ +void* arena_realloc__(POSITION_INFO_DECLARATION, Arena *arena, + void *ptr, size_t size, size_t new_size) { + void *new_ptr = NULL; + if (ptr == NULL) { + new_ptr = arena_alloc__(POSITION_INFO, arena, new_size); + } else if (new_size <= size) { + new_ptr = ptr; + } else { + // FIXME(yukkop): Must tries to expand the arena before allocating new memory + new_ptr = arena_alloc_or_null__(POSITION_INFO, arena, new_size, false); + if (new_ptr) + memcpy(new_ptr, ptr, size); + } + return new_ptr; +} + void arena_reset__(POSITION_INFO_DECLARATION, Arena *arena) { // Function entry logging raise_message(LOG_LEVEL_DEBUG, POSITION_INFO, @@ -503,22 +605,6 @@ char* arena_repstr__(POSITION_INFO_DECLARATION, Arena *arena, return new_str; } -// FIXME(yukkop): this is who -void* arena_realloc_copy__(POSITION_INFO_DECLARATION, Arena *arena, - void *old_ptr, size_t old_size, size_t new_size) { - void *new_ptr = NULL; - if (old_ptr == NULL) { - new_ptr = arena_alloc__(POSITION_INFO, arena, new_size); - } else if (new_size <= old_size) { - new_ptr = old_ptr; - } else { - new_ptr = arena_alloc_or_null__(POSITION_INFO, arena, new_size, true); - if (new_ptr) - memcpy(new_ptr, old_ptr, old_size); - } - return new_ptr; -} - // ---------- // -- misc -- // ---------- diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index e14bc01..7410806 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -290,8 +290,8 @@ char* arena_strdup_fmt__(const char *file, const char *func, int line, Arena *ar char* arena_repstr__(const char *file, const char *func, int line, Arena *arena, const char *src, size_t start, size_t len, const char *rep); -void* arena_realloc_copy__(const char *file, const char *func, int line, Arena *arena, - void *old_ptr, size_t old_size, size_t new_size); +void* arena_realloc__(const char *file, const char *func, int line, Arena *arena, + void *ptr, size_t size, size_t new_size); char* arena_strncpy__(const char *file, const char *func, int line, Arena *arena, const char *start, size_t len); @@ -321,8 +321,8 @@ char* arena_strncpy__(const char *file, const char *func, int line, Arena *arena #define arena_repstr(arena, src, start, len, rep) \ arena_repstr__(__FILE__, __func__, __LINE__, arena, src, start, len, rep) -#define arena_realloc_copy(arena, old_ptr, old_size, new_size) \ - arena_realloc_copy__(__FILE__, __func__, __LINE__, arena, old_ptr, old_size, new_size) +#define arena_realloc(arena, ptr, size, new_size) \ + arena_realloc__(__FILE__, __func__, __LINE__, arena, ptr, size, new_size) #define arena_strncpy(arena, src, len) \ arena_strncpy__(__FILE__, __func__, __LINE__, arena, src, len) @@ -342,6 +342,20 @@ static Arena disposable_arena __attribute__((unused)) = {0}; // -- Debug -- // ------------ +/* + * Set of pointers to track visited objects + * Used to detect cycles in debug strings + */ +typedef struct PtrSet { + void **data; + size_t size; + size_t capacity; +} PtrSet; + +PtrSet *ptrset_init(Arena *arena); +bool debug_ptrset_contains__(PtrSet *set, void *ptr); +void debug_ptrset_add__(const char *file, const char *func, int line, Arena *arena, PtrSet *set, void *ptr); + #define DEBUGSTR(arena, type, value) DEBUGSTR_##type(arena, value) #define DEBUGSTR_Slice(arena, value) slice_to_debug_str(arena, value) @@ -360,6 +374,31 @@ void logger_print_rules(); */ char* logger_rules_to_string(Arena *arena); +char *string_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *name, const char *string); + +char *number_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *name, int number); + +char *struct_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *type, const char *name, void *ptr, int count, ...); + +#define STRING_TO_DEBUG_STR(arena, name, string) \ + string_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, string) +#define NUMBER_TO_DEBUG_STR(arena, name, number) \ + number_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, number) +#define STRUCT_TO_DEBUG_STR(arena, type, name, self, count, ...) \ + struct_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, self, count, ##__VA_ARGS__) + +bool debug_ptrset_contains(PtrSet *set, void *ptr); + +#define debug_check_cycle__(file, func, line, arena, type, name, self, visited) __extension__ ({ \ + if (debug_ptrset_contains__(visited, self)) \ + return struct_to_debug_str__(file, func, line, arena, \ + #type, name, self, 1, "cycle detected"); \ + debug_ptrset_add__(file, func, line, arena, visited, self); \ +}) + +#define DEBUG_CHECK_CYCLE(arena, type, name, self, visited) \ + debug_check_cycle__(__FILE__, __func__, __LINE__, arena, type, name, self, visited) + // ---------- // -- Json -- // ---------- diff --git a/package/c/hectic/test/01-debug.c b/package/c/hectic/test/01-debug.c index 15f230f..23939ee 100755 --- a/package/c/hectic/test/01-debug.c +++ b/package/c/hectic/test/01-debug.c @@ -19,58 +19,19 @@ struct TestStruct2 { TestStruct *other; }; +#define test_struct_to_debug_str(arena, name, self) test_struct_to_debug_str__(arena, name, self, ptrset_init(arena)) -typedef struct PtrSet { - void **data; - size_t size; - size_t capacity; -} PtrSet; - -static bool debug_ptrset_contains(PtrSet *set, void *ptr) { - for (size_t i = 0; i < set->size; i++) { - if (set->data[i] == ptr) - return true; - } - return false; -} - -static void debug_ptrset_add(PtrSet *set, void *ptr) { - if (set->size == set->capacity) { - set->capacity = set->capacity ? set->capacity * 2 : 4; - set->data = realloc(set->data, set->capacity * sizeof(void*)); - } - set->data[set->size++] = ptr; -} - - -#define STRING_TO_DEBUG_STR(arena, name, string) \ - arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s = %p \"%s\"", name, string, string) - -#define NUMBER_TO_DEBUG_STR(arena, name, number) \ - arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s = %d", name, number) - -#define STRUCT_TO_DEBUG_STR(arena, type, name, ptr, ...) __extension__ ({ \ - char *result; \ - if ((ptr) == NULL) { \ - result = arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = NULL", #type, name); \ - } else { \ - char* fields = arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s, %s, %s", __VA_ARGS__); \ - result = arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = {%s} %p", #type, name, fields, ptr); \ - } \ - result; \ -}) - -#define test_struct_to_debug_str(arena, name, self) test_struct_to_debug_str__(arena, name, self) - -char *test_struct_to_debug_str__(Arena *arena, char *name, TestStruct *self) { +char *test_struct_to_debug_str__(Arena *arena, char *name, TestStruct *self, PtrSet *visited) { if (name == NULL) { name = "$1"; } - char *result = STRUCT_TO_DEBUG_STR(arena, TestStruct, name, self, + DEBUG_CHECK_CYCLE(arena, TestStruct, name, self, visited); + + char *result = STRUCT_TO_DEBUG_STR(arena, TestStruct, name, self, 3, NUMBER_TO_DEBUG_STR(arena, "a", self->a), NUMBER_TO_DEBUG_STR(arena, "b", self->b), - test_struct_to_debug_str__(arena, "next", self->next) + test_struct_to_debug_str__(arena, "next", self->next, visited) ); return result; } @@ -91,6 +52,7 @@ int main(void) { init_logger(); TestStruct test_struct = {.a = 1, .b = 2, .next = NULL}; + test_struct.next = &test_struct; raise_notice("%s", test_struct_to_debug_str(DISPOSABLE_ARENA, "test_struct", &test_struct)); printf("%sAll tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));