Debugger: Bring back the expression parser

This commit is contained in:
chaoticgd 2024-09-01 19:13:14 +01:00 committed by Ty
parent f340b5ebd0
commit 90463a4a6c
10 changed files with 183 additions and 81 deletions

View File

@ -93,10 +93,9 @@ void BreakpointDialog::accept()
PostfixExpression expr;
u64 address;
if (!m_cpu->initExpression(m_ui.txtAddress->text().toLocal8Bit().constData(), expr) ||
!m_cpu->parseExpression(expr, address))
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid address \"%1\"").arg(m_ui.txtAddress->text()));
QMessageBox::warning(this, tr("Invalid Address"), getExpressionError());
return;
}
@ -109,9 +108,9 @@ void BreakpointDialog::accept()
bp->hasCond = true;
bp->cond.debug = m_cpu;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toLocal8Bit().constData(), expr))
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid condition \"%1\"").arg(getExpressionError()));
QMessageBox::warning(this, tr("Invalid Condition"), getExpressionError());
return;
}
@ -121,21 +120,17 @@ void BreakpointDialog::accept()
}
if (auto* mc = std::get_if<MemCheck>(&m_bp_mc))
{
PostfixExpression expr;
u64 startAddress;
if (!m_cpu->initExpression(m_ui.txtAddress->text().toLocal8Bit().constData(), expr) ||
!m_cpu->parseExpression(expr, startAddress))
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid address \"%1\"").arg(m_ui.txtAddress->text()));
QMessageBox::warning(this, tr("Invalid Address"), getExpressionError());
return;
}
u64 size;
if (!m_cpu->initExpression(m_ui.txtSize->text().toLocal8Bit(), expr) ||
!m_cpu->parseExpression(expr, size) || !size)
if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size) || !size)
{
QMessageBox::warning(this, tr("Error"), tr("Invalid size \"%1\"").arg(m_ui.txtSize->text()));
QMessageBox::warning(this, tr("Invalid Size"), getExpressionError());
return;
}
@ -147,9 +142,10 @@ void BreakpointDialog::accept()
mc->hasCond = true;
mc->cond.debug = m_cpu;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toLocal8Bit().constData(), expr))
PostfixExpression expr;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr))
{
QMessageBox::warning(this, tr("Error"), tr("Invalid condition \"%1\"").arg(getExpressionError()));
QMessageBox::warning(this, tr("Invalid Condition"), getExpressionError());
return;
}

View File

@ -163,21 +163,20 @@ void DisassemblyWidget::contextFollowBranch()
void DisassemblyWidget::contextGoToAddress()
{
bool ok;
const QString targetString = QInputDialog::getText(this, tr("Go to address"), "",
const QString targetString = QInputDialog::getText(this, tr("Go To In Disassembly"), "",
QLineEdit::Normal, "", &ok);
if (!ok)
return;
const u32 targetAddress = targetString.toUInt(&ok, 16) & ~3;
if (!ok)
u64 address = 0;
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address))
{
QMessageBox::warning(this, tr("Go to address error"), tr("Invalid address"));
QMessageBox::warning(this, tr("Cannot Go To"), getExpressionError());
return;
}
gotoAddressAndSetFocus(targetAddress);
gotoAddressAndSetFocus(static_cast<u32>(address) & ~3);
}
void DisassemblyWidget::contextAddFunction()

View File

@ -592,21 +592,20 @@ void MemoryViewWidget::contextPaste()
void MemoryViewWidget::contextGoToAddress()
{
bool ok;
QString targetString = QInputDialog::getText(this, tr("Go to address"), "",
QString targetString = QInputDialog::getText(this, tr("Go To In Memory View"), "",
QLineEdit::Normal, "", &ok);
if (!ok)
return;
const u32 targetAddress = targetString.toUInt(&ok, 16);
if (!ok)
u64 address = 0;
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address))
{
QMessageBox::warning(this, "Go to address error", "Invalid address");
QMessageBox::warning(this, tr("Cannot Go To"), getExpressionError());
return;
}
gotoAddress(targetAddress);
gotoAddress(static_cast<u32>(address));
}
void MemoryViewWidget::mouseDoubleClickEvent(QMouseEvent* event)

View File

@ -25,6 +25,7 @@ public:
Tag tag = OBJECT;
ccc::MultiSymbolHandle symbol;
QString name;
QString mangled_name;
SymbolTreeLocation location;
bool is_location_editable = false;
std::optional<u32> size;

