diff --git a/src/Simulation/Common/SimulatorBase.cs b/src/Simulation/Common/SimulatorBase.cs
index 66999f33bac..1440e2112db 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();
+ EnableExceptionPrinting();
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 OnException event that logs stack traces
+ /// as plain text via the OnLog event (e.g.: for console output).
+ ///
+ public void EnableExceptionPrinting()
+ {
+ OnException += WriteStackTraceToLog;
+ }
+
+
+ ///
+ /// Disables default handling of stack traces, such that stack
+ /// traces are not written to the OnLog event handler.
+ ///
+ public void DisableExceptionPrinting()
+ {
+ 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