ported over wills debugger to latest branch

This commit is contained in:
Tom Laird-McConnell 2019-04-06 20:55:32 -07:00
Родитель 99e3e1fb14
Коммит ab8ebb4c74
22 изменённых файлов: 1744 добавлений и 14 удалений

Просмотреть файл

@ -104,6 +104,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.TriggerTrees.Tests", "tests\Microsoft.Bot.Builder.AI.TriggerTrees.Tests\Microsoft.Bot.Builder.AI.TriggerTrees.Tests.csproj", "{A9E5DD02-E633-46DC-B702-2ABA1AAC2851}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Builder.Dialogs.Debugging", "libraries\Microsoft.Bot.Builder.Dialogs.Debugging\Microsoft.Bot.Builder.Dialogs.Debugging.csproj", "{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug - NuGet Packages|Any CPU = Debug - NuGet Packages|Any CPU
@ -471,6 +473,14 @@ Global
{A9E5DD02-E633-46DC-B702-2ABA1AAC2851}.Documentation|Any CPU.Build.0 = Debug|Any CPU
{A9E5DD02-E633-46DC-B702-2ABA1AAC2851}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9E5DD02-E633-46DC-B702-2ABA1AAC2851}.Release|Any CPU.Build.0 = Release|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Documentation|Any CPU.ActiveCfg = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Documentation|Any CPU.Build.0 = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -524,6 +534,7 @@ Global
{FB2EA804-158C-4654-AD60-A2105AC366FF} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{D5E70443-4BA2-42ED-992A-010268440B08} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{A9E5DD02-E633-46DC-B702-2ABA1AAC2851} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}

Просмотреть файл

