I have the following example code:
public virtual async Task<TResponse> ExecuteAsync<TResponse>(Func<TServiceClient, Task<TResponse>> func)
{
var st1 = new System.Diagnostics.StackTrace().ToString();
try
{
return await func(_clientInstance);
}
catch (Exception ex)
{
ThrowServiceException(ex, st1);
}
}
private void ThrowServiceException(Exception ex, string st1)
{
var st2 = new System.Diagnostics.StackTrace().ToString();
if (st1 != st2) { }
// ...
}
And when I a exception is thrown, the difference between stacks before and after is similar to following. Before:
at Common.Autorest.AutorestClientWrapper`1.ExecuteAsync[TResponse](Func`2 func)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at Common.Autorest.AutorestClientWrapper`1.ExecuteAsync[TResponse](Func`2 func)
at Common.Autorest.AutorestClientWrapper`1.Execute[TResponse](Func`2 func)
at BankingServiceClient.BankApi.GetBufferBalance(CancellationToken cancellationToken)
at Engine.Services.BalanceWorker.<>c__DisplayClass18_0.<RefreshTreasuryBalance>g__ExecuteTreasuryBalanceRefresh|0(CancellationToken token)
at Engine.Services.BalanceWorker.ExecuteBalanceRefresh(Action`1 executeRefresh, String loggingName)
at Engine.Services.BalanceWorker.RefreshTreasuryBalance(Currency currency)
at Engine.Services.BalanceWorker.<>c__DisplayClass14_2.<.ctor>b__4()
at Engine.Threading.Runnable.Engine.Threading.IRunnable.RunStep()
at Engine.Threading.Runner.ExecuteContinously()
at System.Threading.Thread.StartHelper.Callback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Thread.StartCallback()
After:
at Common.Autorest.AutorestClientWrapper`1.ThrowServiceException(Exception ex, String st1)
at Common.Autorest.AutorestClientWrapper`1.ExecuteAsync[TResponse](Func`2 func)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object s)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.<>c.<OutputWaitEtwEvents>b__12_0(Action innerContinuation, Task innerTask)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke()
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action action, Boolean allowInlining)
at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
at System.Threading.Tasks.Task.FinishContinuations()
at System.Threading.Tasks.Task.FinishStageThree()
at System.Threading.Tasks.Task.FinishStageTwo()
at System.Threading.Tasks.Task.FinishSlow(Boolean userDelegateExecute)
at System.Threading.Tasks.Task.TrySetException(Object exceptionObject)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception, Task`1& taskField)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetException(Exception exception)
at Bank.ApiClient.BufferAccountBankAPI.GetAccountBalanceAsync(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object s)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.<>c.<OutputWaitEtwEvents>b__12_0(Action innerContinuation, Task innerTask)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke()
at System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
at System.Threading.Thread.StartCallback()
So, as you can see, stack trace is lost and it is quite hard to figure it out from where the exception is thrown.
I somehow understand why stack so different and I am wondering if is it possible, for debugging purposes, to force all async methods execute immediately to keep stack trace as it is before calling? Maybe with some global variable or something.
The solution described in Is it possible to get a good stack trace with .NET async methods? does not resolve what I want because that one just sanitized "useless" stack items, but in my case stack trace before call is totally different than after call and it seems there is no connection between before and after.
Edit: Given method is called in multiple occasions.
Some looks like this:
public async Task<MarketLimits> GetMarketLimits(CancellationToken cancellationToken)
{
var response = await _serviceClientWrapper.ExecuteAsync(c => c.GetLimitsAsync(cancellationToken));
// ...
}
Some like this:
public virtual TResponse Execute<TResponse>(Func<TServiceClient, Task<TResponse>> func)
{
return ExecuteAsync(client => func(client)).GetAwaiter().GetResult();
}