mirror of
https://github.com/libretro/Mesen.git
synced 2025-03-02 21:47:59 +00:00
Debugger: Allow code labels to be used in watch/breakpoint expressions
This commit is contained in:
parent
d4b9bd982e
commit
3751711f17
@ -129,9 +129,17 @@ CdlRatios Debugger::GetCdlRatios()
|
||||
|
||||
void Debugger::SetLabel(uint32_t address, string label, string comment)
|
||||
{
|
||||
ExpressionEvaluator::ResetCustomCache();
|
||||
|
||||
auto existingLabel = _codeLabels.find(address);
|
||||
if(existingLabel != _codeLabels.end()) {
|
||||
_codeLabelReverseLookup.erase(existingLabel->second);
|
||||
}
|
||||
|
||||
_codeLabels.erase(address);
|
||||
if(!label.empty()) {
|
||||
_codeLabels.emplace(address, label);
|
||||
_codeLabelReverseLookup.emplace(label, address);
|
||||
}
|
||||
|
||||
_codeComments.erase(address);
|
||||
@ -174,7 +182,7 @@ void Debugger::UpdateBreakpoints()
|
||||
_hasBreakpoint[i] = false;
|
||||
}
|
||||
|
||||
ExpressionEvaluator expEval;
|
||||
ExpressionEvaluator expEval(this);
|
||||
for(Breakpoint &bp : _newBreakpoints) {
|
||||
if(!expEval.Validate(bp.GetCondition())) {
|
||||
//Remove any invalid condition (syntax-wise)
|
||||
@ -208,7 +216,7 @@ bool Debugger::HasMatchingBreakpoint(BreakpointType type, uint32_t addr, int16_t
|
||||
if(condition.empty()) {
|
||||
return true;
|
||||
} else {
|
||||
ExpressionEvaluator expEval;
|
||||
ExpressionEvaluator expEval(this);
|
||||
if(needState) {
|
||||
GetState(&_debugState);
|
||||
needState = false;
|
||||
@ -225,7 +233,7 @@ bool Debugger::HasMatchingBreakpoint(BreakpointType type, uint32_t addr, int16_t
|
||||
|
||||
int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultType)
|
||||
{
|
||||
ExpressionEvaluator expEval;
|
||||
ExpressionEvaluator expEval(this);
|
||||
|
||||
DebugState state;
|
||||
GetState(&state);
|
||||
@ -605,4 +613,13 @@ void Debugger::GetFunctionEntryPoints(int32_t* entryPoints)
|
||||
shared_ptr<MemoryDumper> Debugger::GetMemoryDumper()
|
||||
{
|
||||
return _memoryDumper;
|
||||
}
|
||||
|
||||
int32_t Debugger::GetCodeLabelAddress(string label)
|
||||
{
|
||||
auto result = _codeLabelReverseLookup.find(label);
|
||||
if(result != _codeLabelReverseLookup.end()) {
|
||||
return _mapper->FromAbsoluteAddress(result->second);
|
||||
}
|
||||
return -1;
|
||||
}
|
@ -58,6 +58,7 @@ private:
|
||||
bool _hasBreakpoint[BreakpointTypeCount];
|
||||
|
||||
unordered_map<uint32_t, string> _codeLabels;
|
||||
unordered_map<string, uint32_t> _codeLabelReverseLookup;
|
||||
unordered_map<uint32_t, string> _codeComments;
|
||||
|
||||
deque<uint32_t> _callstackAbsolute;
|
||||
@ -110,6 +111,8 @@ public:
|
||||
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
|
||||
void SetLabel(uint32_t address, string label, string comment);
|
||||
|
||||
int32_t GetCodeLabelAddress(string label);
|
||||
|
||||
void GetFunctionEntryPoints(int32_t* entryPoints);
|
||||
void GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative);
|
||||
void GetState(DebugState *state);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Debugger.h"
|
||||
|
||||
std::unordered_map<string, std::vector<int>, StringHasher> ExpressionEvaluator::_outputCache;
|
||||
std::unordered_map<string, std::vector<int>, StringHasher> ExpressionEvaluator::_customOutputCache;
|
||||
SimpleLock ExpressionEvaluator::_cacheLock;
|
||||
|
||||
bool ExpressionEvaluator::IsOperator(string token, int &precedence, bool unaryOperator)
|
||||
@ -48,10 +49,12 @@ EvalOperators ExpressionEvaluator::GetOperator(string token, bool unaryOperator)
|
||||
bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, string &output)
|
||||
{
|
||||
string token;
|
||||
size_t initialPos = pos;
|
||||
size_t len = expression.size();
|
||||
do {
|
||||
char c = std::tolower(expression[pos]);
|
||||
if(c >= 'a' && c <= 'z') {
|
||||
if(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_') {
|
||||
//Only letters, numbers and underscore are allowed in code labels
|
||||
token += c;
|
||||
pos++;
|
||||
} else {
|
||||
@ -84,7 +87,14 @@ bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, str
|
||||
} else if(!token.compare("romaddress")) {
|
||||
output += std::to_string(EvalValues::AbsoluteAddress);
|
||||
} else {
|
||||
return false;
|
||||
string originalExpression = expression.substr(initialPos, pos - initialPos);
|
||||
int32_t address = _debugger->GetCodeLabelAddress(originalExpression);
|
||||
if(address >= 0) {
|
||||
_containsCustomLabels = true;
|
||||
output += std::to_string(address);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -134,7 +144,7 @@ string ExpressionEvaluator::GetNextToken(string expression, size_t &pos)
|
||||
} else {
|
||||
if(c == '$') {
|
||||
break;
|
||||
} else if(c < 'a' || c > 'z') {
|
||||
} else if((c < 'a' || c > 'z') && c != '_') {
|
||||
//Not a number, not a letter, this is an operator
|
||||
isOperator = true;
|
||||
output += c;
|
||||
@ -307,12 +317,14 @@ int32_t ExpressionEvaluator::EvaluateExpression(vector<int> *outputQueue, DebugS
|
||||
return operandStack[0];
|
||||
}
|
||||
|
||||
ExpressionEvaluator::ExpressionEvaluator()
|
||||
ExpressionEvaluator::ExpressionEvaluator(Debugger* debugger)
|
||||
{
|
||||
_debugger = debugger;
|
||||
}
|
||||
|
||||
int32_t ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue, uint32_t memoryAddr)
|
||||
{
|
||||
vector<int> output;
|
||||
vector<int> *outputQueue = nullptr;
|
||||
|
||||
{
|
||||
@ -321,22 +333,34 @@ int32_t ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &stat
|
||||
auto cacheOutputQueue = _outputCache.find(expression);
|
||||
if(cacheOutputQueue != _outputCache.end()) {
|
||||
outputQueue = &(cacheOutputQueue->second);
|
||||
} else {
|
||||
auto customCacheResult = _customOutputCache.find(expression);
|
||||
if(customCacheResult != _customOutputCache.end()) {
|
||||
output = _customOutputCache[expression];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(outputQueue == nullptr) {
|
||||
vector<int> output;
|
||||
|
||||
|
||||
if(outputQueue == nullptr && output.empty()) {
|
||||
string fixedExp = expression;
|
||||
fixedExp.erase(std::remove(fixedExp.begin(), fixedExp.end(), ' '), fixedExp.end());
|
||||
ToRpn(fixedExp, output);
|
||||
|
||||
LockHandler lock = _cacheLock.AcquireSafe();
|
||||
_outputCache[expression] = output;
|
||||
outputQueue = &_outputCache[expression];
|
||||
if(_containsCustomLabels) {
|
||||
_customOutputCache[expression] = output;
|
||||
outputQueue = &output;
|
||||
} else {
|
||||
_outputCache[expression] = output;
|
||||
outputQueue = &_outputCache[expression];
|
||||
}
|
||||
}
|
||||
|
||||
return EvaluateExpression(outputQueue, state, resultType, memoryValue, memoryAddr);
|
||||
if(outputQueue) {
|
||||
return EvaluateExpression(outputQueue, state, resultType, memoryValue, memoryAddr);
|
||||
} else {
|
||||
return EvaluateExpression(&output, state, resultType, memoryValue, memoryAddr);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, int16_t memoryValue, uint32_t memoryAddr)
|
||||
@ -349,7 +373,7 @@ int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, Eval
|
||||
{
|
||||
try {
|
||||
return PrivateEvaluate(expression, state, resultType, memoryValue, memoryAddr);
|
||||
} catch(std::exception) {
|
||||
} catch(std::exception e) {
|
||||
resultType = EvalResultType::Invalid;
|
||||
return 0;
|
||||
}
|
||||
@ -365,4 +389,10 @@ bool ExpressionEvaluator::Validate(string expression)
|
||||
} catch(std::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionEvaluator::ResetCustomCache()
|
||||
{
|
||||
LockHandler lock = _cacheLock.AcquireSafe();
|
||||
_customOutputCache.clear();
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
|
||||
struct DebugState;
|
||||
class Debugger;
|
||||
|
||||
enum EvalOperators
|
||||
{
|
||||
@ -85,8 +86,12 @@ private:
|
||||
const vector<int> _unaryPrecedence = { { 11, 11, 11, 11 } };
|
||||
|
||||
static std::unordered_map<string, std::vector<int>, StringHasher> _outputCache;
|
||||
static std::unordered_map<string, std::vector<int>, StringHasher> _customOutputCache;
|
||||
static SimpleLock _cacheLock;
|
||||
|
||||
Debugger* _debugger;
|
||||
bool _containsCustomLabels = false;
|
||||
|
||||
bool IsOperator(string token, int &precedence, bool unaryOperator);
|
||||
EvalOperators GetOperator(string token, bool unaryOperator);
|
||||
int GetOperatorPrecendence(string token);
|
||||
@ -97,9 +102,11 @@ private:
|
||||
int32_t PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue, uint32_t memoryAddr);
|
||||
|
||||
public:
|
||||
ExpressionEvaluator();
|
||||
ExpressionEvaluator(Debugger* debugger);
|
||||
|
||||
int32_t Evaluate(string expression, DebugState &state, int16_t memoryValue = 0, uint32_t memoryAddr = 0);
|
||||
int32_t Evaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue = 0, uint32_t memoryAddr = 0);
|
||||
bool Validate(string expression);
|
||||
|
||||
static void ResetCustomCache();
|
||||
};
|
@ -39,7 +39,8 @@ namespace Mesen.GUI.Debugger
|
||||
this.toolTip.SetToolTip(this.chkAbsolute, "Check to set an absolute address based on the exact address in PRG/CHR ROM (not CPU/PPU memory)");
|
||||
this.toolTip.SetToolTip(this.picHelp,
|
||||
"Most expressions/operators are accepted (C++ syntax)." + Environment.NewLine +
|
||||
"Note: Use the $ prefix to denote hexadecimal values." + Environment.NewLine + Environment.NewLine +
|
||||
"Note: Use the $ prefix to denote hexadecimal values." + Environment.NewLine +
|
||||
"Note 2: Labels assigned to the code can be used (their value will match the label's address in CPU memory)." + Environment.NewLine + Environment.NewLine +
|
||||
"A/X/Y/PS/SP: Value of registers" + Environment.NewLine +
|
||||
"Irq/Nmi: True if the Irq/Nmi flags are set" + Environment.NewLine +
|
||||
"Cycle/Scanline: Current cycle (0-340)/scanline(-1 to 260) of the PPU" + Environment.NewLine +
|
||||
|
@ -53,7 +53,8 @@ namespace Mesen.GUI.Debugger
|
||||
|
||||
this.toolTip.SetToolTip(this.picWatchHelp,
|
||||
"Most expressions/operators are accepted (C++ syntax)." + Environment.NewLine +
|
||||
"Note: Use the $ prefix to denote hexadecimal values." + Environment.NewLine + Environment.NewLine +
|
||||
"Note: Use the $ prefix to denote hexadecimal values." + Environment.NewLine +
|
||||
"Note 2: Labels assigned to the code can be used (their value will match the label's address in CPU memory)." + Environment.NewLine + Environment.NewLine +
|
||||
"A/X/Y/PS/SP: Value of registers" + Environment.NewLine +
|
||||
"Irq/Nmi: True if the Irq/Nmi flags are set" + Environment.NewLine +
|
||||
"Cycle/Scanline: Current cycle (0-340)/scanline(-1 to 260) of the PPU" + Environment.NewLine +
|
||||
|
@ -5,6 +5,7 @@ using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Mesen.GUI.Forms;
|
||||
@ -31,9 +32,10 @@ namespace Mesen.GUI.Debugger
|
||||
protected override bool ValidateInput()
|
||||
{
|
||||
CodeLabel existingLabel = LabelManager.GetLabel(txtLabel.Text);
|
||||
|
||||
return (existingLabel == null || existingLabel.Address == _address)
|
||||
&& !txtComment.Text.Contains('\x1') && !txtComment.Text.Contains('\x2')
|
||||
&& !txtLabel.Text.Contains('\x1') && !txtLabel.Text.Contains('\x2');
|
||||
&& (txtLabel.Text.Length == 0 || Regex.IsMatch(txtLabel.Text, "^[_a-zA-Z]+[_a-zA-Z0-9]*"));
|
||||
}
|
||||
|
||||
protected override void OnFormClosed(FormClosedEventArgs e)
|
||||
|
Loading…
x
Reference in New Issue
Block a user