Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debugger: Add memory search types: GreaterThan(OrEqual), LesserThan(OrEqual), and Not Equal #10441

Merged
merged 4 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 121 additions & 63 deletions pcsx2-qt/Debugger/CpuWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
using namespace QtUtils;
using namespace MipsStackWalk;

using SearchComparison = CpuWidget::SearchComparison;
using SearchType = CpuWidget::SearchType;

CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
: m_cpu(cpu)
, m_bpModel(cpu)
Expand Down Expand Up @@ -869,7 +872,7 @@ void CpuWidget::onStackListDoubleClick(const QModelIndex& index)
}

template <typename T>
static bool checkAddressValueMatches(DebugInterface* cpu, u32 addr, T value)
static T readValueAtAddress(DebugInterface* cpu, u32 addr)
{
T val = 0;
switch (sizeof(T))
Expand All @@ -882,50 +885,99 @@ static bool checkAddressValueMatches(DebugInterface* cpu, u32 addr, T value)
break;
case sizeof(u32):
{
if (std::is_same_v<T, float>)
{
const float fTop = value + 0.00001f;
const float fBottom = value - 0.00001f;
const float memValue = std::bit_cast<float, u32>(cpu->read32(addr));
return (fBottom < memValue && memValue < fTop);
}

val = cpu->read32(addr);
break;
}
case sizeof(u64):
{
if (std::is_same_v<T, double>)
val = cpu->read64(addr);
break;
}
}
return val;
}

template <typename T>
static bool memoryValueComparator(SearchComparison searchComparison, T searchValue, T readValue)
{
const bool isNotOperator = searchComparison == SearchComparison::NotEquals;
switch (searchComparison)
{
case SearchComparison::Equals:
case SearchComparison::NotEquals:
{
bool areValuesEqual = false;
if constexpr (std::is_same_v<T, float>)
{
const double dTop = value + 0.00001f;
const double dBottom = value - 0.00001f;
const double memValue = std::bit_cast<double, u64>(cpu->read64(addr));
return (dBottom < memValue && memValue < dTop);
const T fTop = searchValue + 0.00001f;
const T fBottom = searchValue - 0.00001f;
const T memValue = std::bit_cast<float, u32>(readValue);
areValuesEqual = (fBottom < memValue && memValue < fTop);
}

val = cpu->read64(addr);
else if constexpr (std::is_same_v<T, double>)
{
const double dTop = searchValue + 0.00001f;
const double dBottom = searchValue - 0.00001f;
const double memValue = std::bit_cast<double, u64>(readValue);
areValuesEqual = (dBottom < memValue && memValue < dTop);
}
else
{
areValuesEqual = searchValue == readValue;
}
return isNotOperator ? !areValuesEqual : areValuesEqual;
break;
}
case SearchComparison::GreaterThan:
case SearchComparison::GreaterThanOrEqual:
case SearchComparison::LessThan:
case SearchComparison::LessThanOrEqual:
{
const bool hasEqualsCheck = searchComparison == SearchComparison::GreaterThanOrEqual || searchComparison == SearchComparison::LessThanOrEqual;
if (hasEqualsCheck && memoryValueComparator(SearchComparison::Equals, searchValue, readValue))
return true;

const bool isGreaterOperator = searchComparison == SearchComparison::GreaterThan || searchComparison == SearchComparison::GreaterThanOrEqual;
if (std::is_same_v<T, float>)
{
const T fTop = searchValue + 0.00001f;
const T fBottom = searchValue - 0.00001f;
const T memValue = std::bit_cast<float, u32>(readValue);
const bool isGreater = memValue > fTop;
const bool isLesser = memValue < fBottom;
return isGreaterOperator ? isGreater : isLesser;
}
else if (std::is_same_v<T, double>)
{
const double dTop = searchValue + 0.00001f;
const double dBottom = searchValue - 0.00001f;
const double memValue = std::bit_cast<double, u64>(readValue);
const bool isGreater = memValue > dTop;
const bool isLesser = memValue < dBottom;
return isGreaterOperator ? isGreater : isLesser;
}

return isGreaterOperator ? (readValue > searchValue) : (readValue < searchValue);
}
default:
Console.Error("Debugger: Unknown type when doing memory search!");
return false;
break;
}

return val == value;
}