View File

@ -362,6 +362,13 @@ void SymbolTreeWidget::setupMenu()
connect(copy_name, &QAction::triggered, this, &SymbolTreeWidget::onCopyName);
m_context_menu->addAction(copy_name);
if (m_flags & ALLOW_MANGLED_NAME_ACTIONS)
{
QAction* copy_mangled_name = new QAction(tr("Copy Mangled Name"), this);
connect(copy_mangled_name, &QAction::triggered, this, &SymbolTreeWidget::onCopyMangledName);
m_context_menu->addAction(copy_mangled_name);
}
QAction* copy_location = new QAction(tr("Copy Location"), this);
connect(copy_location, &QAction::triggered, this, &SymbolTreeWidget::onCopyLocation);
m_context_menu->addAction(copy_location);
@ -484,6 +491,18 @@ void SymbolTreeWidget::onCopyName()
QApplication::clipboard()->setText(node->name);
}
void SymbolTreeWidget::onCopyMangledName()
{
SymbolTreeNode* node = currentNode();
if (!node)
return;
if (!node->mangled_name.isEmpty())
QApplication::clipboard()->setText(node->mangled_name);
else
QApplication::clipboard()->setText(node->name);
}
void SymbolTreeWidget::onCopyLocation()
{
SymbolTreeNode* node = currentNode();
@ -611,7 +630,7 @@ SymbolTreeNode* SymbolTreeWidget::currentNode()
// *****************************************************************************
FunctionTreeWidget::FunctionTreeWidget(DebugInterface& cpu, QWidget* parent)
: SymbolTreeWidget(ALLOW_GROUPING, 4, cpu, parent)
: SymbolTreeWidget(ALLOW_GROUPING | ALLOW_MANGLED_NAME_ACTIONS, 4, cpu, parent)
{
}
@ -652,6 +671,7 @@ std::unique_ptr<SymbolTreeNode> FunctionTreeWidget::buildNode(
std::unique_ptr<SymbolTreeNode> node = std::make_unique<SymbolTreeNode>();
node->name = std::move(work.name);
node->mangled_name = QString::fromStdString(function.mangled_name());
node->location = SymbolTreeLocation(SymbolTreeLocation::MEMORY, function.address().value);
node->size = function.size();
node->symbol = ccc::MultiSymbolHandle(function);
@ -694,7 +714,7 @@ void FunctionTreeWidget::onNewButtonPressed()
// *****************************************************************************
GlobalVariableTreeWidget::GlobalVariableTreeWidget(DebugInterface& cpu, QWidget* parent)
: SymbolTreeWidget(ALLOW_GROUPING | ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN | ALLOW_TYPE_ACTIONS, 1, cpu, parent)
: SymbolTreeWidget(ALLOW_GROUPING | ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN | ALLOW_TYPE_ACTIONS | ALLOW_MANGLED_NAME_ACTIONS, 1, cpu, parent)
{
}
@ -777,6 +797,7 @@ std::unique_ptr<SymbolTreeNode> GlobalVariableTreeWidget::buildNode(
{
const ccc::GlobalVariable& global_variable = static_cast<const ccc::GlobalVariable&>(*work.symbol);
node->mangled_name = QString::fromStdString(global_variable.mangled_name());
if (global_variable.type())
node->type = ccc::NodeHandle(global_variable, global_variable.type());
node->location = SymbolTreeLocation(SymbolTreeLocation::MEMORY, global_variable.address().value);

View File

@ -84,6 +84,7 @@ protected:
void onDeleteButtonPressed();
void onCopyName();
void onCopyMangledName();
void onCopyLocation();
void onRenameSymbol();
void onGoToInDisassembly();
@ -117,7 +118,8 @@ protected:
NO_SYMBOL_TREE_FLAGS = 0,
ALLOW_GROUPING = 1 << 0,
ALLOW_SORTING_BY_IF_TYPE_IS_KNOWN = 1 << 1,
ALLOW_TYPE_ACTIONS = 1 << 2
ALLOW_TYPE_ACTIONS = 1 << 2,
ALLOW_MANGLED_NAME_ACTIONS = 1 << 3
};
u32 m_flags;

View File

@ -10,17 +10,13 @@
#include "GS.h" // Required for gsNonMirroredRead()
#include "Counters.h"
#include "Host.h"
#include "R3000A.h"
#include "IopMem.h"
#include "VMManager.h"
#include "common/StringUtil.h"
#ifdef __clang__
// TODO: The sprintf() usage here needs to be rewritten...
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
R5900DebugInterface r5900Debug;
R3000DebugInterface r3000Debug;
@ -45,8 +41,20 @@ enum ReferenceIndexType
class MipsExpressionFunctions : public IExpressionFunctions
{
public:
explicit MipsExpressionFunctions(DebugInterface* cpu)
: cpu(cpu){};
explicit MipsExpressionFunctions(DebugInterface* cpu, bool enumerateSymbols)
: m_cpu(cpu)
{
if (!enumerateSymbols)
return;
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
for (const ccc::Function& function : database.functions)
m_mangled_function_names_to_handles.emplace(function.mangled_name(), function.handle());
for (const ccc::GlobalVariable& global : database.global_variables)
m_mangled_global_names_to_handles.emplace(global.mangled_name(), global.handle());
});
}
virtual bool parseReference(char* str, u64& referenceIndex)
{
@ -54,7 +62,7 @@ public:
{
char reg[8];
std::snprintf(reg, std::size(reg), "r%d", i);
if (StringUtil::Strcasecmp(str, reg) == 0 || StringUtil::Strcasecmp(str, cpu->getRegisterName(0, i)) == 0)
if (StringUtil::Strcasecmp(str, reg) == 0 || StringUtil::Strcasecmp(str, m_cpu->getRegisterName(0, i)) == 0)
{
referenceIndex = i;
return true;
@ -108,27 +116,62 @@ public:
virtual bool parseSymbol(char* str, u64& symbolValue)
{
SymbolInfo symbol = cpu->GetSymbolGuardian().SymbolWithName(std::string(str));
if (!symbol.address.valid())
return false;
bool success = false;
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
std::string name = str;
symbolValue = symbol.address.value;
return true;
// Check for mangled function names.
auto function_iterator = m_mangled_function_names_to_handles.find(name);
if (function_iterator != m_mangled_function_names_to_handles.end())
{
const ccc::Function* function = database.functions.symbol_from_handle(function_iterator->second);
if (function && function->address().valid())
{
symbolValue = function->address().value;
success = true;
return;
}
}
// Check for mangled global variable names.
auto global_iterator = m_mangled_global_names_to_handles.find(name);
if (global_iterator != m_mangled_global_names_to_handles.end())
{
const ccc::GlobalVariable* global = database.global_variables.symbol_from_handle(global_iterator->second);
if (global && global->address().valid())
{
symbolValue = global->address().value;
success = true;
return;
}
}
// Check for regular unmangled names.
const ccc::Symbol* symbol = database.symbol_with_name(name);
if (symbol && symbol->address().valid())
{
symbolValue = symbol->address().value;
success = true;
return;
}
});
return success;
}
virtual u64 getReferenceValue(u64 referenceIndex)
{
if (referenceIndex < 32)
return cpu->getRegister(0, referenceIndex)._u64[0];
return m_cpu->getRegister(0, referenceIndex)._u64[0];
if (referenceIndex == REF_INDEX_PC)
return cpu->getPC();
return m_cpu->getPC();
if (referenceIndex == REF_INDEX_HI)
return cpu->getHI()._u64[0];
return m_cpu->getHI()._u64[0];
if (referenceIndex == REF_INDEX_LO)
return cpu->getLO()._u64[0];
return m_cpu->getLO()._u64[0];
if (referenceIndex & REF_INDEX_IS_OPSL)
{
const u32 OP = memRead32(cpu->getPC());
const u32 OP = memRead32(m_cpu->getPC());
const R5900::OPCODE& opcode = R5900::GetInstruction(OP);
if (opcode.flags & IS_MEMORY)
{
@ -154,7 +197,7 @@ public:
}
if (referenceIndex & REF_INDEX_FPU)
{
return cpu->getRegister(EECAT_FPR, referenceIndex & 0x1F)._u64[0];
return m_cpu->getRegister(EECAT_FPR, referenceIndex & 0x1F)._u64[0];
}
return -1;
}
@ -168,7 +211,7 @@ public:
return EXPR_TYPE_UINT;
}
virtual bool getMemoryValue(u32 address, int size, u64& dest, char* error)
virtual bool getMemoryValue(u32 address, int size, u64& dest, std::string& error)
{
switch (size)
{
@ -178,37 +221,40 @@ public:
case 8:
break;
default:
sprintf(error, "Invalid memory access size %d", size);
error = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid memory access size %d."), size);
return false;
}
if (address % size)
{
sprintf(error, "Invalid memory access (unaligned)");
error = TRANSLATE("ExpressionParser", "Invalid memory access (unaligned).");
return false;
}
switch (size)
{
case 1:
dest = cpu->read8(address);
dest = m_cpu->read8(address);
break;
case 2:
dest = cpu->read16(address);
dest = m_cpu->read16(address);
break;
case 4:
dest = cpu->read32(address);
dest = m_cpu->read32(address);
break;
case 8:
dest = cpu->read64(address);
dest = m_cpu->read64(address);
break;
}
return true;
}
private:
DebugInterface* cpu;
protected:
DebugInterface* m_cpu;
std::map<std::string, ccc::FunctionHandle> m_mangled_function_names_to_handles;
std::map<std::string, ccc::GlobalVariableHandle> m_mangled_global_names_to_handles;
};
//
@ -308,19 +354,31 @@ std::optional<u32> DebugInterface::getStackFrameSize(const ccc::Function& functi
return static_cast<u32>(stack_frame_size);
}
bool DebugInterface::evaluateExpression(const char* expression, u64& dest)
{
PostfixExpression postfix;
if (!initExpression(expression, postfix))
return false;
if (!parseExpression(postfix, dest))
return false;
return true;
}
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest)
{
MipsExpressionFunctions funcs(this);
MipsExpressionFunctions funcs(this, true);
return initPostfixExpression(exp, &funcs, dest);
}
bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest)
{
MipsExpressionFunctions funcs(this);
MipsExpressionFunctions funcs(this, false);
return parsePostfixExpression(exp, &funcs, dest);
}
//
// R5900DebugInterface
//

