Debugger: Allow code labels to be used in watch/breakpoint expressions

This commit is contained in:
Souryo 2016-11-22 18:28:59 -05:00
parent d4b9bd982e
commit 3751711f17
7 changed files with 80 additions and 19 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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();
};

View File

@ -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 +

View File

@ -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 +

View File

@ -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)