From e3ec2a422655eb132a484715d179590f7389fcbd Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 2 Mar 2020 17:13:40 -0800 Subject: [PATCH 1/3] Separate stack trace handling into new OnException event. --- src/Simulation/Common/SimulatorBase.cs | 75 +++++++++++++++++--------- src/Simulation/Common/StackTrace.cs | 38 +++++++------ 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/src/Simulation/Common/SimulatorBase.cs b/src/Simulation/Common/SimulatorBase.cs index 66999f33bac..207d7d09507 100644 --- a/src/Simulation/Common/SimulatorBase.cs +++ b/src/Simulation/Common/SimulatorBase.cs @@ -8,6 +8,8 @@ using Microsoft.Quantum.Simulation.Core; +#nullable enable + namespace Microsoft.Quantum.Simulation.Common { /// @@ -19,16 +21,17 @@ namespace Microsoft.Quantum.Simulation.Common /// public abstract class SimulatorBase : AbstractFactory, IOperationFactory { - public event Action OnOperationStart = null; - public event Action OnOperationEnd = null; - public event Action OnFail = null; - public event Action OnAllocateQubits = null; - public event Action> OnReleaseQubits = null; - public event Action OnBorrowQubits = null; - public event Action> OnReturnQubits = null; - public event Action OnLog = null; - - public IQubitManager QubitManager { get; } + public event Action? OnOperationStart = null; + public event Action? OnOperationEnd = null; + public event Action? OnFail = null; + public event Action? OnAllocateQubits = null; + public event Action>? OnReleaseQubits = null; + public event Action? OnBorrowQubits = null; + public event Action>? OnReturnQubits = null; + public event Action? OnLog = null; + public event Action>? OnException = null; + + public IQubitManager? QubitManager { get; } public abstract string Name { get; } @@ -42,15 +45,16 @@ public abstract class SimulatorBase : AbstractFactory, IOperat /// Only Q# operations are tracked and reported in the stack trace. Q# functions or calls from /// classical hosts like C# or Python are not included. /// - public StackFrame[] CallStack { get; private set; } + public StackFrame[]? CallStack { get; private set; } - public SimulatorBase(IQubitManager qubitManager = null) + public SimulatorBase(IQubitManager? qubitManager = null) { this.QubitManager = qubitManager; this.InitBuiltinOperations(this.GetType()); EnableLogToConsole(); + EnableDefaultStackTraceHandling(); if (this.QubitManager != null) { @@ -92,7 +96,7 @@ public override AbstractCallable CreateInstance(Type t) return new GenericCallable(this, t); } - AbstractCallable result = null; + AbstractCallable? result = null; t = t.GetNativeImplementation() ?? t; @@ -110,6 +114,19 @@ public override AbstractCallable CreateInstance(Type t) return result; } + protected void WriteStackTraceToLog(Exception exception, IEnumerable callStack) + { + OnLog?.Invoke($"Unhandled exception. {exception.GetType().FullName}: {exception.Message}"); + var first = true; + foreach (var sf in callStack) + { + var msg = (first ? " ---> " : " at ") + sf.ToStringWithBestSourceLocation(); + OnLog?.Invoke(msg); + first = false; + } + OnLog?.Invoke(""); + } + public virtual O Execute(I args) where T : AbstractCallable, ICallable { StackTraceCollector stackTraceCollector = new StackTraceCollector(this); @@ -124,16 +141,7 @@ public virtual O Execute(I args) where T : AbstractCallable, ICallable catch (Exception e) // Dumps q# call-stack in case of exception if CallStack tracking was enabled { this.CallStack = stackTraceCollector.CallStack; - OnLog?.Invoke($"Unhandled exception. {e.GetType().FullName}: {e.Message}"); - bool first = true; - foreach (StackFrame sf in this.CallStack) - { - var msg = (first ? " ---> " : " at ") + sf.ToStringWithBestSourceLocation(); - OnLog?.Invoke(msg); - first = false; - } - OnLog?.Invoke(""); - + this.OnException?.Invoke(e, this.CallStack); throw; } finally @@ -165,6 +173,25 @@ public void DisableLogToConsole() OnLog -= Console.WriteLine; } + /// + /// Adds an event to the OnStackTrace event that logs stack traces + /// as plain text via the OnLog event (e.g.: for console output). + /// + public void EnableDefaultStackTraceHandling() + { + OnException += WriteStackTraceToLog; + } + + + /// + /// Disables default handling of stack traces, such that stack + /// traces are not written to the OnLog event handler. + /// + public void DisableDefaultStackTraceHandling() + { + OnException -= WriteStackTraceToLog; + } + /// /// Verifies that the Qubit can be used as part of an operation. /// @@ -210,7 +237,7 @@ public void CheckQubits(IQArray qubits, string arrayName) public class Allocate : Intrinsic.Allocate { private SimulatorBase sim; - private IQubitManager manager; + private IQubitManager? manager; public Allocate(SimulatorBase m) : base(m) { diff --git a/src/Simulation/Common/StackTrace.cs b/src/Simulation/Common/StackTrace.cs index 335c13e6b4b..c0840d02100 100644 --- a/src/Simulation/Common/StackTrace.cs +++ b/src/Simulation/Common/StackTrace.cs @@ -89,6 +89,22 @@ public override string ToString() return string.Format(messageFormat, Callable.FullName, $"{SourceFile}:line {FailedLineNumber}"); } + /// + /// Gets the best possible source location for this stack frame. + /// If the source is not available on local machine, the source + // location will be replaced by a URL pointing to GitHub repository. + /// + /// + /// This is more costly than because it + /// checks if source file exists on disk. + /// If the file does not exist it calls to get the URL + /// which is also more costly than . + /// + public virtual string GetBestSourceLocation() => + System.IO.File.Exists(SourceFile) + ? SourceFile + : GetURLFromPDB() ?? SourceFile; + /// /// The same as , but tries to point to best source location. /// If the source is not available on local machine, source location will be replaced @@ -97,26 +113,8 @@ public override string ToString() /// If the file does not exist it calls to get the URL /// which is also more costly than . /// - public virtual string ToStringWithBestSourceLocation() - { - string message = ToString(); - if (System.IO.File.Exists(SourceFile)) - { - return message; - } - else - { - string url = GetURLFromPDB(); - if (url == null) - { - return message; - } - else - { - return string.Format(messageFormat, Callable.FullName, url); - } - } - } + public virtual string ToStringWithBestSourceLocation() => + string.Format(messageFormat, Callable.FullName, $"{GetBestSourceLocation()}:line {FailedLineNumber}"); /// /// Finds correspondence between Q# and C# stack frames and populates Q# stack frame information from C# stack frames From 7c58ad435b88f6a6bbe26da5616ecb44fa19b632 Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Mon, 9 Mar 2020 23:01:03 -0700 Subject: [PATCH 2/3] Update src/Simulation/Common/SimulatorBase.cs Co-Authored-By: Andres Paz --- src/Simulation/Common/SimulatorBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Common/SimulatorBase.cs b/src/Simulation/Common/SimulatorBase.cs index 207d7d09507..a6d5eaca71e 100644 --- a/src/Simulation/Common/SimulatorBase.cs +++ b/src/Simulation/Common/SimulatorBase.cs @@ -174,7 +174,7 @@ public void DisableLogToConsole() } /// - /// Adds an event to the OnStackTrace event that logs stack traces + /// Adds an event to the OnException event that logs stack traces /// as plain text via the OnLog event (e.g.: for console output). /// public void EnableDefaultStackTraceHandling() From 7e2341d8ffb479c8f3e818de0a6540b71af9ec13 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 9 Mar 2020 23:13:02 -0700 Subject: [PATCH 3/3] Addressing feedback from @anpaz-msft. --- src/Simulation/Common/SimulatorBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Simulation/Common/SimulatorBase.cs b/src/Simulation/Common/SimulatorBase.cs index 207d7d09507..3c8fbfc408a 100644 --- a/src/Simulation/Common/SimulatorBase.cs +++ b/src/Simulation/Common/SimulatorBase.cs @@ -54,7 +54,7 @@ public SimulatorBase(IQubitManager? qubitManager = null) this.InitBuiltinOperations(this.GetType()); EnableLogToConsole(); - EnableDefaultStackTraceHandling(); + EnableExceptionPrinting(); if (this.QubitManager != null) { @@ -177,7 +177,7 @@ public void DisableLogToConsole() /// Adds an event to the OnStackTrace event that logs stack traces /// as plain text via the OnLog event (e.g.: for console output). /// - public void EnableDefaultStackTraceHandling() + public void EnableExceptionPrinting() { OnException += WriteStackTraceToLog; } @@ -187,7 +187,7 @@ public void EnableDefaultStackTraceHandling() /// Disables default handling of stack traces, such that stack /// traces are not written to the OnLog event handler. /// - public void DisableDefaultStackTraceHandling() + public void DisableExceptionPrinting() { OnException -= WriteStackTraceToLog; }