Debugger: Ability to define assert statements in comments

This commit is contained in:
Sour 2020-04-18 17:55:35 -04:00
parent 77e04a679e
commit 1ff7ed7b8c
5 changed files with 107 additions and 20 deletions

View file

@ -292,7 +292,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) {
return false;
}
operatorExpected = true;
operatorOrEndTokenExpected = true;
} else if(token[0] == '[') {
bracketCount++;
opStack.push(EvalOperators::Bracket);
@ -302,7 +302,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) {
return false;
}
operatorExpected = true;
operatorOrEndTokenExpected = true;
} else if(token[0] == '{') {
braceCount++;
opStack.push(EvalOperators::Braces);
@ -312,7 +312,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){
return false;
}
operatorExpected = true;
operatorOrEndTokenExpected = true;
} else {
if(token[0] < '0' || token[0] > '9') {
return false;
@ -605,5 +605,8 @@ void ExpressionEvaluator::RunTests()
test(":($1FFF+1)", EvalResultType::Numeric, -1);
test(":$1FFF+1", EvalResultType::Numeric, 0x800);
test("1+:$100", EvalResultType::Numeric, 0x101);
test("[$4100+[$4100]]", EvalResultType::Numeric, 0x41);
test("-($10+[$4100])", EvalResultType::Numeric, -0x51);
}
#endif

View file

@ -22,6 +22,15 @@ namespace Mesen.GUI.Debugger
}
}
public static List<Breakpoint> Asserts { internal get; set; } = new List<Breakpoint>();
public static List<Breakpoint> GetAllBreakpoints()
{
List<Breakpoint> breakpoints = new List<Breakpoint>(BreakpointManager.Breakpoints);
breakpoints.AddRange(BreakpointManager.Asserts);
return breakpoints;
}
public static void RefreshBreakpoints(Breakpoint bp = null)
{
if(BreakpointsChanged != null) {
@ -124,8 +133,15 @@ namespace Mesen.GUI.Debugger
public static void SetBreakpoints()
{
List<InteropBreakpoint> breakpoints = new List<InteropBreakpoint>();
for(int i = 0; i < Breakpoints.Count; i++) {
breakpoints.Add(Breakpoints[i].ToInteropBreakpoint(i));
ReadOnlyCollection<Breakpoint> userBreakpoints = BreakpointManager.Breakpoints;
for(int i = 0; i < userBreakpoints.Count; i++) {
breakpoints.Add(userBreakpoints[i].ToInteropBreakpoint(breakpoints.Count));
}
List<Breakpoint> assertBreakpoints = BreakpointManager.Asserts;
for(int i = 0; i < assertBreakpoints.Count; i++) {
breakpoints.Add(assertBreakpoints[i].ToInteropBreakpoint(breakpoints.Count));
}
InteropEmu.DebugSetBreakpoints(breakpoints.ToArray(), (UInt32)breakpoints.Count);
}

View file

@ -410,7 +410,7 @@ namespace Mesen.GUI.Debugger
_scopes.Add(scope.ID, scope);
return true;
} else if(row.StartsWith("scope")) {
System.Diagnostics.Debug.Fail("Regex doesn't match scope");
//System.Diagnostics.Debug.Fail("Regex doesn't match scope");
}
return false;
@ -559,8 +559,12 @@ namespace Mesen.GUI.Debugger
}
}
private string[] _splitOnNewLine = { Environment.NewLine };
private void LoadComments()
{
SortedDictionary<string, int> constants = GetConstants();
foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
try {
LineInfo line = kvp.Value;
@ -622,7 +626,18 @@ namespace Mesen.GUI.Debugger
if(address >= 0 && addressType != null) {
CodeLabel label = this.CreateLabel(address, addressType.Value, 1);
if(label != null) {
label.Comment = comment;
//Parse and replace content of asserts as needed
string[] commentLines = comment.Split(_splitOnNewLine, StringSplitOptions.None);
for(int i = 0; i < commentLines.Length; i++) {
Match m = LabelManager.AssertRegex.Match(commentLines[i]);
if(m.Success) {
foreach(KeyValuePair<string, int> entry in constants) {
commentLines[i] = commentLines[i].Replace(entry.Key, entry.Value.ToString());
}
}
}
label.Comment = string.Join(Environment.NewLine, commentLines);
}
}
}
@ -632,6 +647,26 @@ namespace Mesen.GUI.Debugger
}
}
private SortedDictionary<string, int> GetConstants()
{
SortedDictionary<string, int> constants = new SortedDictionary<string, int>(Comparer<string>.Create((string a, string b) => {
if(a.Length == b.Length) {
return a.CompareTo(b);
}
return b.Length - a.Length;
}));
foreach(Ld65DbgImporter.SymbolInfo symbol in GetSymbols()) {
AddressTypeInfo addressInfo = GetSymbolAddressInfo(symbol);
bool isConstant = addressInfo == null;
if(isConstant && symbol.Address.HasValue) {
constants[symbol.Name] = symbol.Address.Value;
}
}
return constants;
}
private void LoadFileData(string path)
{
Dictionary<int, int> maxLineCountByFile = new Dictionary<int, int>();

View file

@ -55,6 +55,7 @@ namespace Mesen.GUI.Debugger
public class LabelManager
{
public static Regex LabelRegex { get; } = new Regex("^[@_a-zA-Z]+[@_a-zA-Z0-9]*$", RegexOptions.Compiled);
public static Regex AssertRegex { get; } = new Regex(@"assert\((.*)\)", RegexOptions.Compiled);
private static Dictionary<UInt32, CodeLabel> _labelsByKey = new Dictionary<UInt32, CodeLabel>();
private static HashSet<CodeLabel> _labels = new HashSet<CodeLabel>();
@ -98,10 +99,37 @@ namespace Mesen.GUI.Debugger
SetLabel(label.Address, label.AddressType, label.Label, label.Comment, false, label.Flags, label.Length);
}
if(raiseEvents) {
OnLabelUpdated?.Invoke(null, null);
ProcessLabelUpdate();
}
}
private static void ProcessLabelUpdate()
{
OnLabelUpdated?.Invoke(null, null);
UpdateAssertBreakpoints();
}
private static void UpdateAssertBreakpoints()
{
List<Breakpoint> asserts = new List<Breakpoint>();
foreach(CodeLabel label in LabelManager.GetLabels()) {
foreach(string commentLine in label.Comment.Split('\n')) {
Match m = LabelManager.AssertRegex.Match(commentLine);
if(m.Success) {
asserts.Add(new Breakpoint() {
BreakOnExec = true,
MemoryType = label.AddressType.ToMemoryType(),
Address = label.Address,
Condition = "!(" + m.Groups[1].Value + ")"
});
}
}
}
BreakpointManager.Asserts = asserts;
BreakpointManager.SetBreakpoints();
}
public static List<CodeLabel> GetLabels()
{
return _labels.ToList<CodeLabel>();
@ -154,7 +182,7 @@ namespace Mesen.GUI.Debugger
}
if(raiseEvent) {
OnLabelUpdated?.Invoke(null, null);
ProcessLabelUpdate();
}
return true;
@ -180,7 +208,7 @@ namespace Mesen.GUI.Debugger
}
if(needEvent) {
OnLabelUpdated?.Invoke(null, null);
ProcessLabelUpdate();
}
}

