Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve grpc-dotnet debugging #2192

Merged
merged 5 commits into from
Jul 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

namespace Grpc.AspNetCore.Server.Internal;

[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(HttpContextServerCallContextDebugView))]
internal sealed partial class HttpContextServerCallContext : ServerCallContext, IServerCallContextFeature
{
private static readonly AuthContext UnauthenticatedContext = new AuthContext(null, new Dictionary<string, List<AuthProperty>>());
Expand Down Expand Up @@ -572,4 +574,30 @@ internal void ValidateAcceptEncodingContainsResponseEncoding()
GrpcServerLog.EncodingNotInAcceptEncoding(Logger, resolvedResponseGrpcEncoding);
}
}

private string DebuggerToString() => $"Host = {Host}, Method = {Method}";

private sealed class HttpContextServerCallContextDebugView
{
private readonly HttpContextServerCallContext _context;

public HttpContextServerCallContextDebugView(HttpContextServerCallContext context)
{
_context = context;
}

public AuthContext AuthContext => _context.AuthContext;
public CancellationToken CancellationToken => _context.CancellationToken;
public DateTime Deadline => _context.Deadline;
public string Host => _context.Host;
public string Method => _context.Method;
public string Peer => _context.Peer;
public Metadata RequestHeaders => _context.RequestHeaders;
public Metadata ResponseTrailers => _context.ResponseTrailers;
public Status Status => _context.Status;
public IDictionary<object, object> UserState => _context.UserState;
public WriteOptions? WriteOptions => _context.WriteOptions;

public HttpContext HttpContext => _context.HttpContext;
}
}
23 changes: 22 additions & 1 deletion src/Grpc.AspNetCore.Server/Internal/HttpContextStreamReader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#region Copyright notice and license
#region Copyright notice and license

// Copyright 2019 The gRPC Authors
//
Expand All @@ -16,16 +16,20 @@

#endregion

using System.Diagnostics;
using Grpc.Core;
using Grpc.Shared;

namespace Grpc.AspNetCore.Server.Internal;

[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(HttpContextStreamReader<>.HttpContextStreamReaderDebugView))]
internal class HttpContextStreamReader<TRequest> : IAsyncStreamReader<TRequest> where TRequest : class
{
private readonly HttpContextServerCallContext _serverCallContext;
private readonly Func<DeserializationContext, TRequest> _deserializer;
private bool _completed;
private long _readCount;

public HttpContextStreamReader(HttpContextServerCallContext serverCallContext, Func<DeserializationContext, TRequest> deserializer)
{
Expand Down Expand Up @@ -75,11 +79,28 @@ private bool ProcessPayload(TRequest? request)
}

Current = request;
Interlocked.Increment(ref _readCount);
return true;
}

public void Complete()
{
_completed = true;
}

private string DebuggerToString() => $"ReadCount = {_readCount}, CallCompleted = {(_completed ? "true" : "false")}";

private sealed class HttpContextStreamReaderDebugView
{
private readonly HttpContextStreamReader<TRequest> _reader;

public HttpContextStreamReaderDebugView(HttpContextStreamReader<TRequest> reader)
{
_reader = reader;
}

public bool ReaderCompleted => _reader._completed;
public long ReadCount => _reader._readCount;
public TRequest Current => _reader.Current;
}
}
22 changes: 22 additions & 0 deletions src/Grpc.AspNetCore.Server/Internal/HttpContextStreamWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

#endregion

using System.Diagnostics;
using Grpc.Core;

namespace Grpc.AspNetCore.Server.Internal;

[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(HttpContextStreamWriter<>.HttpContextStreamWriterDebugView))]
internal class HttpContextStreamWriter<TResponse> : IServerStreamWriter<TResponse>
where TResponse : class
{
Expand All @@ -28,6 +31,7 @@ internal class HttpContextStreamWriter<TResponse> : IServerStreamWriter<TRespons
private readonly object _writeLock;
private Task? _writeTask;
private bool _completed;
private long _writeCount;

public HttpContextStreamWriter(HttpContextServerCallContext context, Action<TResponse, SerializationContext> serializer)
{
Expand Down Expand Up @@ -93,6 +97,7 @@ private async Task WriteCoreAsync(TResponse message, CancellationToken cancellat
}

await _writeTask;
Interlocked.Increment(ref _writeCount);
}
finally
{
Expand All @@ -117,4 +122,21 @@ private bool IsWriteInProgressUnsynchronized
return writeTask != null && !writeTask.IsCompleted;
}
}

private string DebuggerToString() => $"WriteCount = {_writeCount}, CallCompleted = {(_completed ? "true" : "false")}";

private sealed class HttpContextStreamWriterDebugView
{
private readonly HttpContextStreamWriter<TResponse> _writer;

public HttpContextStreamWriterDebugView(HttpContextStreamWriter<TResponse> writer)
{
_writer = writer;
}

public bool WriterCompleted => _writer._completed;
public bool IsWriteInProgress => _writer.IsWriteInProgressUnsynchronized;
public long WriteCount => _writer._writeCount;
public WriteOptions? WriteOptions => _writer.WriteOptions;
}
}
1 change: 0 additions & 1 deletion src/Grpc.Core.Api/AsyncCallState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#endregion


