From fc133f49944dbc1f74cddd734fb933e351f6bb5c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Sep 2023 19:03:57 -0700 Subject: [PATCH] Debugger: Use a thread to flush meminfo. --- Core/Debugger/MemBlockInfo.cpp | 103 ++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index f66db106741f..9daa39d4faee 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -17,8 +17,10 @@ #include #include +#include #include #include +#include #include "Common/Log.h" #include "Common/Serialize/Serializer.h" @@ -84,7 +86,9 @@ struct PendingNotifyMem { char tag[128]; }; -static constexpr size_t MAX_PENDING_NOTIFIES = 512; +// 160 KB. +static constexpr size_t MAX_PENDING_NOTIFIES = 1024; +static constexpr size_t MAX_PENDING_NOTIFIES_THREAD = 1000; static MemSlabMap allocMap; static MemSlabMap suballocMap; static MemSlabMap writeMap; @@ -94,9 +98,17 @@ static std::atomic pendingNotifyMinAddr1; static std::atomic pendingNotifyMaxAddr1; static std::atomic pendingNotifyMinAddr2; static std::atomic pendingNotifyMaxAddr2; -static std::mutex pendingMutex; +// To prevent deadlocks, acquire Read before Write if you're going to acquire both. +static std::mutex pendingWriteMutex; +static std::mutex pendingReadMutex; static int detailedOverride; +static std::thread flushThread; +static std::atomic flushThreadRunning; +static std::atomic flushThreadPending; +static std::mutex flushLock; +static std::condition_variable flushCond; + MemSlabMap::MemSlabMap() { Reset(); } @@ -373,8 +385,22 @@ void MemSlabMap::FillHeads(Slab *slab) { size_t FormatMemWriteTagAtNoFlush(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size); void FlushPendingMemInfo() { - std::lock_guard guard(pendingMutex); - for (const auto &info : pendingNotifies) { + // This lock prevents us from another thread reading while we're busy flushing. + std::lock_guard guard(pendingReadMutex); + std::vector thisBatch; + { + std::lock_guard guard(pendingWriteMutex); + thisBatch = std::move(pendingNotifies); + pendingNotifies.clear(); + pendingNotifies.reserve(MAX_PENDING_NOTIFIES); + + pendingNotifyMinAddr1 = 0xFFFFFFFF; + pendingNotifyMaxAddr1 = 0; + pendingNotifyMinAddr2 = 0xFFFFFFFF; + pendingNotifyMaxAddr2 = 0; + } + + for (const auto &info : thisBatch) { if (info.copySrc != 0) { char tagData[128]; size_t tagSize = FormatMemWriteTagAtNoFlush(tagData, sizeof(tagData), info.tag, info.copySrc, info.size); @@ -402,11 +428,6 @@ void FlushPendingMemInfo() { writeMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag); } } - pendingNotifies.clear(); - pendingNotifyMinAddr1 = 0xFFFFFFFF; - pendingNotifyMaxAddr1 = 0; - pendingNotifyMinAddr2 = 0xFFFFFFFF; - pendingNotifyMaxAddr2 = 0; } static inline uint32_t NormalizeAddress(uint32_t addr) { @@ -465,7 +486,7 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_ memcpy(info.tag, tagStr, copyLength); info.tag[copyLength] = 0; - std::lock_guard guard(pendingMutex); + std::lock_guard guard(pendingWriteMutex); // Sometimes we get duplicates, quickly check. if (!MergeRecentMemInfo(info, copyLength)) { if (start < 0x08000000) { @@ -477,11 +498,15 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_ } pendingNotifies.push_back(info); } - needFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES; + needFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES_THREAD; } if (needFlush) { - FlushPendingMemInfo(); + { + std::lock_guard guard(flushLock); + flushThreadPending = true; + } + flushCond.notify_one(); } if (!(flags & MemBlockFlags::SKIP_MEMCHECK)) { @@ -501,6 +526,7 @@ void NotifyMemInfoCopy(uint32_t destPtr, uint32_t srcPtr, uint32_t size, const c if (size == 0) return; + bool needsFlush = false; if (CBreakPoints::HasMemChecks()) { // This will cause a flush, but it's needed to trigger memchecks with proper data. char tagData[128]; @@ -519,7 +545,7 @@ void NotifyMemInfoCopy(uint32_t destPtr, uint32_t srcPtr, uint32_t size, const c // Store the prefix for now. The correct tag will be calculated on flush. truncate_cpy(info.tag, prefix); - std::lock_guard guard(pendingMutex); + std::lock_guard guard(pendingWriteMutex); if (destPtr < 0x08000000) { pendingNotifyMinAddr1 = std::min(pendingNotifyMinAddr1.load(), destPtr); pendingNotifyMaxAddr1 = std::max(pendingNotifyMaxAddr1.load(), destPtr + size); @@ -528,10 +554,15 @@ void NotifyMemInfoCopy(uint32_t destPtr, uint32_t srcPtr, uint32_t size, const c pendingNotifyMaxAddr2 = std::max(pendingNotifyMaxAddr2.load(), destPtr + size); } pendingNotifies.push_back(info); + needsFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES_THREAD; } - if (pendingNotifies.size() > MAX_PENDING_NOTIFIES) { - FlushPendingMemInfo(); + if (needsFlush) { + { + std::lock_guard guard(flushLock); + flushThreadPending = true; + } + flushCond.notify_one(); } } @@ -630,22 +661,50 @@ size_t FormatMemWriteTagAtNoFlush(char *buf, size_t sz, const char *prefix, uint return snprintf(buf, sz, "%s%08x_size_%08x", prefix, start, size); } +static void FlushMemInfoThread() { + while (flushThreadRunning.load()) { + flushThreadPending = false; + FlushPendingMemInfo(); + + std::unique_lock guard(flushLock); + flushCond.wait(guard, [] { + return flushThreadPending.load(); + }); + } +} + void MemBlockInfoInit() { - std::lock_guard guard(pendingMutex); + std::lock_guard guard(pendingReadMutex); + std::lock_guard guardW(pendingWriteMutex); pendingNotifies.reserve(MAX_PENDING_NOTIFIES); pendingNotifyMinAddr1 = 0xFFFFFFFF; pendingNotifyMaxAddr1 = 0; pendingNotifyMinAddr2 = 0xFFFFFFFF; pendingNotifyMaxAddr2 = 0; + + flushThreadRunning = true; + flushThreadPending = false; + flushThread = std::thread(&FlushMemInfoThread); } void MemBlockInfoShutdown() { - std::lock_guard guard(pendingMutex); - allocMap.Reset(); - suballocMap.Reset(); - writeMap.Reset(); - textureMap.Reset(); - pendingNotifies.clear(); + { + std::lock_guard guard(pendingReadMutex); + std::lock_guard guardW(pendingWriteMutex); + allocMap.Reset(); + suballocMap.Reset(); + writeMap.Reset(); + textureMap.Reset(); + pendingNotifies.clear(); + } + + if (flushThreadRunning.load()) { + std::lock_guard guard(flushLock); + flushThreadRunning = false; + flushThreadPending = true; + } + flushCond.notify_one(); + flushThread.join(); } void MemBlockInfoDoState(PointerWrap &p) {