From 807ea838924857fe5b8d1f8254786f485daa620b Mon Sep 17 00:00:00 2001 From: yukkop Date: Sun, 13 Apr 2025 23:13:28 +0000 Subject: [PATCH] fix: `hectic` C: impruve cycle detection for debug strig constructor --- package/c/hectic/cpp-test.c | 1 - package/c/hectic/hectic.c | 26 +++++++++--------- package/c/hectic/hectic.h | 46 +++++++++++++++++++++++++++----- package/c/hectic/test/01-debug.c | 4 +-- 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/package/c/hectic/cpp-test.c b/package/c/hectic/cpp-test.c index 09f4423..5097091 100755 --- a/package/c/hectic/cpp-test.c +++ b/package/c/hectic/cpp-test.c @@ -30,7 +30,6 @@ void set_output_color_mode(ColorMode mode); * DEBUG_COLOR_MODE is the color mode for debug output after USE_COLOR() check. * used for debug colorized output */ -#define DEBUG_COLOR_MODE COLOR_MODE_FORCE #define USE_COLOR_IN_DEBUG() (USE_COLOR() ? DEBUG_COLOR_MODE : COLOR_MODE_DISABLE) #define COLOR_RED "\033[1;31m" diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index cba3631..21abb8d 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -257,28 +257,34 @@ char* raise_message( PtrSet *ptrset_init__(POSITION_INFO_DECLARATION, Arena *arena) { PtrSet *set = arena_alloc__(POSITION_INFO, arena, sizeof(PtrSet)); - set->data = arena_alloc__(POSITION_INFO, arena, 4 * sizeof(void*)); + set->data = arena_alloc__(POSITION_INFO, arena, 4 * sizeof(struct { void const *ptr; const char *type; const char *field_name; })); set->size = 0; set->capacity = 4; return set; } -bool debug_ptrset_contains__(PtrSet *set, const void *ptr) { +bool debug_ptrset_contains__(PtrSet *set, const void *ptr, const char *type, const char *field_name) { if (!set) return false; for (size_t i = 0; i < set->size; i++) { - if (set->data[i] == ptr) + if (set->data[i].ptr == ptr && + strcmp(set->data[i].type, type) == 0 && + strcmp(set->data[i].field_name, field_name) == 0) return true; } return false; } -void debug_ptrset_add__(CTX_DECLARATION, PtrSet *set, const void *ptr) { +void debug_ptrset_add__(CTX_DECLARATION, PtrSet *set, const void *ptr, const char *type, const char *field_name) { if (!set) return; 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 = arena_realloc__(CTX(arena), set->data, set->capacity * sizeof(struct { void const *ptr; const char *type; const char *field_name; }), + set->capacity * 2 * sizeof(struct { void const *ptr; const char *type; const char *field_name; })); } - set->data[set->size++] = ptr; + set->data[set->size].ptr = ptr; + set->data[set->size].type = type; + set->data[set->size].field_name = field_name; + set->size++; } char *enum_to_debug_str__(CTX_DECLARATION, const char *name, size_t enum_value, const char *enum_str) { @@ -2106,14 +2112,6 @@ char *template_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, con return result; } - -//struct TemplateNode { -// TemplateNodeType type; -// TemplateValue value; -// TemplateNode *children; // child nodes -// TemplateNode *next; // sibling nodes -//}; - char *template_node_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateNode *self, PtrSet *visited) { char *result = arena_alloc(arena, MEM_KiB); STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 4, diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index a56d640..5f92f44 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -393,7 +393,11 @@ static Arena disposable_arena __attribute__((unused)) = {0}; * Used to detect cycles in debug strings */ typedef struct PtrSet { - void const **data; + struct { + void const *ptr; + const char *type; + const char *field_name; // Add field name to distinguish between same-type fields in unions + } *data; size_t size; size_t capacity; } PtrSet; @@ -401,8 +405,8 @@ typedef struct PtrSet { PtrSet *ptrset_init__(const char *file, const char *func, int line, Arena *arena); #define ptrset_init(arena) ptrset_init__(__FILE__, __func__, __LINE__, arena) -bool debug_ptrset_contains__(PtrSet *set, const void *ptr); -void debug_ptrset_add__(const char *file, const char *func, int line, Arena *arena, PtrSet *set, const void *ptr); +bool debug_ptrset_contains__(PtrSet *set, const void *ptr, const char *type, const char *field_name); +void debug_ptrset_add__(const char *file, const char *func, int line, Arena *arena, PtrSet *set, const void *ptr, const char *type, const char *field_name); #define DEBUGSTR(arena, type, value) DEBUGSTR_##type(arena, value) @@ -436,6 +440,8 @@ char *char_to_debug_str__(const char *file, const char *func, int line, Arena *a char *struct_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *type, const char *name, const void *ptr, int count, ...); +bool debug_ptrset_contains(PtrSet *set, void *ptr); + #define ENUM_TO_DEBUG_STR(arena, name, enum_value, enum_str) \ enum_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, enum_value, enum_str) #define STRING_TO_DEBUG_STR(arena, name, string) \ @@ -451,23 +457,49 @@ char *struct_to_debug_str__(const char *file, const char *func, int line, Arena #define CHAR_TO_DEBUG_STR(arena, name, c) \ char_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, c) -bool debug_ptrset_contains(PtrSet *set, void *ptr); - +/* + * STRUCT_TO_DEBUG_STR - Converts a structure into a debug string. + * + * Parameters: + * arena - Pointer to the memory allocation arena. + * buffer - Variable that will hold the resulting debug string. + * type - Data type of the structure (used for formatting). + * name - Name of the structure; if NULL, it is replaced with "$1". + * ptr - Pointer to the structure. + * visited - Set of visited pointers (for cycle detection). + * count - Count of fields or elements to output. + * ... - Additional arguments for struct_to_debug_str__. + * + * Details: + * - If 'ptr' is already present in 'visited', the macro returns a string indicating "cycle detected". + * - If 'ptr' is NULL, the macro returns a string indicating that the structure is NULL. + * - Otherwise, 'ptr' is added to 'visited' and the structure is converted into a debug string. + * + * Restrictions: + * - This macro must be used at the function level only. Nested usage is not allowed, + * as the 'return' statements within the macro will exit the current function. + * + * Returns: + * - A debug string created by arena_strdup_fmt__ containing the structure's debugging information. + */ #define STRUCT_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ...) do { \ if (!name) \ name = "$1"; \ \ - if (debug_ptrset_contains__(visited, ptr)) \ + if (debug_ptrset_contains__(visited, ptr, #type, name)) \ return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = {cycle detected} %p", #type, name, ptr); \ \ if (!ptr) \ return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = NULL", #type, name); \ \ - debug_ptrset_add__(__FILE__, __func__, __LINE__, arena, visited, ptr); \ + debug_ptrset_add__(__FILE__, __func__, __LINE__, arena, visited, ptr, #type, name); \ \ buffer = struct_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, ptr, count, ##__VA_ARGS__); \ } while (0) +#define UNION_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ...) \ + STRUCT_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ##__VA_ARGS__) + // ------------------ // -- Logger Rules -- // ------------------ diff --git a/package/c/hectic/test/01-debug.c b/package/c/hectic/test/01-debug.c index 5ceb1c3..a4be226 100755 --- a/package/c/hectic/test/01-debug.c +++ b/package/c/hectic/test/01-debug.c @@ -58,7 +58,7 @@ void test_struct_to_debug_str(Arena *arena) { raise_notice("result: %s", result); char *check = arena_alloc(arena, MEM_KiB); - sprintf(check, "Struct struct = {a = 1, b = 2, Struct next = {cycle detected} %p} %p", (void*)&test_struct, (void*)&test_struct); + sprintf(check, "Struct struct = {a = 1, b = 2, Struct next = {a = 1, b = 2, Struct next = {cycle detected} %p} %p} %p", (void*)&test_struct, (void*)&test_struct, (void*)&test_struct); raise_notice("check: %s", check); assert(strcmp(result, check) == 0); } @@ -74,7 +74,7 @@ void test_struct2_to_debug_str(Arena *arena) { char *result = struct2_to_debug_str(arena, "struct2", &test_struct2, visited); raise_notice("result: %s", result); char *check = arena_alloc(arena, MEM_KiB); - sprintf(check, "Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", Struct other = {a = 1, b = 2, Struct next = {cycle detected} %p} %p, Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct2); + sprintf(check, "Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", Struct other = {a = 1, b = 2, Struct next = {a = 1, b = 2, Struct next = {cycle detected} %p} %p} %p, Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2); raise_notice("check: %s", check); assert(strcmp(result, check) == 0); }