Fix infrequent hangs in test-runner. (#16793)
[mono-project.git] / mcs / class / referencesource / System / services / monitoring / system / diagnosticts / PerformanceCounter.cs
blobea7922a76045cfe91b5ab05b2de248215e807e52
1 //------------------------------------------------------------------------------
2 // <copyright file="PerformanceCounter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 namespace System.Diagnostics {
8 using System.Runtime.Serialization.Formatters;
9 using System.Runtime.InteropServices;
10 using System.ComponentModel;
11 using System.Diagnostics;
12 using System;
13 using System.Collections;
14 using System.Globalization;
15 using System.Security;
16 using System.Security.Permissions;
17 using System.Runtime.CompilerServices;
18 using System.Runtime.ConstrainedExecution;
19 using System.Threading;
22 /// <devdoc>
23 /// Performance Counter component.
24 /// This class provides support for NT Performance counters.
25 /// It handles both the existing counters (accesible by Perf Registry Interface)
26 /// and user defined (extensible) counters.
27 /// This class is a part of a larger framework, that includes the perf dll object and
28 /// perf service.
29 /// </devdoc>
31 InstallerType("System.Diagnostics.PerformanceCounterInstaller," + AssemblyRef.SystemConfigurationInstall),
32 SRDescription(SR.PerformanceCounterDesc),
33 HostProtection(Synchronization = true, SharedState = true)
35 public sealed class PerformanceCounter : Component, ISupportInitialize {
36 private string machineName;
37 private string categoryName;
38 private string counterName;
39 private string instanceName;
40 private PerformanceCounterInstanceLifetime instanceLifetime = PerformanceCounterInstanceLifetime.Global;
42 private bool isReadOnly;
43 private bool initialized = false;
44 private string helpMsg = null;
45 private int counterType = -1;
47 // Cached old sample
48 private CounterSample oldSample = CounterSample.Empty;
50 // Cached IP Shared Performanco counter
51 private SharedPerformanceCounter sharedCounter;
53 [Obsolete("This field has been deprecated and is not used. Use machine.config or an application configuration file to set the size of the PerformanceCounter file mapping.")]
54 public static int DefaultFileMappingSize = 524288;
56 private Object m_InstanceLockObject;
57 private Object InstanceLockObject {
58 get {
59 if (m_InstanceLockObject == null) {
60 Object o = new Object();
61 Interlocked.CompareExchange(ref m_InstanceLockObject, o, null);
63 return m_InstanceLockObject;
67 /// <devdoc>
68 /// The defaut constructor. Creates the perf counter object
69 /// </devdoc>
70 public PerformanceCounter() {
71 machineName = ".";
72 categoryName = String.Empty;
73 counterName = String.Empty;
74 instanceName = String.Empty;
75 this.isReadOnly = true;
76 GC.SuppressFinalize(this);
79 /// <devdoc>
80 /// Creates the Performance Counter Object
81 /// </devdoc>
82 public PerformanceCounter(string categoryName, string counterName, string instanceName, string machineName) {
83 this.MachineName = machineName;
84 this.CategoryName = categoryName;
85 this.CounterName = counterName;
86 this.InstanceName = instanceName;
87 this.isReadOnly = true;
88 Initialize();
89 GC.SuppressFinalize(this);
92 internal PerformanceCounter(string categoryName, string counterName, string instanceName, string machineName, bool skipInit) {
93 this.MachineName = machineName;
94 this.CategoryName = categoryName;
95 this.CounterName = counterName;
96 this.InstanceName = instanceName;
97 this.isReadOnly = true;
98 this.initialized = true;
99 GC.SuppressFinalize(this);
102 /// <devdoc>
103 /// Creates the Performance Counter Object on local machine.
104 /// </devdoc>
105 public PerformanceCounter(string categoryName, string counterName, string instanceName) :
106 this(categoryName, counterName, instanceName, true) {
109 /// <devdoc>
110 /// Creates the Performance Counter Object on local machine.
111 /// </devdoc>
112 public PerformanceCounter(string categoryName, string counterName, string instanceName, bool readOnly) {
113 if(!readOnly) {
114 VerifyWriteableCounterAllowed();
116 this.MachineName = ".";
117 this.CategoryName = categoryName;
118 this.CounterName = counterName;
119 this.InstanceName = instanceName;
120 this.isReadOnly = readOnly;
121 Initialize();
122 GC.SuppressFinalize(this);
125 /// <devdoc>
126 /// Creates the Performance Counter Object, assumes that it's a single instance
127 /// </devdoc>
128 public PerformanceCounter(string categoryName, string counterName) :
129 this(categoryName, counterName, true) {
132 /// <devdoc>
133 /// Creates the Performance Counter Object, assumes that it's a single instance
134 /// </devdoc>
135 public PerformanceCounter(string categoryName, string counterName, bool readOnly) :
136 this(categoryName, counterName, "", readOnly) {
139 /// <devdoc>
140 /// Returns the performance category name for this performance counter
141 /// </devdoc>
143 ReadOnly(true),
144 DefaultValue(""),
145 TypeConverter("System.Diagnostics.Design.CategoryValueConverter, " + AssemblyRef.SystemDesign),
146 SRDescription(SR.PCCategoryName),
147 SettingsBindable(true)
149 public string CategoryName {
150 get {
151 return categoryName;
153 set {
154 if (value == null)
155 throw new ArgumentNullException("value");
157 if (categoryName == null || String.Compare(categoryName, value, StringComparison.OrdinalIgnoreCase) != 0) {
158 categoryName = value;
159 Close();
164 /// <devdoc>
165 /// Returns the description message for this performance counter
166 /// </devdoc>
168 ReadOnly(true),
169 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
170 MonitoringDescription(SR.PC_CounterHelp)
172 public string CounterHelp {
173 get {
174 string currentCategoryName = categoryName;
175 string currentMachineName = machineName;
177 PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName);
178 permission.Demand();
179 Initialize();
181 if (helpMsg == null)
182 helpMsg = PerformanceCounterLib.GetCounterHelp(currentMachineName, currentCategoryName, this.counterName);
184 return helpMsg;
188 /// <devdoc>
189 /// Sets/returns the performance counter name for this performance counter
190 /// </devdoc>
192 ReadOnly(true),
193 DefaultValue(""),
194 TypeConverter("System.Diagnostics.Design.CounterNameConverter, " + AssemblyRef.SystemDesign),
195 SRDescription(SR.PCCounterName),
196 SettingsBindable(true)
198 public string CounterName {
199 get {
200 return counterName;
202 set {
203 if (value == null)
204 throw new ArgumentNullException("value");
206 if (counterName == null || String.Compare(counterName, value, StringComparison.OrdinalIgnoreCase) != 0) {
207 counterName = value;
208 Close();
213 /// <devdoc>
214 /// Sets/Returns the counter type for this performance counter
215 /// </devdoc>
217 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
218 MonitoringDescription(SR.PC_CounterType)
220 public PerformanceCounterType CounterType {
221 get {
222 if (counterType == -1) {
223 string currentCategoryName = categoryName;
224 string currentMachineName = machineName;
226 // This is the same thing that NextSample does, except that it doesn't try to get the actual counter
227 // value. If we wanted the counter value, we would need to have an instance name.
228 PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName);
229 permission.Demand();
231 Initialize();
232 CategorySample categorySample = PerformanceCounterLib.GetCategorySample(currentMachineName, currentCategoryName);
233 CounterDefinitionSample counterSample = categorySample.GetCounterDefinitionSample(this.counterName);
234 this.counterType = counterSample.CounterType;
237 return(PerformanceCounterType) counterType;
242 DefaultValue(PerformanceCounterInstanceLifetime.Global),
243 SRDescription(SR.PCInstanceLifetime),
245 public PerformanceCounterInstanceLifetime InstanceLifetime {
246 get { return instanceLifetime; }
247 set {
248 if (value > PerformanceCounterInstanceLifetime.Process || value < PerformanceCounterInstanceLifetime.Global)
249 throw new ArgumentOutOfRangeException("value");
251 if (initialized)
252 throw new InvalidOperationException(SR.GetString(SR.CantSetLifetimeAfterInitialized));
254 instanceLifetime = value;
258 /// <devdoc>
259 /// Sets/returns an instance name for this performance counter
260 /// </devdoc>
262 ReadOnly(true),
263 DefaultValue(""),
264 TypeConverter("System.Diagnostics.Design.InstanceNameConverter, " + AssemblyRef.SystemDesign),
265 SRDescription(SR.PCInstanceName),
266 SettingsBindable(true)
268 public string InstanceName {
269 get {
270 return instanceName;
272 set {
273 if (value == null && instanceName == null)
274 return;
276 if ((value == null && instanceName != null) ||
277 (value != null && instanceName == null) ||
278 String.Compare(instanceName, value, StringComparison.OrdinalIgnoreCase) != 0) {
279 instanceName = value;
280 Close();
285 /// <devdoc>
286 /// Returns true if counter is read only (system counter, foreign extensible counter or remote counter)
287 /// </devdoc>
289 Browsable(false),
290 DefaultValue(true),
291 MonitoringDescription(SR.PC_ReadOnly)
293 public bool ReadOnly {
294 get {
295 return isReadOnly;
298 set {
299 if (value != this.isReadOnly) {
300 if(value == false) {
301 VerifyWriteableCounterAllowed();
303 this.isReadOnly = value;
304 Close();
310 /// <devdoc>
311 /// Set/returns the machine name for this performance counter
312 /// </devdoc>
314 Browsable(false),
315 DefaultValue("."),
316 SRDescription(SR.PCMachineName),
317 SettingsBindable(true)
319 public string MachineName {
320 get {
321 return machineName;
323 set {
324 if (!SyntaxCheck.CheckMachineName(value))
325 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", value));
327 if (machineName != value) {
328 machineName = value;
329 Close();
334 /// <devdoc>
335 /// Directly accesses the raw value of this counter. If counter type is of a 32-bit size, it will truncate
336 /// the value given to 32 bits. This can be significantly more performant for scenarios where
337 /// the raw value is sufficient. Note that this only works for custom counters created using
338 /// this component, non-custom counters will throw an exception if this property is accessed.
339 /// </devdoc>
341 Browsable(false),
342 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
343 MonitoringDescription(SR.PC_RawValue)
345 public long RawValue {
346 get {
347 if (ReadOnly) {
348 //No need to initialize or Demand, since NextSample already does.
349 return NextSample().RawValue;
351 else {
352 Initialize();
354 return this.sharedCounter.Value;
357 set {
358 if (ReadOnly)
359 ThrowReadOnly();
361 Initialize();
363 this.sharedCounter.Value = value;
367 /// <devdoc>
368 /// </devdoc>
369 public void BeginInit() {
370 this.Close();
373 /// <devdoc>
374 /// Frees all the resources allocated by this counter
375 /// </devdoc>
376 public void Close() {
377 this.helpMsg = null;
378 this.oldSample = CounterSample.Empty;
379 this.sharedCounter = null;
380 this.initialized = false;
381 this.counterType = -1;
384 /// <devdoc>
385 /// Frees all the resources allocated for all performance
386 /// counters, frees File Mapping used by extensible counters,
387 /// unloads dll's used to read counters.
388 /// </devdoc>
389 public static void CloseSharedResources() {
390 PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, ".", "*");
391 permission.Demand();
392 PerformanceCounterLib.CloseAllLibraries();
395 /// <internalonly/>
396 /// <devdoc>
397 /// </devdoc>
398 protected override void Dispose(bool disposing) {
399 // safe to call while finalizing or disposing
401 if (disposing) {
402 //Dispose managed and unmanaged resources
403 Close();
406 base.Dispose(disposing);
409 /// <devdoc>
410 /// Decrements counter by one using an efficient atomic operation.
411 /// </devdoc>
412 public long Decrement() {
413 if (ReadOnly)
414 ThrowReadOnly();
416 Initialize();
418 return this.sharedCounter.Decrement();
421 /// <devdoc>
422 /// </devdoc>
423 public void EndInit() {
424 Initialize();
427 /// <devdoc>
428 /// Increments the value of this counter. If counter type is of a 32-bit size, it'll truncate
429 /// the value given to 32 bits. This method uses a mutex to guarantee correctness of
430 /// the operation in case of multiple writers. This method should be used with caution because of the negative
431 /// impact on performance due to creation of the mutex.
432 /// </devdoc>
433 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
434 public long IncrementBy(long value) {
435 if (isReadOnly)
436 ThrowReadOnly();
438 Initialize();
440 return this.sharedCounter.IncrementBy(value);
443 /// <devdoc>
444 /// Increments counter by one using an efficient atomic operation.
445 /// </devdoc>
446 public long Increment() {
447 if (isReadOnly)
448 ThrowReadOnly();
450 Initialize();
452 return this.sharedCounter.Increment();
455 private void ThrowReadOnly() {
456 throw new InvalidOperationException(SR.GetString(SR.ReadOnlyCounter));
459 private static void VerifyWriteableCounterAllowed() {
460 if(EnvironmentHelpers.IsAppContainerProcess) {
461 throw new NotSupportedException(SR.GetString(SR.PCNotSupportedUnderAppContainer));
465 private void Initialize() {
466 // Keep this method small so the JIT will inline it.
467 if (!initialized && !DesignMode) {
468 InitializeImpl();
472 /// <devdoc>
473 /// Intializes required resources
474 /// </devdoc>
475 //[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
476 private void InitializeImpl() {
477 bool tookLock = false;
478 RuntimeHelpers.PrepareConstrainedRegions();
479 try {
480 Monitor.Enter(InstanceLockObject, ref tookLock);
482 if (!initialized) {
483 string currentCategoryName = categoryName;
484 string currentMachineName = machineName;
486 if (currentCategoryName == String.Empty)
487 throw new InvalidOperationException(SR.GetString(SR.CategoryNameMissing));
488 if (this.counterName == String.Empty)
489 throw new InvalidOperationException(SR.GetString(SR.CounterNameMissing));
491 if (this.ReadOnly) {
492 PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName);
494 permission.Demand();
496 if (!PerformanceCounterLib.CounterExists(currentMachineName, currentCategoryName, counterName))
497 throw new InvalidOperationException(SR.GetString(SR.CounterExists, currentCategoryName, counterName));
499 PerformanceCounterCategoryType categoryType = PerformanceCounterLib.GetCategoryType(currentMachineName, currentCategoryName);
500 if (categoryType == PerformanceCounterCategoryType.MultiInstance) {
501 if (String.IsNullOrEmpty(instanceName))
502 throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, currentCategoryName));
503 } else if (categoryType == PerformanceCounterCategoryType.SingleInstance) {
504 if (!String.IsNullOrEmpty(instanceName))
505 throw new InvalidOperationException(SR.GetString(SR.SingleInstanceOnly, currentCategoryName));
508 if (instanceLifetime != PerformanceCounterInstanceLifetime.Global)
509 throw new InvalidOperationException(SR.GetString(SR.InstanceLifetimeProcessonReadOnly));
511 this.initialized = true;
512 } else {
513 PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Write, currentMachineName, currentCategoryName);
514 permission.Demand();
516 if (currentMachineName != "." && String.Compare(currentMachineName, PerformanceCounterLib.ComputerName, StringComparison.OrdinalIgnoreCase) != 0)
517 throw new InvalidOperationException(SR.GetString(SR.RemoteWriting));
519 SharedUtils.CheckNtEnvironment();
521 if (!PerformanceCounterLib.IsCustomCategory(currentMachineName, currentCategoryName))
522 throw new InvalidOperationException(SR.GetString(SR.NotCustomCounter));
524 // check category type
525 PerformanceCounterCategoryType categoryType = PerformanceCounterLib.GetCategoryType(currentMachineName, currentCategoryName);
526 if (categoryType == PerformanceCounterCategoryType.MultiInstance) {
527 if (String.IsNullOrEmpty(instanceName))
528 throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, currentCategoryName));
529 } else if (categoryType == PerformanceCounterCategoryType.SingleInstance) {
530 if (!String.IsNullOrEmpty(instanceName))
531 throw new InvalidOperationException(SR.GetString(SR.SingleInstanceOnly, currentCategoryName));
534 if (String.IsNullOrEmpty(instanceName) && InstanceLifetime == PerformanceCounterInstanceLifetime.Process)
535 throw new InvalidOperationException(SR.GetString(SR.InstanceLifetimeProcessforSingleInstance));
537 this.sharedCounter = new SharedPerformanceCounter(currentCategoryName.ToLower(CultureInfo.InvariantCulture), counterName.ToLower(CultureInfo.InvariantCulture), instanceName.ToLower(CultureInfo.InvariantCulture), instanceLifetime);
538 this.initialized = true;
541 } finally {
542 if (tookLock)
543 Monitor.Exit(InstanceLockObject);
548 // Will cause an update, raw value
549 /// <devdoc>
550 /// Obtains a counter sample and returns the raw value for it.
551 /// </devdoc>
552 public CounterSample NextSample() {
553 string currentCategoryName = categoryName;
554 string currentMachineName = machineName;
556 PerformanceCounterPermission permission = new PerformanceCounterPermission(PerformanceCounterPermissionAccess.Read, currentMachineName, currentCategoryName);
557 permission.Demand();
559 Initialize();
560 CategorySample categorySample = PerformanceCounterLib.GetCategorySample(currentMachineName, currentCategoryName);
561 CounterDefinitionSample counterSample = categorySample.GetCounterDefinitionSample(this.counterName);
562 this.counterType = counterSample.CounterType;
563 if (!categorySample.IsMultiInstance) {
564 if (instanceName != null && instanceName.Length != 0)
565 throw new InvalidOperationException(SR.GetString(SR.InstanceNameProhibited, this.instanceName));
567 return counterSample.GetSingleValue();
569 else {
570 if (instanceName == null || instanceName.Length == 0)
571 throw new InvalidOperationException(SR.GetString(SR.InstanceNameRequired));
573 return counterSample.GetInstanceValue(this.instanceName);
577 /// <devdoc>
578 /// Obtains a counter sample and returns the calculated value for it.
579 /// NOTE: For counters whose calculated value depend upon 2 counter reads,
580 /// the very first read will return 0.0.
581 /// </devdoc>
582 public float NextValue() {
583 //No need to initialize or Demand, since NextSample already does.
584 CounterSample newSample = NextSample();
585 float retVal = 0.0f;
587 retVal = CounterSample.Calculate(oldSample, newSample);
588 oldSample = newSample;
590 return retVal;
593 /// <devdoc>
594 /// Removes this counter instance from the shared memory
595 /// </devdoc>
596 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
597 public void RemoveInstance() {
598 if (isReadOnly)
599 throw new InvalidOperationException(SR.GetString(SR.ReadOnlyRemoveInstance));
601 Initialize();
602 sharedCounter.RemoveInstance(this.instanceName.ToLower(CultureInfo.InvariantCulture), instanceLifetime);