Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/exception breakpoints #1434

Merged
8 changes: 7 additions & 1 deletion src/OneScript.DebugProtocol/IDebuggerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ public interface IDebuggerService
/// Все точки останова уже установлены, все настройки сделаны
/// </summary>
void Execute(int threadId);


/// <summary>
/// Добавление фильтров точек останова для исплючений
/// </summary>
/// <param name="filters"></param>
void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters);

/// <summary>
/// Установка точек остановки
/// </summary>
Expand Down
34 changes: 29 additions & 5 deletions src/OneScript.DebugServices/DefaultBreakpointManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@ This Source Code Form is subject to the terms of the
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.Linq;
using OneScript.Commons;
using ScriptEngine.Machine;

namespace OneScript.DebugServices
{
public class DefaultBreakpointManager : IBreakpointManager
{
private readonly Dictionary<string, string> _exceptionBreakpointsFilters = new Dictionary<string, string>();
private readonly List<BreakpointDescriptor> _breakpoints = new List<BreakpointDescriptor>();
private int _idsGenerator;

public void SetExceptionBreakpoints((string Id, string Condition)[] filters)
{
_exceptionBreakpointsFilters?.Clear();
filters?.ForEach(c =>_exceptionBreakpointsFilters.Add(c.Id, c.Condition));
}

public void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints)
{
var cleaned = _breakpoints.Where(x => x.Module != module)
Expand All @@ -27,17 +36,32 @@ public void SetBreakpoints(string module, (int Line, string Condition)[] breakpo
_breakpoints.AddRange(cleaned);
}

public bool Find(string module, int line)
{
return _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line) != null;
}
public bool FindBreakpoint(string module, int line)
=> _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line) != null;

public string GetCondition(string module, int line)
=> _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line).Condition;

public void Clear()
=> _breakpoints.Clear();

public bool StopOnAnyException(string message)
=> NeedStopOnException("all", message);

public bool StopOnUncaughtException(string message)
=> NeedStopOnException("uncaught", message);

private bool NeedStopOnException(string filterId, string message)
{
_breakpoints.Clear();
if (_exceptionBreakpointsFilters?.TryGetValue(filterId, out var condition) == true)
{
if (string.IsNullOrEmpty(condition))
return true;
else
return message.Contains(condition);
}

return false;
}
}
}
9 changes: 9 additions & 0 deletions src/OneScript.DebugServices/DefaultDebugService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ public void Execute(int threadId)
}
}

public void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters)
{
_breakpointManager.SetExceptionBreakpoints(filters);

// Уведомить все потоки о новых фильтрах
foreach (var machine in _threadManager.GetAllTokens().Select(x => x.Machine))
machine.SetDebugMode(_breakpointManager);
}

public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet)
{
var confirmedBreakpoints = new List<Breakpoint>();
Expand Down
8 changes: 7 additions & 1 deletion src/ScriptEngine/Machine/IDebugController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ public interface IDebugController : IDisposable

public interface IBreakpointManager
{
void SetExceptionBreakpoints((string Id, string Condition)[] filters);

void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints);

bool StopOnAnyException(string message);

bool StopOnUncaughtException(string message);

bool Find(string module, int line);
bool FindBreakpoint(string module, int line);

string GetCondition(string module, int line);

Expand Down
19 changes: 18 additions & 1 deletion src/ScriptEngine/Machine/MachineInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,14 @@ private void ExecuteCode()
{
SetScriptExceptionSource(exc);

if (ShouldRethrowException(exc))
var shouldRethrow = ShouldRethrowException(exc);

if (MachineStopped != null && _stopManager != null)
if (_stopManager.Breakpoints.StopOnAnyException(exc.MessageWithoutCodeFragment) ||
shouldRethrow && _stopManager.Breakpoints.StopOnUncaughtException(exc.MessageWithoutCodeFragment))
EmitStopOnException();

if (shouldRethrow)
throw;
}
}
Expand Down Expand Up @@ -1297,6 +1304,16 @@ private void LineNum(int arg)
NextInstruction();
}

