Debugger: Ability to define assert statements in comments
This commit is contained in:
parent
77e04a679e
commit
1ff7ed7b8c
5 changed files with 107 additions and 20 deletions
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue