mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-09-08 13:42:53 +02:00
Currently, slowmem is used at any time that memory breakpoints are in use. This commit makes it so that whenever the DBAT gets updated, if the address is overllaping any memchecks, it forces the use of slowmem. This allows to keep fastmem for any other cases and noticably increases performance when using memory breakpoints.
338 lines
7.8 KiB
C++
338 lines
7.8 KiB
C++
// Copyright 2008 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#include "Core/PowerPC/BreakPoints.h"
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/DebugInterface.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/PowerPC/JitCommon/JitBase.h"
|
|
#include "Core/PowerPC/JitCommon/JitCache.h"
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
|
|
bool BreakPoints::IsAddressBreakPoint(u32 address) const
|
|
{
|
|
return std::any_of(m_breakpoints.begin(), m_breakpoints.end(),
|
|
[address](const auto& bp) { return bp.address == address; });
|
|
}
|
|
|
|
bool BreakPoints::IsTempBreakPoint(u32 address) const
|
|
{
|
|
return std::any_of(m_breakpoints.begin(), m_breakpoints.end(), [address](const auto& bp) {
|
|
return bp.address == address && bp.is_temporary;
|
|
});
|
|
}
|
|
|
|
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
|
|
{
|
|
TBreakPointsStr bp_strings;
|
|
for (const TBreakPoint& bp : m_breakpoints)
|
|
{
|
|
if (!bp.is_temporary)
|
|
{
|
|
std::stringstream ss;
|
|
ss << std::hex << bp.address << " " << (bp.is_enabled ? "n" : "");
|
|
bp_strings.push_back(ss.str());
|
|
}
|
|
}
|
|
|
|
return bp_strings;
|
|
}
|
|
|
|
void BreakPoints::AddFromStrings(const TBreakPointsStr& bp_strings)
|
|
{
|
|
for (const std::string& bp_string : bp_strings)
|
|
{
|
|
TBreakPoint bp;
|
|
std::stringstream ss;
|
|
ss << std::hex << bp_string;
|
|
ss >> bp.address;
|
|
bp.is_enabled = bp_string.find('n') != bp_string.npos;
|
|
bp.is_temporary = false;
|
|
Add(bp);
|
|
}
|
|
}
|
|
|
|
void BreakPoints::Add(const TBreakPoint& bp)
|
|
{
|
|
if (!IsAddressBreakPoint(bp.address))
|
|
{
|
|
m_breakpoints.push_back(bp);
|
|
if (g_jit)
|
|
g_jit->GetBlockCache()->InvalidateICache(bp.address, 4, true);
|
|
}
|
|
}
|
|
|
|
void BreakPoints::Add(u32 address, bool temp)
|
|
{
|
|
if (!IsAddressBreakPoint(address)) // only add new addresses
|
|
{
|
|
TBreakPoint bp; // breakpoint settings
|
|
bp.is_enabled = true;
|
|
bp.is_temporary = temp;
|
|
bp.address = address;
|
|
|
|
m_breakpoints.push_back(bp);
|
|
|
|
if (g_jit)
|
|
g_jit->GetBlockCache()->InvalidateICache(address, 4, true);
|
|
}
|
|
}
|
|
|
|
void BreakPoints::Remove(u32 address)
|
|
{
|
|
for (auto i = m_breakpoints.begin(); i != m_breakpoints.end(); ++i)
|
|
{
|
|
if (i->address == address)
|
|
{
|
|
m_breakpoints.erase(i);
|
|
if (g_jit)
|
|
g_jit->GetBlockCache()->InvalidateICache(address, 4, true);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BreakPoints::Clear()
|
|
{
|
|
if (g_jit)
|
|
{
|
|
for (const TBreakPoint& bp : m_breakpoints)
|
|
{
|
|
g_jit->GetBlockCache()->InvalidateICache(bp.address, 4, true);
|
|
}
|
|
}
|
|
|
|
m_breakpoints.clear();
|
|
}
|
|
|
|
void BreakPoints::ClearAllTemporary()
|
|
{
|
|
auto bp = m_breakpoints.begin();
|
|
while (bp != m_breakpoints.end())
|
|
{
|
|
if (bp->is_temporary)
|
|
{
|
|
if (g_jit)
|
|
g_jit->GetBlockCache()->InvalidateICache(bp->address, 4, true);
|
|
bp = m_breakpoints.erase(bp);
|
|
}
|
|
else
|
|
{
|
|
++bp;
|
|
}
|
|
}
|
|
}
|
|
|
|
MemChecks::TMemChecksStr MemChecks::GetStrings() const
|
|
{
|
|
TMemChecksStr mc_strings;
|
|
for (const TMemCheck& mc : m_mem_checks)
|
|
{
|
|
std::stringstream ss;
|
|
ss << std::hex << mc.start_address;
|
|
ss << " " << (mc.is_ranged ? mc.end_address : mc.start_address) << " "
|
|
<< (mc.is_ranged ? "n" : "") << (mc.is_break_on_read ? "r" : "")
|
|
<< (mc.is_break_on_write ? "w" : "") << (mc.log_on_hit ? "l" : "")
|
|
<< (mc.break_on_hit ? "p" : "");
|
|
mc_strings.push_back(ss.str());
|
|
}
|
|
|
|
return mc_strings;
|
|
}
|
|
|
|
void MemChecks::AddFromStrings(const TMemChecksStr& mc_strings)
|
|
{
|
|
for (const std::string& mc_string : mc_strings)
|
|
{
|
|
TMemCheck mc;
|
|
std::stringstream ss;
|
|
ss << std::hex << mc_string;
|
|
ss >> mc.start_address;
|
|
mc.is_ranged = mc_string.find('n') != mc_string.npos;
|
|
mc.is_break_on_read = mc_string.find('r') != mc_string.npos;
|
|
mc.is_break_on_write = mc_string.find('w') != mc_string.npos;
|
|
mc.log_on_hit = mc_string.find('l') != mc_string.npos;
|
|
mc.break_on_hit = mc_string.find('p') != mc_string.npos;
|
|
if (mc.is_ranged)
|
|
ss >> mc.end_address;
|
|
else
|
|
mc.end_address = mc.start_address;
|
|
Add(mc);
|
|
}
|
|
}
|
|
|
|
void MemChecks::Add(const TMemCheck& memory_check)
|
|
{
|
|
if (GetMemCheck(memory_check.start_address) == nullptr)
|
|
{
|
|
bool had_any = HasAny();
|
|
bool lock = Core::PauseAndLock(true);
|
|
m_mem_checks.push_back(memory_check);
|
|
// If this is the first one, clear the JIT cache so it can switch to
|
|
// watchpoint-compatible code.
|
|
if (!had_any && g_jit)
|
|
g_jit->ClearCache();
|
|
PowerPC::DBATUpdated();
|
|
Core::PauseAndLock(false, lock);
|
|
}
|
|
}
|
|
|
|
void MemChecks::Remove(u32 address)
|
|
{
|
|
for (auto i = m_mem_checks.begin(); i != m_mem_checks.end(); ++i)
|
|
{
|
|
if (i->start_address == address)
|
|
{
|
|
bool lock = Core::PauseAndLock(true);
|
|
m_mem_checks.erase(i);
|
|
if (!HasAny() && g_jit)
|
|
g_jit->ClearCache();
|
|
PowerPC::DBATUpdated();
|
|
Core::PauseAndLock(false, lock);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
TMemCheck* MemChecks::GetMemCheck(u32 address)
|
|
{
|
|
for (TMemCheck& mc : m_mem_checks)
|
|
{
|
|
if (mc.is_ranged)
|
|
{
|
|
if (address >= mc.start_address && address <= mc.end_address)
|
|
return &mc;
|
|
}
|
|
else if (mc.start_address == address)
|
|
{
|
|
return &mc;
|
|
}
|
|
}
|
|
|
|
// none found
|
|
return nullptr;
|
|
}
|
|
|
|
bool MemChecks::OverlapsMemcheck(u32 address, u32 length)
|
|
{
|
|
if (!HasAny())
|
|
return false;
|
|
u32 page_end_suffix = length - 1;
|
|
u32 page_end_address = address | page_end_suffix;
|
|
for (TMemCheck memcheck : m_mem_checks)
|
|
{
|
|
if (((memcheck.start_address | page_end_suffix) == page_end_address ||
|
|
(memcheck.end_address | page_end_suffix) == page_end_address) ||
|
|
((memcheck.start_address | page_end_suffix) < page_end_address &&
|
|
(memcheck.end_address | page_end_suffix) > page_end_address))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TMemCheck::Action(DebugInterface* debug_interface, u32 value, u32 addr, bool write, int size,
|
|
u32 pc)
|
|
{
|
|
if ((write && is_break_on_write) || (!write && is_break_on_read))
|
|
{
|
|
if (log_on_hit)
|
|
{
|
|
NOTICE_LOG(MEMMAP, "MBP %08x (%s) %s%i %0*x at %08x (%s)", pc,
|
|
debug_interface->GetDescription(pc).c_str(), write ? "Write" : "Read", size * 8,
|
|
size * 2, value, addr, debug_interface->GetDescription(addr).c_str());
|
|
}
|
|
if (break_on_hit)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Watches::IsAddressWatch(u32 address) const
|
|
{
|
|
return std::any_of(m_watches.begin(), m_watches.end(),
|
|
[address](const auto& watch) { return watch.address == address; });
|
|
}
|
|
|
|
Watches::TWatchesStr Watches::GetStrings() const
|
|
{
|
|
TWatchesStr watch_strings;
|
|
for (const TWatch& watch : m_watches)
|
|
{
|
|
std::stringstream ss;
|
|
ss << std::hex << watch.address << " " << watch.name;
|
|
watch_strings.push_back(ss.str());
|
|
}
|
|
|
|
return watch_strings;
|
|
}
|
|
|
|
void Watches::AddFromStrings(const TWatchesStr& watch_strings)
|
|
{
|
|
for (const std::string& watch_string : watch_strings)
|
|
{
|
|
TWatch watch;
|
|
std::stringstream ss;
|
|
ss << std::hex << watch_string;
|
|
ss >> watch.address;
|
|
ss >> std::ws;
|
|
std::getline(ss, watch.name);
|
|
Add(watch);
|
|
}
|
|
}
|
|
|
|
void Watches::Add(const TWatch& watch)
|
|
{
|
|
if (!IsAddressWatch(watch.address))
|
|
{
|
|
m_watches.push_back(watch);
|
|
}
|
|
}
|
|
|
|
void Watches::Add(u32 address)
|
|
{
|
|
if (!IsAddressWatch(address)) // only add new addresses
|
|
{
|
|
TWatch watch; // watch settings
|
|
watch.is_enabled = true;
|
|
watch.address = address;
|
|
|
|
m_watches.push_back(watch);
|
|
}
|
|
}
|
|
|
|
void Watches::Update(int count, u32 address)
|
|
{
|
|
m_watches.at(count).address = address;
|
|
}
|
|
|
|
void Watches::UpdateName(int count, const std::string name)
|
|
{
|
|
m_watches.at(count).name = name;
|
|
}
|
|
|
|
void Watches::Remove(u32 address)
|
|
{
|
|
for (auto i = m_watches.begin(); i != m_watches.end(); ++i)
|
|
{
|
|
if (i->address == address)
|
|
{
|
|
m_watches.erase(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Watches::Clear()
|
|
{
|
|
m_watches.clear();
|
|
}
|