Reviewed-on: #3 Co-authored-by: Musa <musasmahmood@gmail.com> Co-committed-by: Musa <musasmahmood@gmail.com>
176 lines
5.1 KiB
C++
176 lines
5.1 KiB
C++
// #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<Thread*> 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;
|
|
// }
|
|
}
|