using System;
using System.Threading.Tasks;

Expand Down
23 changes: 23 additions & 0 deletions src/Grpc.Core.Api/AsyncClientStreamingCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#endregion

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

Expand All @@ -27,6 +29,8 @@ namespace Grpc.Core;
/// </summary>
/// <typeparam name="TRequest">Request message type for this call.</typeparam>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncClientStreamingCall<,>.AsyncClientStreamingCallDebugView))]
public sealed class AsyncClientStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
Expand Down Expand Up @@ -164,4 +168,23 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncClientStreamingCallDebugView
{
private readonly AsyncClientStreamingCall<TRequest, TResponse> _call;

public AsyncClientStreamingCallDebugView(AsyncClientStreamingCall<TRequest, TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.GetAwaiter().GetResult() : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public IClientStreamWriter<TRequest> RequestStream => _call.RequestStream;
public TResponse? Response => _call.ResponseAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseAsync.Result : default;
}
}
23 changes: 23 additions & 0 deletions src/Grpc.Core.Api/AsyncDuplexStreamingCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

Expand All @@ -26,6 +28,8 @@ namespace Grpc.Core;
/// </summary>
/// <typeparam name="TRequest">Request message type for this call.</typeparam>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncDuplexStreamingCall<,>.AsyncDuplexStreamingCallDebugView))]
public sealed class AsyncDuplexStreamingCall<TRequest, TResponse> : IDisposable
{
readonly IClientStreamWriter<TRequest> requestStream;
Expand Down Expand Up @@ -141,4 +145,23 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncDuplexStreamingCallDebugView
{
private readonly AsyncDuplexStreamingCall<TRequest, TResponse> _call;

public AsyncDuplexStreamingCallDebugView(AsyncDuplexStreamingCall<TRequest, TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.Result : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public IAsyncStreamReader<TResponse> ResponseStream => _call.ResponseStream;
public IClientStreamWriter<TRequest> RequestStream => _call.RequestStream;
}
}
22 changes: 22 additions & 0 deletions src/Grpc.Core.Api/AsyncServerStreamingCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@
#endregion

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

/// <summary>
/// Return type for server streaming calls.
/// </summary>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncServerStreamingCall<>.AsyncServerStreamingCallDebugView))]
public sealed class AsyncServerStreamingCall<TResponse> : IDisposable
{
readonly IAsyncStreamReader<TResponse> responseStream;
Expand Down Expand Up @@ -122,4 +126,22 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncServerStreamingCallDebugView
{
private readonly AsyncServerStreamingCall<TResponse> _call;

public AsyncServerStreamingCallDebugView(AsyncServerStreamingCall<TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.Result : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public IAsyncStreamReader<TResponse> ResponseStream => _call.ResponseStream;
}
}
23 changes: 22 additions & 1 deletion src/Grpc.Core.Api/AsyncUnaryCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,24 @@
#endregion

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Grpc.Core.Internal;

namespace Grpc.Core;

/// <summary>
/// Return type for single request - single response call.
/// </summary>
/// <typeparam name="TResponse">Response message type for this call.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(AsyncUnaryCall<>.AsyncUnaryCallDebugView))]
public sealed class AsyncUnaryCall<TResponse> : IDisposable
{
readonly Task<TResponse> responseAsync;
readonly AsyncCallState callState;


/// <summary>
/// Creates a new AsyncUnaryCall object with the specified properties.
/// </summary>
Expand Down Expand Up @@ -146,4 +149,22 @@ public void Dispose()
{
callState.Dispose();
}

private string DebuggerToString() => CallDebuggerHelpers.DebuggerToString(callState);

private sealed class AsyncUnaryCallDebugView
{
private readonly AsyncUnaryCall<TResponse> _call;

public AsyncUnaryCallDebugView(AsyncUnaryCall<TResponse> call)
{
_call = call;
}

public bool IsComplete => CallDebuggerHelpers.GetStatus(_call.callState) != null;
public Status? Status => CallDebuggerHelpers.GetStatus(_call.callState);
public Metadata? ResponseHeaders => _call.ResponseHeadersAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseHeadersAsync.Result : null;
public Metadata? Trailers => CallDebuggerHelpers.GetTrailers(_call.callState);
public TResponse? Response => _call.ResponseAsync.Status == TaskStatus.RanToCompletion ? _call.ResponseAsync.Result : default;
}
}
2 changes: 2 additions & 0 deletions src/Grpc.Core.Api/AuthContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#endregion

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Grpc.Core.Utils;

Expand All @@ -28,6 +29,7 @@ namespace Grpc.Core;
/// Using any other call/context properties for authentication purposes is wrong and inherently unsafe.
/// Note: experimental API that can change or be removed without any prior notice.
/// </summary>
[DebuggerDisplay("IsPeerAuthenticated = {IsPeerAuthenticated}")]
public class AuthContext
{
private readonly string? peerIdentityPropertyName;
Expand Down
Loading
Loading