From 73d38bd9ff92a1f140e93503b23940944f4ff1b7 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sun, 1 Jan 2023 12:31:12 -0500 Subject: [PATCH] game/debugger: handle uncaught loader thread exceptions and better handle that situation on the windows debugger (#2075) --- common/cross_os_debug/xdbg.cpp | 14 ++- common/goos/ReplUtils.cpp | 10 +- decompiler/config/jak2/hacks.jsonc | 2 +- decompiler/config/jak2/type_casts.jsonc | 16 +-- .../opengl_renderer/loader/Loader.cpp | 114 +++++++++--------- 5 files changed, 77 insertions(+), 79 deletions(-) diff --git a/common/cross_os_debug/xdbg.cpp b/common/cross_os_debug/xdbg.cpp index 2a9d7c1cef..6bc4a7c228 100644 --- a/common/cross_os_debug/xdbg.cpp +++ b/common/cross_os_debug/xdbg.cpp @@ -471,12 +471,14 @@ bool check_stopped(const ThreadID& tid, SignalInfo* out) { { auto exc = debugEvent.u.Exception.ExceptionRecord.ExceptionCode; if (is_other) { - if (exc == EXCEPTION_BREAKPOINT) { - out->kind = SignalInfo::BREAK; - } else { - // ignore exceptions outside goal thread - ignore_debug_event(); - } + ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, + DBG_EXCEPTION_NOT_HANDLED); + // if (exc == EXCEPTION_BREAKPOINT) { + // out->kind = SignalInfo::BREAK; + // } else { + // // ignore exceptions outside goal thread + // ignore_debug_event(); + // } } else { switch (exc) { case EXCEPTION_BREAKPOINT: diff --git a/common/goos/ReplUtils.cpp b/common/goos/ReplUtils.cpp index 4fa62c09ad..62cedf0978 100644 --- a/common/goos/ReplUtils.cpp +++ b/common/goos/ReplUtils.cpp @@ -143,18 +143,18 @@ void ReplWrapper::init_default_settings() { // - https://github.com/ClickHouse/ClickHouse/blob/master/base/base/ReplxxLineReader.cpp#L366 repl.set_word_break_characters(" \t"); // Setup default keybinds - // (test-play) : Ctrl-P + // (test-play) : Ctrl-T repl.bind_key(Replxx::KEY::control('T'), commit_text_action("(test-play)")); // (e) : Ctrl-Q repl.bind_key(Replxx::KEY::control('Q'), commit_text_action("(e)")); - // (lt) : Ctrl-G + // (lt) : Ctrl-L repl.bind_key(Replxx::KEY::control('L'), commit_text_action("(lt)")); - /// (:stop) : Ctrl-H + /// (:stop) : Ctrl-W repl.bind_key(Replxx::KEY::control('W'), commit_text_action("(:stop)")); - // (dbgc) : Ctrl-F + // (dbgc) : Ctrl-G repl.bind_key(Replxx::KEY::control('G'), commit_text_action("(dbgc)")); // (:di) : Ctrl-B repl.bind_key(Replxx::KEY::control('B'), commit_text_action("(:di)")); - // (mi) : Ctrl-M + // (mi) : Ctrl-N repl.bind_key(Replxx::KEY::control('N'), commit_text_action("(mi)")); } diff --git a/decompiler/config/jak2/hacks.jsonc b/decompiler/config/jak2/hacks.jsonc index 5d3c153086..6ff7e1c490 100644 --- a/decompiler/config/jak2/hacks.jsonc +++ b/decompiler/config/jak2/hacks.jsonc @@ -461,7 +461,7 @@ 115, // goto L55 121, 131 - ], + ], "(anon-function 4 gun-states)": [94, 96, 98], "target-board-handler": [ 13, 14, 18 diff --git a/decompiler/config/jak2/type_casts.jsonc b/decompiler/config/jak2/type_casts.jsonc index 476fd23f70..de19384d1b 100644 --- a/decompiler/config/jak2/type_casts.jsonc +++ b/decompiler/config/jak2/type_casts.jsonc @@ -5377,9 +5377,7 @@ [11, "v1", "drawable-tree-instance-shrub"], [38, "v1", "drawable-tree-instance-tie"] ], - "dma-add-process-drawable-hud": [ - [11, "a0", "foreground-work"] - ], + "dma-add-process-drawable-hud": [[11, "a0", "foreground-work"]], "find-instance-by-index": [ [26, "t1", "drawable-tree-instance-shrub"], [40, "t1", "drawable-tree-instance-tie"] @@ -5395,15 +5393,9 @@ [[44, 64], "s1", "drawable-inline-array-instance-tie"], [[331, 450], "s5", "prototype-bucket-tie"] ], - "set-shadow-by-name": [ - [7, "v1", "process-drawable"] - ], - "get-shadow-by-name": [ - [7, "v1", "process-drawable"] - ], - "(anon-function 2 memory-usage)": [ - [211, "v1", "collide-shape-moving"] - ], + "set-shadow-by-name": [[7, "v1", "process-drawable"]], + "get-shadow-by-name": [[7, "v1", "process-drawable"]], + "(anon-function 2 memory-usage)": [[211, "v1", "collide-shape-moving"]], "(method 9 screen-filter)": [ [[25, 31], "a0", "dma-packet"], [[34, 40], "a0", "gs-gif-tag"], diff --git a/game/graphics/opengl_renderer/loader/Loader.cpp b/game/graphics/opengl_renderer/loader/Loader.cpp index 0c2d7adf86..a2c8a633a2 100644 --- a/game/graphics/opengl_renderer/loader/Loader.cpp +++ b/game/graphics/opengl_renderer/loader/Loader.cpp @@ -101,66 +101,70 @@ std::vector Loader::get_in_use_levels() { * This is used for file I/O and unpacking. */ void Loader::loader_thread() { - while (!m_want_shutdown) { - std::unique_lock lk(m_loader_mutex); + try { + while (!m_want_shutdown) { + std::unique_lock lk(m_loader_mutex); - // this will keep us asleep until we've got a level to load. - m_loader_cv.wait(lk, [&] { return !m_level_to_load.empty() || m_want_shutdown; }); - if (m_want_shutdown) { - return; - } - std::string lev = m_level_to_load; - // don't hold the lock while reading the file. - lk.unlock(); - - // simulate slower hard drive (so that the loader thread can lose to the game loads) - // std::this_thread::sleep_for(std::chrono::milliseconds(1500)); - - // load the fr3 file - Timer disk_timer; - auto data = - file_util::read_binary_file(m_base_path / fmt::format("{}.fr3", uppercase_string(lev))); - double disk_load_time = disk_timer.getSeconds(); - - // the FR3 files are compressed - Timer decomp_timer; - auto decomp_data = compression::decompress_zstd(data.data(), data.size()); - double decomp_time = decomp_timer.getSeconds(); - - // Read back into the tfrag3::Level structure - Timer import_timer; - auto result = std::make_unique(); - Serializer ser(decomp_data.data(), decomp_data.size()); - result->serialize(ser); - double import_time = import_timer.getSeconds(); - - // and finally "unpack", which creates the vertex data we'll upload to the GPU - Timer unpack_timer; - for (auto& tie_tree : result->tie_trees) { - for (auto& tree : tie_tree) { - tree.unpack(); + // this will keep us asleep until we've got a level to load. + m_loader_cv.wait(lk, [&] { return !m_level_to_load.empty() || m_want_shutdown; }); + if (m_want_shutdown) { + return; } - } - for (auto& t_tree : result->tfrag_trees) { - for (auto& tree : t_tree) { - tree.unpack(); + std::string lev = m_level_to_load; + // don't hold the lock while reading the file. + lk.unlock(); + + // simulate slower hard drive (so that the loader thread can lose to the game loads) + // std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + + // load the fr3 file + Timer disk_timer; + auto data = + file_util::read_binary_file(m_base_path / fmt::format("{}.fr3", uppercase_string(lev))); + double disk_load_time = disk_timer.getSeconds(); + + // the FR3 files are compressed + Timer decomp_timer; + auto decomp_data = compression::decompress_zstd(data.data(), data.size()); + double decomp_time = decomp_timer.getSeconds(); + + // Read back into the tfrag3::Level structure + Timer import_timer; + auto result = std::make_unique(); + Serializer ser(decomp_data.data(), decomp_data.size()); + result->serialize(ser); + double import_time = import_timer.getSeconds(); + + // and finally "unpack", which creates the vertex data we'll upload to the GPU + Timer unpack_timer; + for (auto& tie_tree : result->tie_trees) { + for (auto& tree : tie_tree) { + tree.unpack(); + } + } + for (auto& t_tree : result->tfrag_trees) { + for (auto& tree : t_tree) { + tree.unpack(); + } } - } - for (auto& shrub_tree : result->shrub_trees) { - shrub_tree.unpack(); - } - fmt::print( - "------------> Load from file: {:.3f}s, import {:.3f}s, decomp {:.3f}s unpack {:.3f}s\n", - disk_load_time, import_time, decomp_time, unpack_timer.getSeconds()); + for (auto& shrub_tree : result->shrub_trees) { + shrub_tree.unpack(); + } + fmt::print( + "------------> Load from file: {:.3f}s, import {:.3f}s, decomp {:.3f}s unpack {:.3f}s\n", + disk_load_time, import_time, decomp_time, unpack_timer.getSeconds()); - // grab the lock again - lk.lock(); - // move this level to "initializing" state. - m_initializing_tfrag3_levels[lev] = std::make_unique(); // reset load state - m_initializing_tfrag3_levels[lev]->level = std::move(result); - m_level_to_load = ""; - m_file_load_done_cv.notify_all(); + // grab the lock again + lk.lock(); + // move this level to "initializing" state. + m_initializing_tfrag3_levels[lev] = std::make_unique(); // reset load state + m_initializing_tfrag3_levels[lev]->level = std::move(result); + m_level_to_load = ""; + m_file_load_done_cv.notify_all(); + } + } catch (std::exception& e) { + ASSERT_MSG(false, fmt::format("Exception {} encountered in loader_thread", e.what())); } }