dotnet / diagnostics

This repository contains the source code for various .NET Core runtime diagnostic tools and documents.
MIT License
1.19k stars 354 forks source link

Exception when running `dotnet-counters monitor` under Windows Terminal (and with reduced console buffer sizes) #1716

Open thargy opened 4 years ago

thargy commented 4 years ago

Description

When running dotnet-counters monitor ... under Windows Terminal Preview, with 30+ counters on screen, I get the following exception:

[ERROR] System.ArgumentOutOfRangeException: The value must be greater than or equal to zero and less than the console's buffer size in that dimension.
Parameter name: top
Actual value was 30.
   at System.ConsolePal.SetCursorPosition(Int32 left, Int32 top)
   at System.Console.SetCursorPosition(Int32 left, Int32 top)
   at Microsoft.Diagnostics.Tools.Counters.Exporters.ConsoleWriter.CounterPayloadReceived(String providerName, ICounterPayload payload, Boolean pauseCmdSet) in /_/src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs:line 148
   at Microsoft.Diagnostics.Tracing.TraceEventDispatcher.DoDispatch(TraceEvent anEvent)
   at Microsoft.Diagnostics.Tracing.EventPipeEventSource.DispatchEventRecord(EVENT_RECORD* eventRecord)
   at Microsoft.Diagnostics.Tracing.EventPipeEventSource.EventCache_OnEvent(EventPipeEventHeader& header)
   at Microsoft.Diagnostics.Tracing.EventPipe.EventCache.SortAndDispatch(Int64 stopTimestamp)
   at Microsoft.Diagnostics.Tracing.EventPipe.EventCache.ProcessEventBlock(Byte[] eventBlockData)
   at Microsoft.Diagnostics.Tracing.EventPipeBlock.FromStream(Deserializer deserializer)
   at FastSerialization.Deserializer.ReadObjectDefinition(Tags tag, StreamLabel objectLabel)
   at FastSerialization.Deserializer.ReadObject()
   at Microsoft.Diagnostics.Tracing.EventPipeEventSource.Process()
   at Microsoft.Diagnostics.Tools.Counters.CounterMonitor.<>c__DisplayClass22_0.<Start>b__1() in /_/src/Tools/dotnet-counters/CounterMonitor.cs:line 381

This is probably caused by the code assuming that the console buffer is greater than the number of lines it is trying to write; which is something that isn't true under Windows Terminal (where the buffer appears to be the current screen size), and isn't necessarily true under any other terminal window (where such things are generally configurable, even if they are normally set to a 'large' value).

Configuration

To experience the crash, dotnet-counters needs to be run in monitor mode, and the application it is monitoring must be outputting enough counters to exceed the current buffer size of the terminal to which it is outputting.

mikelle-rogers commented 3 years ago

I tried to repro this using Windows Terminal with PowerShell, Command Prompt and Ubuntu, I was not able to repro it. I am closing this issue. Please feel free to reopen this issue if the problem is encountered again.

thargy commented 3 years ago

I tried to repro this using Windows Terminal with PowerShell, Command Prompt and Ubuntu, I was not able to repro it. I am closing this issue. Please feel free to reopen this issue if the problem is encountered again.

NGL, was genuinely surprised to see you say this. I've moved on since reporting this almost a year ago (new install of Windows 11 and Ubuntu, etc.), so thought I'd see if it was fixed; but haven't looked in a long time.

Re-read, my comment:

When running dotnet-counters monitor ... under Windows Terminal Preview, with 30+ counters on screen, I get the following exception:

Thought "OK, to reproduce this create >30 counters, and run dotnet monitor against it", shouldn't be rocket science right?

Wrote the following code, (took me 20 minutes after reminding myself how to create EventSources etc). Compiled to Example.exe:

using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

Console.WriteLine($"Starting {ExampleEventSource.Count} counters. Press any key to stop");
using var source = ExampleEventSource.Instance;

while (!Console.KeyAvailable)
{
    Console.Write('.');
    source.Increment();
    await Task.Delay(TimeSpan.FromSeconds(1));
}

[EventSource(Name = "ExampleEventSource")]
public sealed class ExampleEventSource : EventSource
{
    public const int Count = 31;
    internal static readonly ExampleEventSource Instance = new ExampleEventSource(Count);
    private IncrementingEventCounter?[]? _counters;

    private ExampleEventSource(int count)
    {
        var oneSec = TimeSpan.FromSeconds(1);
        var counters = new IncrementingEventCounter [count];
        for (var i = 0; i < count; i++)
        {
            counters[i] = new IncrementingEventCounter($"Counter {i + 1}", this)
            {
                DisplayRateTimeScale = oneSec,
                DisplayName = $"Example counter {i + 1}"
            };
        }

        _counters = counters;
    }

    public void Increment()
    {
        var counters = _counters;
        if (counters is null) return;
        foreach (var t in counters) t?.Increment();
    }

    protected override void Dispose(bool disposing)
    {
        var counters = Interlocked.Exchange(ref _counters, null);
        if (counters is not null)
        {
            for (var i = 0; i < counters.Length; i++)
            {
                counters[i]?.Dispose();
                counters[i] = null;
            }
        }

        base.Dispose(disposing);
    }
}

Ran it in the console, opened Windows Terminal, in powershell mode. And ran:

dotnet tool install --global dotnet-counters
dotnet-counters monitor -n Example --counters ExampleEventSource

(The first line because this was clean build and I hadn't installed dotnet-counters yet, as you've attempted a repo you should be able to skip that step).

Following error resulted:

Unhandled exception: System.ArgumentOutOfRangeException: The value must be greater than or equal to zero and less than the console's buffer size in that dimension. (Parameter 'top')
Actual value was 30.
   at System.ConsolePal.SetCursorPosition(Int32 left, Int32 top)
   at System.Console.SetCursorPosition(Int32 left, Int32 top)
   at Microsoft.Diagnostics.Tools.Counters.Exporters.ConsoleWriter.CounterPayloadReceived(CounterPayload payload, Boolean pauseCmdSet) in /_/src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs:line 200
   at Microsoft.Diagnostics.Tools.Counters.CounterMonitor.HandleBufferedEvents() in /_/src/Tools/dotnet-counters/CounterMonitor.cs:line 398
   at Microsoft.Diagnostics.Tools.Counters.CounterMonitor.Start() in /_/src/Tools/dotnet-counters/CounterMonitor.cs:line 843
   at Microsoft.Diagnostics.Tools.Counters.CounterMonitor.Monitor(CancellationToken ct, List`1 counter_list, String counters, IConsole console, Int32 processId, Int32 refreshInterval, String name, String diagnosticPort, Boolean resumeRuntime, Int32 maxHistograms, Int32 maxTimeSeries) in /_/src/Tools/dotnet-counters/CounterMonitor.cs:line 489
   at System.CommandLine.Invocation.CommandHandler.GetResultCodeAsync(Object value, InvocationContext context)
   at System.CommandLine.Invocation.ModelBindingCommandHandler.InvokeAsync(InvocationContext context)
   at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseParseErrorReporting>b__21_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass16_0.<<UseHelp>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass25_0.<<UseVersionOption>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass23_0.<<UseTypoCorrections>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseSuggestDirective>b__22_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseParseDirective>b__20_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseDebugDirective>b__11_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__10_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass14_0.<<UseExceptionHandler>b__0>d.MoveNext()

Ran it in cmd, and, surprisingly, it just froze rather than producing an error message, which is new.

thargy commented 3 years ago

Please feel free to reopen this issue if the problem is encountered again.

@mikelle-rogers Please note I do not have permission to reopen this issue, so please re-open, or would you like me to re-raise it?