From 4a42979029059f8ad24475f6ad3c4ee0126e7b64 Mon Sep 17 00:00:00 2001 From: Zach Madsen Date: Tue, 3 Mar 2026 16:44:00 -0800 Subject: [PATCH 1/3] Allow redirected input/output in collect-linux --- .../CommandLine/Commands/CollectLinuxCommand.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs index 32fec2755d..5910136c2c 100644 --- a/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs +++ b/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs @@ -23,6 +23,8 @@ internal partial class CollectLinuxCommandHandler private LineRewriter rewriter; private long statusUpdateTimestamp; private Version minRuntimeSupportingUserEventsIPCCommand = new(10, 0, 0); + private bool stopOnEnter = true; + private bool printStatusUpdate = true; internal sealed record CollectLinuxArgs( CancellationToken Ct, @@ -41,6 +43,8 @@ public CollectLinuxCommandHandler(IConsole console = null) { Console = console ?? new DefaultConsole(); rewriter = new LineRewriter(Console); + stopOnEnter = !Console.IsInputRedirected; + printStatusUpdate = !Console.IsOutputRedirected; } internal static bool IsSupported() @@ -491,12 +495,12 @@ private int OutputHandler(uint type, IntPtr data, UIntPtr dataLen) } } - if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Enter) + if (stopOnEnter && Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Enter) { stopTracing = true; } - if (ot == OutputType.Progress) + if (printStatusUpdate && ot == OutputType.Progress) { long currentTimestamp = Stopwatch.GetTimestamp(); if (statusUpdateTimestamp != 0 && currentTimestamp < statusUpdateTimestamp) From 459271aee956fa69ccd093d2ccea2ee2ce7d5e67 Mon Sep 17 00:00:00 2001 From: Zach Madsen Date: Wed, 4 Mar 2026 16:21:12 -0800 Subject: [PATCH 2/3] Change var names to match collect, and add tests --- .../Commands/CollectLinuxCommand.cs | 12 ++--- src/tests/Common/MockConsole.cs | 10 ++-- .../CollectLinuxCommandFunctionalTests.cs | 53 ++++++++++++++++++- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs index 5910136c2c..6c6d622863 100644 --- a/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs +++ b/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs @@ -23,8 +23,8 @@ internal partial class CollectLinuxCommandHandler private LineRewriter rewriter; private long statusUpdateTimestamp; private Version minRuntimeSupportingUserEventsIPCCommand = new(10, 0, 0); - private bool stopOnEnter = true; - private bool printStatusUpdate = true; + private readonly bool cancelOnEnter = true; + private readonly bool printStatusOverTime = true; internal sealed record CollectLinuxArgs( CancellationToken Ct, @@ -43,8 +43,8 @@ public CollectLinuxCommandHandler(IConsole console = null) { Console = console ?? new DefaultConsole(); rewriter = new LineRewriter(Console); - stopOnEnter = !Console.IsInputRedirected; - printStatusUpdate = !Console.IsOutputRedirected; + cancelOnEnter = !Console.IsInputRedirected; + printStatusOverTime = !Console.IsOutputRedirected; } internal static bool IsSupported() @@ -495,12 +495,12 @@ private int OutputHandler(uint type, IntPtr data, UIntPtr dataLen) } } - if (stopOnEnter && Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Enter) + if (cancelOnEnter && Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Enter) { stopTracing = true; } - if (printStatusUpdate && ot == OutputType.Progress) + if (printStatusOverTime && ot == OutputType.Progress) { long currentTimestamp = Stopwatch.GetTimestamp(); if (statusUpdateTimestamp != 0 && currentTimestamp < statusUpdateTimestamp) diff --git a/src/tests/Common/MockConsole.cs b/src/tests/Common/MockConsole.cs index 7046b473b9..a8f2307d1e 100644 --- a/src/tests/Common/MockConsole.cs +++ b/src/tests/Common/MockConsole.cs @@ -47,9 +47,9 @@ public MockConsole(int width, int height, ITestOutputHelper outputHelper = null) public bool IsOutputRedirected { get; set; } - public bool IsInputRedirected { get; private set; } + public bool IsInputRedirected { get; set; } - public bool KeyAvailable { get; private set; } + public bool KeyAvailable { get; set; } public TextWriter Out => this; @@ -186,9 +186,11 @@ public void FlushTestLogging() public string GetLineText(int row) => new string(_chars[row]).TrimEnd(); - public ConsoleKeyInfo ReadKey() => Console.ReadKey(); + public ConsoleKeyInfo NextKeyInfo { get; set; } = new ConsoleKeyInfo('\0', ConsoleKey.Enter, false, false, false); - public ConsoleKeyInfo ReadKey(bool intercept) => Console.ReadKey(intercept); + public ConsoleKeyInfo ReadKey() => NextKeyInfo; + + public ConsoleKeyInfo ReadKey(bool intercept) => NextKeyInfo; public string[] Lines { diff --git a/src/tests/dotnet-trace/CollectLinuxCommandFunctionalTests.cs b/src/tests/dotnet-trace/CollectLinuxCommandFunctionalTests.cs index 8d0a6639aa..80741796b4 100644 --- a/src/tests/dotnet-trace/CollectLinuxCommandFunctionalTests.cs +++ b/src/tests/dotnet-trace/CollectLinuxCommandFunctionalTests.cs @@ -23,7 +23,7 @@ public class CollectLinuxCommandFunctionalTests { public static bool IsCollectLinuxSupported => CollectLinuxCommandHandler.IsSupported(); public static bool IsCollectLinuxNotSupported => !CollectLinuxCommandHandler.IsSupported(); - + private readonly ITestOutputHelper _outputHelper; public CollectLinuxCommandFunctionalTests(ITestOutputHelper outputHelper) @@ -269,6 +269,57 @@ public void CollectLinuxCommand_DoesNotChangeCursorVisibility_WhenOutputIsRedire Assert.Equal(initialCursorVisible, console.CursorVisible); } + [ConditionalFact(nameof(IsCollectLinuxSupported))] + public void CollectLinuxCommand_DoesNotPrintStatusUpdates_WhenOutputIsRedirected() + { + MockConsole console = new(200, 30, _outputHelper); + console.IsOutputRedirected = true; + + var handler = new CollectLinuxCommandHandler(console); + handler.RecordTraceInvoker = (cmd, len, cb) => { + // Send progress output type. + cb((uint)3, IntPtr.Zero, UIntPtr.Zero); + return 0; + }; + + int exitCode = handler.CollectLinux(TestArgs()); + Assert.Equal((int)ReturnCode.Ok, exitCode); + + string[] lines = console.Lines; + Assert.DoesNotContain(lines, l => l.Contains("Recording trace", StringComparison.OrdinalIgnoreCase)); + Assert.DoesNotContain(lines, l => l.Contains("Press ", StringComparison.OrdinalIgnoreCase)); + } + + [ConditionalFact(nameof(IsCollectLinuxSupported))] + public void CollectLinuxCommand_DoesNotReadKey_WhenInputIsRedirected() + { + MockConsole console = new(200, 30, _outputHelper); + console.IsInputRedirected = true; + console.KeyAvailable = true; + console.NextKeyInfo = new ConsoleKeyInfo('\r', ConsoleKey.Enter, false, false, false); + + var handler = new CollectLinuxCommandHandler(console); + bool callbackInvoked = false; + handler.RecordTraceInvoker = (cmd, len, cb) => { + // Send progress output type. + int result = cb((uint)3, IntPtr.Zero, UIntPtr.Zero); + + // It should return 0, i.e., continue tracing, even though + // enter was pressed. + Assert.Equal(0, result); + + callbackInvoked = true; + + return 0; + }; + + int exitCode = handler.CollectLinux(TestArgs()); + Assert.Equal((int)ReturnCode.Ok, exitCode); + + // The important assertion is in the callback so make sure it was called. + Assert.True(callbackInvoked); + } + private static int Run(object args, MockConsole console) { var handler = new CollectLinuxCommandHandler(console); From 91744ccc87c6273a518daf91adbf8e9a0c04f7d8 Mon Sep 17 00:00:00 2001 From: Zach Madsen Date: Tue, 10 Mar 2026 07:55:21 -0700 Subject: [PATCH 3/3] Remove redundant init for the new fields --- .../dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs index 6c6d622863..fec9826e6d 100644 --- a/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs +++ b/src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs @@ -23,8 +23,8 @@ internal partial class CollectLinuxCommandHandler private LineRewriter rewriter; private long statusUpdateTimestamp; private Version minRuntimeSupportingUserEventsIPCCommand = new(10, 0, 0); - private readonly bool cancelOnEnter = true; - private readonly bool printStatusOverTime = true; + private readonly bool cancelOnEnter; + private readonly bool printStatusOverTime; internal sealed record CollectLinuxArgs( CancellationToken Ct,