Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions src/libraries/System.Net.Quic/src/System.Net.Quic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
<Reference Include="System.Collections.Concurrent" />
<Reference Include="System.Collections.NonGeneric" />
<Reference Include="System.Console" Condition="'$(Configuration)' == 'Debug'" />
<Reference Include="System.Diagnostics.DiagnosticSource" />
<Reference Include="System.Diagnostics.Tracing" />
<Reference Include="System.Memory" />
<Reference Include="System.Net.NameResolution" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@ internal enum QUIC_PERFORMANCE_COUNTERS
PATH_FAILURE,
SEND_STATELESS_RESET,
SEND_STATELESS_RETRY,
CONN_LOAD_REJECT,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to microsoft/msquic#4120, we should wait until that one merges.

We don't need to wait with MsQuic update before merging, the counter will be 0 until a version which supports it is loaded.

MAX,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Diagnostics.Metrics;
using System.Net.Quic;

using Microsoft.Quic;
using static Microsoft.Quic.MsQuic;

namespace System.Net
{
internal sealed partial class NetEventSource
{
private static Meter s_meter = new Meter("Private.InternalDiagnostics.System.Net.Quic");
private static readonly long[] s_counters = new long[(int)QUIC_PERFORMANCE_COUNTERS.MAX];

public static readonly ObservableGauge<long> MsQuicCountersGauge = s_meter.CreateObservableGauge<long>(
name: "MsQuic",
observeValues: GetGauges,
unit: null,
description: "MsQuic performance counters");

public static readonly ObservableCounter<long> MsQuicCountersCounter = s_meter.CreateObservableCounter<long>(
name: "MsQuic",
observeValues: GetCounters,
unit: null,
description: "MsQuic performance counters");

[NonEvent]
private static void UpdateCounters()
{
unsafe
{
fixed (long* pCounters = s_counters)
{
uint size = (uint)s_counters.Length * sizeof(long);
MsQuicApi.Api.ApiTable->GetParam(null, QUIC_PARAM_GLOBAL_PERF_COUNTERS, &size, (byte*)pCounters);
}
}
}

[NonEvent]
private static IEnumerable<Measurement<long>> GetGauges()
{
if (!MsQuicApi.IsQuicSupported)
{
// Avoid calling into MsQuic if not supported (or not initialized yet)
return Array.Empty<Measurement<long>>();
}

UpdateCounters();

var measurements = new List<Measurement<long>>();

static void AddMeasurement(List<Measurement<long>> measurements, QUIC_PERFORMANCE_COUNTERS counter)
{
measurements.Add(new Measurement<long>(s_counters[(int)counter], new KeyValuePair<string, object?>("Name", counter)));
}

AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_ACTIVE);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_CONNECTED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.STRM_ACTIVE);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_QUEUE_DEPTH);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_OPER_QUEUE_DEPTH);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.WORK_OPER_QUEUE_DEPTH);

return measurements;
}


[NonEvent]
private static IEnumerable<Measurement<long>> GetCounters()
{
if (!MsQuicApi.IsQuicSupported)
{
// Avoid calling into MsQuic if not supported (or not initialized yet)
return Array.Empty<Measurement<long>>();
}

UpdateCounters();

var measurements = new List<Measurement<long>>();

static void AddMeasurement(List<Measurement<long>> measurements, QUIC_PERFORMANCE_COUNTERS counter)
{
measurements.Add(new Measurement<long>(s_counters[(int)counter], new KeyValuePair<string, object?>("Name", counter)));
}

AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_CREATED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_HANDSHAKE_FAIL);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_APP_REJECT);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_RESUMED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_PROTOCOL_ERRORS);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_NO_ALPN);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.PKTS_SUSPECTED_LOST);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.PKTS_DROPPED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.PKTS_DECRYPTION_FAIL);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.UDP_RECV);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.UDP_SEND);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.UDP_RECV_BYTES);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.UDP_SEND_BYTES);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.UDP_RECV_EVENTS);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.UDP_SEND_CALLS);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.APP_SEND_BYTES);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.APP_RECV_BYTES);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_OPER_QUEUED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_OPER_COMPLETED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.WORK_OPER_QUEUED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.WORK_OPER_COMPLETED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.PATH_VALIDATED);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.PATH_FAILURE);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.SEND_STATELESS_RESET);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.SEND_STATELESS_RETRY);
AddMeasurement(measurements, QUIC_PERFORMANCE_COUNTERS.CONN_LOAD_REJECT);

