<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\EventPipe.cs" />
- <Compile Condition="'$(FeaturePerfTracing)' == 'true'" Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventProvider.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipeEventProvider.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Contracts\Contracts.cs" />
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern void DeleteProvider(IntPtr provHandle);
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern int EventActivityIdControl(uint controlCode, ref Guid activityId);
+
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern unsafe void WriteEvent(IntPtr eventHandle, uint eventID, void* pData, uint length, Guid* activityId, Guid* relatedActivityId);
// Get or set the per-thread activity ID.
int IEventProvider.EventActivityIdControl(UnsafeNativeMethods.ManifestEtw.ActivityControl ControlCode, ref Guid ActivityId)
{
- return 0;
+ return EventPipeInternal.EventActivityIdControl((uint)ControlCode, ref ActivityId);
}
// Define an EventPipeEvent handle.
{
public partial class EventSource
{
-#if FEATURE_MANAGED_ETW && FEATURE_PERFTRACING
- // For non-Windows, we use a thread-local variable to hold the activity ID.
- // On Windows, ETW has it's own thread-local variable and we participate in its use.
- [ThreadStatic]
- private static Guid s_currentThreadActivityId;
-#endif // FEATURE_MANAGED_ETW && FEATURE_PERFTRACING
-
// ActivityID support (see also WriteEventWithRelatedActivityIdCore)
/// <summary>
/// When a thread starts work that is on behalf of 'something else' (typically another
// We ignore errors to keep with the convention that EventSources do not throw errors.
// Note we can't access m_throwOnWrites because this is a static method.
-#if FEATURE_PERFTRACING
- s_currentThreadActivityId = activityId;
+#if FEATURE_PERFTRACING && PLATFORM_WINDOWS
+ // Set the activity id via EventPipe.
+ EventPipeInternal.EventActivityIdControl(
+ (uint)UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
+ ref activityId);
+
+ // Set the activity id via ETW and fetch the previous id.
+ if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
+ UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
+ ref activityId) == 0)
+#elif FEATURE_PERFTRACING
+ if (EventPipeInternal.EventActivityIdControl(
+ (uint)UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
+ ref activityId) == 0)
#elif PLATFORM_WINDOWS
if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
ref activityId) == 0)
-#endif // FEATURE_PERFTRACING
+#endif // FEATURE_PERFTRACING && PLATFORM_WINDOWS
{
#if FEATURE_ACTIVITYSAMPLING
var activityDying = s_activityDying;
// We ignore errors to keep with the convention that EventSources do not throw errors.
// Note we can't access m_throwOnWrites because this is a static method.
-#if FEATURE_PERFTRACING
- oldActivityThatWillContinue = s_currentThreadActivityId;
- s_currentThreadActivityId = activityId;
-#elif PLATFORM_WINDOWS
+#if FEATURE_PERFTRACING && PLATFORM_WINDOWS
+ EventPipeInternal.EventActivityIdControl(
+ (uint)UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_SET_ID,
+ ref oldActivityThatWillContinue);
+#elif FEATURE_PERFTRACING
+ EventPipeInternal.EventActivityIdControl(
+ (uint)UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
+ ref oldActivityThatWillContinue);
+#endif // FEATURE_PERFTRACING && PLATFORM_WINDOWS
+
+#if PLATFORM_WINDOWS
UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_SET_ID,
ref oldActivityThatWillContinue);
-#endif // FEATURE_PERFTRACING
+#endif // PLATFORM_WINDOWS
#endif // FEATURE_MANAGED_ETW
// We don't call the activityDying callback here because the caller has declared that
// errors. Note we can't access m_throwOnWrites because this is a static method.
Guid retVal = new Guid();
#if FEATURE_MANAGED_ETW
-#if FEATURE_PERFTRACING
- retVal = s_currentThreadActivityId;
-#elif PLATFORM_WINDOWS
+#if PLATFORM_WINDOWS
UnsafeNativeMethods.ManifestEtw.EventActivityIdControl(
UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
ref retVal);
-#endif // FEATURE_PERFTRACING
+#elif FEATURE_PERFTRACING
+ EventPipeInternal.EventActivityIdControl(
+ (uint)UnsafeNativeMethods.ManifestEtw.ActivityControl.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref retVal);
+#endif // PLATFORM_WINDOWS
#endif // FEATURE_MANAGED_ETW
return retVal;
}
QCFuncElement("CreateProvider", EventPipeInternal::CreateProvider)
QCFuncElement("DefineEvent", EventPipeInternal::DefineEvent)
QCFuncElement("DeleteProvider", EventPipeInternal::DeleteProvider)
+ QCFuncElement("EventActivityIdControl", EventPipeInternal::EventActivityIdControl)
QCFuncElement("WriteEvent", EventPipeInternal::WriteEvent)
QCFuncElement("WriteEventData", EventPipeInternal::WriteEventData)
FCFuncEnd()
return;
}
+ // If the activity id isn't specified, pull it from the current thread.
+ if(pActivityId == NULL)
+ {
+ pActivityId = pThread->GetActivityId();
+ }
+
if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
{
if(!s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId))
END_QCALL;
}
+int QCALLTYPE EventPipeInternal::EventActivityIdControl(
+ uint controlCode,
+ GUID *pActivityId)
+{
+
+ QCALL_CONTRACT;
+
+ int retVal = 0;
+
+ BEGIN_QCALL;
+
+ Thread *pThread = GetThread();
+ if(pThread == NULL || pActivityId == NULL)
+ {
+ retVal = 1;
+ }
+ else
+ {
+ ActivityControlCode activityControlCode = (ActivityControlCode)controlCode;
+ GUID currentActivityId;
+ switch(activityControlCode)
+ {
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID:
+
+ *pActivityId = *pThread->GetActivityId();
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID:
+
+ pThread->SetActivityId(pActivityId);
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID:
+
+ CoCreateGuid(pActivityId);
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID:
+
+ currentActivityId = *pThread->GetActivityId();
+ pThread->SetActivityId(pActivityId);
+ *pActivityId = currentActivityId;
+
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID:
+
+ *pActivityId = *pThread->GetActivityId();
+ CoCreateGuid(¤tActivityId);
+ pThread->SetActivityId(¤tActivityId);
+ break;
+
+ default:
+ retVal = 1;
+ };
+ }
+
+ END_QCALL;
+ return retVal;
+}
+
void QCALLTYPE EventPipeInternal::WriteEvent(
INT_PTR eventHandle,
UINT32 eventID,
class EventPipeInternal
{
+private:
+
+ enum class ActivityControlCode
+ {
+ EVENT_ACTIVITY_CONTROL_GET_ID = 1,
+ EVENT_ACTIVITY_CONTROL_SET_ID = 2,
+ EVENT_ACTIVITY_CONTROL_CREATE_ID = 3,
+ EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4,
+ EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5
+ };
public:
static void QCALLTYPE DeleteProvider(
INT_PTR provHandle);
+ static int QCALLTYPE EventActivityIdControl(
+ uint controlCode,
+ GUID *pActivityId);
+
static void QCALLTYPE WriteEvent(
INT_PTR eventHandle,
UINT32 eventID,
#ifdef FEATURE_PERFTRACING
m_pEventPipeBufferList = NULL;
m_eventWriteInProgress = false;
+ memset(&m_activityId, 0, sizeof(m_activityId));
#endif // FEATURE_PERFTRACING
m_HijackReturnKind = RT_Illegal;
}
// True if the thread was in cooperative mode. False if it was in preemptive when the suspension started.
Volatile<ULONG> m_gcModeOnSuspension;
+ // The activity ID for the current thread.
+ // An activity ID of zero means the thread is not executing in the context of an activity.
+ GUID m_activityId;
+
public:
EventPipeBufferList* GetEventPipeBufferList()
{
{
m_gcModeOnSuspension = 0;
}
+
+ LPCGUID GetActivityId() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return &m_activityId;
+ }
+
+ void SetActivityId(LPCGUID pActivityId)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(pActivityId != NULL);
+
+ m_activityId = *pActivityId;
+ }
#endif // FEATURE_PERFTRACING
#ifdef FEATURE_HIJACK
--- /dev/null
+using System;
+
+namespace Tracing.Tests.Common
+{
+ public static class Assert
+ {
+ public static void Equal<T>(T left, T right) where T : IEquatable<T>
+ {
+ if (left == null && right != null)
+ {
+ throw new Exception(
+ string.Format("Values are not equal! Left=NULL Right='{0}'", right));
+ }
+ else if (left != null && right == null)
+ {
+ throw new Exception(
+ string.Format("Values are not equal! Left='{0}' Right=NULL", left));
+ }
+ else if (!left.Equals(right))
+ {
+ throw new Exception(
+ string.Format("Values are not equal! Left='{0}' Right='{1}'", left, right));
+ }
+ }
+
+ public static void NotEqual<T>(T left, T right) where T : IEquatable<T>
+ {
+ if (left == null && right == null)
+ {
+ throw new Exception(
+ "Values are equal! Left=NULL Right=NULL");
+ }
+ else if (left != null && left.Equals(right))
+ {
+ throw new Exception(
+ string.Format("Values are equal! Left='{0}' Right='{1}'", left, right));
+ }
+ }
+ }
+}
</CodeAnalysisDependentAssemblyPaths>
</ItemGroup>
<ItemGroup>
+ <Compile Include="Assert.cs" />
<Compile Include="TraceControl.cs" />
<Compile Include="TraceConfiguration.cs" />
</ItemGroup>
--- /dev/null
+using System;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Tracing.Tests.Common;
+
+namespace Tracing.Tests
+{
+ public static class EventActivityIdControlTest
+ {
+ internal enum ActivityControlCode : uint
+ {
+ EVENT_ACTIVITY_CTRL_GET_ID = 1,
+ EVENT_ACTIVITY_CTRL_SET_ID = 2,
+ EVENT_ACTIVITY_CTRL_CREATE_ID = 3,
+ EVENT_ACTIVITY_CTRL_GET_SET_ID = 4,
+ EVENT_ACTIVITY_CTRL_CREATE_SET_ID = 5
+ };
+
+ private const uint NumThreads = 10;
+ private const uint NumTasks = 20;
+
+ private static MethodInfo s_EventActivityIdControl;
+ private static object s_EventPipeEventProvider;
+ private static bool s_FailureEncountered = false;
+
+ static int Main(string[] args)
+ {
+ if(!Initialize())
+ {
+ return -1;
+ }
+
+ // Run the test on the start-up thread.
+ TestThreadProc();
+
+ // Run the test on some background threads.
+ Thread[] threads = new Thread[NumThreads];
+ for(int i=0; i<NumThreads; i++)
+ {
+ threads[i] = new Thread(new ThreadStart(TestThreadProc));
+ threads[i].Start();
+ }
+
+ // Wait for all threads to complete.
+ for(int i=0; i<NumThreads; i++)
+ {
+ threads[i].Join();
+ }
+
+ // Run the test on some background tasks.
+ Task[] tasks = new Task[NumTasks];
+ for(int i=0; i<NumTasks; i++)
+ {
+ tasks[i] = Task.Run(new Action(TestThreadProc));
+ }
+ Task.WaitAll(tasks);
+
+ // Return the result.
+ return s_FailureEncountered ? 0 : 100;
+ }
+
+ private static void TestThreadProc()
+ {
+ Guid activityId = Guid.Empty;
+
+ try
+ {
+ // Activity ID starts as Guid.Empty.
+ int retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(activityId, Guid.Empty);
+
+ // Set the activity ID to a random GUID and then confirm that it was properly set.
+ activityId = Guid.NewGuid();
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_SET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+
+ Guid currActivityId = Guid.Empty;
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref currActivityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(currActivityId, activityId);
+
+ // Set and get the activity ID in one call.
+ activityId = Guid.NewGuid();
+ Guid savedActivityId = activityId;
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_SET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(currActivityId, activityId);
+
+ // Validate that the value we specified in the previous call is what comes back from a call to Get.
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(savedActivityId, activityId);
+
+ // Create a new ID but don't change the current value.
+ Guid newActivityId = Guid.Empty;
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_CREATE_ID,
+ ref newActivityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.NotEqual<Guid>(newActivityId, Guid.Empty);
+
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(savedActivityId, activityId);
+
+ // Create a new ID and set it in one action.
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_CREATE_SET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(savedActivityId, activityId);
+
+ savedActivityId = activityId;
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.NotEqual<Guid>(savedActivityId, activityId);
+ Assert.NotEqual<Guid>(activityId, Guid.Empty);
+
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref newActivityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(activityId, newActivityId);
+
+ // Set the activity ID back to zero.
+ activityId = Guid.Empty;
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_SET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+
+ retCode = EventActivityIdControl(
+ ActivityControlCode.EVENT_ACTIVITY_CTRL_GET_ID,
+ ref activityId);
+ Assert.Equal<int>(retCode, 0);
+ Assert.Equal<Guid>(activityId, Guid.Empty);
+
+ // Try pass an invalid control code.
+ activityId = Guid.NewGuid();
+ savedActivityId = activityId;
+ retCode = EventActivityIdControl(
+ (ActivityControlCode)10,
+ ref activityId);
+ Assert.Equal<int>(retCode, 1);
+ Assert.Equal<Guid>(activityId, savedActivityId);
+ }
+ catch(Exception ex)
+ {
+ s_FailureEncountered = true;
+ Console.WriteLine(ex.ToString());
+ }
+ }
+
+ private static int EventActivityIdControl(
+ ActivityControlCode controlCode,
+ ref Guid activityId)
+ {
+ object[] parameters = new object[]
+ {
+ (uint)controlCode,
+ activityId
+ };
+
+ int retCode = (int) s_EventActivityIdControl.Invoke(
+ s_EventPipeEventProvider,
+ parameters);
+
+ // Copy the by ref activityid out of the parameters array.
+ activityId = (Guid)parameters[1];
+ return retCode;
+ }
+
+ private static bool Initialize()
+ {
+ // Reflect over System.Private.CoreLib and get the EventPipeEventProvider type and EventActivityIdControl method.
+ Assembly SPC = typeof(System.Diagnostics.Tracing.EventSource).Assembly;
+ if(SPC == null)
+ {
+ Console.WriteLine("Failed to get System.Private.CoreLib assembly.");
+ return false;
+ }
+ Type eventPipeEventProviderType = SPC.GetType("System.Diagnostics.Tracing.EventPipeEventProvider");
+ if(eventPipeEventProviderType == null)
+ {
+ Console.WriteLine("Failed to get System.Diagnostics.Tracing.EventPipeEventProvider type.");
+ return false;
+ }
+ s_EventActivityIdControl = eventPipeEventProviderType.GetMethod("System.Diagnostics.Tracing.IEventProvider.EventActivityIdControl", BindingFlags.NonPublic | BindingFlags.Instance );
+ if(s_EventActivityIdControl == null)
+ {
+ Console.WriteLine("Failed to get EventActivityIdControl method.");
+ return false;
+ }
+
+ // Create an instance of EventPipeEventProvider.
+ s_EventPipeEventProvider = Activator.CreateInstance(eventPipeEventProviderType);
+ if(s_EventPipeEventProvider == null)
+ {
+ Console.WriteLine("Failed to create EventPipeEventProvider instance.");
+ }
+
+ return true;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{8E3244CB-407F-4142-BAAB-E7A55901A5FA}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <CLRTestKind>BuildAndRun</CLRTestKind>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <CLRTestPriority>0</CLRTestPriority>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="EventActivityIdControl.cs" />
+ <ProjectReference Include="../common/common.csproj" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>