// Copyright 2008 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include "Core/PowerPC/BreakPoints.h" #include #include #include #include "Common/CommonTypes.h" #include "Common/DebugInterface.h" #include "Core/PowerPC/JitCommon/JitBase.h" #include "Core/PowerPC/JitCommon/JitCache.h" bool BreakPoints::IsAddressBreakPoint(u32 address) const { for (const TBreakPoint& bp : m_breakpoints) if (bp.address == address) return true; return false; } bool BreakPoints::IsTempBreakPoint(u32 address) const { for (const TBreakPoint& bp : m_breakpoints) if (bp.address == address && bp.is_temporary) return true; return false; } 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) { bool had_any = HasAny(); if (GetMemCheck(memory_check.start_address) == nullptr) 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->GetBlockCache()->SchedulateClearCacheThreadSafe(); } void MemChecks::Remove(u32 address) { for (auto i = m_mem_checks.begin(); i != m_mem_checks.end(); ++i) { if (i->start_address == address) { m_mem_checks.erase(i); if (!HasAny() && g_jit) g_jit->GetBlockCache()->SchedulateClearCacheThreadSafe(); 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 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 { for (const TWatch& watch : m_watches) if (watch.address == address) return true; return false; } 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(); }