Debugger: Make the expression parser thread safe

This commit is contained in:
chaoticgd 2024-11-12 22:56:19 +00:00 committed by Ty
parent 8132a8a7f8
commit efb43ac7f9
10 changed files with 97 additions and 93 deletions

View File

@ -80,6 +80,8 @@ void BreakpointDialog::onRdoButtonToggled()
void BreakpointDialog::accept()
{
std::string error;
if (m_purpose == PURPOSE::CREATE)
{
if (m_ui.rdoExecute->isChecked())
@ -93,9 +95,9 @@ void BreakpointDialog::accept()
PostfixExpression expr;
u64 address;
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address))
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address, error))
{
QMessageBox::warning(this, tr("Invalid Address"), getExpressionError());
QMessageBox::warning(this, tr("Invalid Address"), QString::fromStdString(error));
return;
}
@ -108,9 +110,9 @@ void BreakpointDialog::accept()
bp->hasCond = true;
bp->cond.debug = m_cpu;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr))
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error))
{
QMessageBox::warning(this, tr("Invalid Condition"), getExpressionError());
QMessageBox::warning(this, tr("Invalid Condition"), QString::fromStdString(error));
return;
}
@ -121,16 +123,16 @@ void BreakpointDialog::accept()
if (auto* mc = std::get_if<MemCheck>(&m_bp_mc))
{
u64 startAddress;
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress))
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress, error))
{
QMessageBox::warning(this, tr("Invalid Address"), getExpressionError());
QMessageBox::warning(this, tr("Invalid Address"), QString::fromStdString(error));
return;
}
u64 size;
if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size) || !size)
if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size, error) || !size)
{
QMessageBox::warning(this, tr("Invalid Size"), getExpressionError());
QMessageBox::warning(this, tr("Invalid Size"), QString::fromStdString(error));
return;
}
@ -143,9 +145,9 @@ void BreakpointDialog::accept()
mc->cond.debug = m_cpu;
PostfixExpression expr;
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr))
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error))
{
QMessageBox::warning(this, tr("Invalid Condition"), getExpressionError());
QMessageBox::warning(this, tr("Invalid Condition"), QString::fromStdString(error));
return;
}

View File

@ -170,9 +170,10 @@ void DisassemblyWidget::contextGoToAddress()
return;
u64 address = 0;
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address))
std::string error;
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error))
{
QMessageBox::warning(this, tr("Cannot Go To"), getExpressionError());
QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
return;
}

View File

@ -599,9 +599,10 @@ void MemoryViewWidget::contextGoToAddress()
return;
u64 address = 0;
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address))
std::string error;
if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error))
{
QMessageBox::warning(this, tr("Cannot Go To"), getExpressionError());
QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
return;
}

View File