@ -146,7 +146,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Rules
protected void RegisterSourceLocation(string path, int lineNumber)
{
Debugger.SourceRegistry.Add(this, new Source.Range()
DebugSupport.SourceRegistry.Add(this, new Source.Range()
{
Path = path,
Start = new Source.Point() { LineIndex = lineNumber, CharIndex = 0 },

Просмотреть файл

@ -0,0 +1,468 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public sealed class DebugAdapter : DebugTransport, IMiddleware, DebugSupport.IDebugger
{
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
private readonly Source.IRegistry registry;
private readonly IBreakpoints breakpoints;
// lifetime scoped to IMiddleware.OnTurnAsync
private readonly ConcurrentDictionary<ITurnContext, TurnThreadModel> threadByContext = new ConcurrentDictionary<ITurnContext, TurnThreadModel>();
private readonly Identifier<IThreadModel> threads = new Identifier<IThreadModel>();
// TODO: leaks - consider scoping or ConditionalWeakTable
private readonly Identifier<FrameModel> frames = new Identifier<FrameModel>();
// TODO: leaks - consider scoping or ConditionalWeakTable
private readonly Identifier<VariableModel> variables = new Identifier<VariableModel>();
private interface IThreadModel
{
string Name { get; }
IReadOnlyList<FrameModel> Frames { get; }
RunModel Run { get; }
}
private sealed class BotThreadModel : IThreadModel
{
public string Name => "Bot";
public IReadOnlyList<FrameModel> Frames => Array.Empty<FrameModel>();
public RunModel Run { get; } = new RunModel();
}
private sealed class TurnThreadModel : IThreadModel
{
public TurnThreadModel(ITurnContext turnContext)
{
TurnContext = turnContext;
}
public string Name => TurnContext.Activity.Text;
public IReadOnlyList<FrameModel> Frames => Model.FramesFor(LastContext, LastItem, LastMore);
public RunModel Run { get; } = new RunModel();
public ITurnContext TurnContext { get; }
public DialogContext LastContext { get; set; }
public object LastItem { get; set; }
public string LastMore { get; set; }
}
public enum Phase { Started, Continue, Next, Step, Breakpoint, Pause, Exited };
public sealed class RunModel
{
public Phase? PhaseSent { get; set; }
public Phase Phase { get; set; } = Phase.Started;
public object Gate { get; } = new object();
public void Post(Phase what)
{
Monitor.Enter(Gate);
try
{
Phase = what;
Monitor.Pulse(Gate);
}
finally
{
Monitor.Exit(Gate);
}
}
}
private int VariablesReference(VariableModel variable)
{
var value = variable.Value;
if (Policy.ShowAsScalar(variable.Value))
{
return 0;
}
return variables.Add(variable);
}
private readonly Task task;
public DebugAdapter(Source.IRegistry registry, IBreakpoints breakpoints, ILogger logger)
: base(logger)
{
this.registry = registry ?? throw new ArgumentNullException(nameof(registry));
this.breakpoints = breakpoints ?? throw new ArgumentNullException(nameof(breakpoints));
this.task = ListenAsync(new IPEndPoint(IPAddress.Any, port: 4712), cancellationToken.Token);
threads.Add(new BotThreadModel());
}
public async Task DisposeAsync()
{
this.cancellationToken.Cancel();
using (this.cancellationToken)
using (this.task)
{
await this.task.ConfigureAwait(false);
}
}
async Task IMiddleware.OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
{
//if (System.Diagnostics.Debugger.IsAttached)
//{
// var source = new CancellationTokenSource();
// source.CancelAfter(TimeSpan.FromMinutes(2));
// cancellationToken = source.Token;
//}
var thread = new TurnThreadModel(turnContext);
var threadId = threads.Add(thread);
threadByContext.TryAdd(turnContext, thread);
try
{
thread.Run.Post(Phase.Started);
await UpdateThreadPhaseAsync(thread, null, cancellationToken).ConfigureAwait(false);
DebugSupport.IDebugger trace = this;
turnContext.TurnState.Add(trace);
await next(cancellationToken).ConfigureAwait(false);
}
finally
{
thread.Run.Post(Phase.Exited);
await UpdateThreadPhaseAsync(thread, null, cancellationToken).ConfigureAwait(false);
threadByContext.TryRemove(turnContext, out var ignored);
threads.Remove(thread);
}
}
async Task DebugSupport.IDebugger.StepAsync(DialogContext context, object item, string more, CancellationToken cancellationToken)
{
try
{
await OutputAsync($"Step: {Policy.NameFor(item)} {more}", item, cancellationToken).ConfigureAwait(false);
await UpdateBreakpointsAsync(cancellationToken).ConfigureAwait(false);
var thread = threadByContext[context.Context];
thread.LastContext = context;
thread.LastItem = item;
thread.LastMore = more;
var run = thread.Run;
if (breakpoints.IsBreakPoint(item))
{
run.Post(Phase.Breakpoint);
}
// TODO: implement asynchronous condition variables
Monitor.Enter(run.Gate);
try
{
// TODO: remove synchronous waits
UpdateThreadPhaseAsync(thread, item, cancellationToken).GetAwaiter().GetResult();
while (!(run.Phase == Phase.Started || run.Phase == Phase.Continue || run.Phase == Phase.Next))
{
Monitor.Wait(run.Gate);
}
if (run.Phase == Phase.Started)
{
run.Phase = Phase.Continue;
}
// TODO: remove synchronous waits
UpdateThreadPhaseAsync(thread, item, cancellationToken).GetAwaiter().GetResult();
if (run.Phase == Phase.Next)
{
run.Phase = Phase.Step;
}
}
finally
{
Monitor.Exit(run.Gate);
}
}
catch (Exception error)
{
this.logger.LogError(error, error.Message);
}
}
private async Task UpdateBreakpointsAsync(CancellationToken cancellationToken)
{
var breakpoints = this.breakpoints.ApplyUpdates();
foreach (var breakpoint in breakpoints)
{
if (breakpoint.verified)
{
var item = this.breakpoints.ItemFor(breakpoint);
await OutputAsync($"Set breakpoint at {Policy.NameFor(item)}", item, cancellationToken).ConfigureAwait(false);
}
var body = new { reason = "changed", breakpoint };
await SendAsync(Protocol.Event.From(NextSeq, "breakpoint", body), cancellationToken).ConfigureAwait(false);
}
}
private async Task UpdateThreadPhaseAsync(IThreadModel thread, object item, CancellationToken cancellationToken)
{
var run = thread.Run;
if (run.Phase == run.PhaseSent)
{
return;
}
var phase = run.Phase;
var suffix = item != null ? $" at {Policy.NameFor(item)}" : string.Empty;
var description = $"'{thread.Name}' is {phase}{suffix}";
await OutputAsync(description, item, cancellationToken).ConfigureAwait(false);
var threadId = this.threads[thread];
if (phase == Phase.Next)
{
phase = Phase.Continue;
}
string reason = phase.ToString().ToLower();
if (phase == Phase.Started || phase == Phase.Exited)
{
await SendAsync(Protocol.Event.From(NextSeq, "thread", new { threadId, reason }), cancellationToken).ConfigureAwait(false);
}
else if (phase == Phase.Continue)
{
await SendAsync(Protocol.Event.From(NextSeq, "continue", new { threadId, allThreadsContinued = false }), cancellationToken).ConfigureAwait(false);
}
else
{
var body = new
{
reason,
description,
threadId,
text = description,
preserveFocusHint = false,
allThreadsStopped = true,
};
await SendAsync(Protocol.Event.From(NextSeq, "stopped", body), cancellationToken).ConfigureAwait(false);
}
run.PhaseSent = run.Phase;
}
private async Task SendAsync(Protocol.Message message, CancellationToken cancellationToken)
{
var token = JToken.FromObject(message, new JsonSerializer() { NullValueHandling = NullValueHandling.Include });
await SendAsync(token, cancellationToken).ConfigureAwait(false);
}
private async Task OutputAsync(string text, object item, CancellationToken cancellationToken)
{
bool found = this.registry.TryGetValue(item, out var range);
var body = new
{
output = text + Environment.NewLine,
source = found ? new Protocol.Source(range.Path) : null,
line = found ? (int?)range.Start.LineIndex : null,
};
await SendAsync(Protocol.Event.From(NextSeq, "output", body), cancellationToken).ConfigureAwait(false);
}
private int sequence = 0;
private int NextSeq => Interlocked.Increment(ref sequence);
private async Task<Protocol.Message> DispatchAsync(Protocol.Message message, CancellationToken cancellationToken)
{
if (message is Protocol.Request<Protocol.Initialize> initialize)
{
var body = new
{
supportsConfigurationDoneRequest = true,
};
var response = Protocol.Response.From(NextSeq, initialize, body);
await SendAsync(response, cancellationToken).ConfigureAwait(false);
return Protocol.Event.From(NextSeq, "initialized", new { });
}
else if (message is Protocol.Request<Protocol.Launch> launch)
{
return Protocol.Response.From(NextSeq, launch, new { });
}
else if (message is Protocol.Request<Protocol.Attach> attach)
{
return Protocol.Response.From(NextSeq, attach, new { });
}
else if (message is Protocol.Request<Protocol.SetBreakpoints> setBreakpoints)
{
var arguments = setBreakpoints.arguments;
var file = Path.GetFileName(arguments.source.path);
await OutputAsync($"Set breakpoints for {file}", null, cancellationToken).ConfigureAwait(false);
var breakpoints = this.breakpoints.SetBreakpoints(arguments.source, arguments.breakpoints);
foreach (var breakpoint in breakpoints)
{
if (breakpoint.verified)
{
var item = this.breakpoints.ItemFor(breakpoint);
await OutputAsync($"Set breakpoint at {Policy.NameFor(item)}", item, cancellationToken).ConfigureAwait(false);
}
}
return Protocol.Response.From(NextSeq, setBreakpoints, new { breakpoints });
}
else if (message is Protocol.Request<Protocol.Threads> threads)
{
var body = new
{
threads = this.threads.Select(t => new { id = t.Key, name = t.Value.Name }).ToArray()
};
return Protocol.Response.From(NextSeq, threads, body);
}
else if (message is Protocol.Request<Protocol.StackTrace> stackTrace)
{
var arguments = stackTrace.arguments;
var thread = this.threads[arguments.threadId];
var frames = thread.Frames;
var stackFrames = new List<Protocol.StackFrame>();
foreach (var frame in frames)
{
var stackFrame = new Protocol.StackFrame()
{
id = this.frames.Add(frame),
name = frame.Name
};
if (this.registry.TryGetValue(frame.Item, out var range))
{
stackFrame.source = new Protocol.Source(range.Path);
stackFrame.line = range.Start.LineIndex;
stackFrame.column = range.Start.CharIndex;
stackFrame.endLine = range.After.LineIndex;
stackFrame.endColumn = range.After.CharIndex;
}
stackFrames.Add(stackFrame);
}
return Protocol.Response.From(NextSeq, stackTrace, new { stackFrames });
}
else if (message is Protocol.Request<Protocol.Scopes> scopes)
{
var arguments = scopes.arguments;
var frame = this.frames[arguments.frameId];
const bool expensive = false;
var body = new
{
scopes = new[] { new { expensive, name = frame.Name, variablesReference = VariablesReference(frame.Scopes) } }
};
return Protocol.Response.From(NextSeq, scopes, body);
}
else if (message is Protocol.Request<Protocol.Variables> vars)
{
var arguments = vars.arguments;
var variable = this.variables[arguments.variablesReference];
var variables = Model.VariablesFor(variable);
var body = new
{
variables = variables.Select(v => new
{
name = v.Name,
value = Policy.ScalarJsonValue(v.Value),
variablesReference = VariablesReference(v)
}).ToArray()
};
return Protocol.Response.From(NextSeq, vars, body);
}
else if (message is Protocol.Request<Protocol.Continue> cont)
{
bool found = this.threads.TryGetValue(cont.arguments.threadId, out var thread);
if (found)
{
thread.Run.Post(Phase.Continue);
}
return Protocol.Response.From(NextSeq, cont, new { allThreadsContinued = false });
}
else if (message is Protocol.Request<Protocol.Pause> pause)
{
bool found = this.threads.TryGetValue(pause.arguments.threadId, out var thread);
if (found)
{
thread.Run.Post(Phase.Pause);
}
return Protocol.Response.From(NextSeq, pause, new { });
}
else if (message is Protocol.Request<Protocol.Next> next)
{
bool found = this.threads.TryGetValue(next.arguments.threadId, out var thread);
if (found)
{
thread.Run.Post(Phase.Next);
}
return Protocol.Response.From(NextSeq, next, new { });
}
else if (message is Protocol.Request<Protocol.Disconnect> disconnect)
{
// possibly run all threads
return Protocol.Response.From(NextSeq, disconnect, new { });
}
else if (message is Protocol.Request request)
{
return Protocol.Response.From(NextSeq, request, new { });
}
else if (message is Protocol.Event @event)
{
throw new NotImplementedException();
}
else
{
throw new NotImplementedException();
}
}
protected override async Task AcceptAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
var token = await ReadAsync(cancellationToken).ConfigureAwait(false);
var message = Protocol.Parse(token);
var response = await DispatchAsync(message, cancellationToken).ConfigureAwait(false);
await SendAsync(response, cancellationToken).ConfigureAwait(false);
}
catch (Exception error)
{
this.logger.LogError(error, error.Message);
throw;
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public abstract class DebugTransport
{
private readonly ReaderWriterLock connected = new ReaderWriterLock(writer: true);
private readonly SemaphoreSlim readable = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim writable = new SemaphoreSlim(1, 1);
private StreamReader reader;
private StreamWriter writer;
private const string Prefix = @"Content-Length: ";
private static readonly Encoding Encoding = Encoding.UTF8;
protected readonly ILogger logger;
protected DebugTransport(ILogger logger)
{
this.logger = logger ?? NullLogger.Instance;
}
protected async Task ListenAsync(IPEndPoint point, CancellationToken cancellationToken)
{
var listener = new TcpListener(point);
listener.Start();
using (cancellationToken.Register(listener.Stop))
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
using (var client = await listener.AcceptTcpClientAsync().ConfigureAwait(false))
using (var stream = client.GetStream())
using (reader = new StreamReader(stream, Encoding))
using (writer = new StreamWriter(stream, Encoding))
using (cancellationToken.Register(() =>
{
stream.Close();
reader.Close();
writer.Close();
client.Close();
}))
{
connected.ExitWrite();
try
{
await AcceptAsync(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
}
finally
{
await connected.EnterWriteAsync(cancellationToken).ConfigureAwait(false);
}
}
}
catch (Exception error)
{
this.logger.LogError(error, error.Message);
}
}
}
}
protected abstract Task AcceptAsync(CancellationToken cancellationToken);
protected async Task<JToken> ReadAsync(CancellationToken cancellationToken)
{
var acquired = await connected.TryEnterReadAsync(cancellationToken).ConfigureAwait(false);
if (!acquired)
{
throw new InvalidOperationException();
}
try
{
using (await readable.WithWaitAsync(cancellationToken).ConfigureAwait(false))
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null)
{
throw new EndOfStreamException();
}
var empty = await reader.ReadLineAsync().ConfigureAwait(false);
if (empty.Length > 0)
{
throw new InvalidOperationException();
}
if (!line.StartsWith(Prefix))
{
throw new InvalidOperationException();
}
var count = int.Parse(line.Substring(Prefix.Length));
var buffer = new char[count];
int index = 0;
while (index < count)
{
var bytes = await reader.ReadAsync(buffer, index, count - index).ConfigureAwait(false);
index += bytes;
}
var json = new string(buffer);
var token = JToken.Parse(json);
logger.LogTrace($"READ: {token.ToString(Formatting.None)}");
return token;
}
}
finally
{
await connected.ExitReadAsync(cancellationToken).ConfigureAwait(false);
}
}
protected async Task SendAsync(JToken token, CancellationToken cancellationToken)
{
var acquired = await connected.TryEnterReadAsync(cancellationToken).ConfigureAwait(false);
if (!acquired)
{
return;
}
try
{
using (await writable.WithWaitAsync(cancellationToken).ConfigureAwait(false))
{
logger.LogTrace($"SEND: {token.ToString(Formatting.None)}");
var json = token.ToString();
var buffer = Encoding.GetBytes(json);
var length = buffer.Length + writer.NewLine.Length;
await writer.WriteLineAsync(Prefix + length).ConfigureAwait(false);
await writer.WriteLineAsync().ConfigureAwait(false);
await writer.WriteLineAsync(json).ConfigureAwait(false);
await writer.FlushAsync().ConfigureAwait(false);
}
}
finally
{
await connected.ExitReadAsync(cancellationToken).ConfigureAwait(false);
}
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public static partial class Extensions
{
public struct Releaser : IDisposable
{
public SemaphoreSlim Semaphore { get; }
public Releaser(SemaphoreSlim semaphore)
{
Semaphore = semaphore ?? throw new ArgumentNullException(nameof(semaphore));
}
public void Dispose()
{
Semaphore.Release();
}
}
public static async Task<Releaser> WithWaitAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken)
{
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
return new Releaser(semaphore);
}
}
}

Просмотреть файл

@ -0,0 +1,91 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public sealed class ReferenceEquality<T> : IEqualityComparer<T>
{
public static readonly IEqualityComparer<T> Instance = new ReferenceEquality<T>();
private ReferenceEquality()
{
}
bool IEqualityComparer<T>.Equals(T x, T y) => object.ReferenceEquals(x, y);
int IEqualityComparer<T>.GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj);
}
public sealed class Identifier<T> : IEnumerable<KeyValuePair<int, T>>
{
private readonly Dictionary<T, int> codeByItem = new Dictionary<T, int>(ReferenceEquality<T>.Instance);
private readonly Dictionary<int, T> itemByCode = new Dictionary<int, T>();
private readonly object gate = new object();
private int last = 0;
public int Add(T item)
{
lock (gate)
{
if (!this.codeByItem.TryGetValue(item, out var code))
{
// avoid falsey values
code = ++last;
this.codeByItem.Add(item, code);
this.itemByCode.Add(code, item);
}
return code;
}
}
public void Remove(T item)
{
lock (gate)
{
var code = this.codeByItem[item];
this.itemByCode.Remove(code);
this.codeByItem.Remove(item);
}
}
IEnumerator<KeyValuePair<int, T>> IEnumerable<KeyValuePair<int, T>>.GetEnumerator()
{
lock (gate)
{
return this.itemByCode.ToList().GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<int, T>>)this).GetEnumerator();
public T this[int code]
{
get
{
lock (gate)
{
return this.itemByCode[code];
}
}
}
public int this[T item]
{
get
{
lock (gate)
{
return this.codeByItem[item];
}
}
}
public bool TryGetValue(int code, out T item)
{
lock (gate)
{
return this.itemByCode.TryGetValue(code, out item);
}
}
}
}

