Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple EventCounters sessions and make sure EventListeners always send a Disable command #82970

Merged
merged 15 commits into from
Apr 8, 2023

Conversation

davmason
Copy link
Member

@davmason davmason commented Mar 4, 2023

This PR does two things

  1. Refcounts CountGroup so multiple sessions can enable and disable counters. Previously if any session disabled counters it would disable them globally
  2. Adds a check in EventListener.Dispose that will call DisableEvents if the user hasn't already

Fixes #61353

@davmason davmason added this to the 8.0.0 milestone Mar 4, 2023
@davmason davmason requested a review from a team March 4, 2023 02:28
@davmason davmason self-assigned this Mar 4, 2023
@runfoapp runfoapp bot mentioned this pull request Mar 6, 2023
Copy link
Member

@noahfalk noahfalk left a comment

Choose a reason for hiding this comment

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

LGTM

@davmason
Copy link
Member Author

Rebasing against main latest to trigger a new run, hopefully without all the timeouts

…all to disableevents out of the main loop

Update EventSource.cs
@davmason davmason changed the title Allow multiple EventCounters sessions and make sure EventListners always send a Disable command Allow multiple EventCounters sessions and make sure EventListeners always send a Disable command Mar 22, 2023
if (e.Arguments.TryGetValue("EventCounterIntervalSec", out string? valueStr) && float.TryParse(valueStr, out float value))
lock (s_counterGroupLock) // Lock the CounterGroup
{
float intervalValue = 1.0f;
Copy link
Member

Choose a reason for hiding this comment

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

I might be misunderstanding... because this has a non-zero value by default, isn't this going to automatically initialize counters at 1000ms intervals for the very first enabled event source if it doesn't have EventCounterIntervalSec in its spec? Won't that prevent any real request for counters to ever be observed?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's definitely wrong, good catch. As it is we will always enable counters for any Enable command. We would process future Enable commands though so it probably wouldn't impact anyone other than some additional overhead in the process

@davmason
Copy link
Member Author

@noahfalk this is ready to review again with the new approach

{
EnableTimer(value);
// Command is Enable but no EventCounterIntervalSec arg so ignore
Copy link
Member

Choose a reason for hiding this comment

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

I think the behavior here is probably the best we can do within the current limits of EventSource, but it still seems a little odd and merits comments. In particular I am comparing these two scenarios:

Scenario A - first the user enables a session with IntervalCounterIntervalSec=1, then they do an ETW update with new filter args "Foo=6" (which presumably gets translated into an Enable)
Scenario B - user enables a session with filter args Foo=6

Both scenarios end with ETW believing the current filter args are "Foo=6", but in A the counters are on whereas in B they are off. We are effectively saying not only does the current state matter, but also the sequence of steps you took to get there matters. In an ideal world where EventSource could enumerate the current filter args of each session we would know that scenario A shouldn't have the counters enabled anymore, but we don't have that info. Given the ambiguity I agree its probably better to guess that they are still on rather than guessing that they are off now.

{
lock (s_counterGroupLock)

if ((e.Command == EventCommand.Disable && !_eventSource.IsEnabled()) || intervalValue <= 0)
Copy link
Member

Choose a reason for hiding this comment

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

  1. I think a comment here describing why we do it this way and not by ref counting Enable/Disable calls would be very handy knowledge to preserve.

  2. The logic with intervalValue still looks fishy to me. Imagine we get the sequence of events:
    Enable session 1, Enable session 2, Disable session 2 and none of them specify IntervalCounterSec. It appears that when we process the disable command at that time we would enable the timer at 1sec intervals.

@@ -2663,6 +2660,9 @@ internal void DoCommand(EventCommandEventArgs commandArgs)
m_eventSourceEnabled = false;
}
}

this.OnEventCommand(commandArgs);
Copy link
Member

Choose a reason for hiding this comment

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

The previous asymmetry in when the callback ran relative to when the IsEnabled() bit updated puzzled me and this certainly seems more intuitive. However its possible the previous behavior was designed to let people send events during the disable callback and now they would be unable to do so. You should probably test it. If we are lucky sending events during the Disable callback already didn't work in which case this re-ordering is probably safe.

Copy link
Member Author

Choose a reason for hiding this comment

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

Verified that in the existing code you cannot send events during a Disable command, the event is already turned off

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

EventCounters unexpectedly stop reporting data when first of two listeners disconnects
4 participants