mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-20 17:59:08 +02:00
Debugger: Use a thread to flush meminfo.
This commit is contained in:
1 parent
b0da32f41f
commit
fc133f4994
1 file changed
+81
-22
@@ -17,8 +17,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#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<uint32_t> pendingNotifyMinAddr1;
|
||||
static std::atomic<uint32_t> pendingNotifyMaxAddr1;
|
||||
static std::atomic<uint32_t> pendingNotifyMinAddr2;
|
||||
static std::atomic<uint32_t> 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<bool> flushThreadRunning;
|
||||
static std::atomic<bool> 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<std::mutex> guard(pendingMutex);
|
||||
for (const auto &info : pendingNotifies) {
|
||||
// This lock prevents us from another thread reading while we're busy flushing.
|
||||
std::lock_guard<std::mutex> guard(pendingReadMutex);
|
||||
std::vector<PendingNotifyMem> thisBatch;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> guard(pendingMutex);
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> guard(pendingMutex);
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> guard(flushLock);
|
||||
flushCond.wait(guard, [] {
|
||||
return flushThreadPending.load();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MemBlockInfoInit() {
|
||||
std::lock_guard<std::mutex> guard(pendingMutex);
|
||||
std::lock_guard<std::mutex> guard(pendingReadMutex);
|
||||
std::lock_guard<std::mutex> 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<std::mutex> guard(pendingMutex);
|
||||
allocMap.Reset();
|
||||
suballocMap.Reset();
|
||||
writeMap.Reset();
|
||||
textureMap.Reset();
|
||||
pendingNotifies.clear();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(pendingReadMutex);
|
||||
std::lock_guard<std::mutex> guardW(pendingWriteMutex);
|
||||
allocMap.Reset();
|
||||
suballocMap.Reset();
|
||||
writeMap.Reset();
|
||||
textureMap.Reset();
|
||||
pendingNotifies.clear();
|
||||
}
|
||||
|
||||
if (flushThreadRunning.load()) {
|
||||
std::lock_guard<std::mutex> guard(flushLock);
|
||||
flushThreadRunning = false;
|
||||
flushThreadPending = true;
|
||||
}
|
||||
flushCond.notify_one();
|
||||
flushThread.join();
|
||||
}
|
||||
|
||||
void MemBlockInfoDoState(PointerWrap &p) {
|
||||
|
||||
Reference in new issue
Block a user