1 //------------------------------------------------------------------------------
2 // <copyright file="PerformanceCounter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System
.Diagnostics
{
8 using System
.Runtime
.Serialization
.Formatters
;
9 using System
.Runtime
.InteropServices
;
10 using System
.ComponentModel
;
11 using System
.Diagnostics
;
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
;
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
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;
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
{
59 if (m_InstanceLockObject
== null) {
60 Object o
= new Object();
61 Interlocked
.CompareExchange(ref m_InstanceLockObject
, o
, null);
63 return m_InstanceLockObject
;
68 /// The defaut constructor. Creates the perf counter object
70 public PerformanceCounter() {
72 categoryName
= String
.Empty
;
73 counterName
= String
.Empty
;
74 instanceName
= String
.Empty
;
75 this.isReadOnly
= true;
76 GC
.SuppressFinalize(this);
80 /// Creates the Performance Counter Object
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;
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);
103 /// Creates the Performance Counter Object on local machine.
105 public PerformanceCounter(string categoryName
, string counterName
, string instanceName
) :
106 this(categoryName
, counterName
, instanceName
, true) {
110 /// Creates the Performance Counter Object on local machine.
112 public PerformanceCounter(string categoryName
, string counterName
, string instanceName
, bool readOnly
) {
114 VerifyWriteableCounterAllowed();
116 this.MachineName
= ".";
117 this.CategoryName
= categoryName
;
118 this.CounterName
= counterName
;
119 this.InstanceName
= instanceName
;
120 this.isReadOnly
= readOnly
;
122 GC
.SuppressFinalize(this);
126 /// Creates the Performance Counter Object, assumes that it's a single instance
128 public PerformanceCounter(string categoryName
, string counterName
) :
129 this(categoryName
, counterName
, true) {
133 /// Creates the Performance Counter Object, assumes that it's a single instance
135 public PerformanceCounter(string categoryName
, string counterName
, bool readOnly
) :
136 this(categoryName
, counterName
, "", readOnly
) {
140 /// Returns the performance category name for this performance counter
145 TypeConverter("System.Diagnostics.Design.CategoryValueConverter, " + AssemblyRef
.SystemDesign
),
146 SRDescription(SR
.PCCategoryName
),
147 SettingsBindable(true)
149 public string CategoryName
{
155 throw new ArgumentNullException("value");
157 if (categoryName
== null || String
.Compare(categoryName
, value, StringComparison
.OrdinalIgnoreCase
) != 0) {
158 categoryName
= value;
165 /// Returns the description message for this performance counter
169 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
170 MonitoringDescription(SR
.PC_CounterHelp
)
172 public string CounterHelp
{
174 string currentCategoryName
= categoryName
;
175 string currentMachineName
= machineName
;
177 PerformanceCounterPermission permission
= new PerformanceCounterPermission(PerformanceCounterPermissionAccess
.Read
, currentMachineName
, currentCategoryName
);
182 helpMsg
= PerformanceCounterLib
.GetCounterHelp(currentMachineName
, currentCategoryName
, this.counterName
);
189 /// Sets/returns the performance counter name for this performance counter
194 TypeConverter("System.Diagnostics.Design.CounterNameConverter, " + AssemblyRef
.SystemDesign
),
195 SRDescription(SR
.PCCounterName
),
196 SettingsBindable(true)
198 public string CounterName
{
204 throw new ArgumentNullException("value");
206 if (counterName
== null || String
.Compare(counterName
, value, StringComparison
.OrdinalIgnoreCase
) != 0) {
214 /// Sets/Returns the counter type for this performance counter
217 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
218 MonitoringDescription(SR
.PC_CounterType
)
220 public PerformanceCounterType CounterType
{
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
);
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; }
248 if (value > PerformanceCounterInstanceLifetime
.Process
|| value < PerformanceCounterInstanceLifetime
.Global
)
249 throw new ArgumentOutOfRangeException("value");
252 throw new InvalidOperationException(SR
.GetString(SR
.CantSetLifetimeAfterInitialized
));
254 instanceLifetime
= value;
259 /// Sets/returns an instance name for this performance counter
264 TypeConverter("System.Diagnostics.Design.InstanceNameConverter, " + AssemblyRef
.SystemDesign
),
265 SRDescription(SR
.PCInstanceName
),
266 SettingsBindable(true)
268 public string InstanceName
{
273 if (value == null && instanceName
== null)
276 if ((value == null && instanceName
!= null) ||
277 (value != null && instanceName
== null) ||
278 String
.Compare(instanceName
, value, StringComparison
.OrdinalIgnoreCase
) != 0) {
279 instanceName
= value;
286 /// Returns true if counter is read only (system counter, foreign extensible counter or remote counter)
291 MonitoringDescription(SR
.PC_ReadOnly
)
293 public bool ReadOnly
{
299 if (value != this.isReadOnly
) {
301 VerifyWriteableCounterAllowed();
303 this.isReadOnly
= value;
311 /// Set/returns the machine name for this performance counter
316 SRDescription(SR
.PCMachineName
),
317 SettingsBindable(true)
319 public string MachineName
{
324 if (!SyntaxCheck
.CheckMachineName(value))
325 throw new ArgumentException(SR
.GetString(SR
.InvalidParameter
, "machineName", value));
327 if (machineName
!= value) {
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.
342 DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
),
343 MonitoringDescription(SR
.PC_RawValue
)
345 public long RawValue
{
348 //No need to initialize or Demand, since NextSample already does.
349 return NextSample().RawValue
;
354 return this.sharedCounter
.Value
;
363 this.sharedCounter
.Value
= value;
369 public void BeginInit() {
374 /// Frees all the resources allocated by this counter
376 public void Close() {
378 this.oldSample
= CounterSample
.Empty
;
379 this.sharedCounter
= null;
380 this.initialized
= false;
381 this.counterType
= -1;
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.
389 public static void CloseSharedResources() {
390 PerformanceCounterPermission permission
= new PerformanceCounterPermission(PerformanceCounterPermissionAccess
.Read
, ".", "*");
392 PerformanceCounterLib
.CloseAllLibraries();
398 protected override void Dispose(bool disposing
) {
399 // safe to call while finalizing or disposing
402 //Dispose managed and unmanaged resources
406 base.Dispose(disposing
);
410 /// Decrements counter by one using an efficient atomic operation.
412 public long Decrement() {
418 return this.sharedCounter
.Decrement();
423 public void EndInit() {
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.
433 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
434 public long IncrementBy(long value) {
440 return this.sharedCounter
.IncrementBy(value);
444 /// Increments counter by one using an efficient atomic operation.
446 public long Increment() {
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
) {
473 /// Intializes required resources
475 //[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
476 private void InitializeImpl() {
477 bool tookLock
= false;
478 RuntimeHelpers
.PrepareConstrainedRegions();
480 Monitor
.Enter(InstanceLockObject
, ref tookLock
);
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
));
492 PerformanceCounterPermission permission
= new PerformanceCounterPermission(PerformanceCounterPermissionAccess
.Read
, currentMachineName
, currentCategoryName
);
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;
513 PerformanceCounterPermission permission
= new PerformanceCounterPermission(PerformanceCounterPermissionAccess
.Write
, currentMachineName
, currentCategoryName
);
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;
543 Monitor
.Exit(InstanceLockObject
);
548 // Will cause an update, raw value
550 /// Obtains a counter sample and returns the raw value for it.
552 public CounterSample
NextSample() {
553 string currentCategoryName
= categoryName
;
554 string currentMachineName
= machineName
;
556 PerformanceCounterPermission permission
= new PerformanceCounterPermission(PerformanceCounterPermissionAccess
.Read
, currentMachineName
, currentCategoryName
);
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();
570 if (instanceName
== null || instanceName
.Length
== 0)
571 throw new InvalidOperationException(SR
.GetString(SR
.InstanceNameRequired
));
573 return counterSample
.GetInstanceValue(this.instanceName
);
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.
582 public float NextValue() {
583 //No need to initialize or Demand, since NextSample already does.
584 CounterSample newSample
= NextSample();
587 retVal
= CounterSample
.Calculate(oldSample
, newSample
);
588 oldSample
= newSample
;
594 /// Removes this counter instance from the shared memory
596 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
597 public void RemoveInstance() {
599 throw new InvalidOperationException(SR
.GetString(SR
.ReadOnlyRemoveInstance
));
602 sharedCounter
.RemoveInstance(this.instanceName
.ToLower(CultureInfo
.InvariantCulture
), instanceLifetime
);