Add ActivityId Support to EventPipe (#16055)
authorBrian Robbins <brianrob@microsoft.com>
Tue, 30 Jan 2018 02:49:51 +0000 (18:49 -0800)
committerGitHub <noreply@github.com>
Tue, 30 Jan 2018 02:49:51 +0000 (18:49 -0800)
13 files changed:
src/mscorlib/System.Private.CoreLib.csproj
src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs
src/vm/ecalllist.h
src/vm/eventpipe.cpp
src/vm/eventpipe.h
src/vm/threads.cpp
src/vm/threads.h
tests/src/tracing/common/Assert.cs [new file with mode: 0755]
tests/src/tracing/common/common.csproj
tests/src/tracing/eventactivityidcontrol/EventActivityIdControl.cs [new file with mode: 0755]
tests/src/tracing/eventactivityidcontrol/eventactivityidcontrol.csproj [new file with mode: 0644]

index a85e298..df394ed 100644 (file)
     <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" />
index 5363b2a..57c3b8f 100644 (file)
@@ -169,6 +169,9 @@ namespace System.Diagnostics.Tracing
         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);
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
index a8789f5..0d99ff4 100644 (file)
@@ -74,7 +74,7 @@ namespace System.Diagnostics.Tracing
         // 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.
index c5e2b20..98556a7 100644 (file)
@@ -11,13 +11,6 @@ namespace System.Diagnostics.Tracing
 {
     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 
@@ -47,13 +40,25 @@ namespace System.Diagnostics.Tracing
             // 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;
@@ -97,14 +102,21 @@ namespace System.Diagnostics.Tracing
             // 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
@@ -124,13 +136,15 @@ namespace System.Diagnostics.Tracing
                 // 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;
             }
index 8fbfd20..b9706bb 100644 (file)
@@ -1203,6 +1203,7 @@ FCFuncStart(gEventPipeInternalFuncs)
     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()
index 41af3ef..2d7e302 100644 (file)
@@ -545,6 +545,12 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
         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))
@@ -964,6 +970,67 @@ void QCALLTYPE EventPipeInternal::DeleteProvider(
     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(&currentActivityId);
+                pThread->SetActivityId(&currentActivityId);
+                break;
+
+            default:
+                retVal = 1;
+        };
+    }
+
+    END_QCALL;
+    return retVal;
+}
+
 void QCALLTYPE EventPipeInternal::WriteEvent(
     INT_PTR eventHandle,
     UINT32 eventID,
index d1f7d60..907e0d7 100644 (file)
@@ -356,6 +356,16 @@ public:
 
 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:
 
@@ -384,6 +394,10 @@ 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,
index 5482102..422789b 100644 (file)
@@ -1687,6 +1687,7 @@ Thread::Thread()
 #ifdef FEATURE_PERFTRACING
     m_pEventPipeBufferList = NULL;
     m_eventWriteInProgress = false;
+    memset(&m_activityId, 0, sizeof(m_activityId));
 #endif // FEATURE_PERFTRACING
     m_HijackReturnKind = RT_Illegal;
 }
index 57cb872..d55d1f8 100644 (file)
@@ -5256,6 +5256,10 @@ private:
     // 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()
     {
@@ -5297,6 +5301,20 @@ public:
     {
         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
diff --git a/tests/src/tracing/common/Assert.cs b/tests/src/tracing/common/Assert.cs
new file mode 100755 (executable)
index 0000000..c538ecc
--- /dev/null
@@ -0,0 +1,40 @@
+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));
+            }
+        }
+    }
+}
index ca10e7f..7bb5dfc 100644 (file)
@@ -25,6 +25,7 @@
     </CodeAnalysisDependentAssemblyPaths>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Assert.cs" />
     <Compile Include="TraceControl.cs" />
     <Compile Include="TraceConfiguration.cs" />
   </ItemGroup>
diff --git a/tests/src/tracing/eventactivityidcontrol/EventActivityIdControl.cs b/tests/src/tracing/eventactivityidcontrol/EventActivityIdControl.cs
new file mode 100755 (executable)
index 0000000..1a7e288
--- /dev/null
@@ -0,0 +1,221 @@
+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;
+        }
+    }
+}
diff --git a/tests/src/tracing/eventactivityidcontrol/eventactivityidcontrol.csproj b/tests/src/tracing/eventactivityidcontrol/eventactivityidcontrol.csproj
new file mode 100644 (file)
index 0000000..9ec1be2
--- /dev/null
@@ -0,0 +1,30 @@
+<?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>