Skip to content

Commit

Permalink
Dispatch Runtime Events to EventListener (dotnet/coreclr#18649)
Browse files Browse the repository at this point in the history
Commit migrated from dotnet/coreclr@b3b9d08
  • Loading branch information
brianrob authored Jul 2, 2018
1 parent e73eb95 commit 7aef92f
Show file tree
Hide file tree
Showing 23 changed files with 907 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -438,10 +438,13 @@
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventSource_CoreCLR.cs" />
<Compile Condition="'$(FeatureXplatEventSource)' == 'true'" Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\XplatEventLogger.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\FrameworkEventSource.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\DotNETRuntimeEventSource.cs" />
<Compile Include="$(IntermediateOutputPath)..\eventing\DotNETRuntimeEventSource.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipe.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventDispatcher.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventProvider.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeMetadataGenerator.cs" />
<Compile Include="$(IntermediateOutputPath)..\eventing\DotNETRuntimeEventSource.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipePayloadDecoder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\TraceLogging\TraceLoggingEventHandleTable.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Diagnostics.Tracing
{
#if FEATURE_PERFTRACING
/// <summary>
/// RuntimeEventSource is an EventSource that represents the ETW/EventPipe events emitted by the native runtime.
/// Most of RuntimeEventSource is auto-generated by scripts/genRuntimeEventSources.py based on the contents of the Microsoft-Windows-DotNETRuntime provider.
/// </summary>
internal sealed partial class RuntimeEventSource : EventSource
{
/// <summary>
/// Dispatch a single event with the specified event ID and payload.
/// </summary>
/// <param name="eventID">The eventID corresponding to the event as defined in the auto-generated portion of the RuntimeEventSource class.</param>
/// <param name="payload">A span pointing to the data payload for the event.</param>
[NonEvent]
internal unsafe void ProcessEvent(uint eventID, ReadOnlySpan<Byte> payload)
{
// Make sure the eventID is valid.
if (eventID >= m_eventData.Length)
{
return;
}

// Decode the payload.
object[] decodedPayloadFields = EventPipePayloadDecoder.DecodePayload(ref m_eventData[eventID], payload);
WriteToAllListeners((int)eventID, null, null, decodedPayloadFields);
}
}
#endif // FEATURE_PERFTRACING
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@
using System.Security;
using Microsoft.Win32;

#if FEATURE_PERFTRACING

namespace System.Diagnostics.Tracing
{
[StructLayout(LayoutKind.Sequential)]
internal struct EventPipeEventInstanceData
{
internal IntPtr ProviderID;
internal uint EventID;
internal IntPtr Payload;
internal uint PayloadLength;
}

[StructLayout(LayoutKind.Sequential)]
internal struct EventPipeProviderConfiguration
{
Expand Down Expand Up @@ -165,6 +176,9 @@ internal static class EventPipeInternal
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern unsafe IntPtr DefineEvent(IntPtr provHandle, uint eventID, long keywords, uint eventVersion, uint level, void *pMetadata, uint metadataLength);

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern IntPtr GetProvider(string providerName);

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern void DeleteProvider(IntPtr provHandle);

Expand All @@ -176,5 +190,10 @@ internal static class EventPipeInternal

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern unsafe void WriteEventData(IntPtr eventHandle, uint eventID, EventProvider.EventData* pEventData, uint dataCount, Guid* activityId, Guid* relatedActivityId);

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern unsafe bool GetNextEvent(EventPipeEventInstanceData* pInstance);
}
}

#endif // FEATURE_PERFTRACING
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace System.Diagnostics.Tracing
{
#if FEATURE_PERFTRACING
internal sealed class EventPipeEventDispatcher
{
internal sealed class EventListenerSubscription
{
internal EventKeywords MatchAnyKeywords { get; private set; }
internal EventLevel Level { get; private set; }

internal EventListenerSubscription(EventKeywords matchAnyKeywords, EventLevel level)
{
MatchAnyKeywords = matchAnyKeywords;
Level = level;
}
}

internal static readonly EventPipeEventDispatcher Instance = new EventPipeEventDispatcher();

private IntPtr m_RuntimeProviderID;

private bool m_stopDispatchTask;
private Task m_dispatchTask = null;
private object m_dispatchControlLock = new object();
private Dictionary<EventListener, EventListenerSubscription> m_subscriptions = new Dictionary<EventListener, EventListenerSubscription>();

private EventPipeEventDispatcher()
{
// Get the ID of the runtime provider so that it can be used as a filter when processing events.
m_RuntimeProviderID = EventPipeInternal.GetProvider(RuntimeEventSource.EventSourceName);
}

internal void SendCommand(EventListener eventListener, EventCommand command, bool enable, EventLevel level, EventKeywords matchAnyKeywords)
{
if (command == EventCommand.Update && enable)
{
lock (m_dispatchControlLock)
{
// Add the new subscription. This will overwrite an existing subscription for the listener if one exists.
m_subscriptions[eventListener] = new EventListenerSubscription(matchAnyKeywords, level);

// Commit the configuration change.
CommitDispatchConfiguration();
}
}
else if (command == EventCommand.Update && !enable)
{
RemoveEventListener(eventListener);
}
}

internal void RemoveEventListener(EventListener listener)
{
lock (m_dispatchControlLock)
{
// Remove the event listener from the list of subscribers.
if (m_subscriptions.ContainsKey(listener))
{
m_subscriptions.Remove(listener);
}

// Commit the configuration change.
CommitDispatchConfiguration();
}
}

private void CommitDispatchConfiguration()
{
// Ensure that the dispatch task is stopped.
// This is a no-op if the task is already stopped.
StopDispatchTask();

// Stop tracing.
// This is a no-op if it's already disabled.
EventPipeInternal.Disable();

// Check to see if tracing should be enabled.
if (m_subscriptions.Count <= 0)
{
return;
}

// Start collecting events.
EventKeywords aggregatedKeywords = EventKeywords.None;
EventLevel highestLevel = EventLevel.LogAlways;

foreach (EventListenerSubscription subscription in m_subscriptions.Values)
{
aggregatedKeywords |= subscription.MatchAnyKeywords;
highestLevel = (subscription.Level > highestLevel) ? subscription.Level : highestLevel;
}

EventPipeProviderConfiguration[] providerConfiguration = new EventPipeProviderConfiguration[]
{
new EventPipeProviderConfiguration(RuntimeEventSource.EventSourceName, (ulong) aggregatedKeywords, (uint) highestLevel)
};

EventPipeInternal.Enable(null, 1024, 1, providerConfiguration, 1);

// Start the dispatch task.
StartDispatchTask();
}

private void StartDispatchTask()
{
Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));

if (m_dispatchTask == null)
{
m_stopDispatchTask = false;
m_dispatchTask = Task.Factory.StartNew(DispatchEventsToEventListeners, TaskCreationOptions.LongRunning);
}
}

private void StopDispatchTask()
{
Debug.Assert(Monitor.IsEntered(m_dispatchControlLock));

if(m_dispatchTask != null)
{
m_stopDispatchTask = true;
m_dispatchTask.Wait();
m_dispatchTask = null;
}
}

private unsafe void DispatchEventsToEventListeners()
{
// Struct to fill with the call to GetNextEvent.
EventPipeEventInstanceData instanceData;

while (!m_stopDispatchTask)
{
// Get the next event.
while (!m_stopDispatchTask && EventPipeInternal.GetNextEvent(&instanceData))
{
// Filter based on provider.
if (instanceData.ProviderID == m_RuntimeProviderID)
{
// Dispatch the event.
ReadOnlySpan<Byte> payload = new ReadOnlySpan<byte>((void*)instanceData.Payload, (int)instanceData.PayloadLength);
RuntimeEventSource.Log.ProcessEvent(instanceData.EventID, payload);
}
}

// Wait for more events.
if (!m_stopDispatchTask)
{
Thread.Sleep(10);
}
}
}
}
#endif // FEATURE_PERFTRACING
}
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,13 @@ private static TypeCode GetTypeCodeExtended(Type parameterType)
if (parameterType == typeof(Guid)) // Guid is not a part of TypeCode enum
return GuidTypeCode;

// IntPtr and UIntPtr are converted to their non-pointer types.
if (parameterType == typeof(IntPtr))
return IntPtr.Size == 4 ? TypeCode.Int32 : TypeCode.Int64;

if (parameterType == typeof(UIntPtr))
return UIntPtr.Size == 4 ? TypeCode.UInt32 : TypeCode.UInt64;

return Type.GetTypeCode(parameterType);
}
}
Expand Down
Loading

0 comments on commit 7aef92f

Please sign in to comment.