Просмотреть файл

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Bot.Builder.Dialogs\Microsoft.Bot.Builder.Dialogs.csproj" />
<ProjectReference Include="..\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public sealed class VariableModel
{
public VariableModel(string name, object value)
{
Name = name;
Value = value;
}
public string Name { get; }
public object Value { get; }
public override string ToString() => $"{Name}={Value}";
}
public sealed class FrameModel
{
public FrameModel(string name, object item, VariableModel scopes)
{
Name = name;
Item = item;
Scopes = scopes;
}
public string Name { get; }
public object Item { get; }
public VariableModel Scopes { get; }
public override string ToString() => $"{Name}:{Item}";
}
public static class Model
{
public static IReadOnlyList<FrameModel> FramesFor(DialogContext dialogContext, object item, string more)
{
VariableModel scope = null;
var frames = new List<FrameModel>();
while (dialogContext != null)
{
foreach (var instance in dialogContext.Stack)
{
var state = dialogContext.State;
var data = new
{
user = state.User,
conversation = state.Conversation,
dialog = dialogContext.ActiveDialog != null ? state.Dialog : null,
turn = state.Turn,
entities = state.Entities,
tags = dialogContext.ActiveTags,
};
scope = new VariableModel(nameof(data) + frames.Count, data);
var dialog = dialogContext.FindDialog(instance.Id);
var frame = new FrameModel(instance.Id, dialog, scope);
frames.Add(frame);
}
dialogContext = dialogContext.Parent;
}
if (item != null)
{
var name = $"{Policy.NameFor(item)}:{more}";
frames.Insert(0, new FrameModel(name, item, scope));
}
return frames;
}
public static IReadOnlyList<VariableModel> VariablesFor(VariableModel variable)
{
var variables = new List<VariableModel>();
var value = variable.Value;
if (value is IReadOnlyDictionary<string, object> dictionary)
{
foreach (var kv in dictionary)
{
variables.Add(new VariableModel(kv.Key, kv.Value));
}
}
else if (value is IEnumerable<object> items)
{
int index = 0;
foreach (var item in items)
{
variables.Add(new VariableModel(index.ToString(), item));
++index;
}
}
else if (value != null)
{
var type = value.GetType();
var properties = type.GetProperties();
foreach (var property in properties)
{
var index = property.GetIndexParameters();
if (index.Length == 0)
{
variables.Add(new VariableModel(property.Name, property.GetValue(value)));
}
}
}
variables.RemoveAll(v => !Policy.ShowToDebugger(v.Value));
return variables;
}
}
}

