Performance counters real time probe

This commit is contained in:
georgis 2013-06-24 21:40:34 -07:00
Родитель e0627df659
Коммит 107d7f1fa2
7 изменённых файлов: 182 добавлений и 79 удалений

Просмотреть файл

@ -0,0 +1,92 @@
// 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;
using System.Runtime.InteropServices;
namespace Tx.Windows
{
public abstract class PerfCounterReader : IDisposable
{
internal readonly List<PerfCounterInfo> _counters = new List<PerfCounterInfo>();
internal readonly IObserver<PerformanceSample> _observer;
internal PdhQueryHandle _query;
public PerfCounterReader(IObserver<PerformanceSample> 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_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)
{
PdhCounterHandle counter;
PdhStatus status = PdhNativeMethods.PdhAddCounter(_query, counterPath, IntPtr.Zero, out counter);
if (status == PdhStatus.PDH_ENTRY_NOT_IN_LOG_FILE)
return;
PdhUtils.CheckStatus(status, PdhStatus.PDH_CSTATUS_VALID_DATA);
var counterInfo = new PerfCounterInfo(counterPath, counter);
_counters.Add(counterInfo);
}
public virtual void Dispose()
{
foreach (PerfCounterInfo counterInfo in _counters)
{
counterInfo.Dispose();
}
_query.Dispose();
}
}
}

Просмотреть файл

@ -16,6 +16,14 @@ namespace Tx.Windows
typeof (PerfCounterTypeMap));
}
public static void AddPerfCounterProbe(this IPlaybackConfiguration playback, TimeSpan samplingRate, params string[] counterPaths)
{
playback.AddInput(
() => PerfCounterObservable.FromRealTime(samplingRate, counterPaths),
typeof(PerfCounterPartitionTypeMap),
typeof(PerfCounterTypeMap));
}
public static IObservable<PerformanceSample> GetPerformanceCounter(this IPlaybackConfiguration playback,
string counterPath)
{

Просмотреть файл

@ -1,22 +1,18 @@
// 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;
using System.IO;
namespace Tx.Windows
{
public sealed class PerfCounterFileReader : IDisposable
public sealed class PerfCounterFileReader : PerfCounterReader
{
private readonly bool _binaryLog;
private readonly List<PerfCounterInfo> _counters = new List<PerfCounterInfo>();
private readonly IObserver<PerformanceSample> _observer;
private readonly PdhQueryHandle _query;
private bool _firstMove = true;
public PerfCounterFileReader(IObserver<PerformanceSample> observer, string file)
: base(observer)
{
_observer = observer;
string extension = Path.GetExtension(file);
if (extension != null) _binaryLog = extension.ToLowerInvariant() == ".blg";
@ -33,16 +29,6 @@ namespace Tx.Windows
Read();
}
public void Dispose()
{
foreach (PerfCounterInfo counterInfo in _counters)
{
counterInfo.Dispose();
}
_query.Dispose();
}
public void Read()
{
try
@ -85,67 +71,6 @@ namespace Tx.Windows
}
}
private 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_CALC_NEGATIVE_VALUE
|| status == PdhStatus.PDH_CALC_NEGATIVE_DENOMINATOR
|| status == PdhStatus.PDH_CALC_NEGATIVE_TIMEBASE)
{
var sample = new PerformanceSample(counterInfo, 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 sample = new PerformanceSample(counterInfo, timestamp, item->FmtValue.doubleValue);
_observer.OnNext(sample);
}
}
}
}
private void AddCounter(string counterPath)
{
PdhCounterHandle counter;
PdhStatus status = PdhNativeMethods.PdhAddCounter(_query, counterPath, IntPtr.Zero, out counter);
if (status == PdhStatus.PDH_ENTRY_NOT_IN_LOG_FILE)
return;
PdhUtils.CheckStatus(status, PdhStatus.PDH_CSTATUS_VALID_DATA);
var counterInfo = new PerfCounterInfo(counterPath, counter);
_counters.Add(counterInfo);
}
~PerfCounterFileReader()
{
Dispose();

Просмотреть файл

@ -11,5 +11,10 @@ namespace Tx.Windows
{
return Observable.Create<PerformanceSample>(o => new PerfCounterFileReader(o, perfTrace));
}
public static IObservable<PerformanceSample> FromRealTime(TimeSpan samplingRate, params string[] counterPaths)
{
return Observable.Create<PerformanceSample>(o => new PerfCounterRealTimeProbe(o, samplingRate, counterPaths));
}
}
}

