diff --git a/.vs/launch.vs.json b/.vs/launch.vs.json index a099d931a6..15691e1583 100644 --- a/.vs/launch.vs.json +++ b/.vs/launch.vs.json @@ -65,7 +65,10 @@ "--iso_data_path", "${workspaceRoot}/iso_data/jak2", "--game", - "jak2" + "jak2", + "--pretty-print", + "--num_threads", + "32" ] }, { diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 9177c46667..adf0c5e3ee 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -46,12 +46,13 @@ add_library(common util/json_util.cpp util/read_iso_file.cpp util/SimpleThreadGroup.cpp - util/StringUtil.cpp + util/string_util.cpp util/Timer.cpp util/os.cpp util/print_float.cpp util/FrameLimiter.cpp - util/unicode_util.cpp) + util/unicode_util.cpp + util/term_util.cpp) target_link_libraries(common fmt lzokay replxx libzstd_static) diff --git a/common/util/StringUtil.cpp b/common/util/string_util.cpp similarity index 97% rename from common/util/StringUtil.cpp rename to common/util/string_util.cpp index fb04649b5e..cefb956643 100644 --- a/common/util/StringUtil.cpp +++ b/common/util/string_util.cpp @@ -1,4 +1,4 @@ -#include "StringUtil.h" +#include "string_util.h" #include diff --git a/common/util/StringUtil.h b/common/util/string_util.h similarity index 100% rename from common/util/StringUtil.h rename to common/util/string_util.h diff --git a/common/util/term_util.cpp b/common/util/term_util.cpp new file mode 100644 index 0000000000..caf53fa6fa --- /dev/null +++ b/common/util/term_util.cpp @@ -0,0 +1,49 @@ +#include "term_util.h" + +#if defined _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__) +#include +#include +#include + +#include +#endif + +namespace term_util { +void clear() { +#if defined _WIN32 + system("cls"); +#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__) + system("clear"); +#endif +} + +int row_count() { +#if defined _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; +#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__) + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + return w.ws_row; +#endif +} + +int col_count() { +#if defined _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + return csbi.srWindow.Right - csbi.srWindow.Left + 1; +#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__) + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + return w.ws_col; +#endif +} +} // namespace term_util diff --git a/common/util/term_util.h b/common/util/term_util.h new file mode 100644 index 0000000000..fbdf5e8250 --- /dev/null +++ b/common/util/term_util.h @@ -0,0 +1,7 @@ + + +namespace term_util { +void clear(); +int row_count(); +int col_count(); +} // namespace term_util diff --git a/game/kernel/common/kmachine.cpp b/game/kernel/common/kmachine.cpp index 7f5ed7fbae..912d5cb98c 100644 --- a/game/kernel/common/kmachine.cpp +++ b/game/kernel/common/kmachine.cpp @@ -4,8 +4,8 @@ #include "common/log/log.h" #include "common/symbols.h" #include "common/util/FileUtil.h" -#include "common/util/StringUtil.h" #include "common/util/Timer.h" +#include "common/util/string_util.h" #include "game/graphics/gfx.h" #include "game/kernel/common/Ptr.h" diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 37a17f87c7..6e853ea843 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -9,8 +9,8 @@ #include "common/goos/ReplUtils.h" #include "common/util/DgoWriter.h" #include "common/util/FileUtil.h" -#include "common/util/StringUtil.h" #include "common/util/Timer.h" +#include "common/util/string_util.h" #include "goalc/compiler/Compiler.h" #include "goalc/compiler/IR.h" diff --git a/test/offline/framework/execution.cpp b/test/offline/framework/execution.cpp index 8330e87797..23e49a6911 100644 --- a/test/offline/framework/execution.cpp +++ b/test/offline/framework/execution.cpp @@ -1,7 +1,7 @@ #include "execution.h" -#include "common/util/StringUtil.h" #include "common/util/diff.h" +#include "common/util/string_util.h" #include "goalc/compiler/Compiler.h" #include "test/offline/config/config.h" diff --git a/test/offline/framework/orchestration.cpp b/test/offline/framework/orchestration.cpp index b3c76d3589..eeeaae86cb 100644 --- a/test/offline/framework/orchestration.cpp +++ b/test/offline/framework/orchestration.cpp @@ -5,8 +5,9 @@ #include "common/log/log.h" #include "common/util/FileUtil.h" -#include "common/util/StringUtil.h" #include "common/util/diff.h" +#include "common/util/string_util.h" +#include "common/util/term_util.h" #include "decompiler/ObjectFile/ObjectFileDB.h" #include "test/offline/config/config.h" @@ -201,6 +202,14 @@ void OfflineTestThreadStatus::complete_step() { g_offline_test_thread_manager.print_current_test_status(config); } +bool OfflineTestThreadStatus::in_progress() { + return stage == OfflineTestThreadStatus::Stage::IDLE || + stage == OfflineTestThreadStatus::Stage::COMPARING || + stage == OfflineTestThreadStatus::Stage::COMPILING || + stage == OfflineTestThreadStatus::Stage::DECOMPILING || + stage == OfflineTestThreadStatus::Stage::PREPARING; +} + std::tuple thread_stage_to_str(OfflineTestThreadStatus::Stage stage) { switch (stage) { case OfflineTestThreadStatus::Stage::IDLE: @@ -254,23 +263,81 @@ std::string thread_progress_bar(u32 curr_step, u32 total_steps) { return progress_bar; } +int OfflineTestThreadManager::num_threads_pending() { + int count = 0; + for (const auto& status : statuses) { + if (status->in_progress()) { + count++; + } + } + return count; +} +int OfflineTestThreadManager::num_threads_succeeded() { + int count = 0; + for (const auto& status : statuses) { + if (status->stage == OfflineTestThreadStatus::Stage::FINISHED) { + count++; + } + } + return count; +} +int OfflineTestThreadManager::num_threads_failed() { + int count = 0; + for (const auto& status : statuses) { + if (status->stage == OfflineTestThreadStatus::Stage::FAILED) { + count++; + } + } + return count; +} + void OfflineTestThreadManager::print_current_test_status(const OfflineTestConfig& config) { if (!config.pretty_print) { return; } + + std::lock_guard guard(print_lock); + + // Handle terminal height + auto rows_available = term_util::row_count(); + // Truncate any threads we can't display + // - we need to leave 1 row to say how much we are hiding + int threads_to_display = ((rows_available - 2) / 2); + int threads_hidden = statuses.size() - threads_to_display; + int lines_to_clear = (threads_to_display * 2) + (threads_hidden == 0 ? 0 : 1); + // [DECOMP] ▰▰▰▰▰▰▱▱▱▱ (PRI, RUI, FOR, +3 more) // [1/30] - target-turret-shot // MUTED TEXT - std::lock_guard guard(print_lock); - fmt::print("\x1b[{}A", g_offline_test_thread_manager.statuses.size() * 2); // move n lines up - for (const auto& status : g_offline_test_thread_manager.statuses) { + fmt::print("\x1b[{}A", lines_to_clear); // move n lines up + fmt::print("\e[?25l"); // hide the cursor + int threads_shown = 0; + for (int i = 0; i < statuses.size() && threads_shown < threads_to_display; i++) { + const auto& status = statuses.at(i); + // Skip completed threads if there are potential in-progress ones to show + if (threads_hidden != 0 && !status->in_progress() && + (statuses.size() - i) > threads_to_display) { + continue; + } + // first line const auto [color, stage_text] = thread_stage_to_str(status->stage); fmt::print( - "\33[2K\r[{:>12}] {} ({})\n", fmt::styled(stage_text, fmt::fg(color)), + "\33[2K\r[{:>12}] {} ({}) [{}]\n", fmt::styled(stage_text, fmt::fg(color)), fmt::styled(thread_progress_bar(status->curr_step, status->total_steps), fmt::fg(color)), - thread_dgos_to_str(status->dgos)); + thread_dgos_to_str(status->dgos), fmt::styled(i, fmt::fg(fmt::color::gray))); // second line fmt::print(fmt::fg(fmt::color::gray), "\33[2K\r{:>14} - {}\n", fmt::format("[{}/{}]", status->curr_step, status->total_steps), status->curr_file); + threads_shown++; } + if (threads_hidden != 0) { + fmt::print( + fmt::fg(fmt::color::gray), "\33[2K\r+{} other threads. [{} | {} | {}]\n", threads_hidden, + fmt::styled(g_offline_test_thread_manager.num_threads_pending(), + fmt::fg(fmt::color::orange)), + fmt::styled(g_offline_test_thread_manager.num_threads_failed(), fmt::fg(fmt::color::red)), + fmt::styled(g_offline_test_thread_manager.num_threads_succeeded(), + fmt::fg(fmt::color::light_green))); + } + fmt::print("\e[?25h"); // show the cursor } diff --git a/test/offline/framework/orchestration.h b/test/offline/framework/orchestration.h index 16738244dc..be865e9efc 100644 --- a/test/offline/framework/orchestration.h +++ b/test/offline/framework/orchestration.h @@ -93,6 +93,7 @@ class OfflineTestThreadStatus { void update_stage(Stage new_stage); void update_curr_file(const std::string& _curr_file); void complete_step(); + bool in_progress(); }; struct OfflineTestWorkCollection { @@ -109,10 +110,14 @@ struct OfflineTestWorkGroup { class OfflineTestThreadManager { public: - void print_current_test_status(const OfflineTestConfig& config); - std::vector> statuses = {}; + int num_threads_pending(); + int num_threads_succeeded(); + int num_threads_failed(); + + void print_current_test_status(const OfflineTestConfig& config); + private: std::mutex print_lock; }; diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index 587fb806b0..819cb2dd09 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -2,6 +2,7 @@ #include #include "common/log/log.h" +#include "common/util/term_util.h" #include "common/util/unicode_util.h" #include "config/config.h" @@ -12,17 +13,6 @@ #include "third-party/CLI11.hpp" #include "third-party/fmt/format.h" -// TODO - move this into a common lib eventually -void clear_terminal() { -#if defined _WIN32 - system("cls"); -#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) - system("clear"); -#elif defined(__APPLE__) - // system("clear"); -#endif -} - int main(int argc, char* argv[]) { ArgumentGuard u8_guard(argc, argv); @@ -61,7 +51,7 @@ int main(int argc, char* argv[]) { if (pretty_print) { lg::set_stdout_level(lg::level::off); - clear_terminal(); + term_util::clear(); } lg::initialize();