| File: System\Diagnostics\PerformanceData\CounterSet.cs | Web Access |
| Project: src\runtime\src\libraries\System.Diagnostics.PerformanceCounter\src\System.Diagnostics.PerformanceCounter.csproj (System.Diagnostics.PerformanceCounter) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.ComponentModel; using System.Threading; namespace System.Diagnostics.PerformanceData { /// <summary> /// CounterSet is equivalent to "Counter Object" in native performance counter terminology, /// or "Counter Category" in previous framework releases. It defines a abstract grouping of /// counters, where each counter defines measurable matrix. In the new performance counter /// infrastructure, CounterSet is defined by GUID called CounterSetGuid, and is hosted inside /// provider application, which is also defined by another GUID called ProviderGuid. /// </summary> public class CounterSet : IDisposable { internal PerfProvider _provider; internal Guid _providerGuid; internal Guid _counterSet; internal CounterSetInstanceType _instType; private readonly object _lockObject; private bool _instanceCreated; internal Dictionary<string, int> _stringToId; internal Dictionary<int, CounterType> _idToCounter; /// <summary> /// CounterSet constructor. /// </summary> /// <param name="providerGuid">ProviderGuid identifies the provider application. A provider identified by ProviderGuid could publish several CounterSets defined by different CounterSetGuids</param> /// <param name="counterSetGuid">CounterSetGuid identifies the specific CounterSet. CounterSetGuid should be unique.</param> /// <param name="instanceType">One of defined CounterSetInstanceType values</param> public CounterSet(Guid providerGuid, Guid counterSetGuid, CounterSetInstanceType instanceType) { if (!PerfProviderCollection.ValidateCounterSetInstanceType(instanceType)) { throw new ArgumentException(SR.Format(SR.Perflib_Argument_InvalidCounterSetInstanceType, instanceType), nameof(instanceType)); } _providerGuid = providerGuid; _counterSet = counterSetGuid; _instType = instanceType; PerfProviderCollection.RegisterCounterSet(_counterSet); _provider = PerfProviderCollection.QueryProvider(_providerGuid); _lockObject = new object(); _stringToId = new Dictionary<string, int>(); _idToCounter = new Dictionary<int, CounterType>(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~CounterSet() { Dispose(false); } protected virtual void Dispose(bool disposing) { lock (this) { PerfProviderCollection.UnregisterCounterSet(_counterSet); if (_instanceCreated && _provider != null) { lock (_lockObject) { if (_provider != null) { Interlocked.Decrement(ref _provider._counterSet); if (_provider._counterSet <= 0) { PerfProviderCollection.RemoveProvider(_providerGuid); } _provider = null; } } } } } /// <summary> /// Add non-displayable new counter to CounterSet; that is, perfmon would not display the counter. /// </summary> /// <param name="counterId">CounterId uniquely identifies the counter within CounterSet</param> /// <param name="counterType">One of defined CounterType values</param> public void AddCounter(int counterId, CounterType counterType) { if (_provider == null) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_NoActiveProvider, _providerGuid)); } if (!PerfProviderCollection.ValidateCounterType(counterType)) { throw new ArgumentException(SR.Format(SR.Perflib_Argument_InvalidCounterType, counterType), nameof(counterType)); } if (_instanceCreated) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_AddCounterAfterInstance, _counterSet)); } lock (_lockObject) { if (_instanceCreated) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_AddCounterAfterInstance, _counterSet)); } if (_idToCounter.ContainsKey(counterId)) { throw new ArgumentException(SR.Format(SR.Perflib_Argument_CounterAlreadyExists, counterId, _counterSet), nameof(counterId)); } _idToCounter.Add(counterId, counterType); } } /// <summary> /// Add named new counter to CounterSet. /// </summary> /// <param name="counterId">CounterId uniquely identifies the counter within CounterSet</param> /// <param name="counterType">One of defined CounterType values</param> /// <param name="counterName">This is friendly name to help provider developers as indexer. and it might not match what is displayed in counter consumption applications lie perfmon.</param> public void AddCounter(int counterId, CounterType counterType, string counterName) { ArgumentNullException.ThrowIfNull(counterName); if (counterName.Length == 0) { throw new ArgumentException(SR.Perflib_Argument_EmptyCounterName, nameof(counterName)); } if (!PerfProviderCollection.ValidateCounterType(counterType)) { throw new ArgumentException(SR.Format(SR.Perflib_Argument_InvalidCounterType, counterType), nameof(counterType)); } if (_provider == null) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_NoActiveProvider, _providerGuid)); } if (_instanceCreated) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_AddCounterAfterInstance, _counterSet)); } lock (_lockObject) { if (_instanceCreated) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_AddCounterAfterInstance, _counterSet)); } if (_stringToId.ContainsKey(counterName)) { throw new ArgumentException(SR.Format(SR.Perflib_Argument_CounterNameAlreadyExists, counterName, _counterSet), nameof(counterName)); } if (_idToCounter.ContainsKey(counterId)) { throw new ArgumentException(SR.Format(SR.Perflib_Argument_CounterAlreadyExists, counterId, _counterSet), nameof(counterId)); } _stringToId.Add(counterName, counterId); _idToCounter.Add(counterId, counterType); } } /// <summary> /// Create instances of the CounterSet. Created CounterSetInstance identifies active identity and tracks raw counter data for that identity. /// </summary> /// <param name="instanceName">Friendly name identifies the instance. InstanceName would be shown in counter consumption applications like perfmon.</param> /// <returns>CounterSetInstance object</returns> public CounterSetInstance CreateCounterSetInstance(string instanceName) { ArgumentNullException.ThrowIfNull(instanceName); if (instanceName.Length == 0) { throw new ArgumentException(SR.Perflib_Argument_EmptyInstanceName, nameof(instanceName)); } if (_provider == null) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_NoActiveProvider, _providerGuid)); } if (!_instanceCreated) { lock (_lockObject) { if (!_instanceCreated) { if (_provider == null) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_NoActiveProvider, _providerGuid)); } if (_provider._hProvider.IsInvalid) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_NoActiveProvider, _providerGuid)); } if (_idToCounter.Count == 0) { throw new InvalidOperationException(SR.Format(SR.Perflib_InvalidOperation_CounterSetContainsNoCounter, _counterSet)); } uint Status = (uint)Interop.Errors.ERROR_SUCCESS; unsafe { uint CounterSetInfoSize = checked((uint)sizeof(Interop.PerfCounter.PerfCounterSetInfoStruct) + (uint)_idToCounter.Count * (uint)sizeof(Interop.PerfCounter.PerfCounterInfoStruct)); uint CounterSetInfoUsed = 0; Span<byte> CounterSetBuffer = CounterSetInfoSize > 512 ? new byte[(int)CounterSetInfoSize] : stackalloc byte[(int)CounterSetInfoSize]; fixed (byte* pCounterSetBuffer = CounterSetBuffer) { Debug.Assert(sizeof(Interop.PerfCounter.PerfCounterSetInfoStruct) == 40); Debug.Assert(sizeof(Interop.PerfCounter.PerfCounterInfoStruct) == 32); Interop.PerfCounter.PerfCounterSetInfoStruct* CounterSetInfo; Interop.PerfCounter.PerfCounterInfoStruct* CounterInfo; uint CurrentCounter = 0; uint CurrentOffset = 0; CounterSetInfo = (Interop.PerfCounter.PerfCounterSetInfoStruct*)pCounterSetBuffer; CounterSetInfo->CounterSetGuid = _counterSet; CounterSetInfo->ProviderGuid = _providerGuid; CounterSetInfo->NumCounters = (uint)_idToCounter.Count; CounterSetInfo->InstanceType = (uint)_instType; foreach (KeyValuePair<int, CounterType> CounterDef in _idToCounter) { CounterSetInfoUsed = (uint)sizeof(Interop.PerfCounter.PerfCounterSetInfoStruct) + (uint)CurrentCounter * (uint)sizeof(Interop.PerfCounter.PerfCounterInfoStruct); if (CounterSetInfoUsed < CounterSetInfoSize) { CounterInfo = (Interop.PerfCounter.PerfCounterInfoStruct*)(pCounterSetBuffer + CounterSetInfoUsed); CounterInfo->CounterId = (uint)CounterDef.Key; CounterInfo->CounterType = (uint)CounterDef.Value; CounterInfo->Attrib = 0x0000000000000001; // PERF_ATTRIB_BY_REFERENCE CounterInfo->Size = (uint)sizeof(void*); // always use pointer size CounterInfo->DetailLevel = 100; // PERF_DETAIL_NOVICE CounterInfo->Scale = 0; // Default scale CounterInfo->Offset = CurrentOffset; CurrentOffset += CounterInfo->Size; } CurrentCounter++; } Status = Interop.PerfCounter.PerfSetCounterSetInfo(_provider._hProvider, CounterSetInfo, CounterSetInfoSize); // ERROR_INVALID_PARAMETER, ERROR_ALREADY_EXISTS, ERROR_NOT_ENOUGH_MEMORY, ERROR_OUTOFMEMORY if (Status != (uint)Interop.Errors.ERROR_SUCCESS) { throw Status switch { (uint)Interop.Errors.ERROR_ALREADY_EXISTS => new InvalidOperationException(SR.Format(SR.Perflib_Argument_CounterSetAlreadyRegister, _counterSet)), _ => new Win32Exception((int)Status), }; } Interlocked.Increment(ref _provider._counterSet); } } _instanceCreated = true; } } } CounterSetInstance thisInst = new CounterSetInstance(this, instanceName); return thisInst; } } }