return measurements;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace System.Net.Quic.Tests
{
[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
[SkipOnPlatform(TestPlatforms.Windows, "CipherSuitesPolicy is not supported on Windows")]
public class MsQuicCipherSuitesPolicyTests : QuicTestBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace System.Net.Quic.Tests
{
[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
public class MsQuicRemoteExecutorTests : QuicTestBase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void Dispose()
}
}

[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
public class MsQuicTests : QuicTestBase, IClassFixture<CertificateSetup>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace System.Net.Quic.Tests
{
using Configuration = System.Net.Test.Common.Configuration;

[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
public sealed class QuicConnectionTests : QuicTestBase
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.DotNet.XUnitExtensions;
using System;
using System.Net.Quic;
using System.Reflection;
using System.Text;
using System.Linq;
using Xunit;
using Xunit.Abstractions;

using Microsoft.Quic;
using static Microsoft.Quic.MsQuic;

public class QuicCountersListener : IDisposable
{
public unsafe void Dispose()
{
Type msQuicApiType = Type.GetType("System.Net.Quic.MsQuicApi, System.Net.Quic");
object msQuicApiInstance = msQuicApiType.GetProperty("Api", BindingFlags.NonPublic | BindingFlags.Static).GetGetMethod(true).Invoke(null, Array.Empty<object?>());
QUIC_API_TABLE* apiTable = (QUIC_API_TABLE*)(Pointer.Unbox(msQuicApiType.GetProperty("ApiTable").GetGetMethod().Invoke(msQuicApiInstance, Array.Empty<object?>())));

long[] counters = new long[(int)QUIC_PERFORMANCE_COUNTERS.MAX];
int countersAvailable;

int status;
fixed (long* pCounters = counters)
{
uint size = (uint)counters.Length * sizeof(long);
status = apiTable->GetParam(null, QUIC_PARAM_GLOBAL_PERF_COUNTERS, &size, (byte*)pCounters);
countersAvailable = (int)size / sizeof(long);
}

if (StatusFailed(status))
{
System.Console.WriteLine($"Failed to read MsQuic counters: {status}");
return;
}


StringBuilder sb = new StringBuilder();
sb.AppendLine("MsQuic Counters:");

int maxlen = Enum.GetNames(typeof(QUIC_PERFORMANCE_COUNTERS)).Max(s => s.Length);
void DumpCounter(QUIC_PERFORMANCE_COUNTERS counter)
{
var name = $"{counter}:".PadRight(maxlen + 1);
var index = (int)counter;
var value = index < countersAvailable ? counters[(int)counter].ToString() : "N/A";
sb.AppendLine($" {counter} {value}");
}

DumpCounter(QUIC_PERFORMANCE_COUNTERS.CONN_CREATED);
DumpCounter(QUIC_PERFORMANCE_COUNTERS.CONN_HANDSHAKE_FAIL);
DumpCounter(QUIC_PERFORMANCE_COUNTERS.CONN_APP_REJECT);
DumpCounter(QUIC_PERFORMANCE_COUNTERS.CONN_LOAD_REJECT);

System.Console.WriteLine(sb.ToString());
}
}

[CollectionDefinition(nameof(QuicCountersListener), DisableParallelization = true)]
public class QuicCountersCollection : ICollectionFixture<QuicCountersListener>
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace System.Net.Quic.Tests
{
[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
public sealed class QuicListenerTests : QuicTestBase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace System.Net.Quic.Tests
{
[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
public sealed class QuicStreamConformanceTests : ConnectedStreamConformanceTests
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace System.Net.Quic.Tests
{
[Collection(nameof(DisableParallelization))]
[Collection(nameof(QuicCountersListener))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported), nameof(QuicTestBase.IsNotArm32CoreClrStressTest))]
public sealed class QuicStreamTests : QuicTestBase
{
Expand Down Expand Up @@ -1211,16 +1211,16 @@ async ValueTask ReleaseOnReadsClosedAsync()

private const int SmallestPayload = 1;
private const int SmallPayload = 1024;
private const int BufferPayload = 64*1024;
private const int BufferPlusPayload = 64*1024+1;
private const int BigPayload = 1024*1024*1024;
private const int BufferPayload = 64 * 1024;
private const int BufferPlusPayload = 64 * 1024 + 1;
private const int BigPayload = 1024 * 1024 * 1024;

public static IEnumerable<object[]> PayloadSizeAndTwoBools()
{
var boolValues = new [] { true, false };
var boolValues = new[] { true, false };
var payloadValues = !PlatformDetection.IsInHelix ?
new [] { SmallestPayload, SmallPayload, BufferPayload, BufferPlusPayload, BigPayload } :
new [] { SmallestPayload, SmallPayload, BufferPayload, BufferPlusPayload };
new[] { SmallestPayload, SmallPayload, BufferPayload, BufferPlusPayload, BigPayload } :
new[] { SmallestPayload, SmallPayload, BufferPayload, BufferPlusPayload };
return
from payload in payloadValues
from bool1 in boolValues
Expand Down