View File

@ -79,6 +79,7 @@ public:
[[nodiscard]] virtual SymbolGuardian& GetSymbolGuardian() const = 0;
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
bool evaluateExpression(const char* expression, u64& dest);
bool initExpression(const char* exp, PostfixExpression& dest);
bool parseExpression(PostfixExpression& exp, u64& dest);
bool isAlive();

View File

@ -3,6 +3,9 @@
#include "ExpressionParser.h"
#include "Host.h"
#include "common/StringUtil.h"
#include <cctype>
#include <cstdlib>
#include <cstring>
@ -18,7 +21,7 @@ typedef enum {
typedef enum { EXCOMM_CONST, EXCOMM_CONST_FLOAT, EXCOMM_REF, EXCOMM_OP } ExpressionCommand;
static char expressionError[256];
static std::string expressionError;
typedef struct {
char Name[4];
@ -85,8 +88,6 @@ bool parseNumber(char* str, int defaultrad, int len, u64& result)
str+=2;
len-=2;
} else {
if (!(str[0] >= '0' && str[0] <= '9')) return false;
if (tolower(str[len-1]) == 'b' && defaultrad != 16)
{
r = 2;
@ -210,7 +211,7 @@ bool isAlphaNum(char c)
bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest)
{
expressionError[0] = 0;
expressionError.clear();
int infixPos = 0;
int infixLen = (int)strlen(infix);
@ -232,19 +233,26 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
if (first >= '0' && first <= '9')
{
while (isAlphaNum(infix[infixPos]))
while (isAlphaNum(infix[infixPos]) && subPos < static_cast<int>(sizeof(subStr)) - 1)
{
subStr[subPos++] = infix[infixPos++];
}
subStr[subPos] = 0;
if(subPos == sizeof(subStr) - 1)
{
expressionError = TRANSLATE("ExpressionParser", "Token too long.");
return false;
}
u64 value;
bool isFloat = false;
if (parseFloat(subStr,subPos,value))
isFloat = true;
else if (!parseNumber(subStr,16,subPos,value))
{
std::snprintf(expressionError, std::size(expressionError),"Invalid number \"%s\"",subStr);
expressionError = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid number \"%s\"."), subStr);
return false;
}
@ -252,12 +260,18 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
lastOpcode = EXOP_NUMBER;
} else if ((first >= 'a' && first <= 'z') || first == '@')
{
while (isAlphaNum(infix[infixPos]))
while (isAlphaNum(infix[infixPos]) && subPos < static_cast<int>(sizeof(subStr)) - 1)
{
subStr[subPos++] = infix[infixPos++];
}
subStr[subPos] = 0;
if(subPos == sizeof(subStr) - 1)
{
expressionError = TRANSLATE("ExpressionParser", "Token too long.");
return false;
}
u64 value;
if (funcs->parseReference(subStr,value))
{
@ -273,14 +287,23 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
continue;
}
std::snprintf(expressionError, std::size(expressionError),"Invalid symbol \"%s\"",subStr);
if(parseNumber(subStr,16,subPos,value))
{
dest.push_back(ExpressionPair(EXCOMM_CONST,value));
lastOpcode = EXOP_NUMBER;
continue;
}
expressionError = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid symbol \"%s\"."), subStr);
return false;
} else {
int len;
ExpressionOpcodeType type = getExpressionOpcode(&infix[infixPos],len,lastOpcode);
if (type == EXOP_NONE)
{
std::snprintf(expressionError, std::size(expressionError),"Invalid operator at \"%s\"",&infix[infixPos]);
expressionError = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid operator at \"%s\"."), &infix[infixPos]);
return false;
}
@ -295,7 +318,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
{
if (opcodeStack.empty())
{
std::snprintf(expressionError, std::size(expressionError),"Closing parenthesis without opening one");
expressionError = TRANSLATE("ExpressionParser", "Closing parenthesis without opening one.");
return false;
}
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
@ -309,7 +332,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
{
if (opcodeStack.empty())
{
std::snprintf(expressionError, std::size(expressionError),"Closing bracket without opening one");
expressionError = TRANSLATE("ExpressionParser", "Closing bracket without opening one.");
return false;
}
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
@ -362,7 +385,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
if (t == EXOP_BRACKETL) // opening bracket without closing one
{
std::snprintf(expressionError, std::size(expressionError),"Parenthesis not closed");
expressionError = TRANSLATE("ExpressionParser", "Parenthesis not closed.");
return false;
}
dest.push_back(ExpressionPair(EXCOMM_OP,t));
@ -421,7 +444,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
opcode = exp[num++].second;
if (valueStack.size() < ExpressionOpcodes[opcode].args)
{
std::snprintf(expressionError, std::size(expressionError),"Not enough arguments");
expressionError = TRANSLATE("ExpressionParser", "Not enough arguments.");
return false;
}
for (int l = 0; l < ExpressionOpcodes[opcode].args; l++)
@ -436,7 +459,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_MEMSIZE: // must be followed by EXOP_MEM
if (exp[num++].second != EXOP_MEM)
{
std::snprintf(expressionError, std::size(expressionError),"Invalid memsize operator");
expressionError = TRANSLATE("ExpressionParser", "Invalid memsize operator.");
return false;
}
@ -480,7 +503,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_DIV: // a/b
if (arg[0] == 0)
{
std::snprintf(expressionError, std::size(expressionError),"Division by zero");
expressionError = TRANSLATE("ExpressionParser", "Division by zero.");
return false;
}
if (useFloat)
@ -491,7 +514,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_MOD: // a%b
if (arg[0] == 0)
{
std::snprintf(expressionError, std::size(expressionError),"Modulo by zero");
expressionError = TRANSLATE("ExpressionParser", "Modulo by zero.");
return false;
}
valueStack.push_back(arg[1]%arg[0]);
@ -570,7 +593,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_TERTELSE: // exp ? exp : exp, else muss zuerst kommen!
if (exp[num++].second != EXOP_TERTIF)
{
std::snprintf(expressionError, std::size(expressionError),"Invalid tertiary operator");
expressionError = TRANSLATE("ExpressionParser", "Invalid tertiary operator.");
return false;
}
valueStack.push_back(arg[2]?arg[1]:arg[0]);
@ -594,6 +617,8 @@ bool parseExpression(char* exp, IExpressionFunctions* funcs, u64& dest)
const char* getExpressionError()
{
if (expressionError[0] == 0) strcpy(expressionError,"Invalid expression");
return expressionError;
if (expressionError.empty())
return TRANSLATE("ExpressionParser", "Invalid expression.");
return expressionError.c_str();
}

View File

@ -22,7 +22,7 @@ public:
virtual bool parseSymbol(char* str, u64& symbolValue) = 0;
virtual u64 getReferenceValue(u64 referenceIndex) = 0;
virtual ExpressionType getReferenceType(u64 referenceIndex) = 0;
virtual bool getMemoryValue(u32 address, int size, u64& dest, char* error) = 0;
virtual bool getMemoryValue(u32 address, int size, u64& dest, std::string& error) = 0;
};
bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest);