Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara committed Jan 28, 2025
1 parent fe063de commit 84a584e
Show file tree
Hide file tree
Showing 22 changed files with 1,349 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private void PublishedInstrument(Instrument instrument, MeterListener _)
}
}

// TODO Pavel
public void Start()
{
// if already started or already stopped we can't be started again
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1545,13 +1545,17 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\ActivityTracker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\DiagnosticCounter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\CounterGroup.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\CounterGroup.Threads.cs" Condition="('$(TargetsWasi)' != 'true' and '$(TargetsBrowser)' != 'true') or '$(FeatureWasmManagedThreads)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\CounterGroup.Wasm.cs" Condition="('$(TargetsWasi)' == 'true' or '$(TargetsBrowser)' == 'true') and '$(FeatureWasmManagedThreads)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\CounterPayload.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventActivityOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventCounter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventDescriptor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipe.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipe.Internal.cs" Condition="'$(FeaturePerfTracing)' == 'true' and '$(FeatureMono)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipeEventDispatcher.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipeEventDispatcher.Threads.cs" Condition="'$(FeaturePerfTracing)' == 'true' and (('$(TargetsWasi)' != 'true' and '$(TargetsBrowser)' != 'true') or '$(FeatureWasmManagedThreads)' == 'true')" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipeEventDispatcher.Wasm.cs" Condition="'$(FeaturePerfTracing)' == 'true' and (('$(TargetsWasi)' == 'true' or '$(TargetsBrowser)' == 'true') and '$(FeatureWasmManagedThreads)' != 'true')" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipeEventProvider.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipeMetadataGenerator.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventPipePayloadDecoder.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;

namespace System.Diagnostics.Tracing
{
internal sealed partial class CounterGroup
{
private static Thread? s_pollingThread;
// Used for sleeping for a certain amount of time while allowing the thread to be woken up
private static AutoResetEvent? s_pollingThreadSleepEvent;

private static void CreatePollingTimer()
{
// Create the polling thread and init all the shared state if needed
if (s_pollingThread == null)
{
s_pollingThreadSleepEvent = new AutoResetEvent(false);
s_counterGroupEnabledList = new List<CounterGroup>();
// TODO
s_pollingThread = new Thread(PollForValues)
{
IsBackground = true,
Name = ".NET Counter Poller"
};
s_pollingThread.Start();
}
else
{
// notify the polling thread that the polling interval may have changed and the sleep should be recomputed
s_pollingThreadSleepEvent!.Set();
}
}

private static void PollForValues()
{
AutoResetEvent? sleepEvent = null;
lock (s_counterGroupLock)
{
sleepEvent = s_pollingThreadSleepEvent;
}

while (true)
{
var sleepDurationInMilliseconds = PollOnce();

sleepEvent?.WaitOne(sleepDurationInMilliseconds);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;

namespace System.Diagnostics.Tracing
{
internal sealed partial class CounterGroup
{
private static Timer? s_pollingTimer;

private static void CreatePollingTimer()
{
if (s_pollingTimer == null)
{
s_pollingTimer = new Timer(PollForValues, null, 0, 0);
s_counterGroupEnabledList = new List<CounterGroup>();
}
else
{
// notify the polling callback that the polling interval may have changed and the sleep should be recomputed
s_pollingTimer.Change(0, 0);
}
}

private static void PollForValues(object? state)
{
var sleepDurationInMilliseconds = PollOnce();
s_pollingTimer!.Change(sleepDurationInMilliseconds, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

namespace System.Diagnostics.Tracing
{
#if !ES_BUILD_STANDALONE
[UnsupportedOSPlatform("browser")]
#endif
internal sealed class CounterGroup
internal sealed partial class CounterGroup
{
private readonly EventSource _eventSource;
private readonly List<DiagnosticCounter> _counters;
Expand Down Expand Up @@ -159,27 +156,12 @@ private void EnableTimer(float pollingIntervalInSeconds)
_timeStampSinceCollectionStarted = DateTime.UtcNow;
_nextPollingTimeStamp = DateTime.UtcNow + new TimeSpan(0, 0, (int)pollingIntervalInSeconds);

// Create the polling thread and init all the shared state if needed
if (s_pollingThread == null)
{
s_pollingThreadSleepEvent = new AutoResetEvent(false);
s_counterGroupEnabledList = new List<CounterGroup>();
s_pollingThread = new Thread(PollForValues)
{
IsBackground = true,
Name = ".NET Counter Poller"
};
s_pollingThread.Start();
}
CreatePollingTimer();

if (!s_counterGroupEnabledList!.Contains(this))
{
s_counterGroupEnabledList.Add(this);
}

// notify the polling thread that the polling interval may have changed and the sleep should
// be recomputed
s_pollingThreadSleepEvent!.Set();
}
}

Expand Down Expand Up @@ -267,29 +249,21 @@ private void OnTimer()
}
}

private static Thread? s_pollingThread;
// Used for sleeping for a certain amount of time while allowing the thread to be woken up
private static AutoResetEvent? s_pollingThreadSleepEvent;

private static List<CounterGroup>? s_counterGroupEnabledList;
private static List<IncrementingPollingCounter> s_needsResetIncrementingPollingCounters = [];

private static void PollForValues()
private static int PollOnce()
{
AutoResetEvent? sleepEvent = null;

// Cache of onTimer callbacks for each CounterGroup.
// We cache these outside of the scope of s_counterGroupLock because
// calling into the callbacks can cause a re-entrancy into CounterGroup.Enable()
// and result in a deadlock. (See https://github.com/dotnet/runtime/issues/40190 for details)
var onTimers = new List<CounterGroup>();
List<IncrementingPollingCounter>? countersToReset = null;
while (true)
{

int sleepDurationInMilliseconds = int.MaxValue;
lock (s_counterGroupLock)
{
sleepEvent = s_pollingThreadSleepEvent;
foreach (CounterGroup counterGroup in s_counterGroupEnabledList!)
{
DateTime now = DateTime.UtcNow;
Expand All @@ -316,8 +290,6 @@ private static void PollForValues()
{
counter.UpdateMetric();
}

countersToReset = null;
}

foreach (CounterGroup onTimer in onTimers)
Expand All @@ -329,10 +301,9 @@ private static void PollForValues()
{
sleepDurationInMilliseconds = -1; // WaitOne uses -1 to mean infinite
}
sleepEvent?.WaitOne(sleepDurationInMilliseconds);
}
}

return sleepDurationInMilliseconds;
}
#endregion // Timer Processing

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace System.Diagnostics.Tracing
{
internal sealed partial class EventPipeEventDispatcher
{
private void StartDispatchTask(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency)
{
Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
Debug.Assert(sessionID != 0);

m_dispatchTaskCancellationSource = new CancellationTokenSource();
Task? previousDispatchTask = m_dispatchTask;
m_dispatchTask = Task.Factory.StartNew(() => DispatchEventsToEventListeners(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency, previousDispatchTask, m_dispatchTaskCancellationSource.Token), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

private void DispatchEventsToEventListeners(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency, Task? previousDispatchTask, CancellationToken token)
{
Debug.Assert(sessionID != 0);
previousDispatchTask?.Wait(CancellationToken.None);

// Struct to fill with the call to GetNextEvent.
while (!token.IsCancellationRequested)
{
bool eventsReceived = DispatchEventsToEventListenersOnce(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency, token);

// Wait for more events.
if (!token.IsCancellationRequested)
{
if (!eventsReceived)
{
EventPipeInternal.WaitForSessionSignal(sessionID, Timeout.Infinite);
}

Thread.Sleep(10);
}
}

// Wait for SignalSession() to be called before we call disable, otherwise
// the SignalSession() call could be on a disabled session.
SpinWait sw = default;
while (Volatile.Read(ref m_sessionID) == sessionID)
{
sw.SpinOnce();
}

// Disable the old session. This can happen asynchronously since we aren't using the old session
// anymore.
EventPipeInternal.Disable(sessionID);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace System.Diagnostics.Tracing
{
// this is single-threaded version of EventPipeEventDispatcher
internal sealed partial class EventPipeEventDispatcher
{
private void StartDispatchTask(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency)
{
Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
Debug.Assert(sessionID != 0);

m_dispatchTaskCancellationSource = new CancellationTokenSource();
Task? previousDispatchTask = m_dispatchTask;
if (previousDispatchTask != null)
{
m_dispatchTask = previousDispatchTask.ContinueWith(_ => DispatchEventsToEventListeners(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency, m_dispatchTaskCancellationSource.Token),
m_dispatchTaskCancellationSource.Token, TaskContinuationOptions.None, TaskScheduler.Default);
}
else
{
m_dispatchTask = DispatchEventsToEventListeners(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency, m_dispatchTaskCancellationSource.Token);
}
}

private async Task DispatchEventsToEventListeners(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency, CancellationToken token)
{
Debug.Assert(sessionID != 0);

while (!token.IsCancellationRequested)
{
DispatchEventsToEventListenersOnce(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency, token);
await Task.Delay(100, token).ConfigureAwait(false);
}

EventPipeInternal.Disable(sessionID);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System.Diagnostics.Tracing
{
internal sealed class EventPipeEventDispatcher
internal sealed partial class EventPipeEventDispatcher
{
internal sealed class EventListenerSubscription
{
Expand Down Expand Up @@ -129,21 +129,6 @@ private void CommitDispatchConfiguration()
StartDispatchTask(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency);
}

private void StartDispatchTask(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency)
{
if (OperatingSystem.IsBrowser() || OperatingSystem.IsWasi())
{
throw new PlatformNotSupportedException();
}

Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
Debug.Assert(sessionID != 0);

m_dispatchTaskCancellationSource = new CancellationTokenSource();
Task? previousDispatchTask = m_dispatchTask;
m_dispatchTask = Task.Factory.StartNew(() => DispatchEventsToEventListeners(sessionID, syncTimeUtc, syncTimeQPC, timeQPCFrequency, previousDispatchTask, m_dispatchTaskCancellationSource.Token), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

private void SetStopDispatchTask()
{
Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));
Expand All @@ -160,16 +145,13 @@ private void SetStopDispatchTask()
Volatile.Write(ref m_sessionID, 0);
}

private unsafe void DispatchEventsToEventListeners(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency, Task? previousDispatchTask, CancellationToken token)
private unsafe bool DispatchEventsToEventListenersOnce(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency, CancellationToken token)
{
Debug.Assert(sessionID != 0);
previousDispatchTask?.Wait(CancellationToken.None);
bool eventsReceived = false;

// Struct to fill with the call to GetNextEvent.
EventPipeEventInstanceData instanceData;
while (!token.IsCancellationRequested)
{
bool eventsReceived = false;

// Get the next event.
while (!token.IsCancellationRequested && EventPipeInternal.GetNextEvent(sessionID, &instanceData))
{
Expand All @@ -184,30 +166,7 @@ private unsafe void DispatchEventsToEventListeners(ulong sessionID, DateTime syn
NativeRuntimeEventSource.Log.ProcessEvent(instanceData.EventID, instanceData.ThreadID, dateTimeStamp, instanceData.ActivityId, instanceData.ChildActivityId, payload);
}
}

// Wait for more events.
if (!token.IsCancellationRequested)
{
if (!eventsReceived)
{
EventPipeInternal.WaitForSessionSignal(sessionID, Timeout.Infinite);
}

Thread.Sleep(10);
}
}

// Wait for SignalSession() to be called before we call disable, otherwise
// the SignalSession() call could be on a disabled session.
SpinWait sw = default;
while (Volatile.Read(ref m_sessionID) == sessionID)
{
sw.SpinOnce();
}

// Disable the old session. This can happen asynchronously since we aren't using the old session
// anymore.
EventPipeInternal.Disable(sessionID);
return eventsReceived;
}

/// <summary>
Expand Down
Loading

0 comments on commit 84a584e

Please sign in to comment.