template <typename T>
static std::vector<u32> searchWorker(DebugInterface* cpu, std::vector<u32> searchAddresses, u32 start, u32 end, T value)
std::vector<u32> searchWorker(DebugInterface* cpu, std::vector<u32> searchAddresses, SearchComparison searchComparison, u32 start, u32 end, T searchValue)
{
std::vector<u32> hitAddresses;
const bool isSearchingRange = searchAddresses.size() <= 0;
if (isSearchingRange)
{
for (u32 addr = start; addr < end; addr += sizeof(T))
{
if (checkAddressValueMatches(cpu, addr, value))
if (!cpu->isValidAddress(addr))
continue;
T readValue = readValueAtAddress<T>(cpu, addr);
if (memoryValueComparator(searchComparison, searchValue, readValue))
{
hitAddresses.push_back(addr);
}
Expand All @@ -935,11 +987,13 @@ static std::vector<u32> searchWorker(DebugInterface* cpu, std::vector<u32> searc
{
for (const u32 addr : searchAddresses)
{
if (checkAddressValueMatches(cpu, addr, value))
if (!cpu->isValidAddress(addr))
continue;
T readValue = readValueAtAddress<T>(cpu, addr);
if (memoryValueComparator(searchComparison, searchValue, readValue))
{
hitAddresses.push_back(addr);
}

}
}
return hitAddresses;
Expand All @@ -959,7 +1013,6 @@ static bool compareByteArrayAtAddress(DebugInterface* cpu, u32 addr, QByteArray

static std::vector<u32> searchWorkerByteArray(DebugInterface* cpu, std::vector<u32> searchAddresses, u32 start, u32 end, QByteArray value)
{

std::vector<u32> hitAddresses;
const bool isSearchingRange = searchAddresses.size() <= 0;
if (isSearchingRange)
Expand All @@ -986,27 +1039,26 @@ static std::vector<u32> searchWorkerByteArray(DebugInterface* cpu, std::vector<u
return hitAddresses;
}

std::vector<u32> startWorker(DebugInterface* cpu, int type, std::vector<u32> searchAddresses, u32 start, u32 end, QString value, int base)
std::vector<u32> startWorker(DebugInterface* cpu, const SearchType type, const SearchComparison searchComparison, std::vector<u32> searchAddresses, u32 start, u32 end, QString value, int base)
{

const bool isSigned = value.startsWith("-");
switch (type)
{
case 0:
return isSigned ? searchWorker<s8>(cpu, searchAddresses, start, end, value.toShort(nullptr, base)) : searchWorker<u8>(cpu, searchAddresses, start, end, value.toUShort(nullptr, base));
case 1:
return isSigned ? searchWorker<s16>(cpu, searchAddresses, start, end, value.toShort(nullptr, base)) : searchWorker<u16>(cpu, searchAddresses, start, end, value.toUShort(nullptr, base));
case 2:
return isSigned ? searchWorker<s32>(cpu, searchAddresses, start, end, value.toInt(nullptr, base)) : searchWorker<u32>(cpu, searchAddresses, start, end, value.toUInt(nullptr, base));
case 3:
return isSigned ? searchWorker<s64>(cpu, searchAddresses, start, end, value.toLong(nullptr, base)) : searchWorker<s64>(cpu, searchAddresses, start, end, value.toULongLong(nullptr, base));
case 4:
return searchWorker<float>(cpu, searchAddresses, start, end, value.toFloat());
case 5:
return searchWorker<double>(cpu, searchAddresses, start, end, value.toDouble());
case 6:
case SearchType::ByteType:
return isSigned ? searchWorker<s8>(cpu, searchAddresses, searchComparison, start, end, value.toShort(nullptr, base)) : searchWorker<u8>(cpu, searchAddresses, searchComparison, start, end, value.toUShort(nullptr, base));
case SearchType::Int16Type:
return isSigned ? searchWorker<s16>(cpu, searchAddresses, searchComparison, start, end, value.toShort(nullptr, base)) : searchWorker<u16>(cpu, searchAddresses, searchComparison, start, end, value.toUShort(nullptr, base));
case SearchType::Int32Type:
return isSigned ? searchWorker<s32>(cpu, searchAddresses, searchComparison, start, end, value.toInt(nullptr, base)) : searchWorker<u32>(cpu, searchAddresses, searchComparison, start, end, value.toUInt(nullptr, base));
case SearchType::Int64Type:
return isSigned ? searchWorker<s64>(cpu, searchAddresses, searchComparison, start, end, value.toLong(nullptr, base)) : searchWorker<s64>(cpu, searchAddresses, searchComparison, start, end, value.toULongLong(nullptr, base));
case SearchType::FloatType:
return searchWorker<float>(cpu, searchAddresses, searchComparison, start, end, value.toFloat());
case SearchType::DoubleType:
return searchWorker<double>(cpu, searchAddresses, searchComparison, start, end, value.toDouble());
case SearchType::StringType:
return searchWorkerByteArray(cpu, searchAddresses, start, end, value.toUtf8());
case 7:
case SearchType::ArrayType:
return searchWorkerByteArray(cpu, searchAddresses, start, end, QByteArray::fromHex(value.toUtf8()));
default:
Console.Error("Debugger: Unknown type when doing memory search!");
Expand All @@ -1020,7 +1072,7 @@ void CpuWidget::onSearchButtonClicked()
if (!m_cpu.isAlive())
return;

const int searchType = m_ui.cmbSearchType->currentIndex();
const SearchType searchType = static_cast<SearchType>(m_ui.cmbSearchType->currentIndex());
const bool searchHex = m_ui.chkSearchHex->isChecked();

bool ok;
Expand All @@ -1047,25 +1099,33 @@ void CpuWidget::onSearchButtonClicked()
}

const QString searchValue = m_ui.txtSearchValue->text();

const SearchComparison searchComparison = static_cast<SearchComparison>(m_ui.cmbSearchComparison->currentIndex());
const bool isFilterSearch = sender() == m_ui.btnFilterSearch;
unsigned long long value;

const bool isVariableSize = searchType == SearchType::ArrayType || searchType == SearchType::StringType;
if (isVariableSize && searchComparison != SearchComparison::Equals)
{
QMessageBox::critical(this, tr("Debugger"), tr("Search types Array and String can only be used with Equals search comparisons."));
return;
}

switch (searchType)
{
case 0:
case 1:
case 2:
case 3:
case SearchType::ByteType:
case SearchType::Int16Type:
case SearchType::Int32Type:
case SearchType::Int64Type:
value = searchValue.toULongLong(&ok, searchHex ? 16 : 10);
break;
case 4:
case 5:
case SearchType::FloatType:
case SearchType::DoubleType:
searchValue.toDouble(&ok);
break;
case 6:
case SearchType::StringType:
ok = !searchValue.isEmpty();
break;
case 7:
case SearchType::ArrayType:
ok = !searchValue.trimmed().isEmpty();
break;
}
Expand All @@ -1078,21 +1138,21 @@ void CpuWidget::onSearchButtonClicked()

switch (searchType)
{
case 7:
case 6:
case 5:
case 4:
case SearchType::ArrayType:
case SearchType::StringType:
case SearchType::DoubleType:
case SearchType::FloatType:
break;
case 3:
case SearchType::Int64Type:
if (value <= std::numeric_limits<unsigned long long>::max())
break;
case 2:
case SearchType::Int32Type:
if (value <= std::numeric_limits<unsigned long>::max())
break;
case 1:
case SearchType::Int16Type:
if (value <= std::numeric_limits<unsigned short>::max())
break;
case 0:
case SearchType::ByteType:
if (value <= std::numeric_limits<unsigned char>::max())
break;
default:
Expand All @@ -1111,19 +1171,16 @@ void CpuWidget::onSearchButtonClicked()
m_searchResults = results;
loadSearchResults();
m_ui.btnFilterSearch->setDisabled(m_ui.listSearchResults->count() == 0);

});

m_ui.btnSearch->setDisabled(true);
QPushButton* senderButton = qobject_cast<QPushButton*>(sender());
bool isFilterSearch = senderButton == m_ui.btnFilterSearch;
std::vector<u32> addresses;
if (isFilterSearch)
{
addresses = m_searchResults;
}
QFuture<std::vector<u32>> workerFuture =
QtConcurrent::run(startWorker, &m_cpu, searchType, addresses, searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
QtConcurrent::run(startWorker, &m_cpu, searchType, searchComparison, addresses, searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
workerWatcher->setFuture(workerFuture);
}

Expand All @@ -1139,7 +1196,8 @@ void CpuWidget::onSearchResultsListScroll(u32 value)
}
}

void CpuWidget::loadSearchResults() {
void CpuWidget::loadSearchResults()
{
const u32 numLoaded = m_ui.listSearchResults->count();
const u32 amountLeftToLoad = m_searchResults.size() - numLoaded;
if (amountLeftToLoad < 1)
Expand Down
23 changes: 23 additions & 0 deletions pcsx2-qt/Debugger/CpuWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ class CpuWidget final : public QWidget
CpuWidget(QWidget* parent, DebugInterface& cpu);
~CpuWidget();

enum class SearchType
{
ByteType,
Int16Type,
Int32Type,
Int64Type,
FloatType,
DoubleType,
StringType,
ArrayType
};

// Note: The order of these enum values must reflect the order in thee Search Comparison combobox.
enum class SearchComparison
{
Equals,
NotEquals,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual
};

public slots:
void paintEvent(QPaintEvent* event);

Expand Down
34 changes: 34 additions & 0 deletions pcsx2-qt/Debugger/CpuWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,40 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cmbSearchComparison">
<item>
<property name="text">
<string>Equals</string>
</property>
</item>
<item>
<property name="text">
<string>Not Equals</string>
</property>
</item>
<item>
<property name="text">
<string>Greater Than</string>
</property>
</item>
<item>
<property name="text">
<string>Greater Than Or Equal</string>
</property>
</item>
<item>
<property name="text">
<string>Less Than</string>
</property>
</item>
<item>
<property name="text">
<string>Less Than Or Equal</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
Expand Down