| 86 | std::string dump_useful_thread_info(); |
| 87 | |
| 88 | error_code sys_tty_write([[maybe_unused]] ppu_thread &ppu, s32 ch, |
| 89 | vm::cptr<char> buf, u32 len, vm::ptr<u32> pwritelen) { |
| 90 | ppu.state += cpu_flag::wait; |
| 91 | |
| 92 | sys_tty.notice("sys_tty_write(ch=%d, buf=*0x%x, len=%d, pwritelen=*0x%x)", ch, |
| 93 | buf, len, pwritelen); |
| 94 | |
| 95 | std::string msg; |
| 96 | |
| 97 | if (static_cast<s32>(len) > 0 && |
| 98 | vm::check_addr(buf.addr(), vm::page_readable, len)) { |
| 99 | msg.resize(len); |
| 100 | |
| 101 | if (!vm::try_access(buf.addr(), msg.data(), len, false)) { |
| 102 | msg.clear(); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | auto find_word = [](std::string_view msg, std::string_view word) -> bool { |
| 107 | // Match uppercase and lowercase starting words |
| 108 | const usz index = msg.find(word.substr(1)); |
| 109 | |
| 110 | if (index != umax && index >= 1u) { |
| 111 | return std::tolower(static_cast<u8>(msg[index - 1])) == word[0]; |
| 112 | } |
| 113 | |
| 114 | return false; |
| 115 | }; |
| 116 | |
| 117 | std::string_view sample = std::string_view(msg).substr(0, 1024); |
| 118 | |
| 119 | const bool warning = |
| 120 | find_word(sample, "failed"sv) || find_word(sample, "abort"sv) || |
| 121 | find_word(sample, "crash"sv) || find_word(sample, "error"sv) || |
| 122 | find_word(sample, "unexpected"sv) || find_word(sample, "0x8001"sv); |
| 123 | |
| 124 | sample = {}; // Remove reference to string |
| 125 | |
| 126 | if (msg.size() >= 2u && [&]() { |
| 127 | static thread_local u64 last_write = 0; |
| 128 | |
| 129 | // Dump thread about every period which TTY was not being touched for |
| 130 | // about half a second |
| 131 | const u64 current = get_system_time(); |
| 132 | return current - std::exchange(last_write, current) >= |
| 133 | (warning ? 500'000 : 3'000'000); |
| 134 | }()) { |
| 135 | ppu_log.notice("\n%s", dump_useful_thread_info()); |
| 136 | } |
| 137 | |
| 138 | // Hack: write to tty even on CEX mode, but disable all error checks |
| 139 | if (ch < 0 || ch > 15) { |
| 140 | if (g_cfg.core.debug_console_mode) { |
| 141 | return CELL_EINVAL; |
| 142 | } else { |
| 143 | msg.clear(); |
| 144 | } |
| 145 | } |
no test coverage detected