diff --git a/lib/Base/Base.h b/lib/Base/Base.h index 4220045..db27e3d 100644 --- a/lib/Base/Base.h +++ b/lib/Base/Base.h @@ -21,6 +21,7 @@ #define WIN32_LEAN_AND_MEAN #include #undef ERROR // why... + #undef NO_ERROR // ugh... #else #error "This configuration is NOT supported. Only Windows with MSVC is currently supported." #endif @@ -324,5 +325,6 @@ struct Mutex_Lock_Guard { #define for_each_index(_idx_, _until_) for (s64 _idx_ = 0; _idx_ < _until_; ++_idx_) // For-loops for ArrayView<> compatible types #define for_each(_idx_, _array_) for (s64 _idx_ = 0; _idx_ < (_array_).count; ++_idx_) +#define for_each_reverse(_idx_, _array_) for (s64 _idx_ = (_array_).count-1; _idx_ >= 0; _idx_--) // #define for_each_starting_at(_it_, _array_, _start_) for (s64 _it_ = _start_; _it_ < (_array_).count; _it_ += 1) diff --git a/lib/Base/Base_Thread_Context.h b/lib/Base/Base_Thread_Context.h index 64d05c5..c18f6bd 100644 --- a/lib/Base/Base_Thread_Context.h +++ b/lib/Base/Base_Thread_Context.h @@ -14,9 +14,9 @@ struct Thread_Context { Logger logger = {nullptr, nullptr}; String_Builder* log_builder; // Stack_Trace* stack_trace; - + Array child_threads; // maybe should be linked-list? - Thread* thread_that_created_me = nullptr; // so we can remove from above array + Thread_Context* parent_thread_context = nullptr; // so we can remove from above array string thread_name; diff --git a/lib/Base/ErrorType.cpp b/lib/Base/ErrorType.cpp index a59cda9..5e5746d 100644 --- a/lib/Base/ErrorType.cpp +++ b/lib/Base/ErrorType.cpp @@ -1,6 +1,8 @@ // #NOTE: To keep things simple, all allocations for Error should be via GPAllocator. // We really allocate two things: the Error struct and the error string copy. +#define NO_ERROR nullptr + enum class ErrorClass: s32 { NONE = 0, // should not be used, just to avoid a default value being assigned. WARNING = 1, @@ -26,6 +28,7 @@ struct Error { char* error_severity (ErrorClass severity) { switch (severity) { case ErrorClass::NONE: { + return "[NONE]"; } break; case ErrorClass::WARNING: { return "[WARNING]"; @@ -40,7 +43,7 @@ char* error_severity (ErrorClass severity) { return ""; } -void push_error (Error* new_error); +void push_error (Thread_Context* tctx, Error* new_error); string to_string (Error* error) { return { error->count, error->data }; @@ -54,6 +57,9 @@ string to_string (Error* error) { #define log_warning(fmt, ...) \ Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::WARNING, fmt, ##__VA_ARGS__) + +#define log_none(fmt, ...) \ + Log_Error_2(__FILE__, __FUNCTION__, __LINE__, ErrorClass::NONE, fmt, ##__VA_ARGS__) Error* new_error (ErrorClass severity, string error_string) { Error* error = New(); @@ -69,11 +75,12 @@ Error* new_error (ErrorClass severity, string error_string) { // void Log_Error_2 (string file_path, string function_name, s32 line_number, ErrorClass severity, string fmt, ...) { - Assert(thread_context() != nullptr); - push_arena(thread_context()->error_arena); + auto tctx = thread_context(); + Assert(tctx != nullptr); + push_arena(tctx->error_arena); String_Builder* sb = new_string_builder(Arena_Reserve::Size_64K); - // #TODO: prepend severity, other information + print_to_builder(sb, "%s ", error_severity(severity)); va_list args; @@ -86,7 +93,7 @@ void Log_Error_2 (string file_path, string function_name, s32 line_number, Error string error_string = builder_to_string(sb); Error* error = new_error(severity, error_string); // Additional information - error->thread_id = thread_context()->thread_idx; + error->thread_id = tctx->thread_idx; error->source_line = line_number; error->file_path = copy_string(file_path); error->function_name = copy_string(function_name); @@ -94,21 +101,22 @@ void Log_Error_2 (string file_path, string function_name, s32 line_number, Error error->previous_error = nullptr; error->next_error = nullptr; - push_error(error); + push_error(tctx, error); } -void push_error (Error* new_error) { +void push_error (Thread_Context* tctx, Error* new_error) { + Assert(tctx == thread_context()); // Not a real assert, just wondering if we'll ever call this with a non-local context? Assert(new_error != nullptr); if (new_error == nullptr) return; - Error* current_error = thread_context()->current_error; + Error* current_error = tctx->current_error; if (current_error) { current_error->next_error = new_error; new_error->previous_error = current_error; } else { - thread_context()->first_error = new_error; + tctx->first_error = new_error; new_error->previous_error = nullptr; } - thread_context()->current_error = new_error; + tctx->current_error = new_error; switch (new_error->severity) { case ErrorClass::NONE: @@ -122,19 +130,32 @@ void push_error (Error* new_error) { } } -void clear_errors () { // Reset pointers and reset error_arena - arena_reset(thread_context()->error_arena); // maybe overwrite memory? - thread_context()->first_error = nullptr; - thread_context()->current_error = nullptr; +// push error to a context without back-linking to previous context +void push_error_no_context (Thread_Context* tctx, Error* new_error) { + Assert(new_error != nullptr); + if (new_error == nullptr) return; + Error* current_error = tctx->current_error; + if (current_error) { + current_error->next_error = new_error; + } else { + tctx->first_error = new_error; + } + tctx->current_error = new_error; } -void clear_error (Error* error) { +void clear_errors (Thread_Context* tctx) { // Reset pointers and reset error_arena + arena_reset(tctx->error_arena); // maybe overwrite memory? + tctx->first_error = nullptr; + tctx->current_error = nullptr; +} + +void clear_error (Thread_Context* tctx, Error* error) { // If we want to clear a specific error (simply remove from the list) Assert(error != nullptr); - bool is_current_error = (thread_context()->current_error == error); - bool is_first_error = (thread_context()->first_error == error); + bool is_current_error = (tctx->current_error == error); + bool is_first_error = (tctx->first_error == error); - Error* current_error = thread_context()->first_error; + Error* current_error = tctx->first_error; if (current_error == nullptr) return; // no errors in linked list. while (current_error != error && current_error != nullptr) { @@ -145,23 +166,52 @@ void clear_error (Error* error) { Error* the_previous_error = current_error->previous_error; Error* the_next_error = current_error->next_error; // Remove current_node from linked list: - the_previous_error->next_error = the_next_error; - the_next_error->previous_error = the_previous_error; + if (the_previous_error) { + the_previous_error->next_error = the_next_error; + } + if (the_next_error) { + the_next_error->previous_error = the_previous_error; + } if (is_first_error && is_current_error) { // It matches the only item in the list, just empty the list: - clear_errors(); + clear_errors(tctx); return; } if (is_first_error) { // the_next_error becomes new first error. - thread_context()->first_error = the_next_error; + tctx->first_error = the_next_error; } if (is_current_error) { // the current_error becomes the previous error - thread_context()->current_error = the_previous_error; + tctx->current_error = the_previous_error; } } +void push_errors_to_parent_thread (Thread_Context* tctx) { + if (!tctx->first_error) return; + Assert(tctx->parent_thread_context); + while (tctx->first_error) { + push_error_no_context(tctx->parent_thread_context, tctx->first_error); + clear_error(tctx, tctx->first_error); + } +} + +ArrayView get_all_errors (Thread_Context* tctx) { + Array error_array; + // call with temp() recommended. + Error* current_error = tctx->first_error; + while (current_error) { + array_add(error_array, current_error); + current_error = current_error->next_error; + } + + + for_each(t, tctx->child_threads) { + // #TODO: also recurse through child threads? + } + + return error_array; +} // Will need to use __FILE__ and __LINE__ macros diff --git a/lib/Base/Expandable_Arena.cpp b/lib/Base/Expandable_Arena.cpp index 68bcde5..3d16c39 100644 --- a/lib/Base/Expandable_Arena.cpp +++ b/lib/Base/Expandable_Arena.cpp @@ -97,7 +97,8 @@ Allocator allocator (ExpandableArena* arena_ex) { // #TODO: currently this keeps the final arena's memory. Fix this! void arena_reset_to (ExpandableArena* arena_ex, Arena* last_arena, u8* starting_point) { // going backwards from end of arena list - for (s64 i = arena_ex->next_arenas.count-1; i > 0; i -= 1) { + // for (s64 i = arena_ex->next_arenas.count-1; i >= 0; i -= 1) { + for_each_reverse(i, arena_ex->next_arenas) { Arena* arena = arena_ex->next_arenas[i]; if (arena == last_arena) { // return to starting_point arena_ex->current = arena; diff --git a/lib/Base/String.cpp b/lib/Base/String.cpp index 71ba236..be37671 100644 --- a/lib/Base/String.cpp +++ b/lib/Base/String.cpp @@ -284,4 +284,29 @@ string to_lower_copy (string s_orig) { } #define format_cstring(fmt, ...) \ - (char*)format_string(fmt, ##__VA_ARGS__).data \ No newline at end of file + (char*)format_string(fmt, ##__VA_ARGS__).data + +bool is_any (u8 c, string chars) { + for_each(i, chars) { + if (chars.data[i] == c) return true; + } + + return false; +} + +string trim_right (string s, string chars, bool replace_with_zeros) { + s64 count = s.count; + + for_each_reverse(i, s) { + if (is_any(s.data[i], chars)) { + if (replace_with_zeros) { + s.data[i] = 0; + } + count -= 1; + } else { + break; + } + } + + return string_view(s, 0, count); +} \ No newline at end of file diff --git a/lib/Base/String.h b/lib/Base/String.h index e1713e6..c850c7a 100644 --- a/lib/Base/String.h +++ b/lib/Base/String.h @@ -91,6 +91,9 @@ string format_string_no_context (char* format, ...); string to_lower_copy (string s_orig); +string DEFAULT_SPACES = "\r\t\n"; +string trim_right (string s, string chars=DEFAULT_SPACES, bool replace_with_zeros=true); + // #TODO #Parsing stuff: // is_white_space(char: u8) // advance diff --git a/lib/OS/OS_Win32.cpp b/lib/OS/OS_Win32.cpp index 6ce4aa0..08a3cb0 100644 --- a/lib/OS/OS_Win32.cpp +++ b/lib/OS/OS_Win32.cpp @@ -276,22 +276,29 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name= thread->context->error_arena = next_arena(Arena_Reserve::Size_64M); thread->context->logger = {default_logger_proc, &default_logger}; + thread->context->parent_thread_context = thread_context(); + thread->os_thread.windows_thread = windows_thread; thread->os_thread.windows_thread_id = windows_thread_id; thread->proc = proc; thread->index = this_thread_index; + thread_context()->child_threads.allocator = allocator(thread_context()->arena); + array_add(thread_context()->child_threads, thread); + return true; } internal void thread_deinit (Thread* thread, bool zero_thread=false) { + // Move errors from thread to parent thread + push_errors_to_parent_thread(thread->context); + if (thread->os_thread.windows_thread) { CloseHandle(thread->os_thread.windows_thread); } thread->os_thread.windows_thread = nullptr; - // #TODO: before releasing arena, force-delete extra pages? array_reset(*thread->context->log_builder); free_string_builder(thread->context->log_builder); release_arena(thread->context->error_arena); @@ -382,12 +389,12 @@ internal string get_error_string (OS_Error_Code error_code) { LocalFree(lpMsgBuf); - return result; // trim_right(result, "\r\n"); + return trim_right(result); } internal void log_error_code_and_string () { // #TODO: replace with call to log_error_with_code OS_Error_Code error_code = GetLastError(); - log_error(" > GetLastError code: %d, %s\n", error_code, get_error_string(error_code).data); + log_error(" > GetLastError code: %d, %s", error_code, get_error_string(error_code).data); } internal bool file_is_valid (File file) { @@ -437,7 +444,7 @@ internal void file_close (File* file) { internal bool file_read (File file, u8* data, s64 bytes_to_read_count, s64* bytes_read_count) { // ignore bytes_read_count if null. if (data == nullptr) { - log_error("file_read called with null destination pointer.\n"); + log_error("file_read called with null destination pointer."); log_error_code_and_string(); if (bytes_read_count) (*bytes_read_count) = 0; return false; @@ -1040,7 +1047,7 @@ bool Win32_Discover_Drives () { log(" - volume name: %s", drive->volume_name.data); log(" - file_system: %s", wide_to_utf8(file_system_name).data); } else { - log_error("GetVolumeInformationW failed!"); + log_error("GetVolumeInformationW failed! (drive label: %s)", drive_label.data); log_error_code_and_string(); drive->is_present = false; } diff --git a/lib/OS/OS_Win32_NTFS.cpp b/lib/OS/OS_Win32_NTFS.cpp index 57fe164..7cd934a 100644 --- a/lib/OS/OS_Win32_NTFS.cpp +++ b/lib/OS/OS_Win32_NTFS.cpp @@ -143,11 +143,11 @@ bool NTFS_read_internal (NTFS_MFT_Internal* mft, void* buffer, u64 from, u64 cou // #TODO: Release resources if we face an early return! // #TODO: Maybe this doesn't need to return a value? Return an Error* instead. -Dense_FS* NTFS_MFT_read_raw (OS_Drive* drive) { +Error* NTFS_MFT_read_raw (OS_Drive* drive) { auto start_time = GetUnixTimestamp(); Assert(drive != nullptr); - if (drive == nullptr) { return nullptr; } + if (drive == nullptr) { return nullptr; } string drive_path = drive->label; @@ -298,7 +298,7 @@ Dense_FS* NTFS_MFT_read_raw (OS_Drive* drive) { drive->bytes_accessed = mft->bytes_accessed; drive->time_to_enumerate = (f32)(GetUnixTimestamp() - start_time); - return drive->data; + return NO_ERROR; } struct NTFS_Enumeration_Task { @@ -306,6 +306,8 @@ struct NTFS_Enumeration_Task { ArrayView drives; // Should be part of OS_Drive! + + Error* error; }; s64 ntfs_enumeration_thread_proc (Thread* thread) { @@ -314,8 +316,9 @@ s64 ntfs_enumeration_thread_proc (Thread* thread) { log("[ntfs_enumeration_thread_proc] (Thread index: %lld) Task pointer: %p", thread->index, task); for_each(d, task->drives) { - auto result = NTFS_MFT_read_raw(task->drives[d]); - if (result == nullptr) return 1; + task->error = NTFS_MFT_read_raw(task->drives[d]); + // What we actually want to do here is push all our errors to return to the main thread. + if (task->error) return 1; } return 0; diff --git a/src/Ex1.cpp b/src/Ex1.cpp index 887234d..12d3ff6 100644 --- a/src/Ex1.cpp +++ b/src/Ex1.cpp @@ -83,15 +83,11 @@ void Ex1_Control_Panel () { using namespace ImGui; SameLine(); Text("Enumerated in %.2f seconds", drive->time_to_enumerate); } - SameLine(); - if (Button(format_cstring("Read NTFS MFT Raw##%s", drive->label.data))) { - push_arena(thread_context()->arena); - // auto_release(thread_context()->arena); - auto dfs = NTFS_MFT_read_raw(drive); - if (dfs == nullptr) { - log("[NTFS_MFT_read_raw] operation failed"); - } - } + // SameLine(); + // if (Button(format_cstring("Read NTFS MFT Raw##%s", drive->label.data))) { + // push_arena(thread_context()->arena); + // Error* error = NTFS_MFT_read_raw(drive); + // } } if (drives.count > 0) { @@ -113,6 +109,7 @@ void Ex1_Control_Panel () { using namespace ImGui; // if drive count exceeds the number of threads, we need to group them so each thread // can enumerate multiple drives. // We need to distribute the drives across our available threads: + // #TODO #is_present - drive_count should only include if `drive->is_present`. Array> drive_split; s32 thread_count = (s32)ex1_ntfs.threads.count; drive_split.allocator = GPAllocator(); @@ -171,6 +168,9 @@ void Ex1_Control_Panel () { using namespace ImGui; auto task = thread_task(NTFS_Enumeration_Task); release_arena(task->pool); + // #TODO Before #deiniting thread we SHOULD copy the errors from the + // thread before it's deleted forever. + thread_deinit(ex1_ntfs.threads_in_flight[t], true); array_unordered_remove_by_index(ex1_ntfs.threads_in_flight, t); t -= 1; // check this element index again! @@ -212,6 +212,22 @@ void ImGui_Debug_Panel () { using namespace ImGui; Text(t); } SeparatorText("Child Threads"); - + SeparatorText("Errors"); + ArrayView errors = get_all_errors(thread_context()); + for_each(e, errors) { + auto button_label = format_cstring("Clear##%d", e); + if (Button(button_label)) { + clear_error(thread_context(), errors[e]); + continue; + } + SameLine(); + Text(" [%d] %s", e, to_string(errors[e]).data); + } + Spacing(); + if (Button("Push some error")) { + log_warning("This is a warning."); + log_error("... and this is an error."); + } + End(); } \ No newline at end of file