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
30 changes: 6 additions & 24 deletions src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Mathematics;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Toolchains;
using BenchmarkDotNet.Toolchains.Parameters;
Expand Down Expand Up @@ -168,15 +167,13 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
var cultureInfo = config.CultureInfo ?? DefaultCultureInfo.Instance;
var reports = new List<BenchmarkReport>();
string title = GetTitle(new[] { benchmarkRunInfo });
var consoleTitle = RuntimeInformation.IsWindows() ? Console.Title : string.Empty;
using var consoleTitler = new ConsoleTitler($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining");

logger.WriteLineInfo($"// Found {benchmarks.Length} benchmarks:");
foreach (var benchmark in benchmarks)
logger.WriteLineInfo($"// {benchmark.DisplayInfo}");
logger.WriteLine();

UpdateTitle(totalBenchmarkCount, benchmarksToRunCount);

using (var powerManagementApplier = new PowerManagementApplier(logger))
{
bool stop = false;
Expand Down Expand Up @@ -241,15 +238,10 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,

benchmarksToRunCount -= stop ? benchmarks.Length - i : 1;

LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, taskbarProgress);
LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, consoleTitler, taskbarProgress);
}
}

if (RuntimeInformation.IsWindows())
{
Console.Title = consoleTitle;
}

var runEnd = runsChronometer.GetElapsed();

return new Summary(title,
Expand Down Expand Up @@ -652,15 +644,7 @@ private static void Cleanup(HashSet<string> artifactsToCleanup)
}
}

private static void UpdateTitle(int totalBenchmarkCount, int benchmarksToRunCount)
{
if (!Console.IsOutputRedirected && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux() || RuntimeInformation.IsMacOS()))
{
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining";
}
}

private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, TaskbarProgress taskbarProgress)
private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, ConsoleTitler consoleTitler, TaskbarProgress taskbarProgress)
{
int executedBenchmarkCount = totalBenchmarkCount - benchmarksToRunCount;
TimeSpan fromNow = GetEstimatedFinishTime(runsChronometer, benchmarksToRunCount, executedBenchmarkCount);
Expand All @@ -669,10 +653,8 @@ private static void LogProgress(ILogger logger, in StartedClock runsChronometer,
$" Estimated finish {estimatedEnd:yyyy-MM-dd H:mm} ({(int)fromNow.TotalHours}h {fromNow.Minutes}m from now) **";
logger.WriteLineHeader(message);

if (!Console.IsOutputRedirected && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux() || RuntimeInformation.IsMacOS()))
{
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish";
}
consoleTitler.UpdateTitle ($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish");

taskbarProgress.SetProgress((float) executedBenchmarkCount / totalBenchmarkCount);
}

Expand Down Expand Up @@ -728,4 +710,4 @@ private static int GetIdToResume(string rootArtifactsFolderPath, string currentL
return -1;
}
}
}
}
80 changes: 80 additions & 0 deletions src/BenchmarkDotNet/Running/ConsoleTitler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.IO;
using BenchmarkDotNet.Portability;

namespace BenchmarkDotNet.Running
{
/// <summary>
/// Updates Console.Title, subject to platform capabilities and Console availability.
/// Restores the original (or fallback) title upon disposal.
/// </summary>
internal class ConsoleTitler : IDisposable
{
/// <summary>
/// Whether this instance has any effect. This will be false if the platform doesn't support Console retitling,
/// or if Console output is redirected.
/// </summary>
public bool IsEnabled { get; private set; }

private string oldConsoleTitle;

public ConsoleTitler(string initialTitle)
{
// Return without enabling if Console output is redirected.
if (Console.IsOutputRedirected)
{
return;
}
Comment on lines +23 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not 100% sure if we want to disable this if output is redirected. I can imagine a scenario where the output is piped to a file, but it would still be useful to see the progress in the title. But not a big deal to me either way.


try
{
oldConsoleTitle = PlatformSupportsTitleRead() ? Console.Title : "";
}
catch (IOException)
{
// We're unable to read Console.Title on a platform that supports it. This can happen when no console
// window is available due to the application being Windows Forms, WPF, Windows Service or a daemon.
oldConsoleTitle = "";
}

try
{
// Enable ConsoleTitler if and only if we can successfully set the Console.Title property.
Console.Title = initialTitle;
IsEnabled = true;
}
catch (IOException)
{
}
catch (PlatformNotSupportedException)
{
// As of .NET 7, platforms other than Windows, Linux and MacOS do not support Console retitling.
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Write the passed in title here. This is where you need the flag to check in UpdateTitle if it is writable. The flag you have now only checks if it's readable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you need a separate try/catch for the write instead of a platform check. We'd want to catch IOException and PlatformNotSupportedException. Let other exceptions propagate.

try
{
   Console.Title = title;
   IsEnabled = true;
}
catch (IOException) { }
catch (PlatformNotSupportedException) { }

}

#if NET6_0_OR_GREATER
[System.Runtime.Versioning.SupportedOSPlatformGuard("windows")]
#endif
private static bool PlatformSupportsTitleRead() => RuntimeInformation.IsWindows();

/// <summary>
/// Updates Console.Title if enabled.
/// </summary>
public void UpdateTitle(string title)
{
if (IsEnabled)
{
Console.Title = title;
}
}

public void Dispose()
{
if (IsEnabled)
{
Console.Title = oldConsoleTitle;
IsEnabled = false;
}
}
}
}