Просмотреть файл

@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public static class Policy
{
public static string NameFor(object item) => item.GetType().Name;
public static bool ShowToDebugger(object value)
{
if (value == null)
{
return true;
}
var type = value.GetType();
return type != typeof(CancellationToken);
}
public static bool ShowAsScalar(object value)
{
if (value == null)
{
return true;
}
var type = value.GetType();
if (type.IsPrimitive || type == typeof(string))
{
return true;
}
return false;
}
public static string ScalarJsonValue(object value)
{
if (value == null)
{
return null;
}
if (ShowAsScalar(value))
{
return value.ToString();
}
if (value is ICollection collection)
{
return $"Count = {collection.Count}";
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
// https://github.com/Microsoft/debug-adapter-protocol/blob/gh-pages/debugAdapterProtocol.json
public static class Protocol
{
public abstract class Message
{
public int seq { get; set; }
public string type { get; set; }
[JsonExtensionData]
public JObject Rest { get; set; }
}
public class Request : Message
{
public string command { get; set; }
public override string ToString() => command;
}
public class Request<Arguments> : Request
{
public Arguments arguments { get; set; }
}
public class Attach
{
}
public class Launch
{
}
public class Initialize
{
public string clientID { get; set; }
public string clientName { get; set; }
public string adapterID { get; set; }
public string pathFormat { get; set; }
public bool linesStartAt1 { get; set; }
public bool columnsStartAt1 { get; set; }
public bool supportsVariableType { get; set; }
public bool supportsVariablePaging { get; set; }
public bool supportsRunInTerminalRequest { get; set; }
public string locale { get; set; }
}
public class SetBreakpoints
{
public Source source { get; set; }
public SourceBreakpoint[] breakpoints { get; set; }
public bool sourceModified { get; set; }
}
public class Threads
{
}
public abstract class PerThread
{
public int threadId { get; set; }
}
public class StackTrace : PerThread
{
public int? startFrame { get; set; }
public int? levels { get; set; }
}
public class Continue : PerThread
{
}
public class Pause : PerThread
{
}
public class Next : PerThread
{
}
public class Scopes
{
public int frameId { get; set; }
}
public class Variables
{
public int variablesReference { get; set; }
}
public class ConfigurationDone
{
}
public class Disconnect
{
public bool restart { get; set; }
public bool terminateDebuggee { get; set; }
}
public class Event : Message
{
public Event(int seq, string @event)
{
this.seq = seq;
this.type = "event";
this.@event = @event;
}
public string @event { get; set; }
public static Event<Body> From<Body>(int seq, string @event, Body body) => new Event<Body>(seq, @event) { body = body };
}
public class Event<Body> : Event
{
public Event(int seq, string @event)
: base(seq, @event)
{
}
public Body body { get; set; }
}
public class Response : Message
{
public Response(int seq, Request request)
{
this.seq = seq;
this.type = "response";
this.request_seq = request.seq;
this.success = true;
this.command = request.command;
}
public int request_seq { get; set; }
public bool success { get; set; }
public string command { get; set; }
public string message { get; set; }
public static Response<Body> From<Body>(int seq, Request request, Body body) => new Response<Body>(seq, request) { body = body };
}
public class Response<Body> : Response
{
public Response(int seq, Request request)
: base(seq, request)
{
}
public Body body { get; set; }
}
public abstract class Reference
{
public int id { get; set; }
}
public class Range : Reference
{
public Source source { get; set; }
public int? line { get; set; }
public int? column { get; set; }
public int? endLine { get; set; }
public int? endColumn { get; set; }
}
public class Breakpoint : Range
{
public bool verified { get; set; }
public string message { get; set; }
}
public class StackFrame : Range
{
public string name { get; set; }
}
public sealed class Thread : Reference
{
public string name { get; set; }
}
public sealed class Source
{
public Source(string path)
{
this.name = Path.GetFileName(path);
this.path = path;
}
public string name { get; set; }
public string path { get; set; }
}
public sealed class SourceBreakpoint
{
public int line { get; set; }
}
public static Message Parse(JToken token)
{
switch ((string)token["type"])
{
case "request":
switch ((string)token["command"])
{
case "launch": return token.ToObject<Request<Launch>>();
case "attach": return token.ToObject<Request<Attach>>();
case "initialize": return token.ToObject<Request<Initialize>>();
case "setBreakpoints": return token.ToObject<Request<SetBreakpoints>>();
case "threads": return token.ToObject<Request<Threads>>();
case "stackTrace": return token.ToObject<Request<StackTrace>>();
case "scopes": return token.ToObject<Request<Scopes>>();
case "variables": return token.ToObject<Request<Variables>>();
case "continue": return token.ToObject<Request<Continue>>();
case "pause": return token.ToObject<Request<Pause>>();
case "next": return token.ToObject<Request<Next>>();
case "stepIn": return token.ToObject<Request<Next>>();
case "stepOut": return token.ToObject<Request<Next>>();
case "configurationDone": return token.ToObject<Request<ConfigurationDone>>();
case "disconnect": return token.ToObject<Request<Disconnect>>();
case "setFunctionBreakpoints":
case "setExceptionBreakpoints":
default: return token.ToObject<Request>();
}
case "event":
switch ((string)token["event"])
{
default: return token.ToObject<Event>();
}
default:
return token.ToObject<Message>();
}
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public sealed class ReaderWriterLock : IDisposable
{
private readonly SemaphoreSlim reader;
private readonly SemaphoreSlim writer;
private int readers = 0;
public ReaderWriterLock(bool writer = false)
{
this.reader = new SemaphoreSlim(1, 1);
this.writer = new SemaphoreSlim(writer ? 0 : 1, 1);
}
public void Dispose()
{
reader.Dispose();
writer.Dispose();
}
public async Task<bool> TryEnterReadAsync(CancellationToken token)
{
using (await reader.WithWaitAsync(token).ConfigureAwait(false))
{
bool acquired = true;
if (readers == 0)
{
acquired = await writer.WaitAsync(TimeSpan.Zero).ConfigureAwait(false);
}
if (acquired)
{
++readers;
}
return acquired;
}
}
public async Task ExitReadAsync(CancellationToken token)
{
using (await reader.WithWaitAsync(token).ConfigureAwait(false))
{
--readers;
if (readers == 0)
{
writer.Release();
}
}
}
public async Task EnterWriteAsync(CancellationToken token)
{
await writer.WaitAsync(token).ConfigureAwait(false);
}
public void ExitWrite()
{
writer.Release();
}
}
}

Просмотреть файл

@ -0,0 +1,176 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public interface IBreakpoints
{
bool IsBreakPoint(object item);
object ItemFor(Protocol.Breakpoint breakpoint);
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints);
IReadOnlyList<Protocol.Breakpoint> ApplyUpdates();
}
public sealed class SourceMap : Source.IRegistry, IBreakpoints
{
private readonly object gate = new object();
private readonly Dictionary<object, Source.Range> sourceByItem = new Dictionary<object, Source.Range>(ReferenceEquality<object>.Instance);
private bool dirty = true;
void Source.IRegistry.Add(object item, Source.Range range)
{
lock (gate)
{
sourceByItem[item] = range;
dirty = true;
}
}
bool Source.IRegistry.TryGetValue(object item, out Source.Range range)
{
lock (gate)
{
if (item != null)
{
return sourceByItem.TryGetValue(item, out range);
}
else
{
range = default(Source.Range);
return false;
}
}
}
public sealed class Row
{
public Row(Protocol.Source source, Protocol.SourceBreakpoint sourceBreakpoint, Protocol.Breakpoint breakpoint)
{
Source = source;
SourceBreakpoint = sourceBreakpoint;
Breakpoint = breakpoint;
}
public Protocol.Source Source { get; }
public Protocol.SourceBreakpoint SourceBreakpoint { get; }
public Protocol.Breakpoint Breakpoint { get; }
public object item { get; set; }
}
private readonly Identifier<Row> rows = new Identifier<Row>();
private readonly HashSet<object> items = new HashSet<object>(ReferenceEquality<object>.Instance);
private IReadOnlyList<Protocol.Breakpoint> Update()
{
lock (gate)
{
items.Clear();
var changes = new List<Protocol.Breakpoint>();
foreach (var kv in rows)
{
var row = kv.Value;
var options = from sourceItem in sourceByItem
let source = sourceItem.Value
where source.Path == row.Source.path
where source.Start.LineIndex >= row.SourceBreakpoint.line
let distance = Math.Abs(source.Start.LineIndex - row.SourceBreakpoint.line)
orderby distance
select sourceItem;
options = options.ToArray();
var best = options.FirstOrDefault();
var itemNew = best.Key;
var verifiedNew = itemNew != null;
var lineNew = verifiedNew
? best.Value.Start.LineIndex
: row.SourceBreakpoint.line;
var itemOld = row.item;
var verifiedOld = row.Breakpoint.verified;
var lineOld = row.Breakpoint.line;
var changed = itemNew != itemOld
|| verifiedNew != verifiedOld
|| lineNew != lineOld;
if (changed)
{
changes.Add(row.Breakpoint);
row.item = itemNew;
row.Breakpoint.verified = verifiedNew;
row.Breakpoint.line = lineNew;
}
if (itemNew != null)
{
items.Add(itemNew);
}
}
return changes;
}
}
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints)
{
lock (gate)
{
var path = source.path;
foreach (var kv in rows)
{
var row = kv.Value;
if (row.Source.path == path)
{
rows.Remove(row);
}
}
foreach (var sourceBreakpoint in sourceBreakpoints)
{
var breakpoint = new Protocol.Breakpoint() { source = source };
var row = new Row(source, sourceBreakpoint, breakpoint);
breakpoint.id = this.rows.Add(row);
}
return Update();
}
}
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.ApplyUpdates()
{
lock (gate)
{
IReadOnlyList<Protocol.Breakpoint> updates = Array.Empty<Protocol.Breakpoint>();
if (dirty)
{
updates = Update();
dirty = false;
}
return updates;
}
}
bool IBreakpoints.IsBreakPoint(object item)
{
lock (gate)
{
return this.items.Contains(item);
}
}
object IBreakpoints.ItemFor(Protocol.Breakpoint breakpoint)
{
lock (gate)
{
return this.rows[breakpoint.id].item;
}
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
{
"comments": {
"lineComment": "//",
"blockComment": [ "/*", "*/" ]
},
"brackets": [
["{", "}"],
["[", "]"]
],
"autoClosingPairs": [
{
"open": "{",
"close": "}",
"notIn": [ "string" ]
},
{ "open": "[", "close": "]", "notIn": ["string"] },
{ "open": "(", "close": ")", "notIn": ["string"] },
{ "open": "'", "close": "'", "notIn": ["string"] },
{ "open": "/*", "close": "*/", "notIn": ["string"] },
{ "open": "\"", "close": "\"", "notIn": ["string", "comment"] },
{ "open": "`", "close": "`", "notIn": ["string", "comment"] }
]
}

Просмотреть файл

@ -0,0 +1,5 @@
{
"name": "dialog-debugger",
"version": "0.0.1",
"lockfileVersion": 1
}

Просмотреть файл

@ -0,0 +1,44 @@
{
"name": "dialog-debugger",
"version": "0.0.1",
"publisher": "Microsoft",
"engines": {
"vscode": "^1.0.0"
},
"contributes": {
"languages": [
{
"id": "dialog",
"aliases": [
"dialog",
"DIALOG"
],
"extensions": [
".dialog"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "dialog",
"scopeName": "source.json",
"path": "./syntaxes/JSON.tmLanguage.json"
}
],
"debuggers": [
{
"type": "dialog",
"label": "Dialog Debugger",
"languages": [
"dialog"
]
}
],
"breakpoints": [
{
"language": "dialog"
}
]
}
}

Просмотреть файл

@ -0,0 +1,213 @@
{
"information_for_contributors": [
"This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage",
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70",
"name": "JSON (Javascript Next)",
"scopeName": "source.json",
"patterns": [
{
"include": "#value"
}
],
"repository": {
"array": {
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.definition.array.begin.json"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.definition.array.end.json"
}
},
"name": "meta.structure.array.json",
"patterns": [
{
"include": "#value"
},
{
"match": ",",
"name": "punctuation.separator.array.json"
},
{
"match": "[^\\s\\]]",
"name": "invalid.illegal.expected-array-separator.json"
}
]
},
"comments": {
"patterns": [
{
"begin": "/\\*\\*(?!/)",
"captures": {
"0": {
"name": "punctuation.definition.comment.json"
}
},
"end": "\\*/",
"name": "comment.block.documentation.json"
},
{
"begin": "/\\*",
"captures": {
"0": {
"name": "punctuation.definition.comment.json"
}
},
"end": "\\*/",
"name": "comment.block.json"
},
{
"captures": {
"1": {
"name": "punctuation.definition.comment.json"
}
},
"match": "(//).*$\\n?",
"name": "comment.line.double-slash.js"
}
]
},
"constant": {
"match": "\\b(?:true|false|null)\\b",
"name": "constant.language.json"
},
"number": {
"match": "(?x) # turn on extended mode\n -? # an optional minus\n (?:\n 0 # a zero\n | # ...or...\n [1-9] # a 1-9 character\n \\d* # followed by zero or more digits\n )\n (?:\n (?:\n \\. # a period\n \\d+ # followed by one or more digits\n )?\n (?:\n [eE] # an e character\n [+-]? # followed by an option +/-\n \\d+ # followed by one or more digits\n )? # make exponent optional\n )? # make decimal portion optional",
"name": "constant.numeric.json"
},
"object": {
"begin": "\\{",
"beginCaptures": {
"0": {
"name": "punctuation.definition.dictionary.begin.json"
}
},
"end": "\\}",
"endCaptures": {
"0": {
"name": "punctuation.definition.dictionary.end.json"
}
},
"name": "meta.structure.dictionary.json",
"patterns": [
{
"comment": "the JSON object key",
"include": "#objectkey"
},
{
"include": "#comments"
},
{
"begin": ":",
"beginCaptures": {
"0": {
"name": "punctuation.separator.dictionary.key-value.json"
}
},
"end": "(,)|(?=\\})",
"endCaptures": {
"1": {
"name": "punctuation.separator.dictionary.pair.json"
}
},
"name": "meta.structure.dictionary.value.json",
"patterns": [
{
"comment": "the JSON object value",
"include": "#value"
},
{
"match": "[^\\s,]",
"name": "invalid.illegal.expected-dictionary-separator.json"
}
]
},
{
"match": "[^\\s\\}]",
"name": "invalid.illegal.expected-dictionary-separator.json"
}
]
},
"string": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.json"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.json"
}
},
"name": "string.quoted.double.json",
"patterns": [
{
"include": "#stringcontent"
}
]
},
"objectkey": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.support.type.property-name.begin.json"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.support.type.property-name.end.json"
}
},
"name": "string.json support.type.property-name.json",
"patterns": [
{
"include": "#stringcontent"
}
]
},
"stringcontent": {
"patterns": [
{
"match": "(?x) # turn on extended mode\n \\\\ # a literal backslash\n (?: # ...followed by...\n [\"\\\\/bfnrt] # one of these characters\n | # ...or...\n u # a u\n [0-9a-fA-F]{4}) # and four hex digits",
"name": "constant.character.escape.json"
},
{
"match": "\\\\.",
"name": "invalid.illegal.unrecognized-string-escape.json"
}
]
},
"value": {
"patterns": [
{
"include": "#constant"
},
{
"include": "#number"
},
{
"include": "#string"
},
{
"include": "#array"
},
{
"include": "#object"
},
{
"include": "#comments"
}
]
}
}
}