Просмотреть файл

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Threading;
namespace Tx.Windows
{
public sealed class PerfCounterRealTimeProbe : PerfCounterReader
{
private bool _firstMove = true;
private readonly Timer _timer;
public PerfCounterRealTimeProbe(IObserver<PerformanceSample> observer, TimeSpan samplingRate, params string[] counterPaths)
: base(observer)
{
PdhStatus status = PdhNativeMethods.PdhOpenQuery(null, IntPtr.Zero, out _query);
PdhUtils.CheckStatus(status, PdhStatus.PDH_CSTATUS_VALID_DATA);
foreach (string counter in counterPaths)
{
AddCounter(counter);
}
_timer = new Timer(OnTimer, null, TimeSpan.Zero, samplingRate);
}
public void OnTimer(object state)
{
try
{
if (_firstMove)
{
// some counters need two samples to calculate their value
// so skip a sample to make sure there are no further complications
PdhNativeMethods.PdhCollectQueryData(_query);
_firstMove = false;
return;
}
long time;
PdhStatus status = PdhNativeMethods.PdhCollectQueryDataWithTime(_query, out time);
PdhUtils.CheckStatus(status, PdhStatus.PDH_CSTATUS_VALID_DATA);
DateTime timestamp = DateTime.FromFileTimeUtc(time);
foreach (PerfCounterInfo counterInfo in _counters)
{
ProduceCounterSamples(counterInfo, timestamp);
}
}
catch (Exception ex)
{
_observer.OnError(ex);
}
}
public override void Dispose()
{
_timer.Dispose();
base.Dispose();
}
~PerfCounterRealTimeProbe()
{
Dispose();
}
}
}

Просмотреть файл

@ -7,12 +7,14 @@ namespace Tx.Windows
public class PerformanceSample
{
private readonly PerfCounterInfo _counterInfo;
private readonly string _instanceName;
private readonly DateTime _timestamp;
private readonly double _value;
internal PerformanceSample(PerfCounterInfo counterInfo, DateTime timestamp, double value)
internal PerformanceSample(PerfCounterInfo counterInfo, string instanceName, DateTime timestamp, double value)
{
_counterInfo = counterInfo;
_instanceName = instanceName;
_timestamp = timestamp;
_value = value;
}
@ -20,6 +22,7 @@ namespace Tx.Windows
public PerformanceSample(PerformanceSample other)
{
_counterInfo = other._counterInfo;
_instanceName = other._instanceName;
_timestamp = other._timestamp;
_value = other._value;
}
@ -36,7 +39,7 @@ namespace Tx.Windows
public string Instance
{
get { return _counterInfo.Instance; }
get { return _instanceName; }
}
public string Machine

Просмотреть файл

@ -75,6 +75,8 @@
<Compile Include="EtwNative\EtwNativeMethods.cs" />
<Compile Include="EtwNative\EtwObservable.cs" />
<Compile Include="InternalsVisibleTo.cs" />
<Compile Include="PerfCounters\PerfCounteReader.cs" />
<Compile Include="PerfCounters\PerfCounterRealTimeProbe.cs" />
<Compile Include="PerfCounters\PerfCounterInfo.cs" />
<Compile Include="PerfCounters\PdhNativeMethods.cs" />
<Compile Include="PerfCounters\PerfCounterExtensions.cs" />