Source/Tx.Windows/PerfCounters/PerfCounterReader.cs (95 lines of code) (raw):
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Tx.Windows
{
public abstract class PerfCounterReader : IDisposable
{
internal readonly List<PerfCounterInfo> _counters = new List<PerfCounterInfo>();
internal readonly IObserver<PerformanceSample> _observer;
internal PdhQueryHandle _query;
private bool disposed = false;
protected PerfCounterReader(IObserver<PerformanceSample> observer)
{
if (observer == null)
{
throw new ArgumentNullException(nameof(observer));
}
_observer = observer;
}
internal void ProduceCounterSamples(PerfCounterInfo counterInfo, DateTime timestamp)
{
uint bufferSize = 0;
uint bufferCount;
PdhStatus status = PdhNativeMethods.PdhGetFormattedCounterArray(
counterInfo.Handle,
PdhFormat.PDH_FMT_DOUBLE,
ref bufferSize,
out bufferCount,
IntPtr.Zero);
PdhUtils.CheckStatus(status, PdhStatus.PDH_MORE_DATA);
var buffer = new byte[bufferSize];
unsafe
{
fixed (byte* pb = buffer)
{
status = PdhNativeMethods.PdhGetFormattedCounterArray(
counterInfo.Handle,
PdhFormat.PDH_FMT_DOUBLE,
ref bufferSize,
out bufferCount,
(IntPtr) pb);
if (status == PdhStatus.PDH_INVALID_DATA
|| status == PdhStatus.PDH_NO_DATA
|| status == PdhStatus.PDH_CALC_NEGATIVE_VALUE
|| status == PdhStatus.PDH_CALC_NEGATIVE_DENOMINATOR
|| status == PdhStatus.PDH_CALC_NEGATIVE_TIMEBASE)
{
var sample = new PerformanceSample(counterInfo, counterInfo.Instance, timestamp, double.NaN);
_observer.OnNext(sample);
return;
}
PdhUtils.CheckStatus(status, PdhStatus.PDH_CSTATUS_VALID_DATA);
var items = (PDH_FMT_COUNTERVALUE_ITEM*) pb;
for (int i = 0; i < bufferCount; i++)
{
PDH_FMT_COUNTERVALUE_ITEM* item = items + i;
var instanceName = new string((char*)item->szName);
var sample = new PerformanceSample(counterInfo, instanceName, timestamp, item->FmtValue.doubleValue);
_observer.OnNext(sample);
}
}
}
}
protected void AddCounter(string counterPath, int index)
{
PdhCounterHandle counter;
PdhStatus status = PdhNativeMethods.PdhAddCounter(_query, counterPath, IntPtr.Zero, out counter);
if (status == PdhStatus.PDH_ENTRY_NOT_IN_LOG_FILE || status == PdhStatus.PDH_CSTATUS_NO_INSTANCE)
return;
PdhUtils.CheckStatus(status, PdhStatus.PDH_CSTATUS_VALID_DATA);
var counterInfo = new PerfCounterInfo(counterPath, counter, index);
_counters.Add(counterInfo);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
foreach (PerfCounterInfo counterInfo in _counters)
{
counterInfo.Dispose();
}
if (_query != null)
{
_query.Dispose();
_query = null;
}
disposed = true;
}
}
}