diff --git a/dotnetcore2.0/run/MockBootstraps/DebuggerExtensions.cs b/dotnetcore2.0/run/MockBootstraps/DebuggerExtensions.cs
new file mode 100644
index 00000000..7c15ea8a
--- /dev/null
+++ b/dotnetcore2.0/run/MockBootstraps/DebuggerExtensions.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+
+namespace MockLambdaRuntime
+{
+ internal static class DebuggerExtensions
+ {
+ ///
+ /// Tries to wait for the debugger to attach by inspecting property in a loop.
+ ///
+ /// representing the frequency of inspection.
+ /// representing the timeout for the operation.
+ /// True if debugger was attached, false if timeout occured.
+ public static bool TryWaitForAttaching(TimeSpan queryInterval, TimeSpan timeout)
+ {
+ var stopwatch = Stopwatch.StartNew();
+
+ while (!Debugger.IsAttached)
+ {
+ if (stopwatch.Elapsed > timeout)
+ {
+ return false;
+ }
+
+ Task.Delay(queryInterval).Wait();
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/dotnetcore2.0/run/MockBootstraps/Program.cs b/dotnetcore2.0/run/MockBootstraps/Program.cs
index ebbe0933..da0fcd1f 100644
--- a/dotnetcore2.0/run/MockBootstraps/Program.cs
+++ b/dotnetcore2.0/run/MockBootstraps/Program.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
@@ -9,50 +10,79 @@ namespace MockLambdaRuntime
{
class Program
{
+ private const string WaitForDebuggerFlag = "--debugger-spin-wait";
+ private const bool WaitForDebuggerFlagDefaultValue = false;
+
/// Task root of lambda task
static string lambdaTaskRoot = EnvHelper.GetOrDefault("LAMBDA_TASK_ROOT", "/var/task");
+ private static readonly TimeSpan _debuggerStatusQueryInterval = TimeSpan.FromMilliseconds(50);
+ private static readonly TimeSpan _debuggerStatusQueryTimeout = TimeSpan.FromMinutes(10);
+
/// Program entry point
static void Main(string[] args)
{
AssemblyLoadContext.Default.Resolving += OnAssemblyResolving;
- var handler = GetFunctionHandler(args);
- var body = GetEventBody(args);
-
- var lambdaContext = new MockLambdaContext(handler, body);
-
- var userCodeLoader = new UserCodeLoader(handler, InternalLogger.NO_OP_LOGGER);
- userCodeLoader.Init(Console.Error.WriteLine);
-
- var lambdaContextInternal = new LambdaContextInternal(lambdaContext.RemainingTime,
- LogAction, new Lazy(),
- lambdaContext.RequestId,
- new Lazy(lambdaContext.Arn),
- new Lazy(string.Empty),
- new Lazy(string.Empty),
- Environment.GetEnvironmentVariables());
-
- Exception lambdaException = null;
-
- LogRequestStart(lambdaContext);
try
{
- userCodeLoader.Invoke(lambdaContext.InputStream, lambdaContext.OutputStream, lambdaContextInternal);
+ var shouldWaitForDebugger = GetShouldWaitForDebuggerFlag(args, out var positionalArgs);
+
+ var handler = GetFunctionHandler(positionalArgs);
+ var body = GetEventBody(positionalArgs);
+
+ if (shouldWaitForDebugger)
+ {
+ Console.Error.WriteLine("Waiting for the debugger to attach...");
+
+ if (!DebuggerExtensions.TryWaitForAttaching(
+ _debuggerStatusQueryInterval,
+ _debuggerStatusQueryTimeout))
+ {
+ Console.Error.WriteLine("Timeout. Proceeding without debugger.");
+ }
+ }
+
+ var lambdaContext = new MockLambdaContext(handler, body);
+
+ var userCodeLoader = new UserCodeLoader(handler, InternalLogger.NO_OP_LOGGER);
+ userCodeLoader.Init(Console.Error.WriteLine);
+
+ var lambdaContextInternal = new LambdaContextInternal(lambdaContext.RemainingTime,
+ LogAction, new Lazy(),
+ lambdaContext.RequestId,
+ new Lazy(lambdaContext.Arn),
+ new Lazy(string.Empty),
+ new Lazy(string.Empty),
+ Environment.GetEnvironmentVariables());
+
+ Exception lambdaException = null;
+
+ LogRequestStart(lambdaContext);
+ try
+ {
+ userCodeLoader.Invoke(lambdaContext.InputStream, lambdaContext.OutputStream, lambdaContextInternal);
+ }
+ catch (Exception ex)
+ {
+ lambdaException = ex;
+ }
+ LogRequestEnd(lambdaContext);
+
+ if (lambdaException == null)
+ {
+ Console.WriteLine(lambdaContext.OutputText);
+ }
+ else
+ {
+ Console.Error.WriteLine(lambdaException);
+ }
}
- catch (Exception ex)
- {
- lambdaException = ex;
- }
- LogRequestEnd(lambdaContext);
- if (lambdaException == null)
- {
- Console.WriteLine(lambdaContext.OutputText);
- }
- else
+ // Catch all unhandled exceptions from runtime, to prevent user from hanging on them while debugging
+ catch (Exception ex)
{
- Console.Error.WriteLine(lambdaException);
+ Console.Error.WriteLine($"\nUnhandled exception occured in runner:\n{ex}");
}
}
@@ -68,6 +98,33 @@ private static void LogAction(string text)
Console.Error.WriteLine(text);
}
+ ///
+ /// Extracts "waitForDebugger" flag from args. Returns other unprocessed arguments.
+ ///
+ /// Args to look through
+ /// Arguments except for the "waitForDebugger" ones
+ /// "waitForDebugger" flag value
+ private static bool GetShouldWaitForDebuggerFlag(string[] args, out string[] unprocessed)
+ {
+ var flagValue = WaitForDebuggerFlagDefaultValue;
+
+ var unprocessedList = new List();
+
+ foreach (var argument in args)
+ {
+ if (argument == WaitForDebuggerFlag)
+ {
+ flagValue = true;
+ continue;
+ }
+
+ unprocessedList.Add(argument);
+ }
+
+ unprocessed = unprocessedList.ToArray();
+ return flagValue;
+ }
+
static void LogRequestStart(MockLambdaContext context)
{
Console.Error.WriteLine($"START RequestId: {context.RequestId} Version: {context.FunctionVersion}");
diff --git a/dotnetcore2.1/run/MockBootstraps/DebuggerExtensions.cs b/dotnetcore2.1/run/MockBootstraps/DebuggerExtensions.cs
new file mode 100644
index 00000000..7c15ea8a
--- /dev/null
+++ b/dotnetcore2.1/run/MockBootstraps/DebuggerExtensions.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+
+namespace MockLambdaRuntime
+{
+ internal static class DebuggerExtensions
+ {
+ ///
+ /// Tries to wait for the debugger to attach by inspecting property in a loop.
+ ///
+ /// representing the frequency of inspection.
+ /// representing the timeout for the operation.
+ /// True if debugger was attached, false if timeout occured.
+ public static bool TryWaitForAttaching(TimeSpan queryInterval, TimeSpan timeout)
+ {
+ var stopwatch = Stopwatch.StartNew();
+
+ while (!Debugger.IsAttached)
+ {
+ if (stopwatch.Elapsed > timeout)
+ {
+ return false;
+ }
+
+ Task.Delay(queryInterval).Wait();
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/dotnetcore2.1/run/MockBootstraps/Program.cs b/dotnetcore2.1/run/MockBootstraps/Program.cs
index ebbe0933..da0fcd1f 100644
--- a/dotnetcore2.1/run/MockBootstraps/Program.cs
+++ b/dotnetcore2.1/run/MockBootstraps/Program.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
@@ -9,50 +10,79 @@ namespace MockLambdaRuntime
{
class Program
{
+ private const string WaitForDebuggerFlag = "--debugger-spin-wait";
+ private const bool WaitForDebuggerFlagDefaultValue = false;
+
/// Task root of lambda task
static string lambdaTaskRoot = EnvHelper.GetOrDefault("LAMBDA_TASK_ROOT", "/var/task");
+ private static readonly TimeSpan _debuggerStatusQueryInterval = TimeSpan.FromMilliseconds(50);
+ private static readonly TimeSpan _debuggerStatusQueryTimeout = TimeSpan.FromMinutes(10);
+
/// Program entry point
static void Main(string[] args)
{
AssemblyLoadContext.Default.Resolving += OnAssemblyResolving;
- var handler = GetFunctionHandler(args);
- var body = GetEventBody(args);
-
- var lambdaContext = new MockLambdaContext(handler, body);
-
- var userCodeLoader = new UserCodeLoader(handler, InternalLogger.NO_OP_LOGGER);
- userCodeLoader.Init(Console.Error.WriteLine);
-
- var lambdaContextInternal = new LambdaContextInternal(lambdaContext.RemainingTime,
- LogAction, new Lazy(),
- lambdaContext.RequestId,
- new Lazy(lambdaContext.Arn),
- new Lazy(string.Empty),
- new Lazy(string.Empty),
- Environment.GetEnvironmentVariables());
-
- Exception lambdaException = null;
-
- LogRequestStart(lambdaContext);
try
{
- userCodeLoader.Invoke(lambdaContext.InputStream, lambdaContext.OutputStream, lambdaContextInternal);
+ var shouldWaitForDebugger = GetShouldWaitForDebuggerFlag(args, out var positionalArgs);
+
+ var handler = GetFunctionHandler(positionalArgs);
+ var body = GetEventBody(positionalArgs);
+
+ if (shouldWaitForDebugger)
+ {
+ Console.Error.WriteLine("Waiting for the debugger to attach...");
+
+ if (!DebuggerExtensions.TryWaitForAttaching(
+ _debuggerStatusQueryInterval,
+ _debuggerStatusQueryTimeout))
+ {
+ Console.Error.WriteLine("Timeout. Proceeding without debugger.");
+ }
+ }
+
+ var lambdaContext = new MockLambdaContext(handler, body);
+
+ var userCodeLoader = new UserCodeLoader(handler, InternalLogger.NO_OP_LOGGER);
+ userCodeLoader.Init(Console.Error.WriteLine);
+
+ var lambdaContextInternal = new LambdaContextInternal(lambdaContext.RemainingTime,
+ LogAction, new Lazy(),
+ lambdaContext.RequestId,
+ new Lazy(lambdaContext.Arn),
+ new Lazy(string.Empty),
+ new Lazy(string.Empty),
+ Environment.GetEnvironmentVariables());
+
+ Exception lambdaException = null;
+
+ LogRequestStart(lambdaContext);
+ try
+ {
+ userCodeLoader.Invoke(lambdaContext.InputStream, lambdaContext.OutputStream, lambdaContextInternal);
+ }
+ catch (Exception ex)
+ {
+ lambdaException = ex;
+ }
+ LogRequestEnd(lambdaContext);
+
+ if (lambdaException == null)
+ {
+ Console.WriteLine(lambdaContext.OutputText);
+ }
+ else
+ {
+ Console.Error.WriteLine(lambdaException);
+ }
}
- catch (Exception ex)
- {
- lambdaException = ex;
- }
- LogRequestEnd(lambdaContext);
- if (lambdaException == null)
- {
- Console.WriteLine(lambdaContext.OutputText);
- }
- else
+ // Catch all unhandled exceptions from runtime, to prevent user from hanging on them while debugging
+ catch (Exception ex)
{
- Console.Error.WriteLine(lambdaException);
+ Console.Error.WriteLine($"\nUnhandled exception occured in runner:\n{ex}");
}
}
@@ -68,6 +98,33 @@ private static void LogAction(string text)
Console.Error.WriteLine(text);
}
+ ///
+ /// Extracts "waitForDebugger" flag from args. Returns other unprocessed arguments.
+ ///
+ /// Args to look through
+ /// Arguments except for the "waitForDebugger" ones
+ /// "waitForDebugger" flag value
+ private static bool GetShouldWaitForDebuggerFlag(string[] args, out string[] unprocessed)
+ {
+ var flagValue = WaitForDebuggerFlagDefaultValue;
+
+ var unprocessedList = new List();
+
+ foreach (var argument in args)
+ {
+ if (argument == WaitForDebuggerFlag)
+ {
+ flagValue = true;
+ continue;
+ }
+
+ unprocessedList.Add(argument);
+ }
+
+ unprocessed = unprocessedList.ToArray();
+ return flagValue;
+ }
+
static void LogRequestStart(MockLambdaContext context)
{
Console.Error.WriteLine($"START RequestId: {context.RequestId} Version: {context.FunctionVersion}");