RPCS3: Notify RAM shortage, Log current and peak RAM usage

This commit is contained in:
Elad 2026-01-24 22:39:51 +02:00
parent 2702417192
commit a4523651c7
7 changed files with 70 additions and 45 deletions

View file

@ -2879,6 +2879,16 @@ void thread_base::exec()
}
}
if (auto [total, current] = utils::get_memory_usage(); total - current <= 256 * 1024 * 1024)
{
if (reason_buf.empty())
{
reason_buf = std::string{reason};
}
fmt::append(reason_buf, " (Possible RAM deficiency: free RAM: %dMB)", (total - current) / (1024 * 1024));
}
if (!reason_buf.empty())
{
reason = reason_buf;

View file

@ -8615,7 +8615,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
// Blocks starting from 0x0 or invalid instruction won't be compiled, may need special interpreter fallback
}
if (!m_patterns.empty())
if (!m_patterns.empty() && g_cfg.core.spu_debug)
{
std::string out_dump;
dump(result, out_dump);

View file

@ -900,10 +900,8 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
switch (auto error = fs::g_tls_error)
{
case fs::error::noent: return {CELL_ENOENT};
default: sys_fs.error("lv2_file::open(): unknown error %s", error); break;
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO};
}
if (flags & CELL_FS_O_MSELF && !verify_mself(file))
@ -1374,8 +1372,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
}
default:
{
sys_fs.error("sys_fs_opendir(): unknown error %s", error);
return {CELL_EIO, path};
fmt::throw_exception("unknown error %s", error);
}
}
}
@ -1597,8 +1594,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
}
default:
{
sys_fs.error("sys_fs_stat(): unknown error %s", error);
return {CELL_EIO, path};
fmt::throw_exception("unknown error %s", error);
}
}
}
@ -1732,10 +1728,8 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
{
return {sys_fs.warning, CELL_EEXIST, path};
}
default: sys_fs.error("sys_fs_mkdir(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, path}; // ???
}
sys_fs.notice("sys_fs_mkdir(): directory %s created", path);
@ -1797,10 +1791,8 @@ error_code sys_fs_rename(ppu_thread& ppu, vm::cptr<char> from, vm::cptr<char> to
{
case fs::error::noent: return {CELL_ENOENT, from};
case fs::error::exist: return {CELL_EEXIST, to};
default: sys_fs.error("sys_fs_rename(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, from}; // ???
}
sys_fs.notice("sys_fs_rename(): %s renamed to %s", from, to);
@ -1852,10 +1844,8 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
{
case fs::error::noent: return {CELL_ENOENT, path};
case fs::error::notempty: return {CELL_ENOTEMPTY, path};
default: sys_fs.error("sys_fs_rmdir(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, path}; // ???
}
sys_fs.notice("sys_fs_rmdir(): directory %s removed", path);
@ -1910,10 +1900,8 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
}
default: sys_fs.error("sys_fs_unlink(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, path}; // ???
}
sys_fs.notice("sys_fs_unlink(): file %s deleted", path);
@ -2632,10 +2620,8 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
switch (auto error = fs::g_tls_error)
{
case fs::error::inval: return {CELL_EINVAL, "fd=%u, offset=0x%x, whence=%d", fd, offset, whence};
default: sys_fs.error("sys_fs_lseek(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return CELL_EIO; // ???
}
lock.unlock();
@ -2751,10 +2737,8 @@ error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u
{
case fs::error::exist: return {CELL_EISDIR, path};
case fs::error::noent: return {CELL_ENOENT, path};
default: sys_fs.error("sys_fs_get_block_size(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, path}; // ???
}
static_cast<void>(ppu.test_stopped());
@ -2809,10 +2793,8 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
}
default: sys_fs.error("sys_fs_truncate(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, path}; // ???
}
return CELL_OK;
@ -2858,10 +2840,8 @@ error_code sys_fs_ftruncate(ppu_thread& ppu, u32 fd, u64 size)
switch (auto error = fs::g_tls_error)
{
case fs::error::ok:
default: sys_fs.error("sys_fs_ftruncate(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return CELL_EIO; // ???
}
return CELL_OK;
@ -3057,10 +3037,8 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUti
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
}
default: sys_fs.error("sys_fs_utime(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return {CELL_EIO, path}; // ???
}
return CELL_OK;

View file

@ -4,14 +4,18 @@
#include "Emu/System.h"
#include "Emu/Cell/timers.hpp"
#include "util/cpu_stats.hpp"
#include "util/sysinfo.hpp"
#include "Utilities/Thread.h"
LOG_CHANNEL(perf_log, "PERF");
void perf_monitor::operator()()
{
constexpr u64 update_interval_us = 1000000; // Update every second
constexpr u64 log_interval_us = 10000000; // Log every 10 seconds
constexpr u64 update_interval_us = 500000; // Update every half second
constexpr u64 log_interval_us_max = 10000000; // Log at minimum every 10 seconds
constexpr u64 log_interval_us_min = 500000; // Log at maximum every half a second (catching possible memory leak)
constexpr u64 log_mem_increase = 50 * (1024 * 1024); // Log when memory usage increased by this amount
u64 elapsed_us = 0;
utils::cpu_stats stats;
@ -19,26 +23,28 @@ void perf_monitor::operator()()
u32 logged_pause = 0;
u64 last_pause_time = umax;
u64 max_memory_usage = 0;
std::vector<double> per_core_usage;
std::string msg;
for (u64 sleep_until = get_system_time(); thread_ctrl::state() != thread_state::aborting;)
for (u64 sleep_until = get_system_time();;)
{
thread_ctrl::wait_until(&sleep_until, update_interval_us);
elapsed_us += update_interval_us;
if (thread_ctrl::state() == thread_state::aborting)
{
break;
}
double total_usage = 0.0;
stats.get_per_core_usage(per_core_usage, total_usage);
if (elapsed_us >= log_interval_us)
const u64 current_mem_use = utils::get_memory_usage().second;
const u64 mem_use_increase = current_mem_use >= max_memory_usage ? current_mem_use - max_memory_usage : 0;
const u64 log_interval = (mem_use_increase >= log_mem_increase ? log_interval_us_min : log_interval_us_max);
if (elapsed_us >= log_interval || thread_ctrl::state() == thread_state::aborting)
{
max_memory_usage = std::max<u64>(current_mem_use, max_memory_usage);
elapsed_us = 0;
const bool is_paused = Emu.IsPaused();
@ -76,7 +82,18 @@ void perf_monitor::operator()()
fmt::append(msg, "%s %.1f%%", i > 0 ? "," : "", per_core_usage[i]);
}
if (max_memory_usage)
{
fmt::append(msg, ", RAM Usage: %dMB (Peak: %dMB)", current_mem_use / (1024 * 1024), max_memory_usage / (1024 * 1024));
}
perf_log.notice("%s", msg);
if (thread_ctrl::state() == thread_state::aborting)
{
// Log once before terminating
break;
}
}
}
}

View file

@ -197,6 +197,10 @@ std::set<std::string> get_one_drive_paths()
fmt::append(buf, "\nBuild: \"%s\"", rpcs3::get_verbose_version());
fmt::append(buf, "\nDate: \"%s\"", std::chrono::system_clock::now());
const auto [total, current] = utils::get_memory_usage();
fmt::append(buf, "\nRAM Usage: %dMB/%dMB (%dMB free)", current / (1024 * 1024), total / (1024 * 1024), (total - current) / (1024 * 1024));
}
std::string_view text = s_is_error_launch ? _text : buf;

View file

@ -736,6 +736,20 @@ std::string utils::get_firmware_version()
return {};
}
std::pair<u64, u64> utils::get_memory_usage()
{
#ifdef _WIN32
::MEMORYSTATUSEX status{};
status.dwLength = sizeof(status);
::GlobalMemoryStatusEx(&status);
return { status.ullTotalPhys, status.ullTotalPhys - status.ullAvailPhys };
#else
// TODO
return { get_total_memory(), 0 };
#endif
}
utils::OS_version utils::get_OS_version()
{
OS_version res {};
@ -1087,7 +1101,7 @@ static const bool s_tsc_freq_evaluated = []() -> bool
u64 utils::get_total_memory()
{
#ifdef _WIN32
::MEMORYSTATUSEX memInfo;
::MEMORYSTATUSEX memInfo{};
memInfo.dwLength = sizeof(memInfo);
::GlobalMemoryStatusEx(&memInfo);
return memInfo.ullTotalPhys;

View file

@ -71,6 +71,8 @@ namespace utils
std::string get_firmware_version();
std::pair<u64, u64> get_memory_usage();
struct OS_version
{
std::string type;