Просмотреть файл

@ -8,7 +8,7 @@ using static Microsoft.Bot.Builder.Dialogs.Debugging.Source;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public static partial class Debugger
public static partial class DebugSupport
{
public static Source.IRegistry SourceRegistry { get; set; } = new NullRegistry();

Просмотреть файл

@ -195,7 +195,7 @@ namespace Microsoft.Bot.Builder.Dialogs
{
if (!string.IsNullOrEmpty(path))
{
Debugger.SourceRegistry.Add(this, new Source.Range()
DebugSupport.SourceRegistry.Add(this, new Source.Range()
{
Path = path,
Start = new Source.Point() { LineIndex = lineNumber, CharIndex = 0 },

Просмотреть файл

@ -4,6 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
@ -28,6 +29,13 @@
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"type": "dialog",
"request": "attach",
"trace": true,
"name": "Attach to Dialog",
"debugServer": 4712
}
]
}

Просмотреть файл

@ -60,6 +60,7 @@
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.AI.LanguageGeneration\Microsoft.Bot.Builder.AI.LanguageGeneration.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Adaptive\Microsoft.Bot.Builder.Dialogs.Adaptive.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Composition\Microsoft.Bot.Builder.Dialogs.Composition.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Debugging\Microsoft.Bot.Builder.Dialogs.Debugging.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Declarative\Microsoft.Bot.Builder.Dialogs.Declarative.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs\Microsoft.Bot.Builder.Dialogs.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />

Просмотреть файл

@ -3,25 +3,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.AI.LanguageGeneration;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Declarative;
using Microsoft.Bot.Builder.Dialogs.Debugging;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
using Microsoft.Bot.Builder.Dialogs.Declarative.Types;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TestBot.Json.Recognizers;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Bot.Builder.Dialogs.Debugging;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
using Microsoft.Bot.Builder;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using System.Linq;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.Logging.Debug;
namespace Microsoft.Bot.Builder.TestBot.Json
{
@ -60,6 +60,19 @@ namespace Microsoft.Bot.Builder.TestBot.Json
{
TelemetryConfiguration.Active.DisableTelemetry = true;
}
// hook up debugging support
var sourceMap = new SourceMap();
DebugAdapter debugAdapter = null;
bool enableDebugger = true;
if (enableDebugger)
{
// by setting the source registry all dialogs will register themselves to be debugged as execution flows
DebugSupport.SourceRegistry = sourceMap;
debugAdapter = new DebugAdapter(sourceMap, sourceMap, new DebugLogger(nameof(DebugAdapter)));
}
// m
services.AddSingleton<IConfiguration>(this.Configuration);
IStorage dataStore = new MemoryStorage();
@ -80,7 +93,7 @@ namespace Microsoft.Bot.Builder.TestBot.Json
(IServiceProvider sp) =>
{
// declarative Adaptive dialogs bot sample
return new TestBot(accessors, resourceExplorer, Source.NullRegistry.Instance);
return new TestBot(accessors, resourceExplorer, DebugSupport.SourceRegistry);
// LG bot sample
// return new TestBotLG(accessors);
@ -96,6 +109,11 @@ namespace Microsoft.Bot.Builder.TestBot.Json
options.Middleware.Add(new RegisterClassMiddleware<IStorage>(dataStore));
options.Middleware.Add(new RegisterClassMiddleware<ResourceExplorer>(resourceExplorer));
if (debugAdapter != null)
{
options.Middleware.Add(debugAdapter);
}
var lg = new LGLanguageGenerator(resourceExplorer);
options.Middleware.Add(new RegisterClassMiddleware<ILanguageGenerator>(lg));
options.Middleware.Add(new RegisterClassMiddleware<IMessageActivityGenerator>(new TextMessageActivityGenerator(lg)));

Просмотреть файл

@ -234,7 +234,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Loader.Tests
string projPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, $@"..\..\..\..\..\samples\Microsoft.Bot.Builder.TestBot.Json\Microsoft.Bot.Builder.TestBot.Json.csproj"));
var resourceExplorer = ResourceExplorer.LoadProject(projPath);
var dialog = DeclarativeTypeLoader.Load<IDialog>(path, resourceExplorer, Source.NullRegistry.Instance);
var dialog = DeclarativeTypeLoader.Load<IDialog>(path, resourceExplorer, DebugSupport.SourceRegistry);
IStorage dataStore = new MemoryStorage();