// #hacky fwd declares struct Source_Code_Location { string file_name; string function_name; s32 line_number; }; struct Stack_Trace_Node { Stack_Trace_Node* next; // Information Source_Code_Location data; s32 call_depth; }; struct Error; struct Graphics; struct Thread_Context { ExpandableArena* temp; // Used for temporary allocations, scratch space. ExpandableArena* arena; // general purpose local arena Allocator allocator; s32 thread_idx; // u16 _padding0; u16 default_allocator_alignment = 16; Logger logger = {nullptr, nullptr}; String_Builder* log_builder; // String builder used by log() and log_error_internal() String_Builder* string_builder; // Secondary builder just for convenience! Stack_Trace_Node* stack_trace; // use `list(stack_trace)` in watch window of raddbg to view as array! Array child_threads; // maybe should be linked-list? Thread_Context* parent_thread_context = nullptr; // so we can remove from above array string thread_name; Allocator error_allocator = default_allocator(); Error* first_error = nullptr; Error* current_error = nullptr; Arena* error_arena; // Graphics stuff: Graphics* graphics; void* userdata; // for appending other arenas, etc. }; // C_LINKAGE thread_static TCTX* tctx_thread_local; thread_static Thread_Context* thread_local_context; // #TODO #NewContext void create_thread_context (Thread_Context** context, string thread_name, bool is_main_thread); // Thread-context #Errors: internal void Bootstrap_Main_Thread_Context (); struct Push_Allocator { Thread_Context* context; Allocator old_allocator; Push_Allocator (Allocator new_allocator) { context = thread_context(); if (this->context != nullptr) { old_allocator = context->allocator; context->allocator = new_allocator; } else { old_allocator = default_allocator(); } } ~Push_Allocator () { if (this->context != nullptr) { context->allocator = old_allocator; } } }; // #stack_trace #define ENABLE_STACK_TRACE BUILD_DEBUG void push_stack_trace_internal (Thread_Context* context, string file_name, string function_name, s32 line_number) { if (context == nullptr) return; Assert(context != nullptr); // #no_context allocation Stack_Trace_Node* new_node = (Stack_Trace_Node*)default_allocator_new(sizeof(Stack_Trace_Node)); new_node->data.file_name = file_name; new_node->data.function_name = function_name; new_node->data.line_number = line_number; new_node->next = nullptr; if (context->stack_trace == nullptr) { new_node->call_depth = 1; } else { new_node->call_depth = context->stack_trace->call_depth + 1; new_node->next = context->stack_trace; } context->stack_trace = new_node; } void pop_stack_trace_internal (Thread_Context* context) { if (context == nullptr) return; Stack_Trace_Node* old_node = context->stack_trace; context->stack_trace = old_node->next; default_allocator_free(old_node); } #if ENABLE_STACK_TRACE #define stack_trace() \ Push_Stack_Trace Concat(_push_stack_trace_guard_, __LINE__)(__FILE__, __FUNCTION__, __LINE__) #else #define stack_trace() #endif struct Push_Stack_Trace { Thread_Context* context; Push_Stack_Trace (string file_name, string function_name, s32 line_number) { context = thread_context(); push_stack_trace_internal(context, file_name, function_name, line_number); } ~Push_Stack_Trace () { pop_stack_trace_internal(context); } }; // #TODO: precede with something like: os_write_string_unsynchronized("Fatal Error!\n\nStack trace:", true); string generate_stack_trace (Thread_Context* context) { // #no_context - we want this to work even if context is utterly broken. String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K); Stack_Trace_Node* node = context->stack_trace; print_to_builder(sb, "Thread index: %d, thread name: %s\n\n", context->thread_idx, context->thread_name.data); while (node) { append(sb, format_string("%s:%d: %s\n", node->data.file_name.data, node->data.line_number, node->data.function_name.data)); node = node->next; } append(sb, "\n"); push_allocator(default_allocator()); string stack_trace_copy = builder_to_string(sb); free_string_builder(sb); return stack_trace_copy; } // We don't want to use context logger here! void print_stack_trace () { Thread_Context* context = thread_context(); Stack_Trace_Node* node = context->stack_trace; constexpr bool TO_STANDARD_ERROR = true; os_write_string_unsynchronized(generate_stack_trace(context), TO_STANDARD_ERROR); // while (node) { // os_write_string_unsynchronized(node->data.file_name, TO_STANDARD_ERROR); // string line_number_str = format_string(":%d: ", node->data.line_number); // maybe I shouldn't do this? // os_write_string_unsynchronized(line_number_str, TO_STANDARD_ERROR); // // os_write_string_unsynchronized("'", TO_STANDARD_ERROR); // os_write_string_unsynchronized(node->data.function_name, TO_STANDARD_ERROR); // os_write_string_unsynchronized("\n", TO_STANDARD_ERROR); // node = node->next; // } }