File: System\ServiceModel\Diagnostics\ServiceModelActivity.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Diagnostics
{
    using System;
    using System.Diagnostics;
    using System.Runtime.Remoting.Messaging;
    using System.Threading;
    using System.Globalization;
    using System.Collections.Generic;
    using System.ServiceModel.Diagnostics.Application;
    using System.Runtime.Diagnostics;
 
 
    class ServiceModelActivity : IDisposable
    {
        [ThreadStatic]
        static ServiceModelActivity currentActivity;
 
        static string[] ActivityTypeNames = new string[(int)ActivityType.NumItems];
 
        ServiceModelActivity previousActivity = null;
        static string activityBoundaryDescription = null;
        ActivityState lastState = ActivityState.Unknown;
        string name = null;
        bool autoStop = false;
        bool autoResume = false;
        Guid activityId;
        bool disposed = false;
        bool isAsync = false;
        int stopCount = 0;
        const int AsyncStopCount = 2;
        TransferActivity activity = null;
        ActivityType activityType = ActivityType.Unknown;
 
        static ServiceModelActivity()
        {
            ActivityTypeNames[(int)ActivityType.Unknown] = "Unknown";
            ActivityTypeNames[(int)ActivityType.Close] = "Close";
            ActivityTypeNames[(int)ActivityType.Construct] = "Construct";
            ActivityTypeNames[(int)ActivityType.ExecuteUserCode] = "ExecuteUserCode";
            ActivityTypeNames[(int)ActivityType.ListenAt] = "ListenAt";
            ActivityTypeNames[(int)ActivityType.Open] = "Open";
            ActivityTypeNames[(int)ActivityType.OpenClient] = "Open";
            ActivityTypeNames[(int)ActivityType.ProcessMessage] = "ProcessMessage";
            ActivityTypeNames[(int)ActivityType.ProcessAction] = "ProcessAction";
            ActivityTypeNames[(int)ActivityType.ReceiveBytes] = "ReceiveBytes";
            ActivityTypeNames[(int)ActivityType.SecuritySetup] = "SecuritySetup";
            ActivityTypeNames[(int)ActivityType.TransferToComPlus] = "TransferToComPlus";
            ActivityTypeNames[(int)ActivityType.WmiGetObject] = "WmiGetObject";
            ActivityTypeNames[(int)ActivityType.WmiPutInstance] = "WmiPutInstance";
        }
 
        ServiceModelActivity(Guid activityId)
        {
            this.activityId = activityId;
            this.previousActivity = ServiceModelActivity.Current;
        }
 
        static string ActivityBoundaryDescription
        {
            get
            {
                if (ServiceModelActivity.activityBoundaryDescription == null)
                {
                    ServiceModelActivity.activityBoundaryDescription = TraceSR.GetString(TraceSR.ActivityBoundary);
                }
                return ServiceModelActivity.activityBoundaryDescription;
            }
        }
 
        internal ActivityType ActivityType
        {
            get { return this.activityType; }
        }
 
        internal ServiceModelActivity PreviousActivity
        {
            get { return this.previousActivity; }
        }
 
        static internal Activity BoundOperation(ServiceModelActivity activity)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            return ServiceModelActivity.BoundOperation(activity, false);
        }
 
        static internal Activity BoundOperation(ServiceModelActivity activity, bool addTransfer)
        {
            return activity == null ? null : ServiceModelActivity.BoundOperationCore(activity, addTransfer);
        }
 
        static Activity BoundOperationCore(ServiceModelActivity activity, bool addTransfer)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            TransferActivity retval = null;
            if (activity != null)
            {
                retval = TransferActivity.CreateActivity(activity.activityId, addTransfer);
                if (retval != null)
                {
                    retval.SetPreviousServiceModelActivity(ServiceModelActivity.Current);
                }
                ServiceModelActivity.Current = activity;
            }
            return retval;
        }
 
        internal static ServiceModelActivity CreateActivity()
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            return ServiceModelActivity.CreateActivity(Guid.NewGuid(), true);
        }
 
        internal static ServiceModelActivity CreateActivity(bool autoStop)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity activity = ServiceModelActivity.CreateActivity(Guid.NewGuid(), true);
            if (activity != null)
            {
                activity.autoStop = autoStop;
            }
            return activity;
        }
 
        internal static ServiceModelActivity CreateActivity(bool autoStop, string activityName, ActivityType activityType)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity activity = ServiceModelActivity.CreateActivity(autoStop);
            ServiceModelActivity.Start(activity, activityName, activityType);
            return activity;
        }
 
        internal static ServiceModelActivity CreateAsyncActivity()
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity activity = ServiceModelActivity.CreateActivity(true);
            if (activity != null)
            {
                activity.isAsync = true;
            }
            return activity;
        }
 
        internal static ServiceModelActivity CreateBoundedActivity()
        {
            return ServiceModelActivity.CreateBoundedActivity(false);
        }
 
        internal static ServiceModelActivity CreateBoundedActivity(bool suspendCurrent)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity activityToSuspend = ServiceModelActivity.Current;
            ServiceModelActivity retval = ServiceModelActivity.CreateActivity(true);
            if (retval != null)
            {
                retval.activity = (TransferActivity)ServiceModelActivity.BoundOperation(retval, true);
                retval.activity.SetPreviousServiceModelActivity(activityToSuspend);
                if (suspendCurrent)
                {
                    retval.autoResume = true;
                }
            }
            if (suspendCurrent && activityToSuspend != null)
            {
                activityToSuspend.Suspend();
            }
            return retval;
        }
 
        internal static ServiceModelActivity CreateBoundedActivity(Guid activityId)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity retval = ServiceModelActivity.CreateActivity(activityId, true);
            if (retval != null)
            {
                retval.activity = (TransferActivity)ServiceModelActivity.BoundOperation(retval, true);
            }
            return retval;
        }
 
        internal static ServiceModelActivity CreateBoundedActivityWithTransferInOnly(Guid activityId)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity retval = ServiceModelActivity.CreateActivity(activityId, true);
            if (retval != null)
            {
                if (null != FxTrace.Trace)
                {
                    FxTrace.Trace.TraceTransfer(activityId);
                }
                retval.activity = (TransferActivity)ServiceModelActivity.BoundOperation(retval);
            }
            return retval;
        }
 
        internal static ServiceModelActivity CreateLightWeightAsyncActivity(Guid activityId)
        {
            return new ServiceModelActivity(activityId);
        }
 
        internal static ServiceModelActivity CreateActivity(Guid activityId)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity retval = null;
            if (activityId != Guid.Empty)
            {
                retval = new ServiceModelActivity(activityId);
            }
            if (retval != null)
            {
                ServiceModelActivity.Current = retval;
            }
            return retval;
        }
 
        internal static ServiceModelActivity CreateActivity(Guid activityId, bool autoStop)
        {
            if (!DiagnosticUtility.ShouldUseActivity)
            {
                return null;
            }
            ServiceModelActivity retval = ServiceModelActivity.CreateActivity(activityId);
            if (retval != null)
            {
                retval.autoStop = autoStop;
            }
            return retval;
        }
 
        internal static ServiceModelActivity Current
        {
            get { return ServiceModelActivity.currentActivity; }
            private set { ServiceModelActivity.currentActivity = value; }
        }
 
        public void Dispose()
        {
            if (!this.disposed)
            {
                this.disposed = true;
                try
                {
                    if (this.activity != null)
                    {
                        this.activity.Dispose();
                    }
                    if (this.autoStop)
                    {
                        this.Stop();
                    }
                    if (this.autoResume &&
                        ServiceModelActivity.Current != null)
                    {
                        ServiceModelActivity.Current.Resume();
                    }
                }
                finally
                {
                    ServiceModelActivity.Current = this.previousActivity;
                    GC.SuppressFinalize(this);
                }
            }
        }
 
        internal Guid Id
        {
            get { return this.activityId; }
        }
 
        ActivityState LastState
        {
            get { return this.lastState; }
            set { this.lastState = value; }
        }
 
        internal string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }
 
        internal void Resume()
        {
            if (this.LastState == ActivityState.Suspend)
            {
                this.LastState = ActivityState.Resume;
                this.TraceMilestone(TraceEventType.Resume);
            }
        }
 
        internal void Resume(string activityName)
        {
            if (string.IsNullOrEmpty(this.Name))
            {
                this.name = activityName;
            }
            this.Resume();
        }
 
        static internal void Start(ServiceModelActivity activity, string activityName, ActivityType activityType)
        {
            if (activity != null && activity.LastState == ActivityState.Unknown)
            {
                activity.LastState = ActivityState.Start;
                activity.name = activityName;
                activity.activityType = activityType;
                activity.TraceMilestone(TraceEventType.Start);
            }
        }
 
        internal void Stop()
        {
            int newStopCount = 0;
            if (this.isAsync)
            {
                newStopCount = Interlocked.Increment(ref this.stopCount);
            }
            if (this.LastState != ActivityState.Stop &&
                (!this.isAsync || (this.isAsync && newStopCount >= ServiceModelActivity.AsyncStopCount)))
            {
                this.LastState = ActivityState.Stop;
                this.TraceMilestone(TraceEventType.Stop);
            }
        }
 
        static internal void Stop(ServiceModelActivity activity)
        {
            if (activity != null)
            {
                activity.Stop();
            }
        }
 
        internal void Suspend()
        {
            if (this.LastState != ActivityState.Stop)
            {
                this.LastState = ActivityState.Suspend;
                this.TraceMilestone(TraceEventType.Suspend);
            }
        }
 
        public override string ToString()
        {
            return this.Id.ToString();
        }
 
        void TraceMilestone(TraceEventType type)
        {
            if (string.IsNullOrEmpty(this.Name))
            {
                if (null != FxTrace.Trace)
                {
                    CallEtwMileStoneEvent(type, null);
                }
                if (null != DiagnosticUtility.DiagnosticTrace)
                {
                    TraceUtility.TraceEventNoCheck(type, TraceCode.ActivityBoundary, ServiceModelActivity.ActivityBoundaryDescription, null, ServiceModelActivity.ActivityBoundaryDescription, (Exception)null);
                }
            }
            else
            {
                if (null != FxTrace.Trace)
                {
                    Dictionary<string, string> values = new Dictionary<string, string>(2);
                    values["ActivityName"] = this.Name;
                    values["ActivityType"] = ServiceModelActivity.ActivityTypeNames[(int)this.activityType];
                    using (DiagnosticUtility.ShouldUseActivity && Guid.Empty == activityId ? null : Activity.CreateActivity(this.Id))
                    {
                        CallEtwMileStoneEvent(type, new DictionaryTraceRecord(values));
                    }
                }
                if (null != DiagnosticUtility.DiagnosticTrace)
                {
                    Dictionary<string, string> values = new Dictionary<string, string>(2);
                    values["ActivityName"] = this.Name;
                    values["ActivityType"] = ServiceModelActivity.ActivityTypeNames[(int)this.activityType];
                    TraceUtility.TraceEventNoCheck(type, TraceCode.ActivityBoundary, ServiceModelActivity.ActivityBoundaryDescription, new DictionaryTraceRecord(values), null, null, this.Id);
                }
            }
        }
 
        void CallEtwMileStoneEvent(TraceEventType type, DictionaryTraceRecord record)
        {
            switch (type)
            {
                case TraceEventType.Start:
 
                    if (TD.StartSignpostEventIsEnabled())
                    {
                        TD.StartSignpostEvent(record);
                    }
                    break;
                case TraceEventType.Stop:
                    if (TD.StopSignpostEventIsEnabled())
                    {
                        TD.StopSignpostEvent(record);
                    }
                    break;
                case TraceEventType.Suspend:
                    if (TD.SuspendSignpostEventIsEnabled())
                    {
                        TD.SuspendSignpostEvent(record);
                    }
                    break;
                case TraceEventType.Resume:
                    if (TD.ResumeSignpostEventIsEnabled())
                    {
                        TD.ResumeSignpostEvent(record);
                    }
                    break;
            }
        }
 
        enum ActivityState
        {
            Unknown,
            Start,
            Suspend,
            Resume,
            Stop,
        }
 
        class TransferActivity : Activity
        {
            bool addTransfer = false;
            bool changeCurrentServiceModelActivity = false;
            ServiceModelActivity previousActivity = null;
 
            TransferActivity(Guid activityId, Guid parentId)
                : base(activityId, parentId)
            {
            }
 
            internal static TransferActivity CreateActivity(Guid activityId, bool addTransfer)
            {
                if (!DiagnosticUtility.ShouldUseActivity)
                {
                    return null;
                }
                TransferActivity retval = null;
                if (DiagnosticUtility.TracingEnabled && activityId != Guid.Empty)
                {
                    Guid currentActivityId = DiagnosticTraceBase.ActivityId;
                    if (activityId != currentActivityId)
                    {
                        if (addTransfer)
                        {
                            if (null != FxTrace.Trace)
                            {
                                FxTrace.Trace.TraceTransfer(activityId);
                            }
                        }
                        TransferActivity activity = new TransferActivity(activityId, currentActivityId);
                        activity.addTransfer = addTransfer;
                        retval = activity;
                    }
                }
                return retval;
            }
 
            internal void SetPreviousServiceModelActivity(ServiceModelActivity previous)
            {
                this.previousActivity = previous;
                this.changeCurrentServiceModelActivity = true;
            }
 
            public override void Dispose()
            {
                try
                {
                    if (addTransfer)
                    {
                        // Make sure that we are transferring from our AID to the 
                        // parent. It is possible for someone else to change the ambient
                        // in user code (MB 49318).
                        using (Activity.CreateActivity(this.Id))
                        {
                            if (null != FxTrace.Trace)
                            {
                                FxTrace.Trace.TraceTransfer(this.parentId);
                            }
                        }
                    }
                }
                finally
                {
                    if (this.changeCurrentServiceModelActivity)
                    {
                        ServiceModelActivity.Current = this.previousActivity;
                    }
                    base.Dispose();
                }
            }
        }
    }
}