From 951d30ee77e32e7b2ff580d5955cb5632669b7fa Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 3 Mar 2020 17:48:06 -0800 Subject: [PATCH 1/3] Use OnException event to display stack traces for exceptions. --- src/Core/Core.csproj | 6 +- src/Jupyter/Extensions.cs | 16 +++ src/Jupyter/IQSharpEngine.cs | 3 + src/Jupyter/Magic/Simulate.cs | 4 +- .../DisplayableExceptionEncoders.cs | 111 ++++++++++++++++++ src/Tool/appsettings.json | 20 ++-- 6 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 src/Jupyter/Visualization/DisplayableExceptionEncoders.cs diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index bd0ac90cff..cd397762ea 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -34,9 +34,9 @@ - - - + + + diff --git a/src/Jupyter/Extensions.cs b/src/Jupyter/Extensions.cs index 068bae1636..3512bc341f 100644 --- a/src/Jupyter/Extensions.cs +++ b/src/Jupyter/Extensions.cs @@ -9,6 +9,7 @@ using Microsoft.Jupyter.Core; using Microsoft.Jupyter.Core.Protocol; using Microsoft.Quantum.IQSharp.Common; +using Microsoft.Quantum.Simulation.Common; using Microsoft.Quantum.Simulation.Core; using Microsoft.Quantum.Simulation.Simulators; @@ -99,5 +100,20 @@ public static QuantumSimulator WithJupyterDisplay(this QuantumSimulator simulato return simulator; } + + public static T WithStackTraceDisplay(this T simulator, IChannel channel) + where T: SimulatorBase + { + simulator.DisableDefaultStackTraceHandling(); + simulator.OnException += (exception, stackTrace) => + { + channel.Display(new DisplayableException + { + Exception = exception, + StackTrace = stackTrace + }); + }; + return simulator; + } } } diff --git a/src/Jupyter/IQSharpEngine.cs b/src/Jupyter/IQSharpEngine.cs index 8c2a194b84..f277e61519 100644 --- a/src/Jupyter/IQSharpEngine.cs +++ b/src/Jupyter/IQSharpEngine.cs @@ -54,6 +54,8 @@ IShellRouter shellRouter RegisterDisplayEncoder(new StateVectorToTextResultEncoder(configurationSource)); RegisterDisplayEncoder(new DataTableToHtmlEncoder()); RegisterDisplayEncoder(new DataTableToTextEncoder()); + RegisterDisplayEncoder(new DisplayableExceptionToHtmlEncoder()); + RegisterDisplayEncoder(new DisplayableExceptionToTextEncoder()); RegisterJsonEncoder(JsonConverters.AllConverters); RegisterSymbolResolver(this.SymbolsResolver); @@ -119,3 +121,4 @@ public override ExecutionResult ExecuteMundane(string input, IChannel channel) } } } + diff --git a/src/Jupyter/Magic/Simulate.cs b/src/Jupyter/Magic/Simulate.cs index 1584848e4e..a82761e07a 100644 --- a/src/Jupyter/Magic/Simulate.cs +++ b/src/Jupyter/Magic/Simulate.cs @@ -36,7 +36,9 @@ public async Task RunAsync(string input, IChannel channel) var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) throw new InvalidOperationException($"Invalid operation name: {name}"); - using var qsim = new QuantumSimulator().WithJupyterDisplay(channel, ConfigurationSource); + using var qsim = new QuantumSimulator() + .WithJupyterDisplay(channel, ConfigurationSource) + .WithStackTraceDisplay(channel); var value = await symbol.Operation.RunAsync(qsim, args); return value.ToExecutionResult(); } diff --git a/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs b/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs new file mode 100644 index 0000000000..93dc4e1418 --- /dev/null +++ b/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Data; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Microsoft.Jupyter.Core; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Microsoft.Quantum.Simulation.Common; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace Microsoft.Quantum.IQSharp +{ + internal struct DisplayableException + { + public Exception Exception; + public IEnumerable StackTrace; + + public string Header => + $"Unhandled exception. {Exception.GetType().FullName}: {Exception.Message}"; + } + + internal static class StackFrameExtensions + { + internal static string ToFriendlyName(this ICallable callable) + { + var fullName = callable.FullName; + return fullName.StartsWith(Snippets.SNIPPETS_NAMESPACE) + ? fullName.Substring(Snippets.SNIPPETS_NAMESPACE.Length + 1) + : fullName; + } + + internal static string ToSourceLink(this StackFrame frame) => + Regex.Match(frame.SourceFile, "snippet_[0-9]*.qs$").Success + ? "(notebook)" + : $"{frame.SourceFile}:{frame.FailedLineNumber}"; + } + + public class DisplayableExceptionToHtmlEncoder : IResultEncoder + { + public string MimeType => MimeTypes.Html; + + public EncodedData? Encode(object displayable) + { + if (displayable is DisplayableException ex) + { + var rows = ex + .StackTrace + .Select(frame => $@" + + {frame.ToSourceLink()} + {frame.Callable.ToFriendlyName()} + + "); + var table = $@" +
+ + Unhandled exception of type {ex.Exception.GetType().FullName}: {ex.Exception.Message} + + + + + + + + + + + {String.Join("\n", rows)} + +
SourceCallable
+
+ "; + return table.ToEncodedData(); + } + else return null; + } + } + public class DisplayableExceptionToTextEncoder : IResultEncoder + { + public string MimeType => MimeTypes.PlainText; + + public EncodedData? Encode(object displayable) + { + if (displayable is DisplayableException ex) + { + var builder = new StringBuilder(); + builder.AppendLine(ex.Header); + var first = true; + foreach (var frame in ex.StackTrace) + { + builder.AppendLine( + (first ? " ---> " : " at ") + + frame.ToStringWithBestSourceLocation() + ); + first = false; + } + return builder.ToString().ToEncodedData(); + } + else return null; + } + } + +} diff --git a/src/Tool/appsettings.json b/src/Tool/appsettings.json index 4530b0a131..0314536f48 100644 --- a/src/Tool/appsettings.json +++ b/src/Tool/appsettings.json @@ -6,18 +6,18 @@ }, "AllowedHosts": "*", "DefaultPackageVersions": [ - "Microsoft.Quantum.Compiler::0.10.2001.2831", + "Microsoft.Quantum.Compiler::0.10.2003.304-beta", - "Microsoft.Quantum.CsharpGeneration::0.10.2001.2831", - "Microsoft.Quantum.Development.Kit::0.10.2001.2831", - "Microsoft.Quantum.Simulators::0.10.2001.2831", - "Microsoft.Quantum.Xunit::0.10.2001.2831", + "Microsoft.Quantum.CsharpGeneration::0.10.2003.304-beta", + "Microsoft.Quantum.Development.Kit::0.10.2003.304-beta", + "Microsoft.Quantum.Simulators::0.10.2003.304-beta", + "Microsoft.Quantum.Xunit::0.10.2003.304-beta", - "Microsoft.Quantum.Standard::0.10.2001.2831", - "Microsoft.Quantum.Chemistry::0.10.2001.2831", - "Microsoft.Quantum.Chemistry.Jupyter::0.10.2001.2831", - "Microsoft.Quantum.Numerics::0.10.2001.2831", + "Microsoft.Quantum.Standard::0.10.2003.304-beta", + "Microsoft.Quantum.Chemistry::0.10.2003.304-beta", + "Microsoft.Quantum.Chemistry.Jupyter::0.10.2003.304-beta", + "Microsoft.Quantum.Numerics::0.10.2003.304-beta", - "Microsoft.Quantum.Research::0.10.2001.2831" + "Microsoft.Quantum.Research::0.10.2003.304-beta" ] } From 1a3f1aee6127c56536e6837340dd8f775ca048ce Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Mar 2020 10:50:48 -0700 Subject: [PATCH 2/3] Addressing feedback from @anpaz-msft. --- src/Core/Core.csproj | 6 +++--- src/Jupyter/Extensions.cs | 2 +- src/Jupyter/Magic/EstimateMagic.cs | 2 +- src/Jupyter/Magic/ToffoliMagic.cs | 2 +- src/Tool/appsettings.json | 20 ++++++++++---------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index cd397762ea..3e8942e5af 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -34,9 +34,9 @@ - - - + + + diff --git a/src/Jupyter/Extensions.cs b/src/Jupyter/Extensions.cs index d03f573b9a..624b4935f6 100644 --- a/src/Jupyter/Extensions.cs +++ b/src/Jupyter/Extensions.cs @@ -158,7 +158,7 @@ public static QuantumSimulator WithJupyterDisplay(this QuantumSimulator simulato public static T WithStackTraceDisplay(this T simulator, IChannel channel) where T: SimulatorBase { - simulator.DisableDefaultStackTraceHandling(); + simulator.DisableExceptionPrinting(); simulator.OnException += (exception, stackTrace) => { channel.Display(new DisplayableException diff --git a/src/Jupyter/Magic/EstimateMagic.cs b/src/Jupyter/Magic/EstimateMagic.cs index ae8e2c668b..c2016c3c4b 100644 --- a/src/Jupyter/Magic/EstimateMagic.cs +++ b/src/Jupyter/Magic/EstimateMagic.cs @@ -56,7 +56,7 @@ public async Task RunAsync(string input, IChannel channel) var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) throw new InvalidOperationException($"Invalid operation name: {name}"); - var qsim = new ResourcesEstimator(); + var qsim = new ResourcesEstimator().WithStackTraceDisplay(channel); qsim.DisableLogToConsole(); await symbol.Operation.RunAsync(qsim, args); diff --git a/src/Jupyter/Magic/ToffoliMagic.cs b/src/Jupyter/Magic/ToffoliMagic.cs index b63ec1cb02..4185b1c4a3 100644 --- a/src/Jupyter/Magic/ToffoliMagic.cs +++ b/src/Jupyter/Magic/ToffoliMagic.cs @@ -47,7 +47,7 @@ public async Task RunAsync(string input, IChannel channel) var symbol = SymbolResolver.Resolve(name) as IQSharpSymbol; if (symbol == null) throw new InvalidOperationException($"Invalid operation name: {name}"); - var qsim = new ToffoliSimulator(); + var qsim = new ToffoliSimulator().WithStackTraceDisplay(channel); qsim.DisableLogToConsole(); qsim.OnLog += channel.Stdout; diff --git a/src/Tool/appsettings.json b/src/Tool/appsettings.json index 0314536f48..68275089e3 100644 --- a/src/Tool/appsettings.json +++ b/src/Tool/appsettings.json @@ -6,18 +6,18 @@ }, "AllowedHosts": "*", "DefaultPackageVersions": [ - "Microsoft.Quantum.Compiler::0.10.2003.304-beta", + "Microsoft.Quantum.Compiler::0.10.2003.1102-beta", - "Microsoft.Quantum.CsharpGeneration::0.10.2003.304-beta", - "Microsoft.Quantum.Development.Kit::0.10.2003.304-beta", - "Microsoft.Quantum.Simulators::0.10.2003.304-beta", - "Microsoft.Quantum.Xunit::0.10.2003.304-beta", + "Microsoft.Quantum.CsharpGeneration::0.10.2003.1102-beta", + "Microsoft.Quantum.Development.Kit::0.10.2003.1102-beta", + "Microsoft.Quantum.Simulators::0.10.2003.1102-beta", + "Microsoft.Quantum.Xunit::0.10.2003.1102-beta", - "Microsoft.Quantum.Standard::0.10.2003.304-beta", - "Microsoft.Quantum.Chemistry::0.10.2003.304-beta", - "Microsoft.Quantum.Chemistry.Jupyter::0.10.2003.304-beta", - "Microsoft.Quantum.Numerics::0.10.2003.304-beta", + "Microsoft.Quantum.Standard::0.10.2003.1102-beta", + "Microsoft.Quantum.Chemistry::0.10.2003.1102-beta", + "Microsoft.Quantum.Chemistry.Jupyter::0.10.2003.1102-beta", + "Microsoft.Quantum.Numerics::0.10.2003.1102-beta", - "Microsoft.Quantum.Research::0.10.2003.304-beta" + "Microsoft.Quantum.Research::0.10.2003.1102-beta" ] } From cdb3f8211f12a0836848845c8f7729899209f366 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Mar 2020 10:53:03 -0700 Subject: [PATCH 3/3] Added API docs. --- .../Visualization/DisplayableExceptionEncoders.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs b/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs index 93dc4e1418..c7a28e7ec2 100644 --- a/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs +++ b/src/Jupyter/Visualization/DisplayableExceptionEncoders.cs @@ -43,10 +43,15 @@ internal static string ToSourceLink(this StackFrame frame) => : $"{frame.SourceFile}:{frame.FailedLineNumber}"; } + /// + /// Encodes exceptions augmented with Q# metadata into HTML tables. + /// public class DisplayableExceptionToHtmlEncoder : IResultEncoder { + /// public string MimeType => MimeTypes.Html; + /// public EncodedData? Encode(object displayable) { if (displayable is DisplayableException ex) @@ -83,10 +88,17 @@ public class DisplayableExceptionToHtmlEncoder : IResultEncoder else return null; } } + + /// + /// Encodes exceptions augmented with Q# metadata into plain text + /// tables. + /// public class DisplayableExceptionToTextEncoder : IResultEncoder { + /// public string MimeType => MimeTypes.PlainText; + /// public EncodedData? Encode(object displayable) { if (displayable is DisplayableException ex)