private void EmitStopOnException()
{
if (MachineStopped != null && _stopManager != null)
{
CreateFullCallstack();
var args = new MachineStoppedEventArgs(MachineStopReason.Exception, Environment.CurrentManagedThreadId, "");
MachineStopped?.Invoke(this, args);
}
}

private void EmitStopEventIfNecessary()
{
if (MachineStopped != null && _stopManager != null && _stopManager.ShouldStopAtThisLine(_module.Source.Location, _currentFrame))
Expand Down
4 changes: 1 addition & 3 deletions src/ScriptEngine/Machine/MachineStopManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ internal enum DebugState
SteppingOut
}



internal class MachineStopManager
{
private struct StopPoint
Expand Down Expand Up @@ -119,7 +117,7 @@ public bool ShouldStopAtThisLine(string module, ExecutionFrame currentFrame)

private bool HitBreakpointOnLine(string module, ExecutionFrame currentFrame)
{
return _breakpoints.Find(module, currentFrame.LineNumber);
return _breakpoints.FindBreakpoint(module, currentFrame.LineNumber);
}

private bool FrameIsInStopList(ExecutionFrame currentFrame)
Expand Down
30 changes: 25 additions & 5 deletions src/VSCode.DebugAdapter/DebugSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ public class Breakpoint
public bool verified { get; }
public int line { get; }

public Breakpoint(bool verified, int line) {
public Breakpoint(bool verified)
{
this.verified = verified;
line = 0;
}

public Breakpoint(bool verified, int line) {
this.verified = verified;
this.line = line;
}
Expand Down Expand Up @@ -180,6 +186,7 @@ public class Capabilities : ResponseBody {
public bool supportsFunctionBreakpoints;
public bool supportsConditionalBreakpoints;
public bool supportsEvaluateForHovers;
public bool supportsExceptionFilterOptions;
public dynamic[] exceptionBreakpointFilters;
public bool supportTerminateDebuggee;
}
Expand Down Expand Up @@ -262,15 +269,28 @@ public class SetBreakpointsResponseBody : ResponseBody

public SetBreakpointsResponseBody(List<Breakpoint> bpts = null) {
if (bpts == null)
breakpoints = new Breakpoint[0];
else
breakpoints = Array.Empty<Breakpoint>();
else
breakpoints = bpts.ToArray<Breakpoint>();
}
}

// ---- The Session --------------------------------------------------------
public class SetExceptionBreakpointsResponseBody : ResponseBody
{
public Breakpoint[] breakpoints { get; }

public SetExceptionBreakpointsResponseBody(List<Breakpoint> bpts = null)
{
if (bpts == null)
breakpoints = Array.Empty<Breakpoint>();
else
breakpoints = bpts.ToArray<Breakpoint>();
}
}

// ---- The Session --------------------------------------------------------

public abstract class DebugSession : ProtocolServer
public abstract class DebugSession : ProtocolServer
{
private bool _clientLinesStartAt1 = true;
private bool _clientPathsAreUri = true;
Expand Down
5 changes: 5 additions & 0 deletions src/VSCode.DebugAdapter/DebugeeProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ public void Kill()
_process.WaitForExit(1500);
}

public void SetExceptionsBreakpoints((string Id, string Condition)[] filters)
{
_debugger.SetMachineExceptionBreakpoints(filters);
}

public Breakpoint[] SetBreakpoints(IEnumerable<Breakpoint> breakpoints)
{
var confirmedBreaks = _debugger.SetMachineBreakpoints(breakpoints.ToArray());
Expand Down
60 changes: 53 additions & 7 deletions src/VSCode.DebugAdapter/OscriptDebugSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ This Source Code Form is subject to the terms of the
using System.IO;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OneScript.DebugProtocol;
using Serilog;
using VSCodeDebug;
using static System.Net.WebRequestMethods;


namespace VSCode.DebugAdapter
Expand Down Expand Up @@ -43,7 +45,26 @@ public override void Initialize(Response response, dynamic args)
supportsConditionalBreakpoints = true,
supportsFunctionBreakpoints = false,
supportsConfigurationDoneRequest = true,
exceptionBreakpointFilters = new dynamic[0],
supportsExceptionFilterOptions = true,
exceptionBreakpointFilters = new dynamic[]
{
new
{
filter = "uncaught",
label = "Необработанные исключения",
description = "Остановка при возникновении необработанного исключения",
supportsCondition = true,
conditionDescription = "Искомая подстрока текста исключения"
},
new
{
filter = "all",
label = "Все исключения",
description = "Остановка при возникновении любого исключения",
supportsCondition = true,
conditionDescription = "Искомая подстрока текста исключения"
}
},
supportsEvaluateForHovers = true,
supportTerminateDebuggee = true
});
Expand Down Expand Up @@ -113,7 +134,7 @@ public override void Launch(Response response, dynamic args)
public override void Attach(Response response, dynamic arguments)
{
LogCommandReceived();
_process.DebugPort = getInt(arguments, "debugPort", 2801);
_process.DebugPort = GetFromContainer(arguments, "debugPort", 2801);
_process.ProcessExited += (s, e) =>
{
Log.Information("Debuggee has exited");
Expand Down Expand Up @@ -148,6 +169,31 @@ public override void Disconnect(Response response, dynamic arguments)
SendResponse(response);
}

public override void SetExceptionBreakpoints(Response response, dynamic arguments)
{
LogCommandReceived(arguments);
Log.Debug("Exception breakpoints: {Data}", JsonConvert.SerializeObject(arguments));

var acceptedFilters = new List<VSCodeDebug.Breakpoint>();
var filters = new List<(string Id, string Condition)>();

foreach(var filter in arguments.filters)
{
filters.Add((filter, ""));
acceptedFilters.Add(new VSCodeDebug.Breakpoint(true));
}

foreach (var filterOption in arguments.filterOptions)
{
filters.Add((filterOption.filterId, filterOption.condition ?? ""));
acceptedFilters.Add(new VSCodeDebug.Breakpoint(true));
}

_process.SetExceptionsBreakpoints(filters.ToArray());

SendResponse(response, new SetExceptionBreakpointsResponseBody(acceptedFilters));
}

public override void SetBreakpoints(Response response, dynamic arguments)
{
LogCommandReceived();
Expand Down Expand Up @@ -322,7 +368,7 @@ public override void StackTrace(Response response, dynamic arguments)
public override void Scopes(Response response, dynamic arguments)
{
LogCommandReceived();
int frameId = getInt(arguments, "frameId");
int frameId = GetFromContainer(arguments, "frameId", 0);
var frame = _framesHandles.Get(frameId, null);
if (frame == null)
{
Expand All @@ -338,7 +384,7 @@ public override void Scopes(Response response, dynamic arguments)
public override void Variables(Response response, dynamic arguments)
{
LogCommandReceived();
int varsHandle = getInt(arguments, "variablesReference");
int varsHandle = GetFromContainer(arguments, "variablesReference", 0);
var variables = _variableHandles.Get(varsHandle, null);
if (variables == null)
{
Expand Down Expand Up @@ -386,7 +432,7 @@ public override void Evaluate(Response response, dynamic arguments)
{
LogCommandReceived();
// expression, frameId, context
int frameId = getInt(arguments, "frameId");
int frameId = GetFromContainer(arguments, "frameId", 0);
var frame = _framesHandles.Get(frameId, null);
if (frame == null)
{
Expand Down Expand Up @@ -438,11 +484,11 @@ private void SendOutput(string category, string data)
}
}

private static int getInt(dynamic container, string propertyName, int dflt = 0)
private static T GetFromContainer<T>(dynamic container, string propertyName, T dflt = default)
{
try
{
return (int)container[propertyName];
return (T)container[propertyName];
}
catch (Exception)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ public void Execute(int threadId)
WriteCommand(threadId);
}

public void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters)
{
WriteCommand(filters);
}

public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet)
{
WriteCommand(breaksToSet);
Expand Down