Skip to content
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 @@ -45,6 +45,7 @@ public class Configuration
public bool UseHttpSys { get; set; }
public bool LogAspNet { get; set; }
public bool Trace { get; set; }
public bool? TrackUnobservedExceptions { get; set; }
public int? ServerMaxConcurrentStreams { get; set; }
public int? ServerMaxFrameSize { get; set; }
public int? ServerInitialConnectionWindowSize { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public enum ExitCode { Success = 0, StressError = 1, CliError = 2 };

public static readonly bool IsQuicSupported = QuicListener.IsSupported && QuicConnection.IsSupported;

private static readonly Dictionary<string, int> s_unobservedExceptions = new Dictionary<string, int>();

public static async Task<int> Main(string[] args)
{
if (!TryParseCli(args, out Configuration? config))
Expand Down Expand Up @@ -69,6 +71,7 @@ private static bool TryParseCli(string[] args, [NotNullWhen(true)] out Configura
cmd.AddOption(new Option("-serverMaxFrameSize", "Overrides kestrel max frame size setting.") { Argument = new Argument<int?>("bytes", null) });
cmd.AddOption(new Option("-serverInitialConnectionWindowSize", "Overrides kestrel initial connection window size setting.") { Argument = new Argument<int?>("bytes", null) });
cmd.AddOption(new Option("-serverMaxRequestHeaderFieldSize", "Overrides kestrel max request header field size.") { Argument = new Argument<int?>("bytes", null) });
cmd.AddOption(new Option("-unobservedEx", "Enable tracking unobserved exceptions.") { Argument = new Argument<bool?>("enable", null) });

ParseResult cmdline = cmd.Parse(args);
if (cmdline.Errors.Count > 0)
Expand Down Expand Up @@ -109,6 +112,7 @@ private static bool TryParseCli(string[] args, [NotNullWhen(true)] out Configura
UseHttpSys = cmdline.ValueForOption<bool>("-httpSys"),
LogAspNet = cmdline.ValueForOption<bool>("-aspnetlog"),
Trace = cmdline.ValueForOption<bool>("-trace"),
TrackUnobservedExceptions = cmdline.ValueForOption<bool?>("-unobservedEx"),
ServerMaxConcurrentStreams = cmdline.ValueForOption<int?>("-serverMaxConcurrentStreams"),
ServerMaxFrameSize = cmdline.ValueForOption<int?>("-serverMaxFrameSize"),
ServerInitialConnectionWindowSize = cmdline.ValueForOption<int?>("-serverInitialConnectionWindowSize"),
Expand Down Expand Up @@ -164,6 +168,9 @@ private static async Task<ExitCode> Run(Configuration config)

Type msQuicApiType = Type.GetType("System.Net.Quic.MsQuicApi, System.Net.Quic")!;
string msQuicLibraryVersion = (string)msQuicApiType.GetProperty("MsQuicLibraryVersion", BindingFlags.NonPublic | BindingFlags.Static)!.GetGetMethod(true)!.Invoke(null, Array.Empty<object?>())!;
bool trackUnobservedExceptions = config.TrackUnobservedExceptions.HasValue
? config.TrackUnobservedExceptions.Value
: config.RunMode.HasFlag(RunMode.client);

Console.WriteLine(" .NET Core: " + GetAssemblyInfo(typeof(object).Assembly));
Console.WriteLine(" ASP.NET Core: " + GetAssemblyInfo(typeof(WebHost).Assembly));
Expand All @@ -184,8 +191,21 @@ private static async Task<ExitCode> Run(Configuration config)
Console.WriteLine(" Cancellation: " + 100 * config.CancellationProbability + "%");
Console.WriteLine("Max Content Size: " + config.MaxContentLength);
Console.WriteLine("Query Parameters: " + config.MaxParameters);
Console.WriteLine(" Unobserved Ex: " + (trackUnobservedExceptions ? "Tracked" : "Not tracked"));
Console.WriteLine();

if (trackUnobservedExceptions)
{
TaskScheduler.UnobservedTaskException += (_, e) =>
{
lock (s_unobservedExceptions)
{
string text = e.Exception.ToString();
Copy link

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

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

Consider using a more concise identifier for exceptions (e.g. exception type and message) instead of the full output of ToString(), to avoid using overly verbose keys in the dictionary and potential memory overhead.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@antonfirsov antonfirsov Apr 3, 2025

Choose a reason for hiding this comment

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

The CI test run will show if the overhead is high. Even if that's the case, fixing #114128 should significantly reduce it.

s_unobservedExceptions[text] = s_unobservedExceptions.GetValueOrDefault(text) + 1;
}
};
}

StressServer? server = null;
if (config.RunMode.HasFlag(RunMode.server))
{
Expand All @@ -210,10 +230,28 @@ private static async Task<ExitCode> Run(Configuration config)
client?.Stop();
client?.PrintFinalReport();

if (trackUnobservedExceptions)
{
PrintUnobservedExceptions();
Copy link
Member

Choose a reason for hiding this comment

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

Should we consider stress as failed if we see any? Just so we don't ignore them by accident.

Copy link
Contributor Author

@antonfirsov antonfirsov Apr 4, 2025

Choose a reason for hiding this comment

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

My plan is to monitor the results after #114226 first to make sure we don't add too much noise here.

}

// return nonzero status code if there are stress errors
return client?.TotalErrorCount == 0 ? ExitCode.Success : ExitCode.StressError;
}

private static void PrintUnobservedExceptions()
{
Console.WriteLine($"Detected {s_unobservedExceptions.Count} unobserved exceptions:");

int i = 1;
foreach (KeyValuePair<string, int> kv in s_unobservedExceptions.OrderByDescending(p => p.Value))
{
Console.WriteLine($"Exception type {i++}/{s_unobservedExceptions.Count} (hit {kv.Value} times):");
Console.WriteLine(kv.Key);
Console.WriteLine();
}
}

private static async Task WaitUntilMaxExecutionTimeElapsedOrKeyboardInterrupt(TimeSpan? maxExecutionTime = null)
{
var tcs = new TaskCompletionSource<bool>();
Expand Down
Loading