Merge pull request #6932 from sepalani/debug-patches

DebugInterface: MemoryPatches methods added
This commit is contained in:
Léo Lam 2018-05-22 19:15:51 +02:00 committed by GitHub
commit 0e9255c469
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 365 additions and 81 deletions

View File

@ -9,6 +9,7 @@ add_library(common
Crypto/AES.cpp
Crypto/bn.cpp
Crypto/ec.cpp
Debug/MemoryPatches.cpp
Debug/Watches.cpp
ENetUtil.cpp
File.cpp

View File

@ -60,6 +60,7 @@
<ClInclude Include="Config\Layer.h" />
<ClInclude Include="CPUDetect.h" />
<ClInclude Include="DebugInterface.h" />
<ClInclude Include="Debug\MemoryPatches.h" />
<ClInclude Include="Debug\Watches.h" />
<ClInclude Include="ENetUtil.h" />
<ClInclude Include="Event.h" />
@ -176,6 +177,7 @@
<ClCompile Include="Config\Config.cpp" />
<ClCompile Include="Config\ConfigInfo.cpp" />
<ClCompile Include="Config\Layer.cpp" />
<ClCompile Include="Debug\MemoryPatches.cpp" />
<ClCompile Include="Debug\Watches.cpp" />
<ClCompile Include="ENetUtil.cpp" />
<ClCompile Include="File.cpp" />

View File

@ -269,6 +269,9 @@
<ClInclude Include="Debug\Watches.h">
<Filter>Debug</Filter>
</ClInclude>
<ClInclude Include="Debug\MemoryPatches.h">
<Filter>Debug</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CDUtils.cpp" />
@ -292,7 +295,6 @@
<ClCompile Include="Network.cpp" />
<ClCompile Include="PcapFile.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="QoSSession.h" />
<ClCompile Include="SDCardUtil.cpp" />
<ClCompile Include="SettingsHandler.cpp" />
<ClCompile Include="StringUtil.cpp" />
@ -344,6 +346,10 @@
<ClCompile Include="Debug\Watches.cpp">
<Filter>Debug</Filter>
</ClCompile>
<ClCompile Include="QoSSession.cpp" />
<ClCompile Include="Debug\MemoryPatches.cpp">
<Filter>Debug</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />

View File

@ -0,0 +1,99 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Common/Debug/MemoryPatches.h"
#include <algorithm>
#include <sstream>
namespace Common::Debug
{
MemoryPatch::MemoryPatch(u32 address_, std::vector<u8> value_)
: address(address_), value(value_), is_enabled(State::Enabled)
{
}
MemoryPatch::MemoryPatch(u32 address, u32 value)
: MemoryPatch(address, {static_cast<u8>(value >> 24), static_cast<u8>(value >> 16),
static_cast<u8>(value >> 8), static_cast<u8>(value)})
{
}
MemoryPatches::MemoryPatches() = default;
MemoryPatches::~MemoryPatches() = default;
void MemoryPatches::SetPatch(u32 address, u32 value)
{
const std::size_t index = m_patches.size();
m_patches.emplace_back(address, value);
Patch(index);
}
void MemoryPatches::SetPatch(u32 address, std::vector<u8> value)
{
const std::size_t index = m_patches.size();
m_patches.emplace_back(address, std::move(value));
Patch(index);
}
const std::vector<MemoryPatch>& MemoryPatches::GetPatches() const
{
return m_patches;
}
void MemoryPatches::UnsetPatch(u32 address)
{
const auto it = std::remove_if(m_patches.begin(), m_patches.end(),
[address](const auto& patch) { return patch.address == address; });
if (it == m_patches.end())
return;
const std::size_t size = m_patches.size();
std::size_t index = size - std::distance(it, m_patches.end());
while (index < size)
{
DisablePatch(index);
++index;
}
m_patches.erase(it, m_patches.end());
}
void MemoryPatches::EnablePatch(std::size_t index)
{
if (m_patches[index].is_enabled == MemoryPatch::State::Enabled)
return;
m_patches[index].is_enabled = MemoryPatch::State::Enabled;
Patch(index);
}
void MemoryPatches::DisablePatch(std::size_t index)
{
if (m_patches[index].is_enabled == MemoryPatch::State::Disabled)
return;
m_patches[index].is_enabled = MemoryPatch::State::Disabled;
Patch(index);
}
bool MemoryPatches::HasEnabledPatch(u32 address) const
{
return std::any_of(m_patches.begin(), m_patches.end(), [address](const MemoryPatch& patch) {
return patch.address == address && patch.is_enabled == MemoryPatch::State::Enabled;
});
}
void MemoryPatches::RemovePatch(std::size_t index)
{
DisablePatch(index);
m_patches.erase(m_patches.begin() + index);
}
void MemoryPatches::ClearPatches()
{
const std::size_t size = m_patches.size();
for (std::size_t index = 0; index < size; ++index)
DisablePatch(index);
m_patches.clear();
}
} // namespace Common::Debug

View File

@ -0,0 +1,52 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
namespace Common::Debug
{
struct MemoryPatch
{
enum class State
{
Enabled,
Disabled
};
u32 address;
std::vector<u8> value;
State is_enabled;
MemoryPatch(u32 address, std::vector<u8> value);
MemoryPatch(u32 address, u32 value);
};
class MemoryPatches
{
public:
MemoryPatches();
virtual ~MemoryPatches();
void SetPatch(u32 address, u32 value);
void SetPatch(u32 address, std::vector<u8> value);
const std::vector<MemoryPatch>& GetPatches() const;
void UnsetPatch(u32 address);
void EnablePatch(std::size_t index);
void DisablePatch(std::size_t index);
bool HasEnabledPatch(u32 address) const;
void RemovePatch(std::size_t index);
void ClearPatches();
protected:
virtual void Patch(std::size_t index) = 0;
std::vector<MemoryPatch> m_patches;
};
} // namespace Common::Debug

View File

@ -10,6 +10,7 @@
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Debug/MemoryPatches.h"
#include "Common/Debug/Watches.h"
class DebugInterface
@ -34,6 +35,17 @@ public:
virtual std::vector<std::string> SaveWatchesToStrings() const = 0;
virtual void ClearWatches() = 0;
// Memory Patches
virtual void SetPatch(u32 address, u32 value) = 0;
virtual void SetPatch(u32 address, std::vector<u8> value) = 0;
virtual const std::vector<Common::Debug::MemoryPatch>& GetPatches() const = 0;
virtual void UnsetPatch(u32 address) = 0;
virtual void EnablePatch(std::size_t index) = 0;
virtual void DisablePatch(std::size_t index) = 0;
virtual bool HasEnabledPatch(u32 address) const = 0;
virtual void RemovePatch(std::size_t index) = 0;
virtual void ClearPatches() = 0;
virtual std::string Disassemble(unsigned int /*address*/) { return "NODEBUGGER"; }
virtual std::string GetRawMemoryString(int /*memory*/, unsigned int /*address*/)
{
@ -59,7 +71,6 @@ public:
virtual void SetPC(unsigned int /*address*/) {}
virtual void Step() {}
virtual void RunToBreakpoint() {}
virtual void Patch(unsigned int /*address*/, unsigned int /*value*/) {}
virtual int GetColor(unsigned int /*address*/) { return 0xFFFFFFFF; }
virtual std::string GetDescription(unsigned int /*address*/) = 0;
virtual void Clear() = 0;

View File

@ -7,6 +7,7 @@
#include <cstddef>
#include <string>
#include "Common/Align.h"
#include "Common/GekkoDisassembler.h"
#include "Common/StringUtil.h"
@ -16,6 +17,33 @@
#include "Core/PowerPC/PPCSymbolDB.h"
#include "Core/PowerPC/PowerPC.h"
void PPCPatches::Patch(std::size_t index)
{
auto& patch = m_patches[index];
if (patch.value.empty())
return;
const u32 address = patch.address;
const std::size_t size = patch.value.size();
if (!PowerPC::HostIsRAMAddress(address))
return;
for (u32 offset = 0; offset < size; ++offset)
{
const u8 value = PowerPC::HostRead_U8(address + offset);
PowerPC::HostWrite_U8(patch.value[offset], address + offset);
patch.value[offset] = value;
if (((address + offset) % 4) == 3)
PowerPC::ScheduleInvalidateCacheThreadSafe(Common::AlignDown(address + offset, 4));
}
if (((address + size) % 4) != 0)
{
PowerPC::ScheduleInvalidateCacheThreadSafe(
Common::AlignDown(address + static_cast<u32>(size), 4));
}
}
std::size_t PPCDebugInterface::SetWatch(u32 address, const std::string& name)
{
return m_watches.SetWatch(address, name);
@ -86,6 +114,51 @@ void PPCDebugInterface::ClearWatches()
m_watches.Clear();
}
void PPCDebugInterface::SetPatch(u32 address, u32 value)
{
m_patches.SetPatch(address, value);
}
void PPCDebugInterface::SetPatch(u32 address, std::vector<u8> value)
{
m_patches.SetPatch(address, value);
}
const std::vector<Common::Debug::MemoryPatch>& PPCDebugInterface::GetPatches() const
{
return m_patches.GetPatches();
}
void PPCDebugInterface::UnsetPatch(u32 address)
{
m_patches.UnsetPatch(address);
}
void PPCDebugInterface::EnablePatch(std::size_t index)
{
m_patches.EnablePatch(index);
}
void PPCDebugInterface::DisablePatch(std::size_t index)
{
m_patches.DisablePatch(index);
}
bool PPCDebugInterface::HasEnabledPatch(u32 address) const
{
return m_patches.HasEnabledPatch(address);
}
void PPCDebugInterface::RemovePatch(std::size_t index)
{
m_patches.RemovePatch(index);
}
void PPCDebugInterface::ClearPatches()
{
m_patches.ClearPatches();
}
std::string PPCDebugInterface::Disassemble(unsigned int address)
{
// PowerPC::HostRead_U32 seemed to crash on shutdown
@ -220,12 +293,6 @@ void PPCDebugInterface::ToggleMemCheck(unsigned int address, bool read, bool wri
}
}
void PPCDebugInterface::Patch(unsigned int address, unsigned int value)
{
PowerPC::HostWrite_U32(value, address);
PowerPC::ScheduleInvalidateCacheThreadSafe(address);
}
// =======================================================
// Separate the blocks with colors.
// -------------
@ -275,5 +342,6 @@ void PPCDebugInterface::Clear()
{
ClearAllBreakpoints();
ClearAllMemChecks();
ClearPatches();
ClearWatches();
}

View File

@ -9,6 +9,12 @@
#include "Common/DebugInterface.h"
class PPCPatches : public Common::Debug::MemoryPatches
{
private:
void Patch(std::size_t index) override;
};
// wrapper between disasm control and Dolphin debugger
class PPCDebugInterface final : public DebugInterface
@ -31,6 +37,17 @@ public:
std::vector<std::string> SaveWatchesToStrings() const override;
void ClearWatches() override;
// Memory Patches
void SetPatch(u32 address, u32 value);
void SetPatch(u32 address, std::vector<u8> value);
const std::vector<Common::Debug::MemoryPatch>& GetPatches() const;
void UnsetPatch(u32 address);
void EnablePatch(std::size_t index);
void DisablePatch(std::size_t index);
bool HasEnabledPatch(u32 address) const;
void RemovePatch(std::size_t index);
void ClearPatches();
std::string Disassemble(unsigned int address) override;
std::string GetRawMemoryString(int memory, unsigned int address) override;
int GetInstructionSize(int /*instruction*/) override { return 4; }
@ -57,7 +74,6 @@ public:
void SetPC(unsigned int address) override;
void Step() override {}
void RunToBreakpoint() override;
void Patch(unsigned int address, unsigned int value) override;
int GetColor(unsigned int address) override;
std::string GetDescription(unsigned int address) override;
@ -65,4 +81,5 @@ public:
private:
Common::Debug::Watches m_watches;
PPCPatches m_patches;
};

View File

@ -17,6 +17,11 @@ namespace DSP
{
namespace LLE
{
void DSPPatches::Patch(std::size_t index)
{
PanicAlert("Patch functionality not supported in DSP module.");
}
std::size_t DSPDebugInterface::SetWatch(u32 address, const std::string& name)
{
return m_watches.SetWatch(address, name);
@ -87,6 +92,51 @@ void DSPDebugInterface::ClearWatches()
m_watches.Clear();
}
void DSPDebugInterface::SetPatch(u32 address, u32 value)
{
m_patches.SetPatch(address, value);
}
void DSPDebugInterface::SetPatch(u32 address, std::vector<u8> value)
{
m_patches.SetPatch(address, value);
}
const std::vector<Common::Debug::MemoryPatch>& DSPDebugInterface::GetPatches() const
{
return m_patches.GetPatches();
}
void DSPDebugInterface::UnsetPatch(u32 address)
{
m_patches.UnsetPatch(address);
}
void DSPDebugInterface::EnablePatch(std::size_t index)
{
m_patches.EnablePatch(index);
}
void DSPDebugInterface::DisablePatch(std::size_t index)
{
m_patches.DisablePatch(index);
}
void DSPDebugInterface::RemovePatch(std::size_t index)
{
m_patches.RemovePatch(index);
}
bool DSPDebugInterface::HasEnabledPatch(u32 address) const
{
return m_patches.HasEnabledPatch(address);
}
void DSPDebugInterface::ClearPatches()
{
m_patches.ClearPatches();
}
std::string DSPDebugInterface::Disassemble(unsigned int address)
{
// we'll treat addresses as line numbers.
@ -202,11 +252,6 @@ void DSPDebugInterface::ToggleMemCheck(unsigned int address, bool read, bool wri
PanicAlert("MemCheck functionality not supported in DSP module.");
}
void DSPDebugInterface::Patch(unsigned int address, unsigned int value)
{
PanicAlert("Patch functionality not supported in DSP module.");
}
// =======================================================
// Separate the blocks with colors.
// -------------
@ -264,6 +309,7 @@ void DSPDebugInterface::RunToBreakpoint()
void DSPDebugInterface::Clear()
{
ClearPatches();
ClearWatches();
}
} // namespace LLE

View File

@ -14,6 +14,12 @@ namespace DSP
{
namespace LLE
{
class DSPPatches : public Common::Debug::MemoryPatches
{
private:
void Patch(std::size_t index) override;
};
class DSPDebugInterface final : public DebugInterface
{
public:
@ -34,6 +40,17 @@ public:
std::vector<std::string> SaveWatchesToStrings() const override;
void ClearWatches() override;
// Memory Patches
void SetPatch(u32 address, u32 value);
void SetPatch(u32 address, std::vector<u8> value);
const std::vector<Common::Debug::MemoryPatch>& GetPatches() const;
void UnsetPatch(u32 address);
void EnablePatch(std::size_t index);
void DisablePatch(std::size_t index);
void RemovePatch(std::size_t index);
bool HasEnabledPatch(u32 address) const;
void ClearPatches();
std::string Disassemble(unsigned int address) override;
std::string GetRawMemoryString(int memory, unsigned int address) override;
int GetInstructionSize(int instruction) override { return 1; }
@ -53,7 +70,6 @@ public:
void SetPC(unsigned int address) override;
void Step() override {}
void RunToBreakpoint() override;
void Patch(unsigned int address, unsigned int value) override;
int GetColor(unsigned int address) override;
std::string GetDescription(unsigned int address) override;
@ -61,6 +77,7 @@ public:
private:
Common::Debug::Watches m_watches;
DSPPatches m_patches;
};
} // namespace LLE
} // namespace DSP

View File

@ -549,7 +549,7 @@ void CheatsManager::Update()
{
if (m_watch[i].locked)
{
PowerPC::debug_interface.Patch(m_watch[i].address, m_watch[i].locked_value);
PowerPC::debug_interface.SetPatch(m_watch[i].address, m_watch[i].locked_value);
}
switch (m_watch[i].type)

View File

@ -199,26 +199,8 @@ void CodeViewWidget::SetAddress(u32 address, SetAddressUpdate update)
void CodeViewWidget::ReplaceAddress(u32 address, ReplaceWith replace)
{
auto found = std::find_if(m_repl_list.begin(), m_repl_list.end(),
[address](ReplStruct r) { return r.address == address; });
if (found != m_repl_list.end())
{
PowerPC::debug_interface.WriteExtraMemory(0, found->old_value, address);
m_repl_list.erase(found);
}
else
{
ReplStruct repl;
repl.address = address;
repl.old_value = PowerPC::debug_interface.ReadInstruction(address);
m_repl_list.push_back(repl);
PowerPC::debug_interface.Patch(address, replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);
}
PowerPC::debug_interface.UnsetPatch(address);
PowerPC::debug_interface.SetPatch(address, replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);
Update();
}
@ -261,6 +243,8 @@ void CodeViewWidget::OnContextMenu()
auto* insert_nop_action = AddAction(menu, tr("Insert &nop"), this, &CodeViewWidget::OnInsertNOP);
auto* replace_action =
AddAction(menu, tr("Re&place instruction"), this, &CodeViewWidget::OnReplaceInstruction);
auto* restore_action =
AddAction(menu, tr("Restore instruction"), this, &CodeViewWidget::OnRestoreInstruction);
follow_branch_action->setEnabled(running && GetBranchFromAddress(addr));
@ -271,6 +255,8 @@ void CodeViewWidget::OnContextMenu()
for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
action->setEnabled(has_symbol);
restore_action->setEnabled(running && PowerPC::debug_interface.HasEnabledPatch(addr));
menu->exec(QCursor::pos());
Update();
}
@ -474,11 +460,20 @@ void CodeViewWidget::OnReplaceInstruction()
if (good)
{
PowerPC::debug_interface.Patch(addr, code);
PowerPC::debug_interface.UnsetPatch(addr);
PowerPC::debug_interface.SetPatch(addr, code);
Update();
}
}
void CodeViewWidget::OnRestoreInstruction()
{
const u32 addr = GetContextAddress();
PowerPC::debug_interface.UnsetPatch(addr);
Update();
}
void CodeViewWidget::resizeEvent(QResizeEvent*)
{
Update();

View File

@ -70,14 +70,8 @@ private:
void OnInsertBLR();
void OnInsertNOP();
void OnReplaceInstruction();
void OnRestoreInstruction();
struct ReplStruct
{
u32 address;
u32 old_value;
};
std::vector<ReplStruct> m_repl_list;
bool m_updating = false;
u32 m_address = 0;

View File

@ -45,6 +45,7 @@ enum
IDM_INSERTBLR,
IDM_INSERTNOP,
IDM_ASSEMBLE,
IDM_RESTORE,
IDM_RUNTOHERE,
IDM_JITRESULTS,
IDM_FOLLOWBRANCH,
@ -201,36 +202,10 @@ u32 CCodeView::AddrToBranch(u32 addr)
return 0;
}
void CCodeView::InsertBlrNop(int Blr)
void CCodeView::InsertBlrNop(int blr)
{
// Check if this address has been modified
int find = -1;
for (u32 i = 0; i < m_blrList.size(); i++)
{
if (m_blrList.at(i).address == m_selection)
{
find = i;
break;
}
}
// Save the old value
if (find >= 0)
{
m_debugger->WriteExtraMemory(0, m_blrList.at(find).oldValue, m_selection);
m_blrList.erase(m_blrList.begin() + find);
}
else
{
BlrStruct temp;
temp.address = m_selection;
temp.oldValue = m_debugger->ReadMemory(m_selection);
m_blrList.push_back(temp);
if (Blr == 0)
m_debugger->Patch(m_selection, 0x4e800020);
else
m_debugger->Patch(m_selection, 0x60000000);
}
m_debugger->UnsetPatch(m_selection);
m_debugger->SetPatch(m_selection, (blr == 0) ? 0x4e800020 : 0x60000000);
Refresh();
}
@ -320,13 +295,19 @@ void CCodeView::OnPopupMenu(wxCommandEvent& event)
unsigned long code;
if (dialog.GetValue().ToULong(&code, 0) && code <= std::numeric_limits<u32>::max())
{
m_debugger->Patch(m_selection, code);
m_debugger->UnsetPatch(m_selection);
m_debugger->SetPatch(m_selection, code);
Refresh();
}
}
break;
}
case IDM_RESTORE:
m_debugger->UnsetPatch(m_selection);
Refresh();
break;
case IDM_JITRESULTS:
{
// Propagate back to the parent window and tell it
@ -451,6 +432,8 @@ void CCodeView::OnMouseUpR(wxMouseEvent& event)
menu.Append(IDM_INSERTBLR, _("&Insert blr"))->Enable(Core::IsRunning());
menu.Append(IDM_INSERTNOP, _("Insert &nop"))->Enable(Core::IsRunning());
menu.Append(IDM_ASSEMBLE, _("Re&place instruction"))->Enable(Core::IsRunning());
menu.Append(IDM_RESTORE, _("Restore instruction"))
->Enable(Core::IsRunning() && m_debugger->HasEnabledPatch(m_selection));
// menu.Append(IDM_PATCHALERT, _("Patch alert"))->Enable(Core::IsRunning());
PopupMenu(&menu);
event.Skip();

View File

@ -55,13 +55,6 @@ private:
u32 AddrToBranch(u32 addr);
void OnResize(wxSizeEvent& event);
struct BlrStruct // for IDM_INSERTBLR
{
u32 address;
u32 oldValue;
};
std::vector<BlrStruct> m_blrList;
static constexpr int LEFT_COL_WIDTH = 16;
DebugInterface* m_debugger;