diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs index 280a9dfee0..979fc9c4e5 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs @@ -76,7 +76,7 @@ internal static string GetDotNetSdkVersion() } internal static ProcessStartInfo BuildStartInfo(string customDotNetCliPath, string workingDirectory, string arguments, - IReadOnlyList environmentVariables = null, bool redirectStandardInput = false) + IReadOnlyList environmentVariables = null, bool redirectStandardInput = false, bool redirectStandardError = true) { const string dotnetMultiLevelLookupEnvVarName = "DOTNET_MULTILEVEL_LOOKUP"; @@ -88,12 +88,16 @@ internal static ProcessStartInfo BuildStartInfo(string customDotNetCliPath, stri UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, - RedirectStandardError = true, + RedirectStandardError = redirectStandardError, RedirectStandardInput = redirectStandardInput, - StandardErrorEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, }; + if (redirectStandardError) // StandardErrorEncoding is only supported when standard error is redirected + { + startInfo.StandardErrorEncoding = Encoding.UTF8; + } + if (environmentVariables != null) foreach (var environmentVariable in environmentVariables) startInfo.EnvironmentVariables[environmentVariable.Key] = environmentVariable.Value; diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs index 62ef814253..6e5fb8debc 100644 --- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliExecutor.cs @@ -64,7 +64,8 @@ private ExecuteResult Execute(BenchmarkCase benchmarkCase, CustomDotNetCliPath, artifactsPaths.BinariesDirectoryPath, $"{executableName.Escape()} {benchmarkId.ToArguments()}", - redirectStandardInput: true); + redirectStandardInput: true, + redirectStandardError: false); // #1629 startInfo.SetEnvironmentVariables(benchmarkCase, resolver); diff --git a/src/BenchmarkDotNet/Toolchains/Executor.cs b/src/BenchmarkDotNet/Toolchains/Executor.cs index 61d7ebfe36..1061c3fdd6 100644 --- a/src/BenchmarkDotNet/Toolchains/Executor.cs +++ b/src/BenchmarkDotNet/Toolchains/Executor.cs @@ -89,7 +89,7 @@ private ProcessStartInfo CreateStartInfo(BenchmarkCase benchmarkCase, ArtifactsP UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = true, - RedirectStandardError = true, + RedirectStandardError = false, // #1629 CreateNoWindow = true, WorkingDirectory = null // by default it's null }; diff --git a/tests/BenchmarkDotNet.IntegrationTests/StandardErrorTests.cs b/tests/BenchmarkDotNet.IntegrationTests/StandardErrorTests.cs new file mode 100644 index 0000000000..e3efd532d3 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/StandardErrorTests.cs @@ -0,0 +1,43 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Tests.Loggers; +using System; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests +{ + public class StandardErrorTests : BenchmarkTestExecutor + { + private const string ErrorMessage = "ErrorMessage"; + + public StandardErrorTests(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void BenchmarkCanWriteToStandardError() => CanExecute(); + + public class WritingToStandardError + { + [Benchmark] + public void Write() => Console.Error.Write(new string('a', 10_000)); // the text needs to be big enough to hit the deadlock + } + + [Fact] + public void ExceptionMessageIsNotLost() + { + var logger = new OutputLogger(Output); + var config = CreateSimpleConfig(logger); + + CanExecute(config, fullValidation: false); + + Assert.Contains(ErrorMessage, logger.GetLog()); + } + + public class ThrowingException + { + [Benchmark] + public void Write() => throw new Exception(ErrorMessage); + } + } +}