@ -273,6 +273,8 @@ Qt::ItemFlags BreakpointModel::flags(const QModelIndex& index) const
bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
std::string error;
if (role == Qt::CheckStateRole && index.column() == BreakpointColumns::ENABLED)
{
auto bp_mc = m_breakpoints.at(index.row());
@ -314,9 +316,9 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
{
PostfixExpression expr;
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr))
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error))
{
QMessageBox::warning(nullptr, "Condition Error", QString(getExpressionError()));
QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error));
return false;
}
@ -347,9 +349,9 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
{
PostfixExpression expr;
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr))
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error))
{
QMessageBox::warning(nullptr, "Condition Error", QString(getExpressionError()));
QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error));
return false;
}
@ -456,17 +458,20 @@ void BreakpointModel::refreshData()
void BreakpointModel::loadBreakpointFromFieldList(QStringList fields)
{
std::string error;
bool ok;
if (fields.size() != BreakpointModel::BreakpointColumns::COLUMN_COUNT)
if (fields.size() != BreakpointColumns::COLUMN_COUNT)
{
Console.WriteLn("Debugger Breakpoint Model: Invalid number of columns, skipping");
return;
}
const int type = fields[BreakpointModel::BreakpointColumns::TYPE].toUInt(&ok);
const int type = fields[BreakpointColumns::TYPE].toUInt(&ok);
if (!ok)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse type '%s', skipping",
fields[BreakpointColumns::TYPE].toUtf8().constData());
return;
}
@ -476,34 +481,37 @@ void BreakpointModel::loadBreakpointFromFieldList(QStringList fields)
BreakPoint bp;
// Address
bp.addr = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16);
bp.addr = fields[BreakpointColumns::OFFSET].toUInt(&ok, 16);
if (!ok)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping",
fields[BreakpointColumns::OFFSET].toUtf8().constData());
return;
}
// Condition
if (!fields[BreakpointModel::BreakpointColumns::CONDITION].isEmpty())
if (!fields[BreakpointColumns::CONDITION].isEmpty())
{
PostfixExpression expr;
bp.hasCond = true;
bp.cond.debug = &m_cpu;
if (!m_cpu.initExpression(fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), expr))
if (!m_cpu.initExpression(fields[BreakpointColumns::CONDITION].toUtf8().constData(), expr, error))
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping",
fields[BreakpointModel::CONDITION].toUtf8().constData());
return;
}
bp.cond.expression = expr;
bp.cond.expressionString = fields[BreakpointModel::BreakpointColumns::CONDITION].toStdString();
bp.cond.expressionString = fields[BreakpointColumns::CONDITION].toStdString();
}
// Enabled
bp.enabled = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok);
bp.enabled = fields[BreakpointColumns::ENABLED].toUInt(&ok);
if (!ok)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse enable flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse enable flag '%s', skipping",
fields[BreakpointColumns::ENABLED].toUtf8().constData());
return;
}
@ -515,49 +523,54 @@ void BreakpointModel::loadBreakpointFromFieldList(QStringList fields)
// Mode
if (type >= MEMCHECK_INVALID)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond type '%s', skipping",
fields[BreakpointColumns::TYPE].toUtf8().constData());
return;
}
mc.memCond = static_cast<MemCheckCondition>(type);
// Address
QString test = fields[BreakpointModel::BreakpointColumns::OFFSET];
mc.start = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16);
QString test = fields[BreakpointColumns::OFFSET];
mc.start = fields[BreakpointColumns::OFFSET].toUInt(&ok, 16);
if (!ok)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping",
fields[BreakpointColumns::OFFSET].toUtf8().constData());
return;
}
// Size
mc.end = fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUInt(&ok) + mc.start;
mc.end = fields[BreakpointColumns::SIZE_LABEL].toUInt(&ok) + mc.start;
if (!ok)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse length '%s', skipping", fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse length '%s', skipping",
fields[BreakpointColumns::SIZE_LABEL].toUtf8().constData());
return;
}
// Condition
if (!fields[BreakpointModel::BreakpointColumns::CONDITION].isEmpty())
if (!fields[BreakpointColumns::CONDITION].isEmpty())
{
PostfixExpression expr;
mc.hasCond = true;
mc.cond.debug = &m_cpu;
if (!m_cpu.initExpression(fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), expr))
if (!m_cpu.initExpression(fields[BreakpointColumns::CONDITION].toUtf8().constData(), expr, error))
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping",
fields[BreakpointColumns::CONDITION].toUtf8().constData());
return;
}
mc.cond.expression = expr;
mc.cond.expressionString = fields[BreakpointModel::BreakpointColumns::CONDITION].toStdString();
mc.cond.expressionString = fields[BreakpointColumns::CONDITION].toStdString();
}
// Result
const int result = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok);
const int result = fields[BreakpointColumns::ENABLED].toUInt(&ok);
if (!ok)
{
Console.WriteLn("Debugger Breakpoint Model: Failed to parse result flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData());
Console.WriteLn("Debugger Breakpoint Model: Failed to parse result flag '%s', skipping",
fields[BreakpointColumns::ENABLED].toUtf8().constData());
return;
}
mc.result = static_cast<MemCheckResult>(result);

View File

@ -24,7 +24,8 @@ struct BreakPointCond
u32 Evaluate()
{
u64 result;
if (!debug->parseExpression(expression, result) || result == 0)
std::string error;
if (!debug->parseExpression(expression, result, error) || result == 0)
return 0;
return 1;
}

View File

@ -340,7 +340,7 @@ std::optional<u32> DebugInterface::getStackFrameSize(const ccc::Function& functi
// The stack frame size isn't stored in the symbol table, so we try
// to extract it from the code by checking for an instruction at the
// start of the current function that is in the form of
// "addui $sp, $sp, frame_size" instead.
// "addiu $sp, $sp, frame_size" instead.
u32 instruction = read32(function.address().value);
@ -354,29 +354,29 @@ std::optional<u32> DebugInterface::getStackFrameSize(const ccc::Function& functi
return static_cast<u32>(stack_frame_size);
}
bool DebugInterface::evaluateExpression(const char* expression, u64& dest)
bool DebugInterface::evaluateExpression(const char* expression, u64& dest, std::string& error)
{
PostfixExpression postfix;
if (!initExpression(expression, postfix))
if (!initExpression(expression, postfix, error))
return false;
if (!parseExpression(postfix, dest))
if (!parseExpression(postfix, dest, error))
return false;
return true;
}
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest)
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest, std::string& error)
{
MipsExpressionFunctions funcs(this, true);
return initPostfixExpression(exp, &funcs, dest);
return initPostfixExpression(exp, &funcs, dest, error);
}
bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest)
bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest, std::string& error)
{
MipsExpressionFunctions funcs(this, false);
return parsePostfixExpression(exp, &funcs, dest);
return parsePostfixExpression(exp, &funcs, dest, error);
}
//
@ -904,12 +904,10 @@ std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() co
return getEEThreads();
}
//
// R3000DebugInterface
//
BreakPointCpu R3000DebugInterface::getCpuType()
{
return BREAKPOINT_IOP;

View File

@ -86,9 +86,9 @@ public:
virtual SymbolImporter* GetSymbolImporter() const = 0;
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 evaluateExpression(const char* expression, u64& dest, std::string& error);
bool initExpression(const char* exp, PostfixExpression& dest, std::string& error);
bool parseExpression(PostfixExpression& exp, u64& dest, std::string& error);
bool isAlive();
bool isCpuPaused();
void pauseCpu();

View File

@ -21,8 +21,6 @@ typedef enum {
typedef enum { EXCOMM_CONST, EXCOMM_CONST_FLOAT, EXCOMM_REF, EXCOMM_OP } ExpressionCommand;
static std::string expressionError;
typedef struct {
char Name[4];
unsigned char Priority;
@ -209,10 +207,8 @@ bool isAlphaNum(char c)
c == '@' || c == '_' || c == '$' || c == '.');
}
bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest)
bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest, std::string& error)
{
expressionError.clear();
int infixPos = 0;
int infixLen = (int)strlen(infix);
ExpressionOpcodeType lastOpcode = EXOP_NONE;
@ -241,7 +237,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
if(subPos == sizeof(subStr) - 1)
{
expressionError = TRANSLATE("ExpressionParser", "Token too long.");
error = TRANSLATE("ExpressionParser", "Token too long.");
return false;
}
@ -251,7 +247,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
isFloat = true;
else if (!parseNumber(subStr,16,subPos,value))
{
expressionError = StringUtil::StdStringFromFormat(
error = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid number \"%s\"."), subStr);
return false;
}
@ -268,7 +264,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
if(subPos == sizeof(subStr) - 1)
{
expressionError = TRANSLATE("ExpressionParser", "Token too long.");
error = TRANSLATE("ExpressionParser", "Token too long.");
return false;
}
@ -294,7 +290,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
continue;
}
expressionError = StringUtil::StdStringFromFormat(
error = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid symbol \"%s\"."), subStr);
return false;
} else {
@ -302,7 +298,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
ExpressionOpcodeType type = getExpressionOpcode(&infix[infixPos],len,lastOpcode);
if (type == EXOP_NONE)
{
expressionError = StringUtil::StdStringFromFormat(
error = StringUtil::StdStringFromFormat(
TRANSLATE("ExpressionParser", "Invalid operator at \"%s\"."), &infix[infixPos]);
return false;
}
@ -318,7 +314,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
{
if (opcodeStack.empty())
{
expressionError = TRANSLATE("ExpressionParser", "Closing parenthesis without opening one.");
error = TRANSLATE("ExpressionParser", "Closing parenthesis without opening one.");
return false;
}
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
@ -332,7 +328,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
{
if (opcodeStack.empty())
{
expressionError = TRANSLATE("ExpressionParser", "Closing bracket without opening one.");
error = TRANSLATE("ExpressionParser", "Closing bracket without opening one.");
return false;
}
ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1];
@ -385,7 +381,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
if (t == EXOP_BRACKETL) // opening bracket without closing one
{
expressionError = TRANSLATE("ExpressionParser", "Parenthesis not closed.");
error = TRANSLATE("ExpressionParser", "Parenthesis not closed.");
return false;
}
dest.push_back(ExpressionPair(EXCOMM_OP,t));
@ -415,7 +411,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf
return true;
}
bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest)
bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest, std::string& error)
{
size_t num = 0;
u64 opcode;
@ -444,7 +440,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
opcode = exp[num++].second;
if (valueStack.size() < ExpressionOpcodes[opcode].args)
{
expressionError = TRANSLATE("ExpressionParser", "Not enough arguments.");
error = TRANSLATE("ExpressionParser", "Not enough arguments.");
return false;
}
for (int l = 0; l < ExpressionOpcodes[opcode].args; l++)
@ -459,12 +455,12 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_MEMSIZE: // must be followed by EXOP_MEM
if (exp[num++].second != EXOP_MEM)
{
expressionError = TRANSLATE("ExpressionParser", "Invalid memsize operator.");
error = TRANSLATE("ExpressionParser", "Invalid memsize operator.");
return false;
}
u64 val;
if(!funcs->getMemoryValue(arg[1],arg[0],val,expressionError))
if(!funcs->getMemoryValue(arg[1],arg[0],val,error))
{
return false;
}
@ -473,7 +469,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_MEM:
{
u64 val;
if (!funcs->getMemoryValue(arg[0],4,val,expressionError))
if (!funcs->getMemoryValue(arg[0],4,val,error))
{
return false;
}
@ -503,7 +499,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_DIV: // a/b
if (arg[0] == 0)
{
expressionError = TRANSLATE("ExpressionParser", "Division by zero.");
error = TRANSLATE("ExpressionParser", "Division by zero.");
return false;
}
if (useFloat)
@ -514,7 +510,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_MOD: // a%b
if (arg[0] == 0)
{
expressionError = TRANSLATE("ExpressionParser", "Modulo by zero.");
error = TRANSLATE("ExpressionParser", "Modulo by zero.");
return false;
}
valueStack.push_back(arg[1]%arg[0]);
@ -593,7 +589,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
case EXOP_TERTELSE: // exp ? exp : exp, else muss zuerst kommen!
if (exp[num++].second != EXOP_TERTIF)
{
expressionError = TRANSLATE("ExpressionParser", "Invalid tertiary operator.");
error = TRANSLATE("ExpressionParser", "Invalid tertiary operator.");
return false;
}
valueStack.push_back(arg[2]?arg[1]:arg[0]);
@ -608,17 +604,9 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs,
return true;
}
bool parseExpression(char* exp, IExpressionFunctions* funcs, u64& dest)
bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest, std::string& error)
{
PostfixExpression postfix;
if (!initPostfixExpression(exp,funcs,postfix)) return false;
return parsePostfixExpression(postfix,funcs,dest);
}
const char* getExpressionError()
{
if (expressionError.empty())
return TRANSLATE("ExpressionParser", "Invalid expression.");
return expressionError.c_str();
if (!initPostfixExpression(exp,funcs,postfix,error)) return false;
return parsePostfixExpression(postfix,funcs,dest,error);
}

View File

@ -26,7 +26,6 @@ public:
virtual bool getMemoryValue(u32 address, int size, u64& dest, std::string& error) = 0;
};
bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest);
bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest);
bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest);
const char* getExpressionError();
bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest, std::string& error);
bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest, std::string& error);
bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest, std::string& error);

View File

@ -355,11 +355,12 @@ bool MipsCheckImmediate(const char* Source, DebugInterface* cpu, int& dest, int&
RetLen = SourceLen;
PostfixExpression postfix;
if (!cpu->initExpression(Buffer,postfix))
std::string error;
if (!cpu->initExpression(Buffer,postfix,error))
return false;
u64 value;
if (!cpu->parseExpression(postfix,value))
if (!cpu->parseExpression(postfix,value,error))
return false;
dest = (int) value;