mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Implement CtrlMemorySearch
This commit is contained in:
parent
f5d0542f06
commit
c863876dee
|
@ -962,6 +962,7 @@ set(pcsx2GuiSources
|
|||
gui/Debugger/CtrlDisassemblyView.cpp
|
||||
gui/Debugger/CtrlRegisterList.cpp
|
||||
gui/Debugger/CtrlMemView.cpp
|
||||
gui/Debugger/CtrlMemSearch.cpp
|
||||
gui/Debugger/DebuggerLists.cpp
|
||||
gui/Debugger/DisassemblyDialog.cpp
|
||||
gui/Debugger/DebugEvents.cpp
|
||||
|
@ -1021,6 +1022,7 @@ set(pcsx2GuiHeaders
|
|||
gui/Debugger/BreakpointWindow.h
|
||||
gui/Debugger/CtrlDisassemblyView.h
|
||||
gui/Debugger/CtrlMemView.h
|
||||
gui/Debugger/CtrlMemSearch.h
|
||||
gui/Debugger/CtrlRegisterList.h
|
||||
gui/Debugger/DebugEvents.h
|
||||
gui/Debugger/DebuggerLists.h
|
||||
|
@ -1230,7 +1232,8 @@ set(pcsx2UtilitiesSources
|
|||
|
||||
# Utilities headers
|
||||
set(pcsx2UtilitiesHeaders
|
||||
Utilities/AsciiFile.h)
|
||||
Utilities/AsciiFile.h
|
||||
Utilities/BitCast.h)
|
||||
|
||||
|
||||
# Windows sources
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// An implementation of bit_cast until we get c++20
|
||||
|
||||
template<class T2, class T1>
|
||||
T2 bit_cast(const T1& src)
|
||||
{
|
||||
static_assert(sizeof(T2) == sizeof(T1), "bit_cast: types must be equal size");
|
||||
static_assert(std::is_pod<T1>::value, "bit_cast: source must be POD");
|
||||
static_assert(std::is_pod<T2>::value, "bit_cast: destination must be POD");
|
||||
T2 dst;
|
||||
memcpy(&dst, &src, sizeof(T2));
|
||||
return dst;
|
||||
}
|
|
@ -0,0 +1,501 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "CtrlMemSearch.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "Utilities/BitCast.h"
|
||||
#include "../Patch.h" // Required for SwapEndian :(
|
||||
|
||||
#include "BreakpointWindow.h"
|
||||
#include "DebugEvents.h"
|
||||
#include "DisassemblyDialog.h"
|
||||
#include <wchar.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
wxDEFINE_EVENT(pxEvt_SearchFinished, wxCommandEvent);
|
||||
|
||||
wxBEGIN_EVENT_TABLE(CtrlMemSearch, wxWindow)
|
||||
EVT_SET_FOCUS(CtrlMemSearch::focusEvent)
|
||||
EVT_KILL_FOCUS(CtrlMemSearch::focusEvent)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
enum MemoryViewMenuIdentifiers {
|
||||
ID_MEMVIEW_GOTOINDISASM = 1,
|
||||
ID_MEMVIEW_GOTOADDRESS,
|
||||
ID_MEMVIEW_COPYADDRESS,
|
||||
ID_MEMVIEW_FOLLOWADDRESS,
|
||||
ID_MEMVIEW_DISPLAYVALUE_8,
|
||||
ID_MEMVIEW_DISPLAYVALUE_16,
|
||||
ID_MEMVIEW_DISPLAYVALUE_32,
|
||||
ID_MEMVIEW_DISPLAYVALUE_64,
|
||||
ID_MEMVIEW_DISPLAYVALUE_128,
|
||||
ID_MEMVIEW_COPYVALUE_8,
|
||||
ID_MEMVIEW_COPYVALUE_16,
|
||||
ID_MEMVIEW_COPYVALUE_32,
|
||||
ID_MEMVIEW_COPYVALUE_64,
|
||||
ID_MEMVIEW_COPYVALUE_128,
|
||||
ID_MEMVIEW_ALIGNWINDOW,
|
||||
};
|
||||
|
||||
// Kind of lame, but it works.
|
||||
const wxString wxStringTypes[] =
|
||||
{
|
||||
"Byte",
|
||||
"2 Bytes",
|
||||
"4 Bytes",
|
||||
"8 Bytes",
|
||||
"Float",
|
||||
"Double",
|
||||
"String"};
|
||||
const wxArrayString SearchTypes = wxArrayString(7, wxStringTypes);
|
||||
|
||||
CtrlMemSearch::CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu)
|
||||
: wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
, cpu(_cpu)
|
||||
{
|
||||
|
||||
m_SearchThread.reset(new SearchThread(this, _cpu));
|
||||
m_SearchThread->SetName("Debugger Memory Search");
|
||||
|
||||
Bind(pxEvt_SearchFinished, &CtrlMemSearch::onSearchFinished, this);
|
||||
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
txtSearch = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
|
||||
btnSearch = new wxButton(this, wxID_ANY, L"Search", wxDefaultPosition);
|
||||
|
||||
Bind(wxEVT_BUTTON, &CtrlMemSearch::Search, this, btnSearch->GetId());
|
||||
|
||||
cmbSearchType = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_READONLY);
|
||||
cmbSearchType->Set(SearchTypes);
|
||||
cmbSearchType->SetSelection(0);
|
||||
|
||||
Bind(wxEVT_COMBOBOX, &CtrlMemSearch::onSearchTypeChanged, this, cmbSearchType->GetId());
|
||||
|
||||
chkHexadecimal = new wxCheckBox(this, wxID_ANY, L"Hex");
|
||||
chkHexadecimal->SetValue(true);
|
||||
|
||||
radBigEndian = new wxRadioButton(this, wxID_ANY, L"Big Endian", wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
|
||||
radLittleEndian = new wxRadioButton(this, wxID_ANY, L"Little Endian");
|
||||
radLittleEndian->SetValue(true);
|
||||
|
||||
txtMemoryStart = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
|
||||
txtMemoryStart->SetValue(L"0x0");
|
||||
txtMemoryEnd = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1));
|
||||
if (_cpu->getCpuType() == BREAKPOINT_EE)
|
||||
txtMemoryEnd->SetValue(L"0x200000");
|
||||
else
|
||||
txtMemoryEnd->SetValue(L"0x20000");
|
||||
|
||||
lblSearchHits = new wxStaticText(this, wxID_ANY, L"Hits: 0");
|
||||
lblSearchHitSelected = new wxStaticText(this, wxID_ANY, L"Selected: 0");
|
||||
|
||||
btnNext = new wxButton(this, wxID_ANY, L"Next", wxDefaultPosition);
|
||||
btnNext->Enable(false);
|
||||
Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchNext, this, btnNext->GetId());
|
||||
btnPrev = new wxButton(this, wxID_ANY, L"Prev", wxDefaultPosition);
|
||||
btnPrev->Enable(false);
|
||||
Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchPrev, this, btnPrev->GetId());
|
||||
|
||||
// GUI design
|
||||
|
||||
// Search box and search button
|
||||
wxBoxSizer* searchSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
searchSizer->Add(txtSearch, 0);
|
||||
searchSizer->Add(btnSearch, 0, wxLEFT, 5);
|
||||
|
||||
mainSizer->Add(searchSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
|
||||
|
||||
// Search type combo box and hexadecimal checkbox
|
||||
wxBoxSizer* searchTypeSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
searchTypeSizer->Add(cmbSearchType, 0, wxEXPAND);
|
||||
searchTypeSizer->Add(chkHexadecimal, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
|
||||
mainSizer->Add(searchTypeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
|
||||
|
||||
// Big / Little Endianess radio button
|
||||
wxBoxSizer* endianessSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
endianessSizer->Add(radLittleEndian, 0, wxALIGN_CENTER_VERTICAL);
|
||||
endianessSizer->Add(radBigEndian, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
|
||||
mainSizer->Add(endianessSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
|
||||
|
||||
// Address range type text boxes
|
||||
wxFlexGridSizer* addrRangeSizer = new wxFlexGridSizer(2, 2, 5);
|
||||
|
||||
addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"Start Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
|
||||
addrRangeSizer->Add(txtMemoryStart, 0, wxEXPAND);
|
||||
|
||||
addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"End Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
|
||||
addrRangeSizer->Add(txtMemoryEnd, 0, wxEXPAND);
|
||||
|
||||
mainSizer->Add(addrRangeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
|
||||
|
||||
wxBoxSizer* resultSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
resultSizer->Add(lblSearchHits, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
|
||||
resultSizer->Add(lblSearchHitSelected, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
|
||||
resultSizer->Add(btnPrev, 0, wxRIGHT, 5);
|
||||
resultSizer->Add(btnNext, 0, wxRIGHT, 5);
|
||||
|
||||
mainSizer->Add(resultSizer, 0, wxUP | wxLEFT | wxRIGHT, 5);
|
||||
this->SetSizer(mainSizer);
|
||||
SetDoubleBuffered(true);
|
||||
}
|
||||
|
||||
void CtrlMemSearch::postEvent(wxEventType type, wxString text)
|
||||
{
|
||||
wxCommandEvent event(type, GetId());
|
||||
event.SetEventObject(this);
|
||||
event.SetClientData(cpu);
|
||||
event.SetString(text);
|
||||
wxPostEvent(this, event);
|
||||
}
|
||||
|
||||
void CtrlMemSearch::postEvent(wxEventType type, int value)
|
||||
{
|
||||
wxCommandEvent event(type, GetId());
|
||||
event.SetEventObject(this);
|
||||
event.SetClientData(cpu);
|
||||
event.SetInt(value);
|
||||
wxPostEvent(this, event);
|
||||
}
|
||||
|
||||
// Set up the thread with out UI params
|
||||
// Then execute the thread
|
||||
void CtrlMemSearch::Search(wxCommandEvent& evt)
|
||||
{
|
||||
if (!m_searchResultsMutex.try_lock())
|
||||
{
|
||||
// Uh, if it doesn't lock then means we somehow tried to search while
|
||||
// the search was still running.
|
||||
return;
|
||||
}
|
||||
m_searchResults.clear();
|
||||
m_searchResultsMutex.unlock();
|
||||
|
||||
lblSearchHits->SetLabelText(L"Hits: 0");
|
||||
btnNext->Enable(false);
|
||||
btnPrev->Enable(false);
|
||||
|
||||
u64 startAddress = 0;
|
||||
u64 endAddress = 0;
|
||||
|
||||
if (!txtMemoryStart->GetValue().ToULong(&startAddress, 16))
|
||||
{
|
||||
wxMessageBox(L"Invalid start address", L"Error", wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!txtMemoryEnd->GetValue().ToULong(&endAddress, 16))
|
||||
{
|
||||
wxMessageBox(L"Invalid end address", L"Error", wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
m_SearchThread->m_start = startAddress;
|
||||
m_SearchThread->m_end = endAddress;
|
||||
m_SearchThread->m_type = static_cast<SEARCHTYPE>(cmbSearchType->GetSelection());
|
||||
|
||||
// Prepare the search value for the thread
|
||||
|
||||
wxString searchString = txtSearch->GetValue();
|
||||
if (cmbSearchType->GetSelection() == SEARCHTYPE::STRING)
|
||||
{
|
||||
m_SearchThread->m_value_string = searchString;
|
||||
}
|
||||
else if (chkHexadecimal->IsChecked())
|
||||
{
|
||||
u64 searchValue = 0;
|
||||
if (!searchString.ToULong(&searchValue, 16))
|
||||
{
|
||||
wxMessageBox(L"Invalid hexadecimal value", L"Error", wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(searchValue, SEARCHTYPEBITS[m_SearchThread->m_type]) : searchValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// According to my logic, we end up here if we aren't searching for a string
|
||||
// Aren't in hexadecimal mode.
|
||||
|
||||
// Let's check if we're searching for a signed or unsigned value
|
||||
// The thread will handle the interpretation for us.
|
||||
m_SearchThread->m_signed = txtSearch->GetValue().First('-');
|
||||
|
||||
// Now we can convert the string to a number
|
||||
|
||||
if (cmbSearchType->GetSelection() == SEARCHTYPE::FLOAT || cmbSearchType->GetSelection() == SEARCHTYPE::DOUBLE)
|
||||
{
|
||||
// We're searching for a float or double, so we need to convert the string to a double
|
||||
double searchValue = 0;
|
||||
if (!searchString.ToDouble(&searchValue))
|
||||
{
|
||||
wxMessageBox(L"Invalid floating point value", L"Error", wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_SearchThread->m_type == SEARCHTYPE::FLOAT)
|
||||
{
|
||||
const u32 u32SearchValue = bit_cast<u32, float>(searchValue);
|
||||
m_SearchThread->m_value = u32SearchValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u64 u64SearchValue = bit_cast<u64, double>(searchValue);
|
||||
m_SearchThread->m_value = u64SearchValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're searching for either a BYTE, WORD, DWORD or QWORD
|
||||
u64 searchValue = 0;
|
||||
if (!searchString.ToULong(&searchValue, 10))
|
||||
{
|
||||
wxMessageBox(L"Invalid decimal search value", L"Error", wxOK | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to bitcast here, because the thread already has the correct value type of u64
|
||||
m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(searchValue, SEARCHTYPEBITS[m_SearchThread->m_type]) : searchValue;
|
||||
}
|
||||
}
|
||||
|
||||
m_SearchThread->Start();
|
||||
};
|
||||
|
||||
void CtrlMemSearch::onSearchFinished(wxCommandEvent& evt)
|
||||
{
|
||||
// We're done searching, so let's update the UI
|
||||
|
||||
m_searchResultsMutex.lock();
|
||||
|
||||
lblSearchHits->SetLabelText(wxString::Format(L"Hits: %d", m_searchResults.size()));
|
||||
|
||||
// Enable the buttons only if we have results
|
||||
// -1 indicates we haven't jumped to a result yet
|
||||
m_searchIter = -1;
|
||||
lblSearchHitSelected->SetLabelText(L"Cur: 0");
|
||||
btnNext->Enable(m_searchResults.size() > 0);
|
||||
btnPrev->Enable(m_searchResults.size() > 0);
|
||||
|
||||
m_searchResultsMutex.unlock();
|
||||
|
||||
this->Layout(); // This makes sure the search hit count label doesn't get cut off
|
||||
}
|
||||
|
||||
void CtrlMemSearch::onSearchNext(wxCommandEvent& evt)
|
||||
{
|
||||
m_searchIter++;
|
||||
if (m_searchIter == m_searchResults.size())
|
||||
{
|
||||
m_searchIter = 0;
|
||||
}
|
||||
|
||||
lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1));
|
||||
this->Layout();
|
||||
postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]);
|
||||
}
|
||||
|
||||
void CtrlMemSearch::onSearchPrev(wxCommandEvent& evt)
|
||||
{
|
||||
m_searchIter--;
|
||||
// Unsigned underflow
|
||||
if (m_searchIter > m_searchResults.size())
|
||||
{
|
||||
m_searchIter = m_searchResults.size() - 1;
|
||||
}
|
||||
|
||||
lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1));
|
||||
this->Layout();
|
||||
postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]);
|
||||
}
|
||||
|
||||
void CtrlMemSearch::onSearchTypeChanged(wxCommandEvent& evt)
|
||||
{
|
||||
// Update the search value text box to match the search type
|
||||
switch (cmbSearchType->GetSelection())
|
||||
{
|
||||
case SEARCHTYPE::BYTE:
|
||||
case SEARCHTYPE::WORD:
|
||||
case SEARCHTYPE::DWORD:
|
||||
case SEARCHTYPE::QWORD:
|
||||
radBigEndian->Enable(true);
|
||||
radLittleEndian->Enable(true);
|
||||
chkHexadecimal->Enable(false);
|
||||
break;
|
||||
case SEARCHTYPE::FLOAT:
|
||||
case SEARCHTYPE::DOUBLE:
|
||||
radBigEndian->Enable(false);
|
||||
radLittleEndian->Enable(false);
|
||||
chkHexadecimal->Enable(false);
|
||||
chkHexadecimal->SetValue(false);
|
||||
break;
|
||||
|
||||
case SEARCHTYPE::STRING:
|
||||
radBigEndian->Enable(true);
|
||||
radLittleEndian->Enable(true);
|
||||
chkHexadecimal->Enable(false);
|
||||
chkHexadecimal->SetValue(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -- SearchThread
|
||||
|
||||
// There might be a prettier way to read from the vtlb
|
||||
template <class T>
|
||||
void memSearch(std::vector<u32>& out, u32 start, u32 end, T value, DebugInterface* cpu)
|
||||
{
|
||||
for (u32 addr = start; addr < end; addr += sizeof(T))
|
||||
{
|
||||
if (!cpu->isValidAddress(addr))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
T val = 0;
|
||||
if (sizeof(T) == 1)
|
||||
{
|
||||
val = cpu->read8(addr);
|
||||
}
|
||||
else if (sizeof(T) == 2)
|
||||
{
|
||||
val = cpu->read16(addr);
|
||||
}
|
||||
else if (sizeof(T) == 4)
|
||||
{
|
||||
if (std::is_same<T, float>::value)
|
||||
{
|
||||
float fpVal = bit_cast<float, u32>(value);
|
||||
// Love floating point
|
||||
if (bit_cast<float, u32>(cpu->read32(addr)) < fpVal + 0.001f && bit_cast<float, u32>(cpu->read32(addr)) > fpVal - 0.001f)
|
||||
{
|
||||
out.push_back(addr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
val = cpu->read32(addr);
|
||||
}
|
||||
else if (sizeof(T) == 8)
|
||||
{
|
||||
if (std::is_same<T, double>::value)
|
||||
{
|
||||
double dbVal = bit_cast<double, u64>(value);
|
||||
if (bit_cast<double, u64>(cpu->read64(addr)) < dbVal + 0.001 && bit_cast<double, u64>(cpu->read64(addr)) > dbVal - 0.001)
|
||||
{
|
||||
out.push_back(addr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
val = cpu->read64(addr);
|
||||
}
|
||||
|
||||
if (val == value)
|
||||
{
|
||||
out.push_back(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchThread::ExecuteTaskInThread()
|
||||
{
|
||||
// First, make sure we can lock the mutex
|
||||
if (!m_parent->m_searchResultsMutex.try_lock())
|
||||
{
|
||||
// We couldn't lock the mutex, this thread either didn't unlock, or we've somehow raced
|
||||
// with the main UI thread ?
|
||||
assert("Failed to lock our memory search mutex?");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case SEARCHTYPE::BYTE:
|
||||
if (m_signed)
|
||||
{
|
||||
memSearch<s8>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
else
|
||||
{
|
||||
memSearch<u8>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
break;
|
||||
case SEARCHTYPE::WORD:
|
||||
if (m_signed)
|
||||
{
|
||||
memSearch<s16>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
else
|
||||
{
|
||||
memSearch<u16>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
break;
|
||||
case SEARCHTYPE::DWORD:
|
||||
if (m_signed)
|
||||
{
|
||||
memSearch<s32>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
else
|
||||
{
|
||||
memSearch<u32>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
break;
|
||||
case SEARCHTYPE::QWORD:
|
||||
if (m_signed)
|
||||
{
|
||||
memSearch<s64>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
else
|
||||
{
|
||||
memSearch<u64>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
}
|
||||
break;
|
||||
case SEARCHTYPE::FLOAT:
|
||||
memSearch<float>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
break;
|
||||
case SEARCHTYPE::DOUBLE:
|
||||
memSearch<double>(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu);
|
||||
break;
|
||||
case SEARCHTYPE::STRING:
|
||||
for (u32 addr = m_start; addr < m_end; addr++)
|
||||
{
|
||||
// There is no easy way to do this with the vtable.
|
||||
bool match = true;
|
||||
for (u32 stringIndex = 0; stringIndex < m_value_string.size(); stringIndex++)
|
||||
{
|
||||
if ((char)m_cpu->read8(addr + stringIndex) != m_value_string[stringIndex])
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
m_parent->m_searchResults.push_back(addr);
|
||||
addr += m_value_string.size() - 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_parent->m_searchResultsMutex.unlock();
|
||||
|
||||
wxCommandEvent done(pxEvt_SearchFinished);
|
||||
m_parent->GetEventHandler()->AddPendingEvent(done);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <wx/wx.h>
|
||||
#include "common/PersistentThread.h"
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/DisassemblyManager.h"
|
||||
|
||||
enum SEARCHTYPE
|
||||
{
|
||||
BYTE,
|
||||
WORD,
|
||||
DWORD,
|
||||
QWORD,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
};
|
||||
|
||||
class CtrlMemSearch;
|
||||
|
||||
class SearchThread : public Threading::pxThread
|
||||
{
|
||||
CtrlMemSearch* m_parent;
|
||||
DebugInterface* m_cpu;
|
||||
public:
|
||||
virtual ~SearchThread()
|
||||
{
|
||||
try
|
||||
{
|
||||
pxThread::Cancel();
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
}
|
||||
SearchThread(CtrlMemSearch* parent, DebugInterface* cpu) { m_parent = parent; m_cpu = cpu; };
|
||||
|
||||
SEARCHTYPE m_type;
|
||||
// Only guaranteed to be valid if m_type == STRING
|
||||
std::string m_value_string;
|
||||
u64 m_value;
|
||||
bool m_signed;
|
||||
u64 m_start,m_end;
|
||||
protected:
|
||||
void ExecuteTaskInThread();
|
||||
};
|
||||
|
||||
class CtrlMemSearch : public wxWindow
|
||||
{
|
||||
public:
|
||||
CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu);
|
||||
|
||||
std::vector<u32> m_searchResults;
|
||||
std::mutex m_searchResultsMutex;
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
|
||||
|
||||
private:
|
||||
void setRowSize(int bytesInRow);
|
||||
void postEvent(wxEventType type, wxString text);
|
||||
void postEvent(wxEventType type, int value);
|
||||
void onPopupClick(wxCommandEvent& evt);
|
||||
void focusEvent(wxFocusEvent& evt) { Refresh(); };
|
||||
void onSearchFinished(wxCommandEvent& evt);
|
||||
void onSearchNext(wxCommandEvent& evt);
|
||||
void onSearchPrev(wxCommandEvent& evt);
|
||||
void onSearchTypeChanged(wxCommandEvent& evt);
|
||||
|
||||
void Search(wxCommandEvent& evt);
|
||||
const u8 SEARCHTYPEBITS[8] = {8,16,32,64,32,64,0};
|
||||
|
||||
DebugInterface* cpu;
|
||||
wxFont font, underlineFont;
|
||||
wxTextCtrl* txtSearch;
|
||||
wxButton* btnSearch;
|
||||
wxComboBox* cmbSearchType;
|
||||
wxCheckBox* chkHexadecimal;
|
||||
wxTextCtrl* txtMemoryStart;
|
||||
wxTextCtrl* txtMemoryEnd;
|
||||
wxRadioButton* radBigEndian;
|
||||
wxRadioButton* radLittleEndian;
|
||||
wxStaticText* lblSearchHits;
|
||||
wxStaticText* lblSearchHitSelected;
|
||||
wxButton* btnNext;
|
||||
wxButton* btnPrev;
|
||||
|
||||
u64 m_searchIter = -1;
|
||||
std::unique_ptr<SearchThread> m_SearchThread;
|
||||
wxMenu menu;
|
||||
};
|
|
@ -90,7 +90,10 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
|
|||
disassembly = new CtrlDisassemblyView(this, cpu);
|
||||
registerList = new CtrlRegisterList(leftTabs, cpu);
|
||||
functionList = new wxListBox(leftTabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxBORDER_NONE | wxLB_SORT);
|
||||
memory = new CtrlMemView(bottomTabs, cpu);
|
||||
|
||||
wxPanel* memoryPanel = new wxPanel(bottomTabs);
|
||||
memory = new CtrlMemView(memoryPanel, cpu);
|
||||
memorySearch = new CtrlMemSearch(memoryPanel, cpu);
|
||||
|
||||
// create register list and disassembly section
|
||||
wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
@ -110,8 +113,17 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
|
|||
middleSizer->Add(disassembly, 2, wxEXPAND);
|
||||
mainSizer->Add(middleSizer, 3, wxEXPAND | wxBOTTOM, 3);
|
||||
|
||||
wxBoxSizer *memorySizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
memorySizer->Add(memory, 1, wxEXPAND);
|
||||
|
||||
// Unfortuneately hacky, probably cause I'm bad at wxWidgets
|
||||
// A max width of 360 ensures that the memory view will never be blocked by the memory search
|
||||
memorySearch->SetMaxSize(wxSize(360, -1));
|
||||
memorySizer->Add(memorySearch, 1, wxEXPAND);
|
||||
memoryPanel->SetSizer(memorySizer);
|
||||
|
||||
// create bottom section
|
||||
bottomTabs->AddPage(memory, L"Memory");
|
||||
bottomTabs->AddPage(memoryPanel, L"Memory");
|
||||
|
||||
breakpointList = new BreakpointList(bottomTabs, cpu, disassembly);
|
||||
bottomTabs->AddPage(breakpointList, L"Breakpoints");
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "CtrlDisassemblyView.h"
|
||||
#include "CtrlRegisterList.h"
|
||||
#include "CtrlMemView.h"
|
||||
#include "CtrlMemSearch.h"
|
||||
#include "DebugEvents.h"
|
||||
#include "DebuggerLists.h"
|
||||
#include "gui/MSWstuff.h"
|
||||
|
@ -49,6 +50,7 @@ public:
|
|||
CtrlDisassemblyView* getDisassembly() { return disassembly; };
|
||||
CtrlRegisterList* getRegisterList() { return registerList; };
|
||||
CtrlMemView* getMemoryView() { return memory; };
|
||||
CtrlMemSearch* getMemorySearch() { return memorySearch; };
|
||||
wxNotebook* getBottomTabs() { return bottomTabs; };
|
||||
void update();
|
||||
void showMemoryView() { setBottomTabPage(memory); };
|
||||
|
@ -68,6 +70,7 @@ private:
|
|||
CtrlRegisterList* registerList;
|
||||
wxListBox* functionList;
|
||||
CtrlMemView* memory;
|
||||
CtrlMemSearch* memorySearch;
|
||||
wxNotebook* bottomTabs;
|
||||
wxNotebook* leftTabs;
|
||||
BreakpointList* breakpointList;
|
||||
|
|
|
@ -310,6 +310,7 @@
|
|||
<ClCompile Include="gui\Debugger\BreakpointWindow.cpp" />
|
||||
<ClCompile Include="gui\Debugger\CtrlDisassemblyView.cpp" />
|
||||
<ClCompile Include="gui\Debugger\CtrlMemView.cpp" />
|
||||
<ClCompile Include="gui\Debugger\CtrlMemSearch.cpp" />
|
||||
<ClCompile Include="gui\Debugger\CtrlRegisterList.cpp" />
|
||||
<ClCompile Include="gui\Debugger\DebugEvents.cpp" />
|
||||
<ClCompile Include="gui\Debugger\DebuggerLists.cpp" />
|
||||
|
@ -753,6 +754,7 @@
|
|||
<ClInclude Include="gui\Debugger\BreakpointWindow.h" />
|
||||
<ClInclude Include="gui\Debugger\CtrlDisassemblyView.h" />
|
||||
<ClInclude Include="gui\Debugger\CtrlMemView.h" />
|
||||
<ClInclude Include="gui\Debugger\CtrlMemSearch.h" />
|
||||
<ClInclude Include="gui\Debugger\CtrlRegisterList.h" />
|
||||
<ClInclude Include="gui\Debugger\DebugEvents.h" />
|
||||
<ClInclude Include="gui\Debugger\DebuggerLists.h" />
|
||||
|
|
|
@ -941,6 +941,9 @@
|
|||
<ClCompile Include="gui\Debugger\CtrlMemView.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gui\Debugger\CtrlMemSearch.cpp">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DebugTools\ExpressionParser.cpp">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2037,6 +2040,9 @@
|
|||
<ClInclude Include="gui\Debugger\CtrlMemView.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gui\Debugger\CtrlMemSearch.h">
|
||||
<Filter>AppHost\Debugger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DebugTools\ExpressionParser.h">
|
||||
<Filter>System\Ps2\Debug</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Reference in New Issue