diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index 09c5d2749e..14180ec046 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -15,20 +15,290 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/Log.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/MemBlockInfo.h" #include "Core/MIPS/MIPS.h" +class MemSlabMap { +public: + MemSlabMap(); + ~MemSlabMap(); + + bool Mark(uint32_t addr, uint32_t size, uint32_t pc, bool allocated, const std::string &tag); + bool Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector &results); + void Reset(); + void DoState(PointerWrap &p); + +private: + struct Slab { + uint32_t start; + uint32_t size; + uint32_t pc; + bool allocated; + std::string tag; + Slab *prev; + Slab *next; + + void DoState(PointerWrap &p); + }; + + Slab *FindSlab(uint32_t addr); + void Clear(); + // Returns the new slab after size. + Slab *Split(Slab *slab, uint32_t size); + void MergeAdjacent(Slab *slab); + bool Same(const Slab *a, const Slab *b) const; + void Merge(Slab *a, Slab *b); + + Slab *first_ = nullptr; +}; + +static MemSlabMap allocMap; +static MemSlabMap suballocMap; +static MemSlabMap writeMap; + +MemSlabMap::MemSlabMap() { + Reset(); +} + +MemSlabMap::~MemSlabMap() { + Clear(); +} + +bool MemSlabMap::Mark(uint32_t addr, uint32_t size, uint32_t pc, bool allocated, const std::string &tag) { + uint32_t end = addr + size; + Slab *slab = FindSlab(addr); + Slab *firstMatch = nullptr; + while (slab != nullptr && slab->start < end) { + if (slab->start < addr) + slab = Split(slab, addr - slab->start); + // Don't replace slab, the return is the after part. + if (slab->start + slab->size > end) { + Split(slab, end - slab->start); + } + + slab->allocated = allocated; + if (pc != 0) + slab->pc = pc; + if (!tag.empty()) + slab->tag = tag; + + // Move on to the next one. + if (firstMatch != nullptr) + firstMatch = slab; + slab = slab->next; + } + + if (firstMatch != nullptr) { + // This will merge all those blocks to one. + MergeAdjacent(firstMatch); + return true; + } + return false; +} + +bool MemSlabMap::Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector &results) { + uint32_t end = addr + size; + Slab *slab = FindSlab(addr); + bool found = false; + while (slab != nullptr && slab->start < end) { + results.push_back({ flags, slab->start, slab->size, slab->pc, slab->tag, slab->allocated }); + found = true; + slab = slab->next; + } + return found; +} + +void MemSlabMap::Reset() { + Clear(); + + first_ = new Slab{ 0, UINT_MAX, 0, false, "", nullptr, nullptr }; +} + +void MemSlabMap::DoState(PointerWrap &p) { + auto s = p.Section("MemSlabMap", 1); + if (!s) + return; + + int count = 0; + if (p.mode == p.MODE_READ) { + Clear(); + Do(p, count); + + first_ = new Slab(); + first_->DoState(p); + first_->prev = nullptr; + first_->next = nullptr; + --count; + + Slab *slab = first_; + for (int i = 0; i < count; ++i) { + slab->next = new Slab(); + slab->DoState(p); + + slab->next->prev = slab; + slab = slab->next; + } + } else { + for (Slab *slab = first_; slab != nullptr; slab = slab->next) + ++count; + Do(p, count); + + first_->DoState(p); + --count; + + Slab *slab = first_; + for (int i = 0; i < count; ++i) { + slab->next->DoState(p); + slab = slab->next; + } + } +} + +void MemSlabMap::Slab::DoState(PointerWrap &p) { + auto s = p.Section("MemSlabMapSlab", 1); + if (!s) + return; + + Do(p, start); + Do(p, size); + Do(p, pc); + Do(p, allocated); + Do(p, tag); +} + +void MemSlabMap::Clear() { + Slab *s = first_; + while (s != nullptr) { + Slab *next = s->next; + delete s; + s = next; + } + first_ = nullptr; +} + +MemSlabMap::Slab *MemSlabMap::FindSlab(uint32_t addr) { + Slab *slab = first_; + while (slab != nullptr && slab->start <= addr) { + if (slab->start + slab->size > addr) + return slab; + slab = slab->next; + } + return nullptr; +} + +MemSlabMap::Slab *MemSlabMap::Split(Slab *slab, uint32_t size) { + uint32_t nextStart = slab->start + size; + uint32_t nextSize = slab->size - size; + Slab *next = new Slab{ nextStart, nextSize, slab->pc, slab->allocated, slab->tag, slab, slab->next }; + slab->next = next; + if (next->next) + next->next->prev = next; + + slab->size = size; + return next; +} + +void MemSlabMap::MergeAdjacent(Slab *slab) { + while (slab->next != nullptr && Same(slab, slab->next)) { + Merge(slab, slab->next); + } + while (slab->prev != nullptr && Same(slab, slab->prev)) { + Merge(slab, slab->prev); + } +} + +bool MemSlabMap::Same(const Slab *a, const Slab *b) const { + if (a->allocated != b->allocated) + return false; + if (a->pc != b->pc) + return false; + if (a->tag != b->tag) + return false; + return true; +} + +void MemSlabMap::Merge(Slab *a, Slab *b) { + if (a->next == b) { + _assert_(a->start + a->size == b->start); + a->next = b->next; + + if (a->next) + a->next->prev = a; + } else if (a->prev == b) { + _assert_(b->start + b->size == a->start); + a->start = b->start; + a->prev = b->prev; + + if (a->prev) + a->prev->next = a; + else if (first_ == b) + first_ = a; + } else { + _assert_(false); + } + a->size += b->size; + delete b; +} + void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const std::string &tag) { NotifyMemInfoPC(flags, start, size, currentMIPS->pc, tag); } void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_t pc, const std::string &tag) { - // TODO + if (size == 0) { + return; + } + // Clear the uncached and kernel bits. + start &= ~0xC0000000; + if (flags & MemBlockFlags::ALLOC) { + allocMap.Mark(start, size, pc, true, tag); + } else if (flags & MemBlockFlags::FREE) { + // Maintain the previous allocation tag for debugging. + allocMap.Mark(start, size, 0, false, ""); + suballocMap.Mark(start, size, 0, false, ""); + } + if (flags & MemBlockFlags::SUB_ALLOC) { + suballocMap.Mark(start, size, pc, true, tag); + } else if (flags & MemBlockFlags::SUB_FREE) { + // Maintain the previous allocation tag for debugging. + suballocMap.Mark(start, size, 0, false, ""); + } if (flags & MemBlockFlags::WRITE) { CBreakPoints::ExecMemCheck(start, true, size, pc, tag); + writeMap.Mark(start, size, pc, true, tag); } else if (flags & MemBlockFlags::READ) { CBreakPoints::ExecMemCheck(start, false, size, pc, tag); } } + +std::vector FindMemInfo(uint32_t start, uint32_t size) { + std::vector results; + allocMap.Find(MemBlockFlags::ALLOC, start, size, results); + suballocMap.Find(MemBlockFlags::SUB_ALLOC, start, size, results); + writeMap.Find(MemBlockFlags::WRITE, start, size, results); + return results; +} + +void MemBlockInfoInit() { +} + +void MemBlockInfoShutdown() { + allocMap.Reset(); + suballocMap.Reset(); + writeMap.Reset(); +} + +void MemBlockInfoDoState(PointerWrap &p) { + auto s = p.Section("MemBlockInfo", 0, 1); + if (!s) + return; + + allocMap.DoState(p); + suballocMap.DoState(p); + writeMap.DoState(p); +} diff --git a/Core/Debugger/MemBlockInfo.h b/Core/Debugger/MemBlockInfo.h index a150b2f272..573b6701f5 100644 --- a/Core/Debugger/MemBlockInfo.h +++ b/Core/Debugger/MemBlockInfo.h @@ -19,18 +19,36 @@ #include #include +#include #include "Common/Common.h" +class PointerWrap; + enum class MemBlockFlags { - FREE = 0, ALLOC = 1, SUB_ALLOC = 2, WRITE = 4, // Not actually logged. READ = 8, - SUB_FREE = 16, + FREE = 16, + SUB_FREE = 32, }; ENUM_CLASS_BITOPS(MemBlockFlags); +struct MemBlockInfo { + MemBlockFlags flags; + uint32_t start; + uint32_t size; + uint32_t pc; + std::string tag; + bool allocated; +}; + void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const std::string &tag); void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_t pc, const std::string &tag); + +std::vector FindMemInfo(uint32_t start, uint32_t size); + +void MemBlockInfoInit(); +void MemBlockInfoShutdown(); +void MemBlockInfoDoState(PointerWrap &p); diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 50439e5d2b..7358dd3ffc 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -427,6 +427,7 @@ void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelMemoryInit() { + MemBlockInfoInit(); kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false); userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false); Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase()); @@ -472,6 +473,8 @@ void __KernelMemoryDoState(PointerWrap &p) if (s >= 2) { Do(p, tlsplThreadEndChecks); } + + MemBlockInfoDoState(p); } void __KernelMemoryShutdown() @@ -487,6 +490,7 @@ void __KernelMemoryShutdown() #endif kernelMemory.Shutdown(); tlsplThreadEndChecks.clear(); + MemBlockInfoShutdown(); } enum SceKernelFplAttr