From c36c425fb9e4b7075a7098aaacb2d1e9eb45d8fd Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 29 May 2020 17:59:14 +0300 Subject: [PATCH] kernel explorer: Improvements --- rpcs3/Emu/Cell/lv2/sys_rsx.cpp | 18 ++- rpcs3/Emu/Cell/lv2/sys_rsx.h | 2 + rpcs3/Emu/Cell/lv2/sys_sync.h | 9 +- rpcs3/rpcs3qt/kernel_explorer.cpp | 216 +++++++++++++++++++++++++----- 4 files changed, 196 insertions(+), 49 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 09c2f3196b..d6e29af4e9 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -11,8 +11,6 @@ LOG_CHANNEL(sys_rsx); extern u64 get_timebased_time(); -static shared_mutex s_rsxmem_mtx; - // Unknown error code returned by sys_rsx_context_attribute enum sys_rsx_error : s32 { @@ -175,7 +173,7 @@ error_code sys_rsx_context_allocate(vm::ptr context_id, vm::ptr lpar_d auto rsx_cfg = g_fxo->get(); - std::lock_guard lock(s_rsxmem_mtx); + std::lock_guard lock(rsx_cfg->mutex); if (rsx_cfg->context_base) { @@ -279,10 +277,10 @@ error_code sys_rsx_context_free(u32 context_id) { sys_rsx.todo("sys_rsx_context_free(context_id=0x%x)", context_id); - std::scoped_lock lock(s_rsxmem_mtx); - auto rsx_cfg = g_fxo->get(); + std::scoped_lock lock(rsx_cfg->mutex); + if (context_id != 0x55555555 || !rsx_cfg->context_base) { return CELL_EINVAL; @@ -328,7 +326,7 @@ error_code sys_rsx_context_iomap(u32 context_id, u32 io, u32 ea, u32 size, u64 f io >>= 20, ea >>= 20, size >>= 20; - std::scoped_lock lock(s_rsxmem_mtx); + std::scoped_lock lock(g_fxo->get()->mutex); for (u32 i = 0; i < size; i++) { @@ -369,7 +367,7 @@ error_code sys_rsx_context_iounmap(u32 context_id, u32 io, u32 size) vm::reader_lock rlock; - std::scoped_lock lock(s_rsxmem_mtx); + std::scoped_lock lock(g_fxo->get()->mutex); for (const u32 end = (io >>= 20) + (size >>= 20); io < end;) { @@ -501,7 +499,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR; } - std::lock_guard lock(s_rsxmem_mtx); + std::lock_guard lock(rsx_cfg->mutex); // Note: no error checking is being done @@ -605,7 +603,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 verify(HERE), !!(a5 & (1 << 30)); } - std::lock_guard lock(s_rsxmem_mtx); + std::lock_guard lock(rsx_cfg->mutex); // When tile is going to be unbound, we can use it as a hint that the address will no longer be used as a surface and can be removed/invalidated // Todo: There may be more checks such as format/size/width can could be done @@ -678,7 +676,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 verify(HERE), !!(a4 & (1ull << 32)), (a6 & 0xFFFFFFFF) == 0u + ((0x2000 << 0) | (0x20 << 16)); } - std::lock_guard lock(s_rsxmem_mtx); + std::lock_guard lock(rsx_cfg->mutex); auto &zcull = render->zculls[a3]; diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.h b/rpcs3/Emu/Cell/lv2/sys_rsx.h index 65b6eca25c..c9bc97723b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.h +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.h @@ -1,5 +1,6 @@ #pragma once +#include "Utilities/mutex.h" #include "Emu/Memory/vm_ptr.h" struct RsxDriverInfo @@ -124,6 +125,7 @@ struct RsxDisplayInfo struct lv2_rsx_config { + shared_mutex mutex; u32 memory_size{}; u32 rsx_event_port{}; u32 context_base{}; diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index c49186cfae..6593c88eb1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -77,14 +77,11 @@ private: public: - static std::string_view name64(const u64& name_u64) + static std::string name64(const u64& name_u64) { - std::string_view str{reinterpret_cast(&name_u64), 7}; + std::string str{reinterpret_cast(&name_u64), 7}; - if (const auto pos = str.find_first_of('\0'); pos != umax) - { - str.remove_suffix(str.size() - pos); - } + str.erase(std::remove_if(str.begin(), str.end(), [](uchar c){ return !std::isprint(c); }), str.end()); return str; }; diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 6f96d39111..0cf0c05cf6 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -14,10 +14,14 @@ #include "Emu/Cell/lv2/sys_prx.h" #include "Emu/Cell/lv2/sys_overlay.h" #include "Emu/Cell/lv2/sys_memory.h" +#include "Emu/Cell/lv2/sys_mmapper.h" #include "Emu/Cell/lv2/sys_spu.h" #include "Emu/Cell/lv2/sys_process.h" +#include "Emu/Cell/lv2/sys_rsx.h" #include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/RSX/RSXThread.h" + #include "kernel_explorer.h" #include @@ -72,7 +76,8 @@ void kernel_explorer::Update() const u32 total_memory_usage = dct->used; QTreeWidgetItem* root = new QTreeWidgetItem(); - root->setText(0, qstr(fmt::format("Process, ID = 0x00000001, Total Memory Usage = 0x%x (%0.2f MB)", total_memory_usage, 1.f * total_memory_usage / (1024 * 1024)))); + root->setText(0, qstr(fmt::format("Process 0x%08x: Total Memory Usage: 0x%x/0x%x (%0.2f/%0.2f MB)", process_getpid(), total_memory_usage, dct->size, 1. * total_memory_usage / (1024 * 1024) + , 1. * dct->size / (1024 * 1024)))); m_tree->addTopLevelItem(root); // TODO: FileSystem @@ -125,64 +130,65 @@ void kernel_explorer::Update() { case SYS_MEM_OBJECT: { - // auto& mem = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Memory: ID = 0x%08x", id))); + auto& mem = static_cast(obj); + l_addTreeChild(node, qstr(fmt::format("Memory 0x%08x: Size: 0x%x (%0.2f MB), Granularity: %s, Mappings: %u", id, mem.size, mem.size * 1. / (1024 * 1024), mem.align == 0x10000u ? "64K" : "1MB", +mem.counter))); break; } case SYS_MUTEX_OBJECT: { auto& mutex = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Mutex: ID = 0x%08x “%s”,%s Owner = 0x%x, Locks = %u, Conds = %u, Wq = %zu", id, lv2_obj::name64(mutex.name), - mutex.recursive == SYS_SYNC_RECURSIVE ? " Recursive," : "", mutex.owner >> 1, +mutex.lock_count, +mutex.cond_count, mutex.sq.size()))); + l_addTreeChild(node, qstr(fmt::format(u8"Mutex 0x%08x: “%s”,%s Owner: %#x, Locks: %u, Key: %#llx, Conds: %u, Wq: %zu", id, lv2_obj::name64(mutex.name), + mutex.recursive == SYS_SYNC_RECURSIVE ? " Recursive," : "", mutex.owner >> 1, +mutex.lock_count, mutex.key, +mutex.cond_count, mutex.sq.size()))); break; } case SYS_COND_OBJECT: { auto& cond = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Cond: ID = 0x%08x “%s”, Waiters = %u", id, lv2_obj::name64(cond.name), +cond.waiters))); + l_addTreeChild(node, qstr(fmt::format(u8"Cond 0x%08x: “%s”, Key: %#llx, Wq: %u", id, lv2_obj::name64(cond.name), cond.key, +cond.waiters))); break; } case SYS_RWLOCK_OBJECT: { auto& rw = static_cast(obj); const s64 val = rw.owner; - l_addTreeChild(node, qstr(fmt::format(u8"RW Lock: ID = 0x%08x “%s”, Owner = 0x%x(%d), Rq = %zu, Wq = %zu", id, lv2_obj::name64(rw.name), - std::max(0, val >> 1), -std::min(0, val >> 1), rw.rq.size(), rw.wq.size()))); + l_addTreeChild(node, qstr(fmt::format(u8"RW Lock 0x%08x: “%s”, Owner: %#x(%d), Key: %#llx, Rq: %zu, Wq: %zu", id, lv2_obj::name64(rw.name), + std::max(0, val >> 1), -std::min(0, val >> 1), rw.key, rw.rq.size(), rw.wq.size()))); break; } case SYS_INTR_TAG_OBJECT: { // auto& tag = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Intr Tag: ID = 0x%08x", id))); + l_addTreeChild(node, qstr(fmt::format("Intr Tag 0x%08x", id))); break; } case SYS_INTR_SERVICE_HANDLE_OBJECT: { // auto& serv = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Intr Svc: ID = 0x%08x", id))); + l_addTreeChild(node, qstr(fmt::format("Intr Svc 0x%08x", id))); break; } case SYS_EVENT_QUEUE_OBJECT: { auto& eq = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Event Queue: ID = 0x%08x “%s”, %s, Key = %#llx, Events = %zu/%d, Waiters = %zu", id, lv2_obj::name64(eq.name), + l_addTreeChild(node, qstr(fmt::format(u8"Event Queue 0x%08x: “%s”, %s, Key: %#llx, Events: %zu/%d, Wq: %zu", id, lv2_obj::name64(eq.name), eq.type == SYS_SPU_QUEUE ? "SPU" : "PPU", eq.key, eq.events.size(), eq.size, eq.sq.size()))); break; } case SYS_EVENT_PORT_OBJECT: { auto& ep = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Event Port: ID = 0x%08x, Name = %#llx", id, ep.name))); + l_addTreeChild(node, qstr(fmt::format("Event Port 0x%08x: Name: %#llx, Bound: %s", id, ep.name, lv2_event_queue::check(ep.queue)))); break; } case SYS_TRACE_OBJECT: { - l_addTreeChild(node, qstr(fmt::format("Trace: ID = 0x%08x", id))); + l_addTreeChild(node, qstr(fmt::format("Trace 0x%08x", id))); break; } case SYS_SPUIMAGE_OBJECT: { - l_addTreeChild(node, qstr(fmt::format("SPU Image: ID = 0x%08x", id))); + auto& spi = static_cast(obj); + l_addTreeChild(node, qstr(fmt::format("SPU Image 0x%08x, Entry: 0x%x, Segs: *0x%x, Num Segs: %d", id, spi.e_entry, spi.segs, spi.nsegs))); break; } case SYS_PRX_OBJECT: @@ -192,23 +198,23 @@ void kernel_explorer::Update() if (!addr0) { - l_addTreeChild(node, qstr(fmt::format("PRX: ID = 0x%08x '%s' (HLE)", id, prx.name))); + l_addTreeChild(node, qstr(fmt::format("PRX 0x%08x: '%s' (HLE)", id, prx.name))); break; } const u32 end0 = addr0 + prx.segs[0].size - 1; - l_addTreeChild(node, qstr(fmt::format("PRX: ID = 0x%08x '%s', seg0 = [0x%x...0x%x]", id, prx.name, addr0, end0))); + l_addTreeChild(node, qstr(fmt::format("PRX 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, prx.name, addr0, end0))); break; } case SYS_SPUPORT_OBJECT: { - l_addTreeChild(node, qstr(fmt::format("SPU Port: ID = 0x%08x", id))); + l_addTreeChild(node, qstr(fmt::format("SPU Port 0x%08x", id))); break; } case SYS_OVERLAY_OBJECT: { auto& ovl = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("OVL: ID = 0x%08x '%s', seg0 = [0x%x...0x%x]", id, ovl.name, ovl.segs[0].addr, ovl.segs[0].addr + ovl.segs[0].size - 1))); + l_addTreeChild(node, qstr(fmt::format("OVL 0x%08x: '%s', Seg0: (0x%x...0x%x)", id, ovl.name, ovl.segs[0].addr, ovl.segs[0].addr + ovl.segs[0].size - 1))); break; } case SYS_LWMUTEX_OBJECT: @@ -232,48 +238,49 @@ void kernel_explorer::Update() } break; - } + } } } else { - l_addTreeChild(node, qstr(fmt::format(u8"LWMutex: ID = 0x%08x “%s”, Wq = %zu (Couldn't extract control data)", id, lv2_obj::name64(lwm.name), lwm.sq.size()))); + l_addTreeChild(node, qstr(fmt::format(u8"LWMutex 0x%08x: “%s”, Wq: %zu (Couldn't extract control data)", id, lv2_obj::name64(lwm.name), lwm.sq.size()))); break; } - l_addTreeChild(node, qstr(fmt::format(u8"LWMutex: ID = 0x%08x “%s”,%s Owner = %s, Locks = %u, Wq = %zu", id, lv2_obj::name64(lwm.name), + l_addTreeChild(node, qstr(fmt::format(u8"LWMutex 0x%08x: “%s”,%s Owner: %s, Locks: %u, Wq: %zu", id, lv2_obj::name64(lwm.name), (lwm_data.attribute & SYS_SYNC_RECURSIVE) ? " Recursive," : "", owner_str, lwm_data.recursive_count, lwm.sq.size()))); break; } case SYS_TIMER_OBJECT: { // auto& timer = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format("Timer: ID = 0x%08x", id))); + l_addTreeChild(node, qstr(fmt::format("Timer 0x%08x", id))); break; } case SYS_SEMAPHORE_OBJECT: { auto& sema = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Semaphore: ID = 0x%08x “%s”, Count = %d, Max Count = %d, Waiters = %#zu", id, lv2_obj::name64(sema.name), - sema.val.load(), sema.max, sema.sq.size()))); + const auto val = +sema.val; + l_addTreeChild(node, qstr(fmt::format(u8"Sema 0x%08x: “%s”, Count: %d/%d, Wq: %zu", id, lv2_obj::name64(sema.name), + std::max(val, 0), sema.max, -std::min(val, 0)))); break; } case SYS_LWCOND_OBJECT: { auto& lwc = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"LWCond: ID = 0x%08x “%s”, Waiters = %zu", id, lv2_obj::name64(lwc.name), +lwc.waiters))); + l_addTreeChild(node, qstr(fmt::format(u8"LWCond 0x%08x: “%s”, Wq: %zu", id, lv2_obj::name64(lwc.name), +lwc.waiters))); break; } case SYS_EVENT_FLAG_OBJECT: { auto& ef = static_cast(obj); - l_addTreeChild(node, qstr(fmt::format(u8"Event Flag: ID = 0x%08x “%s”, Type = 0x%x, Pattern = 0x%llx, Wq = %zu", id, lv2_obj::name64(ef.name), - ef.type, ef.pattern.load(), +ef.waiters))); + l_addTreeChild(node, qstr(fmt::format(u8"Event Flag 0x%08x: “%s”, Type: 0x%x, Key: %#llx, Pattern: 0x%llx, Wq: %zu", id, lv2_obj::name64(ef.name), + ef.type, ef.key, ef.pattern.load(), +ef.waiters))); break; } default: { - l_addTreeChild(node, qstr(fmt::format("Unknown object: ID = 0x%08x", id))); + l_addTreeChild(node, qstr(fmt::format("Unknown object 0x%08x", id))); } } }); @@ -283,7 +290,8 @@ void kernel_explorer::Update() idm::select([&](u32 id, lv2_memory_container& container) { lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format("Memory Container: ID = 0x%08x, total size = 0x%x, used = 0x%x", id, container.size, +container.used))); + const u32 used = container.used; + l_addTreeChild(lv2_types.back().node, qstr(fmt::format("Memory Container 0x%08x: Used: 0x%x/0x%x (%0.2f/%0.2f MB)", id, used, container.size, used * 1. / (1024 * 1024), container.size * 1. / (1024 * 1024)))); }); lv2_types.emplace_back(l_addTreeChild(root, "PPU Threads")); @@ -291,7 +299,10 @@ void kernel_explorer::Update() idm::select>([&](u32 id, ppu_thread& ppu) { lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"PPU: ID = 0x%07x “%s”, priority = %d, joiner = %s, state = %s", id, *ppu.ppu_tname.load(), +ppu.prio, ppu.joiner.load(), ppu.state.load()))); + + const auto func = ppu.last_function; + l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"PPU 0x%07x: “%s”, PRIO: %d, Joiner: %s, State: %s, %s func: “%s”", id, *ppu.ppu_tname.load(), +ppu.prio, ppu.joiner.load(), ppu.state.load() + , ppu.current_function ? "In" : "Last", func ? func : ""))); }); lv2_types.emplace_back(l_addTreeChild(root, "SPU Threads")); @@ -299,7 +310,19 @@ void kernel_explorer::Update() idm::select>([&](u32 /*id*/, spu_thread& spu) { lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"SPU: ID = 0x%07x “%s”, state = %s", spu.lv2_id, *spu.spu_tname.load(), spu.state.load()))); + + std::string_view type = "threaded"; + + if (spu.is_isolated) + { + type = "isolated"; + } + else if (spu.offset >= RAW_SPU_BASE_ADDR) + { + type = "raw"; + } + + l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"SPU 0x%07x: “%s”, State: %s, Type: %s", spu.lv2_id, *spu.spu_tname.load(), spu.state.load(), type))); }); lv2_types.emplace_back(l_addTreeChild(root, "SPU Thread Groups")); @@ -307,15 +330,142 @@ void kernel_explorer::Update() idm::select([&](u32 id, lv2_spu_group& tg) { lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"SPU Group: ID = 0x%07x “%s”, status = %s, priority = %d, type = 0x%x", id, tg.name, tg.run_state.load(), +tg.prio, tg.type))); + l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"SPU Group 0x%07x: “%s”, Status = %s, Priority = %d, Type = 0x%x", id, tg.name, tg.run_state.load(), +tg.prio, tg.type))); }); + lv2_types.emplace_back(l_addTreeChild(root, "RSX Contexts")); + + do + { + // Currently a single context is supported at a time + const auto rsx = rsx::get_current_renderer(); + const auto context_info = g_fxo->get(); + + if (!rsx || !context_info) + { + break; + } + + const auto base = context_info->context_base; + + if (!base) + { + break; + } + + lv2_types.back().count++; + const auto tree = l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"RSX Context 0x55555555, Local Size: %u MB, Base Addr: 0x%x, Device Addr: 0x%x", context_info->memory_size >> 20, base, context_info->device_addr))); + + lv2_obj_rec io_tree = l_addTreeChild(tree, "IO-EA Table"); + lv2_obj_rec zc_tree = l_addTreeChild(tree, "Zcull Bindings"); + lv2_obj_rec db_tree = l_addTreeChild(tree, "Display Buffers"); + + decltype(rsx->iomap_table) table; + decltype(rsx->display_buffers) dbs; + decltype(rsx->zculls) zcs; + { + std::lock_guard lock(context_info->mutex); + std::memcpy(&table, &rsx->iomap_table, sizeof(table)); + std::memcpy(&dbs, rsx->display_buffers, sizeof(dbs)); + std::memcpy(&zcs, &rsx->zculls, sizeof(zcs)); + } + + for (u32 i = 0, size_block = 0, first_ea = 0, first_io = 0;;) + { + const auto addr = table.get_addr(i << 20); + + if (addr == umax) + { + if (size_block) + { + // Print block + l_addTreeChild(io_tree.node, qstr(fmt::format("IO: %08x, EA: %08x, Size: %uMB", first_io, first_ea, size_block))); + io_tree.count++; + } + + if (i >= 512u) + { + break; + } + + size_block = 0; + i++; + continue; + } + + const auto old_block_size = size_block++; + + i++; + + if (old_block_size) + { + if (first_ea + (old_block_size << 20) == addr) + { + continue; + } + else + { + // Print last block before we continue to a new one + l_addTreeChild(io_tree.node, qstr(fmt::format("IO: %08x, EA: %08x, Size: %uMB", first_io, first_ea, old_block_size))); + size_block = 1; + first_ea = addr; + first_io = (i - 1) << 20; + io_tree.count++; + continue; + } + } + else + { + first_ea = addr; + first_io = (i - 1) << 20; + } + } + + for (const auto& zc : zcs) + { + if (zc.bound) + { + l_addTreeChild(zc_tree.node, qstr(fmt::format("O: %07x, W: %u, H: %u, Zformat: 0x%x, AAformat: 0x%x, Dir: 0x%x, sFunc: 0x%x, sRef: %02x, sMask: %02x" + , zc.offset, zc.height, zc.width, zc.zFormat, zc.aaFormat, zc.zcullDir, zc.sFunc, zc.sRef, zc.sMask))); + zc_tree.count++; + } + } + + for (const auto& db : dbs) + { + if (db.valid()) + { + l_addTreeChild(db_tree.node, qstr(fmt::format("Offset: %07x, Width: %u, Height: %u, Pitch: %u" + , db.offset, db.height, db.width, db.pitch))); + db_tree.count++; + } + } + + for (auto&& entry : {&io_tree, &zc_tree, &db_tree}) + { + if (entry->node) + { + if (entry->count) + { + // Append object count + entry->node->setText(0, entry->node->text(0) + qstr(fmt::format(" (%zu)", entry->count))); + } + else + { + // Delete node otherwise + delete entry->node; + } + } + } + } + while (0); + lv2_types.emplace_back(l_addTreeChild(root, "File Descriptors")); idm::select([&](u32 id, lv2_fs_object& fo) { lv2_types.back().count++; - l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"FD: ID = 0x%08x “%s”", id, fo.name.data()))); + l_addTreeChild(lv2_types.back().node, qstr(fmt::format(u8"FD %u: “%s”", id, fo.name.data()))); }); for (auto&& entry : lv2_types)