Skip to content

Commit

Permalink
Debugger: Track memory allocations and writes.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Feb 15, 2021
1 parent 9ead436 commit ca2f7df
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 3 deletions.
272 changes: 271 additions & 1 deletion Core/Debugger/MemBlockInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MemBlockInfo> &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<MemBlockInfo> &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<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size) {
std::vector<MemBlockInfo> 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);
}
22 changes: 20 additions & 2 deletions Core/Debugger/MemBlockInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,36 @@

#include <cstdint>
#include <string>
#include <vector>
#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<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size);

void MemBlockInfoInit();
void MemBlockInfoShutdown();
void MemBlockInfoDoState(PointerWrap &p);
4 changes: 4 additions & 0 deletions Core/HLE/sceKernelMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -472,6 +473,8 @@ void __KernelMemoryDoState(PointerWrap &p)
if (s >= 2) {
Do(p, tlsplThreadEndChecks);
}

MemBlockInfoDoState(p);
}

void __KernelMemoryShutdown()
Expand All @@ -487,6 +490,7 @@ void __KernelMemoryShutdown()
#endif
kernelMemory.Shutdown();
tlsplThreadEndChecks.clear();
MemBlockInfoShutdown();
}

enum SceKernelFplAttr
Expand Down

0 comments on commit ca2f7df

Please sign in to comment.