fix: hectic C: impruve cycle detection for debug strig constructor
This commit is contained in:
@@ -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.
|
* DEBUG_COLOR_MODE is the color mode for debug output after USE_COLOR() check.
|
||||||
* used for debug colorized output
|
* 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 USE_COLOR_IN_DEBUG() (USE_COLOR() ? DEBUG_COLOR_MODE : COLOR_MODE_DISABLE)
|
||||||
|
|
||||||
#define COLOR_RED "\033[1;31m"
|
#define COLOR_RED "\033[1;31m"
|
||||||
|
|||||||
@@ -257,28 +257,34 @@ char* raise_message(
|
|||||||
|
|
||||||
PtrSet *ptrset_init__(POSITION_INFO_DECLARATION, Arena *arena) {
|
PtrSet *ptrset_init__(POSITION_INFO_DECLARATION, Arena *arena) {
|
||||||
PtrSet *set = arena_alloc__(POSITION_INFO, arena, sizeof(PtrSet));
|
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->size = 0;
|
||||||
set->capacity = 4;
|
set->capacity = 4;
|
||||||
return set;
|
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;
|
if (!set) return false;
|
||||||
for (size_t i = 0; i < set->size; i++) {
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
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) return;
|
||||||
if (set->size == set->capacity) {
|
if (set->size == set->capacity) {
|
||||||
set->capacity = set->capacity ? set->capacity * 2 : 4;
|
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) {
|
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;
|
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 *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);
|
char *result = arena_alloc(arena, MEM_KiB);
|
||||||
STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 4,
|
STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 4,
|
||||||
|
|||||||
@@ -393,7 +393,11 @@ static Arena disposable_arena __attribute__((unused)) = {0};
|
|||||||
* Used to detect cycles in debug strings
|
* Used to detect cycles in debug strings
|
||||||
*/
|
*/
|
||||||
typedef struct PtrSet {
|
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 size;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
} PtrSet;
|
} PtrSet;
|
||||||
@@ -401,8 +405,8 @@ typedef struct PtrSet {
|
|||||||
PtrSet *ptrset_init__(const char *file, const char *func, int line, Arena *arena);
|
PtrSet *ptrset_init__(const char *file, const char *func, int line, Arena *arena);
|
||||||
#define ptrset_init(arena) ptrset_init__(__FILE__, __func__, __LINE__, arena)
|
#define ptrset_init(arena) ptrset_init__(__FILE__, __func__, __LINE__, arena)
|
||||||
|
|
||||||
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);
|
||||||
void debug_ptrset_add__(const char *file, const char *func, int line, Arena *arena, PtrSet *set, const void *ptr);
|
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)
|
#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, ...);
|
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) \
|
#define ENUM_TO_DEBUG_STR(arena, name, enum_value, enum_str) \
|
||||||
enum_to_debug_str__(__FILE__, __func__, __LINE__, 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) \
|
#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) \
|
#define CHAR_TO_DEBUG_STR(arena, name, c) \
|
||||||
char_to_debug_str__(__FILE__, __func__, __LINE__, 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 { \
|
#define STRUCT_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ...) do { \
|
||||||
if (!name) \
|
if (!name) \
|
||||||
name = "$1"; \
|
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); \
|
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = {cycle detected} %p", #type, name, ptr); \
|
||||||
\
|
\
|
||||||
if (!ptr) \
|
if (!ptr) \
|
||||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = NULL", #type, name); \
|
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__); \
|
buffer = struct_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, ptr, count, ##__VA_ARGS__); \
|
||||||
} while (0)
|
} 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 --
|
// -- Logger Rules --
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ void test_struct_to_debug_str(Arena *arena) {
|
|||||||
raise_notice("result: %s", result);
|
raise_notice("result: %s", result);
|
||||||
|
|
||||||
char *check = arena_alloc(arena, MEM_KiB);
|
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);
|
raise_notice("check: %s", check);
|
||||||
assert(strcmp(result, check) == 0);
|
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);
|
char *result = struct2_to_debug_str(arena, "struct2", &test_struct2, visited);
|
||||||
raise_notice("result: %s", result);
|
raise_notice("result: %s", result);
|
||||||
char *check = arena_alloc(arena, MEM_KiB);
|
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);
|
raise_notice("check: %s", check);
|
||||||
assert(strcmp(result, check) == 0);
|
assert(strcmp(result, check) == 0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user