1 namespace System.ServiceModel.Activities.Diagnostics
2 {
3     using System.Collections.Generic;
4     using System.Diagnostics;
5     using System.Diagnostics.PerformanceData;
6     using System.Runtime;
7     using System.Security;
8     using System.ServiceModel;
9     using System.ServiceModel.Activation;
10     using System.ServiceModel.Administration;
11     using System.ServiceModel.Diagnostics;
12 
13     sealed class WorkflowServiceHostPerformanceCounters : PerformanceCountersBase
14     {
15         static object syncRoot = new object();
16         static Guid workflowServiceHostProviderId = new Guid("{f6c5ad57-a5be-4259-9060-b2c4ebfccd96}");
17         static Guid workflowServiceHostCounterSetId = new Guid("{1f7207c2-0b8c-48de-9dcd-64ff98cc24e1}");
18 
19         // Double-checked locking pattern requires volatile for read/write synchronization
20         static volatile CounterSet workflowServiceHostCounterSet;         // Defines the counter set
21 
22         // The strings defined here are not used in the counter set and defined only to implement CounterNames property.
23         // CounterNames is not used currently and hence these strings need not be localized.
24         // The Counter Names and description are defined in the manifest which will be localized and installed by the setup.
25         static readonly string[] perfCounterNames =
26         {
27             "Workflows Created",
28             "Workflows Created Per Second",
29             "Workflows Executing",
30             "Workflows Completed",
31             "Workflows Completed Per Second",
32             "Workflows Aborted",
33             "Workflows Aborted Per Second",
34             "Workflows In Memory",
35             "Workflows Persisted",
36             "Workflows Persisted Per Second",
37             "Workflows Terminated",
38             "Workflows Terminated Per Second",
39             "Workflows Loaded",
40             "Workflows Loaded Per Second",
41             "Workflows Unloaded",
42             "Workflows Unloaded Per Second",
43             "Workflows Suspended",
44             "Workflows Suspended Per Second",
45             "Workflows Idle Per Second",
46             "Average Workflow Load Time",
47             "Average Workflow Load Time Base",
48             "Average Workflow Persist Time",
49             "Average Workflow Persist Time Base",
50         };
51 
52         WorkflowServiceHost serviceHost;
53 
54         const int maxCounterLength = 64;
55         const int hashLength = 2;
56 
57         CounterSetInstance workflowServiceHostCounterSetInstance; // Instance of the counter set
58         bool initialized;
59         CounterData[] counters;
60 
61         string instanceName;
62         bool isPerformanceCounterEnabled;
63 
64         internal override string InstanceName
65         {
66             get
67             {
68                 return this.instanceName;
69             }
70         }
71 
72         internal override string[] CounterNames
73         {
74             get
75             {
76                 return perfCounterNames;
77             }
78         }
79 
80         internal override int PerfCounterStart
81         {
82             get
83             {
84                 return (int)PerfCounters.WorkflowsCreated;
85             }
86         }
87 
88         internal override int PerfCounterEnd
89         {
90             get
91             {
92                 return (int)PerfCounters.TotalCounters;
93             }
94         }
95 
96         internal bool PerformanceCountersEnabled
97         {
98             get
99             {
100                 return this.isPerformanceCounterEnabled;
101             }
102         }
103 
104         internal override bool Initialized
105         {
106             get { return this.initialized; }
107         }
108 
WorkflowServiceHostPerformanceCounters(WorkflowServiceHost serviceHost)109         internal WorkflowServiceHostPerformanceCounters(WorkflowServiceHost serviceHost)
110         {
111             this.serviceHost = serviceHost;
112         }
113 
EnsureCounterSet()114         internal static void EnsureCounterSet()
115         {
116             if (workflowServiceHostCounterSet == null)
117             {
118                 lock (syncRoot)
119                 {
120                     if (workflowServiceHostCounterSet == null)
121                     {
122                         CounterSet localCounterSet = CreateCounterSet();
123 
124                         // Add the counters to the counter set definition.
125                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsCreated, CounterType.RawData32);
126                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsCreatedPerSecond, CounterType.RateOfCountPerSecond32);
127                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsExecuting, CounterType.RawData32);
128                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsCompleted, CounterType.RawData32);
129                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsCompletedPerSecond, CounterType.RateOfCountPerSecond32);
130                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsAborted, CounterType.RawData32);
131                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsAbortedPerSecond, CounterType.RateOfCountPerSecond32);
132                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsInMemory, CounterType.RawData32);
133                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsPersisted, CounterType.RawData32);
134                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsPersistedPerSecond, CounterType.RateOfCountPerSecond32);
135                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsTerminated, CounterType.RawData32);
136                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsTerminatedPerSecond, CounterType.RateOfCountPerSecond32);
137                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsLoaded, CounterType.RawData32);
138                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsLoadedPerSecond, CounterType.RateOfCountPerSecond32);
139                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsUnloaded, CounterType.RawData32);
140                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsUnloadedPerSecond, CounterType.RateOfCountPerSecond32);
141                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsSuspended, CounterType.RawData32, perfCounterNames[(int)PerfCounters.WorkflowsSuspended]);
142                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsSuspendedPerSecond, CounterType.RateOfCountPerSecond32);
143                         localCounterSet.AddCounter((int)PerfCounters.WorkflowsIdlePerSecond, CounterType.RateOfCountPerSecond32);
144                         localCounterSet.AddCounter((int)PerfCounters.AverageWorkflowLoadTime, CounterType.AverageTimer32);
145                         localCounterSet.AddCounter((int)PerfCounters.AverageWorkflowLoadTimeBase, CounterType.AverageBase);
146                         localCounterSet.AddCounter((int)PerfCounters.AverageWorkflowPersistTime, CounterType.AverageTimer32);
147                         localCounterSet.AddCounter((int)PerfCounters.AverageWorkflowPersistTimeBase, CounterType.AverageBase);
148 
149                         workflowServiceHostCounterSet = localCounterSet;
150                     }
151                 }
152             }
153         }
154 
CreateFriendlyInstanceName(ServiceHostBase serviceHost)155         static internal string CreateFriendlyInstanceName(ServiceHostBase serviceHost)
156         {
157             // instance name is: serviceName@uri
158             ServiceInfo serviceInfo = new ServiceInfo(serviceHost);
159             string serviceName = serviceInfo.ServiceName;
160             string uri;
161             if (!TryGetFullVirtualPath(serviceHost, out uri))
162             {
163                 uri = serviceInfo.FirstAddress;
164             }
165             int length = serviceName.Length + uri.Length + 2;
166 
167             if (length > maxCounterLength)
168             {
169                 int count = 0;
170 
171                 InstanceNameTruncOptions tasks = GetCompressionTasks(
172                     length, serviceName.Length, uri.Length);
173 
174                 //if necessary, compress service name to 8 chars with a 2 char hash code
175                 if ((tasks & InstanceNameTruncOptions.Service32) > 0)
176                 {
177                     count = 32;
178                     serviceName = GetHashedString(serviceName, count - hashLength, serviceName.Length - count + hashLength, true);
179                 }
180 
181                 //if necessary,  compress uri to 36 chars with a 2 char hash code
182                 if ((tasks & InstanceNameTruncOptions.Uri31) > 0)
183                 {
184                     count = 31;
185                     uri = GetHashedString(uri, 0, uri.Length - count + hashLength, false);
186                 }
187             }
188 
189             // replace '/' with '|' because perfmon fails when '/' is in perfcounter instance name
190             return serviceName + "@" + uri.Replace('/', '|');
191         }
192 
TryGetFullVirtualPath(ServiceHostBase serviceHost, out string uri)193         static bool TryGetFullVirtualPath(ServiceHostBase serviceHost, out string uri)
194         {
195             VirtualPathExtension pathExtension = serviceHost.Extensions.Find<VirtualPathExtension>();
196             if (pathExtension == null)
197             {
198                 uri = null;
199                 return false;
200             }
201             uri = pathExtension.ApplicationVirtualPath + pathExtension.VirtualPath.ToString().Replace("~", "");
202             return uri != null;
203         }
204 
GetCompressionTasks(int totalLen, int serviceLen, int uriLen)205         static InstanceNameTruncOptions GetCompressionTasks(int totalLen, int serviceLen, int uriLen)
206         {
207             InstanceNameTruncOptions bitmask = 0;
208 
209             if (totalLen > maxCounterLength)
210             {
211                 int workingLen = totalLen;
212 
213                 //note: order of if statements important (see spec)!
214                 if (workingLen > maxCounterLength && serviceLen > 32)
215                 {
216                     bitmask |= InstanceNameTruncOptions.Service32; //compress service name to 16 chars
217                     workingLen -= serviceLen - 32;
218                 }
219                 if (workingLen > maxCounterLength && uriLen > 31)
220                 {
221                     bitmask |= InstanceNameTruncOptions.Uri31; //compress uri to 31 chars
222                 }
223             }
224 
225             return bitmask;
226         }
227 
228         [Fx.Tag.SecurityNote(Critical = "Calls into Sys.Diag.PerformanceData.CounterSet..ctor marked as SecurityCritical", Safe = "We only make the call if PartialTrustHelper.AppDomainFullyTrusted is true.")]
229         [SecuritySafeCritical]
CreateCounterSet()230         static CounterSet CreateCounterSet()
231         {
232             if (PartialTrustHelpers.AppDomainFullyTrusted)
233             {
234                 return new CounterSet(workflowServiceHostProviderId, workflowServiceHostCounterSetId, CounterSetInstanceType.Multiple);
235             }
236             else
237                 return null;
238         }
239 
240         [Fx.Tag.SecurityNote(Critical = "Calls into Sys.Diag.PerformanceData.CounterSetInstance.CreateCounterSetInstance marked as SecurityCritical",
241             Safe = "We only make the call if PartialTrustHelper.AppDomainFullyTrusted is true.")]
242         [SecuritySafeCritical]
CreateCounterSetInstance(string name)243         static CounterSetInstance CreateCounterSetInstance(string name)
244         {
245             CounterSetInstance workflowServiceHostCounterSetInstance = null;
246 
247             if (PartialTrustHelpers.AppDomainFullyTrusted)
248             {
249                 try
250                 {
251                     workflowServiceHostCounterSetInstance = workflowServiceHostCounterSet.CreateCounterSetInstance(name);
252                 }
253                 catch (Exception exception)
254                 {
255                     if (Fx.IsFatal(exception))
256                     {
257                         throw;
258                     }
259                     // A conflicting instance name already exists and probably the unmanaged resource is not yet disposed.
260                     FxTrace.Exception.AsWarning(exception);
261                     workflowServiceHostCounterSetInstance = null;
262                 }
263             }
264 
265             return workflowServiceHostCounterSetInstance;
266         }
267 
InitializePerformanceCounters()268         internal void InitializePerformanceCounters()
269         {
270             this.instanceName = CreateFriendlyInstanceName(this.serviceHost);
271 
272             if (PerformanceCounters.PerformanceCountersEnabled)
273             {
274                 EnsureCounterSet();
275                 // Create an instance of the counter set (contains the counter data).
276                 this.workflowServiceHostCounterSetInstance = CreateCounterSetInstance(this.InstanceName);
277 
278                 if (this.workflowServiceHostCounterSetInstance != null)
279                 {
280                     this.counters = new CounterData[(int)PerfCounters.TotalCounters];
281                     for (int i = 0; i < (int)PerfCounters.TotalCounters; i++)
282                     {
283                         this.counters[i] = this.workflowServiceHostCounterSetInstance.Counters[i];
284                         this.counters[i].Value = 0;
285                     }
286                     // Enable perf counter only if CounterSetInstance is created without instance name conflict.
287                     this.isPerformanceCounterEnabled = PerformanceCounters.PerformanceCountersEnabled;
288                 }
289             }
290             this.initialized = true;
291         }
292 
Dispose(bool disposing)293         protected override void Dispose(bool disposing)
294         {
295             try
296             {
297                 if (disposing && this.workflowServiceHostCounterSetInstance != null)
298                 {
299                     this.workflowServiceHostCounterSetInstance.Dispose();
300                     this.workflowServiceHostCounterSetInstance = null;
301                     this.isPerformanceCounterEnabled = false;
302                 }
303             }
304             finally
305             {
306                 base.Dispose(disposing);
307             }
308         }
309 
WorkflowCreated()310         internal void WorkflowCreated()
311         {
312             if (PerformanceCountersEnabled)
313             {
314                 this.counters[(int)PerfCounters.WorkflowsCreated].Increment();
315                 this.counters[(int)PerfCounters.WorkflowsCreatedPerSecond].Increment();
316             }
317         }
318 
WorkflowExecuting(bool increment)319         internal void WorkflowExecuting(bool increment)
320         {
321             if (PerformanceCountersEnabled)
322             {
323                 if (increment)
324                 {
325                     this.counters[(int)PerfCounters.WorkflowsExecuting].Increment();
326                 }
327                 else
328                 {
329                     this.counters[(int)PerfCounters.WorkflowsExecuting].Decrement();
330                 }
331             }
332         }
333 
WorkflowCompleted()334         internal void WorkflowCompleted()
335         {
336             if (PerformanceCountersEnabled)
337             {
338                 this.counters[(int)PerfCounters.WorkflowsCompleted].Increment();
339                 this.counters[(int)PerfCounters.WorkflowsCompletedPerSecond].Increment();
340             }
341         }
342 
WorkflowAborted()343         internal void WorkflowAborted()
344         {
345             if (PerformanceCountersEnabled)
346             {
347                 this.counters[(int)PerfCounters.WorkflowsAborted].Increment();
348                 this.counters[(int)PerfCounters.WorkflowsAbortedPerSecond].Increment();
349             }
350         }
351 
WorkflowInMemory()352         internal void WorkflowInMemory()
353         {
354             if (PerformanceCountersEnabled)
355             {
356                 this.counters[(int)PerfCounters.WorkflowsInMemory].Increment();
357             }
358         }
359 
WorkflowOutOfMemory()360         internal void WorkflowOutOfMemory()
361         {
362             if (PerformanceCountersEnabled)
363             {
364                 if (this.counters[(int)PerfCounters.WorkflowsInMemory].RawValue > 0 )
365                     this.counters[(int)PerfCounters.WorkflowsInMemory].Decrement();
366             }
367         }
368 
WorkflowPersisted()369         internal void WorkflowPersisted()
370         {
371             if (PerformanceCountersEnabled)
372             {
373                 this.counters[(int)PerfCounters.WorkflowsPersisted].Increment();
374                 this.counters[(int)PerfCounters.WorkflowsPersistedPerSecond].Increment();
375             }
376         }
377 
WorkflowTerminated()378         internal void WorkflowTerminated()
379         {
380             if (PerformanceCountersEnabled)
381             {
382                 this.counters[(int)PerfCounters.WorkflowsTerminated].Increment();
383                 this.counters[(int)PerfCounters.WorkflowsTerminatedPerSecond].Increment();
384             }
385         }
386 
WorkflowLoaded()387         internal void WorkflowLoaded()
388         {
389             if (PerformanceCountersEnabled)
390             {
391                 this.counters[(int)PerfCounters.WorkflowsLoaded].Increment();
392                 this.counters[(int)PerfCounters.WorkflowsLoadedPerSecond].Increment();
393             }
394         }
395 
WorkflowUnloaded()396         internal void WorkflowUnloaded()
397         {
398             if (PerformanceCountersEnabled)
399             {
400                 this.counters[(int)PerfCounters.WorkflowsUnloaded].Increment();
401                 this.counters[(int)PerfCounters.WorkflowsUnloadedPerSecond].Increment();
402             }
403         }
404 
WorkflowSuspended()405         internal void WorkflowSuspended()
406         {
407             if (PerformanceCountersEnabled)
408             {
409                 this.counters[(int)PerfCounters.WorkflowsSuspended].Increment();
410                 this.counters[(int)PerfCounters.WorkflowsSuspendedPerSecond].Increment();
411             }
412         }
413 
WorkflowIdle()414         internal void WorkflowIdle()
415         {
416             if (PerformanceCountersEnabled)
417             {
418                 this.counters[(int)PerfCounters.WorkflowsIdlePerSecond].Increment();
419             }
420         }
421 
WorkflowLoadDuration(long time)422         internal void WorkflowLoadDuration(long time)
423         {
424             if (PerformanceCountersEnabled)
425             {
426                     this.counters[(int)PerfCounters.AverageWorkflowLoadTime].IncrementBy(time);
427                     this.counters[(int)PerfCounters.AverageWorkflowLoadTimeBase].Increment();
428             }
429         }
430 
WorkflowPersistDuration(long time)431         internal void WorkflowPersistDuration(long time)
432         {
433             if (PerformanceCountersEnabled)
434             {
435                     this.counters[(int)PerfCounters.AverageWorkflowPersistTime].IncrementBy(time);
436                     this.counters[(int)PerfCounters.AverageWorkflowPersistTimeBase].Increment();
437             }
438         }
439 
440         internal enum PerfCounters : int
441         {
442             WorkflowsCreated = 0,
443             WorkflowsCreatedPerSecond,
444             WorkflowsExecuting,
445             WorkflowsCompleted,
446             WorkflowsCompletedPerSecond,
447             WorkflowsAborted,
448             WorkflowsAbortedPerSecond,
449             WorkflowsInMemory,
450             WorkflowsPersisted,
451             WorkflowsPersistedPerSecond,
452             WorkflowsTerminated,
453             WorkflowsTerminatedPerSecond,
454             WorkflowsLoaded,
455             WorkflowsLoadedPerSecond,
456             WorkflowsUnloaded,
457             WorkflowsUnloadedPerSecond,
458             WorkflowsSuspended,
459             WorkflowsSuspendedPerSecond,
460             WorkflowsIdlePerSecond,
461             AverageWorkflowLoadTime,
462             AverageWorkflowLoadTimeBase,
463             AverageWorkflowPersistTime,
464             AverageWorkflowPersistTimeBase,
465             TotalCounters = AverageWorkflowPersistTimeBase + 1
466         }
467 
468         // Truncate options for the Instance name in ServiceName@uri format.
469         [Flags]
470         enum InstanceNameTruncOptions : uint
471         {
472             NoBits = 0,
473             Service32 = 0x01, //compress service name to 16 chars
474             Uri31 = 0x04      //compress uri to 31 chars
475         }
476 
477     }
478 }
479