View file

@ -451,19 +451,24 @@ namespace Mesen.GUI.Debugger
BreakpointType bpType = (BreakpointType)(byte)((param >> 8) & 0x0F);
UInt16 bpAddress = (UInt16)(param >> 16);
ReadOnlyCollection<Breakpoint> breakpoints = BreakpointManager.Breakpoints;
int regularBpCount = BreakpointManager.Breakpoints.Count;
List<Breakpoint> breakpoints = BreakpointManager.GetAllBreakpoints();
if(breakpointId >= 0 && breakpointId < breakpoints.Count) {
Breakpoint bp = breakpoints[breakpointId];
if(bpType != BreakpointType.Global) {
message += ": " + ResourceHelper.GetEnumText(bpType) + " ($" + bpAddress.ToString("X4") + ":$" + bpValue.ToString("X2") + ")";
}
if(!string.IsNullOrWhiteSpace(bp.Condition)) {
string cond = bp.Condition.Trim();
if(cond.Length > 27) {
message += Environment.NewLine + cond.Substring(0, 24) + "...";
} else {
message += Environment.NewLine + cond;
if(breakpointId < regularBpCount) {
if(bpType != BreakpointType.Global) {
message += ": " + ResourceHelper.GetEnumText(bpType) + " ($" + bpAddress.ToString("X4") + ":$" + bpValue.ToString("X2") + ")";
}
if(!string.IsNullOrWhiteSpace(bp.Condition)) {
string cond = bp.Condition.Trim();
if(cond.Length > 27) {
message += Environment.NewLine + cond.Substring(0, 24) + "...";
} else {
message += Environment.NewLine + cond;
}
}
} else {
message = "Assert failed: " + bp.Condition.Substring(2, bp.Condition.Length - 3);
}
}
} else if(source == BreakSource.BreakOnUninitMemoryRead) {