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/CtrlDisassemblyView.cpp
|
||||||
gui/Debugger/CtrlRegisterList.cpp
|
gui/Debugger/CtrlRegisterList.cpp
|
||||||
gui/Debugger/CtrlMemView.cpp
|
gui/Debugger/CtrlMemView.cpp
|
||||||
|
gui/Debugger/CtrlMemSearch.cpp
|
||||||
gui/Debugger/DebuggerLists.cpp
|
gui/Debugger/DebuggerLists.cpp
|
||||||
gui/Debugger/DisassemblyDialog.cpp
|
gui/Debugger/DisassemblyDialog.cpp
|
||||||
gui/Debugger/DebugEvents.cpp
|
gui/Debugger/DebugEvents.cpp
|
||||||
|
@ -1021,6 +1022,7 @@ set(pcsx2GuiHeaders
|
||||||
gui/Debugger/BreakpointWindow.h
|
gui/Debugger/BreakpointWindow.h
|
||||||
gui/Debugger/CtrlDisassemblyView.h
|
gui/Debugger/CtrlDisassemblyView.h
|
||||||
gui/Debugger/CtrlMemView.h
|
gui/Debugger/CtrlMemView.h
|
||||||
|
gui/Debugger/CtrlMemSearch.h
|
||||||
gui/Debugger/CtrlRegisterList.h
|
gui/Debugger/CtrlRegisterList.h
|
||||||
gui/Debugger/DebugEvents.h
|
gui/Debugger/DebugEvents.h
|
||||||
gui/Debugger/DebuggerLists.h
|
gui/Debugger/DebuggerLists.h
|
||||||
|
@ -1230,7 +1232,8 @@ set(pcsx2UtilitiesSources
|
||||||
|
|
||||||
# Utilities headers
|
# Utilities headers
|
||||||
set(pcsx2UtilitiesHeaders
|
set(pcsx2UtilitiesHeaders
|
||||||
Utilities/AsciiFile.h)
|
Utilities/AsciiFile.h
|
||||||
|
Utilities/BitCast.h)
|
||||||
|
|
||||||
|
|
||||||
# Windows sources
|
# 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);
|
disassembly = new CtrlDisassemblyView(this, cpu);
|
||||||
registerList = new CtrlRegisterList(leftTabs, cpu);
|
registerList = new CtrlRegisterList(leftTabs, cpu);
|
||||||
functionList = new wxListBox(leftTabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxBORDER_NONE | wxLB_SORT);
|
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
|
// create register list and disassembly section
|
||||||
wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL);
|
wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
@ -110,8 +113,17 @@ CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu)
|
||||||
middleSizer->Add(disassembly, 2, wxEXPAND);
|
middleSizer->Add(disassembly, 2, wxEXPAND);
|
||||||
mainSizer->Add(middleSizer, 3, wxEXPAND | wxBOTTOM, 3);
|
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
|
// create bottom section
|
||||||
bottomTabs->AddPage(memory, L"Memory");
|
bottomTabs->AddPage(memoryPanel, L"Memory");
|
||||||
|
|
||||||
breakpointList = new BreakpointList(bottomTabs, cpu, disassembly);
|
breakpointList = new BreakpointList(bottomTabs, cpu, disassembly);
|
||||||
bottomTabs->AddPage(breakpointList, L"Breakpoints");
|
bottomTabs->AddPage(breakpointList, L"Breakpoints");
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "CtrlDisassemblyView.h"
|
#include "CtrlDisassemblyView.h"
|
||||||
#include "CtrlRegisterList.h"
|
#include "CtrlRegisterList.h"
|
||||||
#include "CtrlMemView.h"
|
#include "CtrlMemView.h"
|
||||||
|
#include "CtrlMemSearch.h"
|
||||||
#include "DebugEvents.h"
|
#include "DebugEvents.h"
|
||||||
#include "DebuggerLists.h"
|
#include "DebuggerLists.h"
|
||||||
#include "gui/MSWstuff.h"
|
#include "gui/MSWstuff.h"
|
||||||
|
@ -49,6 +50,7 @@ public:
|
||||||
CtrlDisassemblyView* getDisassembly() { return disassembly; };
|
CtrlDisassemblyView* getDisassembly() { return disassembly; };
|
||||||
CtrlRegisterList* getRegisterList() { return registerList; };
|
CtrlRegisterList* getRegisterList() { return registerList; };
|
||||||
CtrlMemView* getMemoryView() { return memory; };
|
CtrlMemView* getMemoryView() { return memory; };
|
||||||
|
CtrlMemSearch* getMemorySearch() { return memorySearch; };
|
||||||
wxNotebook* getBottomTabs() { return bottomTabs; };
|
wxNotebook* getBottomTabs() { return bottomTabs; };
|
||||||
void update();
|
void update();
|
||||||
void showMemoryView() { setBottomTabPage(memory); };
|
void showMemoryView() { setBottomTabPage(memory); };
|
||||||
|
@ -68,6 +70,7 @@ private:
|
||||||
CtrlRegisterList* registerList;
|
CtrlRegisterList* registerList;
|
||||||
wxListBox* functionList;
|
wxListBox* functionList;
|
||||||
CtrlMemView* memory;
|
CtrlMemView* memory;
|
||||||
|
CtrlMemSearch* memorySearch;
|
||||||
wxNotebook* bottomTabs;
|
wxNotebook* bottomTabs;
|
||||||
wxNotebook* leftTabs;
|
wxNotebook* leftTabs;
|
||||||
BreakpointList* breakpointList;
|
BreakpointList* breakpointList;
|
||||||
|
|
|
@ -310,6 +310,7 @@
|
||||||
<ClCompile Include="gui\Debugger\BreakpointWindow.cpp" />
|
<ClCompile Include="gui\Debugger\BreakpointWindow.cpp" />
|
||||||
<ClCompile Include="gui\Debugger\CtrlDisassemblyView.cpp" />
|
<ClCompile Include="gui\Debugger\CtrlDisassemblyView.cpp" />
|
||||||
<ClCompile Include="gui\Debugger\CtrlMemView.cpp" />
|
<ClCompile Include="gui\Debugger\CtrlMemView.cpp" />
|
||||||
|
<ClCompile Include="gui\Debugger\CtrlMemSearch.cpp" />
|
||||||
<ClCompile Include="gui\Debugger\CtrlRegisterList.cpp" />
|
<ClCompile Include="gui\Debugger\CtrlRegisterList.cpp" />
|
||||||
<ClCompile Include="gui\Debugger\DebugEvents.cpp" />
|
<ClCompile Include="gui\Debugger\DebugEvents.cpp" />
|
||||||
<ClCompile Include="gui\Debugger\DebuggerLists.cpp" />
|
<ClCompile Include="gui\Debugger\DebuggerLists.cpp" />
|
||||||
|
@ -753,6 +754,7 @@
|
||||||
<ClInclude Include="gui\Debugger\BreakpointWindow.h" />
|
<ClInclude Include="gui\Debugger\BreakpointWindow.h" />
|
||||||
<ClInclude Include="gui\Debugger\CtrlDisassemblyView.h" />
|
<ClInclude Include="gui\Debugger\CtrlDisassemblyView.h" />
|
||||||
<ClInclude Include="gui\Debugger\CtrlMemView.h" />
|
<ClInclude Include="gui\Debugger\CtrlMemView.h" />
|
||||||
|
<ClInclude Include="gui\Debugger\CtrlMemSearch.h" />
|
||||||
<ClInclude Include="gui\Debugger\CtrlRegisterList.h" />
|
<ClInclude Include="gui\Debugger\CtrlRegisterList.h" />
|
||||||
<ClInclude Include="gui\Debugger\DebugEvents.h" />
|
<ClInclude Include="gui\Debugger\DebugEvents.h" />
|
||||||
<ClInclude Include="gui\Debugger\DebuggerLists.h" />
|
<ClInclude Include="gui\Debugger\DebuggerLists.h" />
|
||||||
|
|
|
@ -941,6 +941,9 @@
|
||||||
<ClCompile Include="gui\Debugger\CtrlMemView.cpp">
|
<ClCompile Include="gui\Debugger\CtrlMemView.cpp">
|
||||||
<Filter>AppHost\Debugger</Filter>
|
<Filter>AppHost\Debugger</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="gui\Debugger\CtrlMemSearch.cpp">
|
||||||
|
<Filter>AppHost\Debugger</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="DebugTools\ExpressionParser.cpp">
|
<ClCompile Include="DebugTools\ExpressionParser.cpp">
|
||||||
<Filter>System\Ps2\Debug</Filter>
|
<Filter>System\Ps2\Debug</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -2037,6 +2040,9 @@
|
||||||
<ClInclude Include="gui\Debugger\CtrlMemView.h">
|
<ClInclude Include="gui\Debugger\CtrlMemView.h">
|
||||||
<Filter>AppHost\Debugger</Filter>
|
<Filter>AppHost\Debugger</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="gui\Debugger\CtrlMemSearch.h">
|
||||||
|
<Filter>AppHost\Debugger</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="DebugTools\ExpressionParser.h">
|
<ClInclude Include="DebugTools\ExpressionParser.h">
|
||||||
<Filter>System\Ps2\Debug</Filter>
|
<Filter>System\Ps2\Debug</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
Loading…
Reference in New Issue