// **************************************************************************** // Copyright (C) Microsoft Corporation. All rights reserved. // // CONTENTS // Performance counters used by default host // using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Xml; using System.Reflection; using System.Threading; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Transactions; using System.Workflow.Runtime.Hosting; namespace System.Workflow.Runtime { internal enum PerformanceCounterOperation { Increment, Decrement } internal enum PerformanceCounterAction { Aborted, Completion, Creation, Unloading, Executing, Idle, NotExecuting, Persisted, Loading, Runnable, Suspension, Resumption, Termination, Starting, } internal sealed class PerformanceCounterManager { private static String c_PerformanceCounterCategoryName = ExecutionStringManager.PerformanceCounterCategory; // Create a declarative model for specifying performance counter behavior. private static PerformanceCounterData[] s_DefaultPerformanceCounters = new PerformanceCounterData[] { new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesCreatedName, ExecutionStringManager.PerformanceCounterSchedulesCreatedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Creation, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesCreatedRateName, ExecutionStringManager.PerformanceCounterSchedulesCreatedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Creation, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesUnloadedName, ExecutionStringManager.PerformanceCounterSchedulesUnloadedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Unloading, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesUnloadedRateName, ExecutionStringManager.PerformanceCounterSchedulesUnloadedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Unloading, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesLoadedName, ExecutionStringManager.PerformanceCounterSchedulesLoadedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Loading, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesLoadedRateName, ExecutionStringManager.PerformanceCounterSchedulesLoadedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Loading, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesCompletedName, ExecutionStringManager.PerformanceCounterSchedulesCompletedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Completion, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesCompletedRateName, ExecutionStringManager.PerformanceCounterSchedulesCompletedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Completion, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesSuspendedName, ExecutionStringManager.PerformanceCounterSchedulesSuspendedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Suspension, PerformanceCounterOperation.Increment ), new PerformanceCounterActionMapping( PerformanceCounterAction.Resumption, PerformanceCounterOperation.Decrement ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesSuspendedRateName, ExecutionStringManager.PerformanceCounterSchedulesSuspendedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Suspension, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesTerminatedName, ExecutionStringManager.PerformanceCounterSchedulesTerminatedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Termination, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesTerminatedRateName, ExecutionStringManager.PerformanceCounterSchedulesTerminatedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Termination, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesInMemoryName, ExecutionStringManager.PerformanceCounterSchedulesInMemoryDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Creation, PerformanceCounterOperation.Increment ), new PerformanceCounterActionMapping( PerformanceCounterAction.Loading, PerformanceCounterOperation.Increment ), new PerformanceCounterActionMapping( PerformanceCounterAction.Unloading, PerformanceCounterOperation.Decrement ), new PerformanceCounterActionMapping( PerformanceCounterAction.Completion, PerformanceCounterOperation.Decrement ), new PerformanceCounterActionMapping( PerformanceCounterAction.Termination, PerformanceCounterOperation.Decrement ), new PerformanceCounterActionMapping( PerformanceCounterAction.Aborted, PerformanceCounterOperation.Decrement ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesExecutingName, ExecutionStringManager.PerformanceCounterSchedulesExecutingDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Executing, PerformanceCounterOperation.Increment ), new PerformanceCounterActionMapping( PerformanceCounterAction.NotExecuting, PerformanceCounterOperation.Decrement ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesIdleRateName, ExecutionStringManager.PerformanceCounterSchedulesIdleRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Idle, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesRunnableName, ExecutionStringManager.PerformanceCounterSchedulesRunnableDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Runnable, PerformanceCounterOperation.Increment ), new PerformanceCounterActionMapping( PerformanceCounterAction.NotExecuting, PerformanceCounterOperation.Decrement ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesAbortedName, ExecutionStringManager.PerformanceCounterSchedulesAbortedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Aborted, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesAbortedRateName, ExecutionStringManager.PerformanceCounterSchedulesAbortedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Aborted, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesPersistedName, ExecutionStringManager.PerformanceCounterSchedulesPersistedDescription, PerformanceCounterType.NumberOfItems64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Persisted, PerformanceCounterOperation.Increment ), }), new PerformanceCounterData( ExecutionStringManager.PerformanceCounterSchedulesPersistedRateName, ExecutionStringManager.PerformanceCounterSchedulesPersistedRateDescription, PerformanceCounterType.RateOfCountsPerSecond64, new PerformanceCounterActionMapping[] { new PerformanceCounterActionMapping( PerformanceCounterAction.Persisted, PerformanceCounterOperation.Increment ), }), }; private String m_instanceName; private Dictionary> m_actionStatements; internal PerformanceCounterManager() { } internal void Initialize(WorkflowRuntime runtime) { runtime.WorkflowExecutorInitializing += WorkflowExecutorInitializing; } internal void Uninitialize(WorkflowRuntime runtime) { runtime.WorkflowExecutorInitializing -= WorkflowExecutorInitializing; } [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Design has been approved.")] internal void SetInstanceName(String instanceName) { PerformanceCounterData[] data = s_DefaultPerformanceCounters; if (String.IsNullOrEmpty(instanceName)) { try { // The assert is safe here as we never give out the instance name. new System.Security.Permissions.SecurityPermission(System.Security.Permissions.PermissionState.Unrestricted).Assert(); Process process = Process.GetCurrentProcess(); ProcessModule mainModule = process.MainModule; instanceName = mainModule.ModuleName; } finally { System.Security.CodeAccessPermission.RevertAssert(); } } this.m_instanceName = instanceName; // Build a mapping of PerformanceCounterActions to the actual actions that need // to be performed. If this become a perf issue, we could build the default mapping // at build time. Dictionary> actionStatements = new Dictionary>(); if (PerformanceCounterCategory.Exists(c_PerformanceCounterCategoryName)) { for (int i = 0; i < data.Length; ++i) { PerformanceCounterData currentData = data[i]; for (int j = 0; j < currentData.Mappings.Length; ++j) { PerformanceCounterActionMapping currentMapping = currentData.Mappings[j]; if (!actionStatements.ContainsKey(currentMapping.Action)) { actionStatements.Add(currentMapping.Action, new List()); } List lStatements = actionStatements[currentMapping.Action]; PerformanceCounterStatement newStatement = new PerformanceCounterStatement(CreateCounters(currentData.Name), currentMapping.Operation); lStatements.Add(newStatement); } } } this.m_actionStatements = actionStatements; } private void Notify(PerformanceCounterAction action, WorkflowExecutor executor) { System.Diagnostics.Debug.Assert(this.m_actionStatements != null); List lStatements; if (this.m_actionStatements.TryGetValue(action, out lStatements)) { foreach (PerformanceCounterStatement statement in lStatements) { NotifyCounter(action, statement, executor); } } } internal List CreateCounters(String name) { List counters = new List(); counters.Add( new PerformanceCounter( c_PerformanceCounterCategoryName, name, "_Global_", false)); if (!String.IsNullOrEmpty(this.m_instanceName)) { counters.Add( new PerformanceCounter( c_PerformanceCounterCategoryName, name, this.m_instanceName, false)); } return counters; } private void NotifyCounter(PerformanceCounterAction action, PerformanceCounterStatement statement, WorkflowExecutor executor) { foreach (PerformanceCounter counter in statement.Counters) { switch (statement.Operation) { case PerformanceCounterOperation.Increment: counter.Increment(); break; case PerformanceCounterOperation.Decrement: counter.Decrement(); break; default: System.Diagnostics.Debug.Assert(false, "Unknown performance counter operation."); break; } } } private void WorkflowExecutorInitializing(object sender, WorkflowRuntime.WorkflowExecutorInitializingEventArgs e) { if (null == sender) throw new ArgumentNullException("sender"); if (null == e) throw new ArgumentNullException("e"); if (!typeof(WorkflowExecutor).IsInstanceOfType(sender)) throw new ArgumentException("sender"); WorkflowExecutor exec = (WorkflowExecutor)sender; exec.WorkflowExecutionEvent += new EventHandler(WorkflowExecutionEvent); } private void WorkflowExecutionEvent(object sender, WorkflowExecutor.WorkflowExecutionEventArgs e) { if (null == sender) throw new ArgumentNullException("sender"); if (!typeof(WorkflowExecutor).IsInstanceOfType(sender)) throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidArgumentType, "sender", typeof(WorkflowExecutor).ToString())); WorkflowExecutor exec = (WorkflowExecutor)sender; PerformanceCounterAction action; switch (e.EventType) { case WorkflowEventInternal.Created: action = PerformanceCounterAction.Creation; break; case WorkflowEventInternal.Started: action = PerformanceCounterAction.Starting; break; case WorkflowEventInternal.Runnable: action = PerformanceCounterAction.Runnable; break; case WorkflowEventInternal.Executing: action = PerformanceCounterAction.Executing; break; case WorkflowEventInternal.NotExecuting: action = PerformanceCounterAction.NotExecuting; break; case WorkflowEventInternal.Resumed: action = PerformanceCounterAction.Resumption; break; case WorkflowEventInternal.SchedulerEmpty: // // SchedulerEmpty signals that are about to persist // after which we will be idle. We need to do the idle // work now so that it is included in the state for the idle persist. action = PerformanceCounterAction.Idle; break; case WorkflowEventInternal.Completed: action = PerformanceCounterAction.Completion; break; case WorkflowEventInternal.Suspended: action = PerformanceCounterAction.Suspension; break; case WorkflowEventInternal.Terminated: action = PerformanceCounterAction.Termination; break; case WorkflowEventInternal.Loaded: action = PerformanceCounterAction.Loading; break; case WorkflowEventInternal.Aborted: action = PerformanceCounterAction.Aborted; break; case WorkflowEventInternal.Unloaded: action = PerformanceCounterAction.Unloading; break; case WorkflowEventInternal.Persisted: action = PerformanceCounterAction.Persisted; break; default: return; } Notify(action, exec); } } internal struct PerformanceCounterData { internal String Name; internal String Description; internal PerformanceCounterType CounterType; internal PerformanceCounterActionMapping[] Mappings; internal PerformanceCounterData( String name, String description, PerformanceCounterType counterType, PerformanceCounterActionMapping[] mappings) { System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(name)); System.Diagnostics.Debug.Assert(!String.IsNullOrEmpty(description)); System.Diagnostics.Debug.Assert(mappings != null && mappings.Length != 0); this.Name = name; this.Description = description; this.CounterType = counterType; this.Mappings = mappings; } }; internal struct PerformanceCounterActionMapping { internal PerformanceCounterOperation Operation; internal PerformanceCounterAction Action; internal PerformanceCounterActionMapping(PerformanceCounterAction action, PerformanceCounterOperation operation) { this.Operation = operation; this.Action = action; } } internal struct PerformanceCounterStatement { internal List Counters; internal PerformanceCounterOperation Operation; internal PerformanceCounterStatement(List counters, PerformanceCounterOperation operation) { System.Diagnostics.Debug.Assert(counters != null); this.Counters = counters; this.Operation = operation; } } }