| File: System\Diagnostics\CounterSampleCalculator.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.ComponentModel; using System.Globalization; using System.IO; using System.Runtime.InteropServices; namespace System.Diagnostics { /// <summary> /// Set of utility functions for interpreting the counter data /// </summary> public static class CounterSampleCalculator { /// <summary> /// Converts 100NS elapsed time to fractional seconds /// </summary> /// <internalonly/> private static float GetElapsedTime(CounterSample oldSample, CounterSample newSample) { float eSeconds; float eDifference; if (newSample.RawValue == 0) { // no data [start time = 0] so return 0 return 0.0f; } else { float eFreq; eFreq = (float)(ulong)oldSample.CounterFrequency; if (oldSample.UnsignedRawValue >= (ulong)newSample.CounterTimeStamp || eFreq <= 0.0f) return 0.0f; // otherwise compute difference between current time and start time eDifference = (float)((ulong)newSample.CounterTimeStamp - oldSample.UnsignedRawValue); // convert to fractional seconds using object counter eSeconds = eDifference / eFreq; return eSeconds; } } /// <summary> /// Computes the calculated value given a raw counter sample. /// </summary> public static float ComputeCounterValue(CounterSample newSample) { return ComputeCounterValue(CounterSample.Empty, newSample); } /// <summary> /// Computes the calculated value given a raw counter sample. /// </summary> public static float ComputeCounterValue(CounterSample oldSample, CounterSample newSample) { int newCounterType = (int)newSample.CounterType; if (oldSample.SystemFrequency == 0) { if ((newCounterType != Interop.Kernel32.PerformanceCounterOptions.PERF_RAW_FRACTION) && (newCounterType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_RAWCOUNT) && (newCounterType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_RAWCOUNT_HEX) && (newCounterType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_LARGE_RAWCOUNT) && (newCounterType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_LARGE_RAWCOUNT_HEX) && (newCounterType != Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_MULTI_BASE)) { // Since oldSample has a system frequency of 0, this means the newSample is the first sample // on a two sample calculation. Since we can't do anything with it, return 0. return 0.0f; } } else if (oldSample.CounterType != newSample.CounterType) { throw new InvalidOperationException(SR.MismatchedCounterTypes); } if (newCounterType == Interop.Kernel32.PerformanceCounterOptions.PERF_ELAPSED_TIME) return (float)GetElapsedTime(oldSample, newSample); Interop.Kernel32.PerformanceCounterOptions.PDH_RAW_COUNTER newPdhValue = default; Interop.Kernel32.PerformanceCounterOptions.PDH_RAW_COUNTER oldPdhValue = default; FillInValues(oldSample, newSample, ref oldPdhValue, ref newPdhValue); Interop.Kernel32.PerformanceCounterOptions.PDH_FMT_COUNTERVALUE pdhFormattedValue = default; long timeBase = newSample.SystemFrequency; int result = Interop.Pdh.PdhFormatFromRawValue((uint)newCounterType, Interop.Kernel32.PerformanceCounterOptions.PDH_FMT_DOUBLE | Interop.Kernel32.PerformanceCounterOptions.PDH_FMT_NOSCALE | Interop.Kernel32.PerformanceCounterOptions.PDH_FMT_NOCAP100, ref timeBase, ref newPdhValue, ref oldPdhValue, ref pdhFormattedValue); if (result != Interop.Errors.ERROR_SUCCESS) { // If the numbers go negative, just return 0. This better matches the old behavior. if (result == Interop.Kernel32.PerformanceCounterOptions.PDH_CALC_NEGATIVE_VALUE || result == Interop.Kernel32.PerformanceCounterOptions.PDH_CALC_NEGATIVE_DENOMINATOR || result == Interop.Kernel32.PerformanceCounterOptions.PDH_NO_DATA) return 0; else throw new Win32Exception(result, SR.Format(SR.PerfCounterPdhError, result.ToString("x", CultureInfo.InvariantCulture))); } return (float)pdhFormattedValue.data; } // This method figures out which values are supposed to go into which structures so that PDH can do the // calculation for us. This was ported from Window's cutils.c private static void FillInValues(CounterSample oldSample, CounterSample newSample, ref Interop.Kernel32.PerformanceCounterOptions.PDH_RAW_COUNTER oldPdhValue, ref Interop.Kernel32.PerformanceCounterOptions.PDH_RAW_COUNTER newPdhValue) { int newCounterType = (int)newSample.CounterType; switch (newCounterType) { case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_COUNTER: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_QUEUELEN_TYPE: case Interop.Kernel32.PerformanceCounterOptions.PERF_SAMPLE_COUNTER: case Interop.Kernel32.PerformanceCounterOptions.PERF_OBJ_TIME_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE: newPdhValue.FirstValue = newSample.RawValue; newPdhValue.SecondValue = newSample.TimeStamp; oldPdhValue.FirstValue = oldSample.RawValue; oldPdhValue.SecondValue = oldSample.TimeStamp; break; case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_100NS_QUEUELEN_TYPE: newPdhValue.FirstValue = newSample.RawValue; newPdhValue.SecondValue = newSample.TimeStamp100nSec; oldPdhValue.FirstValue = oldSample.RawValue; oldPdhValue.SecondValue = oldSample.TimeStamp100nSec; break; case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_TIMER_INV: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_BULK_COUNT: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_LARGE_QUEUELEN_TYPE: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_MULTI_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_MULTI_TIMER_INV: newPdhValue.FirstValue = newSample.RawValue; newPdhValue.SecondValue = newSample.TimeStamp; oldPdhValue.FirstValue = oldSample.RawValue; oldPdhValue.SecondValue = oldSample.TimeStamp; if (newCounterType == Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_MULTI_TIMER || newCounterType == Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_MULTI_TIMER_INV) { // this is to make PDH work like PERFMON for // this counter type newPdhValue.FirstValue *= (uint)newSample.CounterFrequency; if (oldSample.CounterFrequency != 0) { oldPdhValue.FirstValue *= (uint)oldSample.CounterFrequency; } } if ((newCounterType & Interop.Kernel32.PerformanceCounterOptions.PERF_MULTI_COUNTER) == Interop.Kernel32.PerformanceCounterOptions.PERF_MULTI_COUNTER) { newPdhValue.MultiCount = (int)newSample.BaseValue; oldPdhValue.MultiCount = (int)oldSample.BaseValue; } break; // // These counters do not use any time reference // case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_RAWCOUNT: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_RAWCOUNT_HEX: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_DELTA: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_LARGE_RAWCOUNT: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_LARGE_RAWCOUNT_HEX: case Interop.Kernel32.PerformanceCounterOptions.PERF_COUNTER_LARGE_DELTA: newPdhValue.FirstValue = newSample.RawValue; newPdhValue.SecondValue = 0; oldPdhValue.FirstValue = oldSample.RawValue; oldPdhValue.SecondValue = 0; break; // // These counters use the 100 Ns time base in thier calculation // case Interop.Kernel32.PerformanceCounterOptions.PERF_100NSEC_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_100NSEC_TIMER_INV: case Interop.Kernel32.PerformanceCounterOptions.PERF_100NSEC_MULTI_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_100NSEC_MULTI_TIMER_INV: newPdhValue.FirstValue = newSample.RawValue; newPdhValue.SecondValue = newSample.TimeStamp100nSec; oldPdhValue.FirstValue = oldSample.RawValue; oldPdhValue.SecondValue = oldSample.TimeStamp100nSec; if ((newCounterType & Interop.Kernel32.PerformanceCounterOptions.PERF_MULTI_COUNTER) == Interop.Kernel32.PerformanceCounterOptions.PERF_MULTI_COUNTER) { newPdhValue.MultiCount = (int)newSample.BaseValue; oldPdhValue.MultiCount = (int)oldSample.BaseValue; } break; // // These counters use two data points // case Interop.Kernel32.PerformanceCounterOptions.PERF_SAMPLE_FRACTION: case Interop.Kernel32.PerformanceCounterOptions.PERF_RAW_FRACTION: case Interop.Kernel32.PerformanceCounterOptions.PERF_LARGE_RAW_FRACTION: case Interop.Kernel32.PerformanceCounterOptions.PERF_PRECISION_SYSTEM_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_PRECISION_100NS_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_PRECISION_OBJECT_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_AVERAGE_TIMER: case Interop.Kernel32.PerformanceCounterOptions.PERF_AVERAGE_BULK: newPdhValue.FirstValue = newSample.RawValue; newPdhValue.SecondValue = newSample.BaseValue; oldPdhValue.FirstValue = oldSample.RawValue; oldPdhValue.SecondValue = oldSample.BaseValue; break; default: // an unidentified counter was returned so newPdhValue.FirstValue = 0; newPdhValue.SecondValue = 0; oldPdhValue.FirstValue = 0; oldPdhValue.SecondValue = 0; break; } } } }