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