From 479e91c5f29f64b133eb2c7ab40564b44da3d11b Mon Sep 17 00:00:00 2001 From: Sour Date: Sun, 26 Apr 2020 22:32:17 -0400 Subject: [PATCH] Debugger: Add support for assert expressions --- UI/Debugger/Breakpoints/Breakpoint.cs | 1 + UI/Debugger/Breakpoints/BreakpointManager.cs | 26 ++++++++--- UI/Debugger/Integration/DbgImporter.cs | 39 ++++++++++++++++- UI/Debugger/Labels/LabelManager.cs | 45 ++++++++++++++++++-- UI/Debugger/frmDebugger.cs | 9 +++- 5 files changed, 109 insertions(+), 11 deletions(-) diff --git a/UI/Debugger/Breakpoints/Breakpoint.cs b/UI/Debugger/Breakpoints/Breakpoint.cs index 8935831..5dd7eb9 100644 --- a/UI/Debugger/Breakpoints/Breakpoint.cs +++ b/UI/Debugger/Breakpoints/Breakpoint.cs @@ -21,6 +21,7 @@ namespace Mesen.GUI.Debugger public CpuType CpuType; public BreakpointAddressType AddressType = BreakpointAddressType.SingleAddress; public string Condition = ""; + public bool IsAssert = false; public SnesMemoryType MemoryType { diff --git a/UI/Debugger/Breakpoints/BreakpointManager.cs b/UI/Debugger/Breakpoints/BreakpointManager.cs index 34b9711..badf981 100644 --- a/UI/Debugger/Breakpoints/BreakpointManager.cs +++ b/UI/Debugger/Breakpoints/BreakpointManager.cs @@ -18,6 +18,8 @@ namespace Mesen.GUI.Debugger get { return _breakpoints.ToList().AsReadOnly(); } } + public static List Asserts { internal get; set; } + public static void AddCpuType(CpuType cpuType) { _activeCpuTypes.Add(cpuType); @@ -121,20 +123,34 @@ namespace Mesen.GUI.Debugger public static void SetBreakpoints() { List breakpoints = new List(); - for(int i = 0; i < Breakpoints.Count; i++) { - if(_activeCpuTypes.Contains(Breakpoints[i].CpuType)) { - breakpoints.Add(Breakpoints[i].ToInteropBreakpoint(i)); + + ReadOnlyCollection userBreakpoints = BreakpointManager.Breakpoints; + for(int i = 0; i < userBreakpoints.Count; i++) { + if(_activeCpuTypes.Contains(userBreakpoints[i].CpuType)) { + breakpoints.Add(userBreakpoints[i].ToInteropBreakpoint(breakpoints.Count)); } } + + List assertBreakpoints = BreakpointManager.Asserts; + for(int i = 0; i < assertBreakpoints.Count; i++) { + if(_activeCpuTypes.Contains(assertBreakpoints[i].CpuType)) { + breakpoints.Add(assertBreakpoints[i].ToInteropBreakpoint(breakpoints.Count)); + } + } + DebugApi.SetBreakpoints(breakpoints.ToArray(), (UInt32)breakpoints.Count); } public static Breakpoint GetBreakpointById(int breakpointId) { - if(breakpointId < 0 || breakpointId >= _breakpoints.Count) { + if(breakpointId < 0) { return null; + } else if(breakpointId < _breakpoints.Count) { + return _breakpoints[breakpointId]; + } else if(breakpointId < _breakpoints.Count + Asserts.Count) { + return Asserts[breakpointId - _breakpoints.Count]; } - return _breakpoints[breakpointId]; + return null; } } } diff --git a/UI/Debugger/Integration/DbgImporter.cs b/UI/Debugger/Integration/DbgImporter.cs index 985e784..5cec94b 100644 --- a/UI/Debugger/Integration/DbgImporter.cs +++ b/UI/Debugger/Integration/DbgImporter.cs @@ -596,6 +596,7 @@ namespace Mesen.GUI.Debugger.Integration private void LoadComments() { DbgIntegrationConfig config = ConfigManager.Config.Debug.DbgIntegration; + SortedDictionary constants = GetConstants(); foreach(KeyValuePair kvp in _lines) { try { LineInfo line = kvp.Value; @@ -668,7 +669,7 @@ namespace Mesen.GUI.Debugger.Integration if(address >= 0 && memoryType != null) { CodeLabel label = this.CreateLabel(address, memoryType.Value, 1); if(label != null) { - label.Comment = comment; + label.Comment = ParseAsserts(constants, comment); } } } @@ -678,6 +679,42 @@ namespace Mesen.GUI.Debugger.Integration } } + private string[] _splitOnNewLine = { Environment.NewLine }; + private string ParseAsserts(SortedDictionary constants, string 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 entry in constants) { + commentLines[i] = commentLines[i].Replace(entry.Key, entry.Value.ToString()); + } + } + } + + return string.Join(Environment.NewLine, commentLines); + } + + private SortedDictionary GetConstants() + { + SortedDictionary constants = new SortedDictionary(Comparer.Create((string a, string b) => { + if(a.Length == b.Length) { + return a.CompareTo(b); + } + return b.Length - a.Length; + })); + + foreach(SymbolInfo symbol in _symbols.Values) { + AddressInfo? addressInfo = GetSymbolAddressInfo(symbol); + if(!addressInfo.HasValue && symbol.Address.HasValue) { + constants[symbol.Name] = symbol.Address.Value; + } + } + + return constants; + } + private void LoadFileData(string path) { Dictionary maxLineCountByFile = new Dictionary(); diff --git a/UI/Debugger/Labels/LabelManager.cs b/UI/Debugger/Labels/LabelManager.cs index 031533e..db2ccaa 100644 --- a/UI/Debugger/Labels/LabelManager.cs +++ b/UI/Debugger/Labels/LabelManager.cs @@ -12,6 +12,7 @@ namespace Mesen.GUI.Debugger.Labels 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 _labelsByKey = new Dictionary(); private static HashSet _labels = new HashSet(); @@ -54,7 +55,7 @@ namespace Mesen.GUI.Debugger.Labels SetLabel(label, false); } if(raiseEvents) { - OnLabelUpdated?.Invoke(null, null); + ProcessLabelUpdate(); } } @@ -135,7 +136,7 @@ namespace Mesen.GUI.Debugger.Labels } if(raiseEvent) { - OnLabelUpdated?.Invoke(null, null); + ProcessLabelUpdate(); RefreshDisassembly(label); } @@ -162,7 +163,7 @@ namespace Mesen.GUI.Debugger.Labels } if(needEvent) { - OnLabelUpdated?.Invoke(null, null); + ProcessLabelUpdate(); RefreshDisassembly(label); } } @@ -200,6 +201,44 @@ namespace Mesen.GUI.Debugger.Labels LabelManager.SetLabels(new List(_labels), true); } + private static void ProcessLabelUpdate() + { + OnLabelUpdated?.Invoke(null, null); + UpdateAssertBreakpoints(); + } + + private static void UpdateAssertBreakpoints() + { + List asserts = new List(); + + Action addAssert = (CodeLabel label, string condition, CpuType cpuType) => { + asserts.Add(new Breakpoint() { + BreakOnExec = true, + MemoryType = label.MemoryType, + CpuType = cpuType, + Address = label.Address, + Condition = "!(" + condition + ")", + IsAssert = true + }); + }; + + foreach(CodeLabel label in _labels) { + foreach(string commentLine in label.Comment.Split('\n')) { + Match m = LabelManager.AssertRegex.Match(commentLine); + if(m.Success) { + CpuType cpuType = label.MemoryType.ToCpuType(); + addAssert(label, m.Groups[1].Value, cpuType); + if(cpuType == CpuType.Cpu) { + addAssert(label, m.Groups[1].Value, CpuType.Sa1); + } + } + } + } + + BreakpointManager.Asserts = asserts; + BreakpointManager.SetBreakpoints(); + } + public static void SetDefaultLabels() { //B-Bus registers diff --git a/UI/Debugger/frmDebugger.cs b/UI/Debugger/frmDebugger.cs index 196be8c..fe7a6c7 100644 --- a/UI/Debugger/frmDebugger.cs +++ b/UI/Debugger/frmDebugger.cs @@ -476,8 +476,9 @@ namespace Mesen.GUI.Debugger void ProcessBreakEvent(BreakEvent evt, DebugState state, int activeAddress) { + Breakpoint bp = null; if(ConfigManager.Config.Debug.Debugger.BringToFrontOnBreak) { - Breakpoint bp = BreakpointManager.GetBreakpointById(evt.BreakpointId); + bp = BreakpointManager.GetBreakpointById(evt.BreakpointId); if(bp?.CpuType == _cpuType || evt.Source > BreakSource.PpuStep) { DebugWindowManager.BringToFront(this); } @@ -489,7 +490,11 @@ namespace Mesen.GUI.Debugger if(evt.Source == BreakSource.Breakpoint || evt.Source > BreakSource.PpuStep) { string message = ResourceHelper.GetEnumText(evt.Source); if(evt.Source == BreakSource.Breakpoint) { - message += ": " + ResourceHelper.GetEnumText(evt.Operation.Type) + " ($" + evt.Operation.Address.ToString("X4") + ":$" + evt.Operation.Value.ToString("X2") + ")"; + if(bp != null && bp.IsAssert) { + message = "Assert failed: " + bp.Condition.Substring(2, bp.Condition.Length - 3); + } else { + message += ": " + ResourceHelper.GetEnumText(evt.Operation.Type) + " ($" + evt.Operation.Address.ToString("X4") + ":$" + evt.Operation.Value.ToString("X2") + ")"; + } } ctrlDisassemblyView.SetMessage(new TextboxMessageInfo() { Message = message }); }