using System;
using System.Diagnostics;
using System.Reflection;
namespace Common.Diagnostics
{
/// <summary>
/// Attribute to tag the performance counters for a given class to a performance counter category.
/// </summary>
/// <remarks>
/// Attribute to tag the performance counters for a given class to a performance counter category.
/// </remarks>
[AttributeUsage(AttributeTargets.Class)]
public class PerformanceCounterCategoryAttribute : Attribute
{
private string name;
private string help;
private PerformanceCounterCategoryType categoryType;
/// <summary>
/// Constructor
/// </summary>
/// <remarks>
/// PerformanceCounterCategoryType defaults to Unknown
/// </remarks>
/// <param name="name">Performance counter category name.</param>
public PerformanceCounterCategoryAttribute(string name) : this(name, null, PerformanceCounterCategoryType.Unknown)
{
// nothing to do
}
/// <summary>
/// Constructor
/// </summary>
/// <remarks>
/// PerformanceCounterCategoryType defaults to Unknown
/// </remarks>
/// <param name="name">Performance counter category name.</param>
/// <param name="help">Performance counter category help.</param>
public PerformanceCounterCategoryAttribute(string name, string help) : this(name, help, PerformanceCounterCategoryType.Unknown)
{
// nothing to do
}
/// <summary>
/// Constructor
/// </summary>
/// <remarks>
/// Constructor
/// </remarks>
/// <param name="name">Performance counter category name.</param>
/// <param name="categoryType">Performance counter category type.</param>
public PerformanceCounterCategoryAttribute(string name, PerformanceCounterCategoryType categoryType) : this(name, null, categoryType)
{
// nothing to do
}
/// <summary>
/// Constructor
/// </summary>
/// <remarks>
/// Constructor
/// </remarks>
/// <param name="name">Performance counter category name.</param>
/// <param name="help">Performance counter category help.</param>
/// <param name="categoryType">Performance counter category type.</param>
public PerformanceCounterCategoryAttribute(string name, string help, PerformanceCounterCategoryType categoryType)
{
this.name = name;
this.help = help;
this.categoryType = categoryType;
}
/// <summary>
/// Gets the performance counter category.
/// </summary>
/// <remarks>
/// Gets the performance counter category.
/// </remarks>
public string Name
{
get { return name; }
}
/// <summary>
/// Gets the performance counter category help.
/// </summary>
/// <remarks>
/// Gets the performance counter category help.
/// </remarks>
public string Help
{
get { return help; }
}
/// <summary>
/// Gets the performance counter category type.
/// </summary>
/// <remarks>
/// Default is Unknown.
/// </remarks>
public PerformanceCounterCategoryType CategoryType
{
get { return categoryType; }
}
}
/// <summary>
/// Defines a performance counter for the class that it is tagged with it.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class PerformanceCounterAttribute : Attribute
{
#region Private Members
string name;
string help;
string instance;
PerformanceCounterType type;
#endregion
#region Constructor
/// <summary>
/// Creates a new performance counter attribute with the given counter name.
/// </summary>
/// <remarks>
/// Creates a new performance counter attribute with the given counter name.
/// </remarks>
/// <param name="name">Counter name.</param>
public PerformanceCounterAttribute(string name) : this(name, null, PerformanceCounterType.NumberOfItems64, null)
{
// nothing to do
}
/// <summary>
/// Creates a new performance counter attribute with the given counter name and type.
/// </summary>
/// <remarks>
/// Creates a new performance counter attribute with the given counter name and type.
/// </remarks>
/// <param name="name">Counter name.</param>
/// <param name="type">Counter type.</param>
public PerformanceCounterAttribute(string name, PerformanceCounterType type) : this(name, null, type, null)
{
// nothing to do
}
/// <summary>
/// Creates a new performance counter attribute with the given counter name and type.
/// </summary>
/// <remarks>
/// Creates a new performance counter attribute with the given counter name and type.
/// </remarks>
/// <param name="name">Counter name.</param>
/// <param name="type">Counter type.</param>
/// <param name="instance">Counter instance name.</param>
public PerformanceCounterAttribute(string name, PerformanceCounterType type, string instance) : this(name, null, type, instance)
{
// nothing to do
}
/// <summary>
/// Creates a new performance counter attribute with the given counter name and type.
/// </summary>
/// <remarks>
/// Creates a new performance counter attribute with the given counter name and type.
/// </remarks>
/// <param name="name">Counter name.</param>
/// <param name="help">Counter help.</param>
/// <param name="type">Counter type.</param>
public PerformanceCounterAttribute(string name, string help, PerformanceCounterType type) : this(name, help, type, null)
{
// nothing to do
}
/// <summary>
/// Creates a new performance counter attribute with the given counter name, help, member name and type.
/// </summary>
/// <remarks>
/// Creates a new performance counter attribute with the given counter name, help, member name and type.
/// </remarks>
/// <param name="name">Counter name.</param>
/// <param name="type">Counter type.</param>
/// <param name="help">Counter help.</param>
/// <param name="instance">Counter instance name.</param>
public PerformanceCounterAttribute(string name, string help, PerformanceCounterType type, string instance)
{
this.name = name;
this.instance = (instance == null) ? string.Empty : instance;
this.help = help;
this.type = type;
}
#endregion
#region Properties
/// <summary>
/// Gets the performance counter name.
/// </summary>
/// <remarks>
/// Gets the performance counter name.
/// </remarks>
public string Name { get { return name; }}
/// <summary>
/// Gets the performance counter instance name.
/// </summary>
/// <remarks>
/// Gets the performance counter instance name.
/// </remarks>
public string Instance { get { return instance; }}
/// <summary>
/// Gets the performance counter help.
/// </summary>
/// <remarks>
/// Gets the performance counter help.
/// </remarks>
public string Help { get { return help; }}
/// <summary>
/// Gets the type of the performance counter.
/// </summary>
/// <remarks>
/// Gets the type of the performance counter.
/// </remarks>
public PerformanceCounterType Type { get { return type; }}
#endregion
}
/// <summary>
/// Wrapper and Helper class for a category of performance counters.
/// </summary>
/// <remarks>
/// Wrapper and Helper class for a category of performance counters.
/// </remarks>
public class PerformanceCounterFactory
{
#region Private Members
private string categoryName;
private string categoryHelp;
private PerformanceCounterCategoryType categoryType;
private CounterCreationDataCollection counters;
#endregion
#region Constructor
/// <summary>
/// Creates a new performance counter category with the given name and help description.
/// </summary>
/// <remarks>
/// Creates a new performance counter category with the given name and help description.
/// </remarks>
/// <param name="categoryName">Performance counter category name.</param>
/// <param name="categoryHelp">Performance counter category help text.</param>
public PerformanceCounterFactory(string categoryName, string categoryHelp)
{
this.categoryName = categoryName;
this.categoryHelp = categoryHelp;
this.categoryType = PerformanceCounterCategoryType.Unknown;
counters = new CounterCreationDataCollection();
}
/// <summary>
/// Creates a new performance counter category with the given name.
/// </summary>
/// <remarks>
/// Creates a new performance counter category with the given name.
/// </remarks>
/// <param name="categoryName"></param>
public PerformanceCounterFactory(string categoryName) : this(categoryName, null)
{
// nothing to do
}
/// <summary>
/// Creates a new category for the specified type.
/// </summary>
/// <remarks>
/// Type must have <c>PerformanceCounterCategoryAttribute</c> and <c>PerformanceCounterAttributes</c> set.
/// </remarks>
/// <exception cref="ArgumentException">
/// Thrown if the specified type has no performance counter category attribute set.
/// </exception>
/// <param name="type">Type to create Performance counters for.</param>
public PerformanceCounterFactory(Type type)
{
counters = new CounterCreationDataCollection();
PerformanceCounterCategoryAttribute category = GetCategory(type);
if (category == null) throw new ArgumentException(String.Format("Type {0} has no PerformanceCounterCategory attribute", type.Name), "type");
this.categoryName = category.Name;
this.categoryHelp = category.Help;
this.categoryType = category.CategoryType;
_AddCounters(type);
}
#endregion
#region Properties
/// <summary>
/// Gets the performance counter category name.
/// </summary>
/// <value>
/// Performance counter category name.
/// </value>
public string Name
{
get { return categoryName; }
}
/// <summary>
/// Gets the performance counter category help.
/// </summary>
/// <value>
/// Performance counter category help.
/// </value>
public string Help
{
get { return categoryHelp; }
}
/// <summary>
/// Gets the performance counter category Type.
/// </summary>
/// <value>
/// Performance counter category type.
/// </value>
public PerformanceCounterCategoryType CategoryType
{
get { return categoryType; }
}
/// <summary>
/// Gets a writable instance of a performance counter in this category.
/// </summary>
/// <value>
/// Writable instance of a performance counter in this category.
/// </value>
public PerformanceCounter this[string counterName]
{
get { return GetCounter(categoryName, counterName, false); }
}
/// <summary>
/// Gets the counter creation data collection
/// </summary>
/// <remarks>
/// Can be passed to a PerformanceCounterInstaller.
/// </remarks>
public CounterCreationDataCollection Counters
{
get { return counters; }
}
#endregion
#region Public Methods
/// <summary>
/// Gets an instance of the specified performance counter in this category.
/// </summary>
/// <param name="counterName">Performance counter name.</param>
/// <param name="instanceName">Performance counter instance name.</param>
/// <param name="readOnly">If true, the returned counter is readonly. If false, the returned counter is writable.</param>
/// <returns>Performance counter instance.</returns>
public PerformanceCounter GetCounter(string counterName, string instanceName, bool readOnly)
{
return GetCounter(categoryName, counterName, instanceName, readOnly);
}
/// <summary>
/// Gets a readonly instance of the specified performance counter in this category.
/// </summary>
/// <param name="counterName">Performance counter name.</param>
/// <param name="instanceName">Performance counter instance name.</param>
/// <returns>Performance counter instance.</returns>
public PerformanceCounter GetCounter(string counterName, string instanceName)
{
return GetCounter(counterName, instanceName, false);
}
/// <summary>
/// Gets an instance of the specified performance counter in this category.
/// </summary>
/// <param name="counterName">Performance counter name.</param>
/// <param name="readOnly">If true, the returned counter is readonly. If false, the returned counter is writable.</param>
/// <returns>Performance counter instance.</returns>
public PerformanceCounter GetCounter(string counterName, bool readOnly)
{
return GetCounter(categoryName, counterName, string.Empty, readOnly);
}
/// <summary>
/// Gets a readonly instance of the specified performance counter in this category.
/// </summary>
/// <param name="counterName">Performance counter name</param>
/// <returns>Performance counter instance.</returns>
public PerformanceCounter GetCounter(string counterName)
{
return GetCounter(counterName, false);
}
/// <summary>
/// Adds a performance counter of the given type to the category collection.
/// </summary>
/// <remarks>
/// Adds a performance counter of the given type to the category collection.
/// </remarks>
/// <param name="name">Performance counter name.</param>
/// <param name="type">Performance counter type.</param>
/// <param name="help">Performance counter help text.</param>
public void AddCounter(string name, PerformanceCounterType type, string help)
{
CounterCreationData counter = new CounterCreationData();
counter.CounterName = name;
counter.CounterType = type;
if (help != null)
counter.CounterHelp = help;
counters.Add(counter);
}
/// <summary>
/// Adds a performance counter of the given type to the category collection.
/// </summary>
/// <remarks>
/// Adds a performance counter of the given type to the category collection.
/// </remarks>
/// <param name="name">Performance counter name.</param>
/// <param name="type">Performance counter type.</param>
public void AddCounter(string name, PerformanceCounterType type)
{
AddCounter(name, type, null);
}
/// <summary>
/// Adds a NumberOfItems64 performance counter to the category collection.
/// </summary>
/// <remarks>
/// Adds a NumberOfItems64 performance counter to the category collection.
/// </remarks>
/// <param name="name">Performance counter name.</param>
/// <param name="help">Performance counter help text.</param>
public void AddNumberCounter(string name, string help)
{
AddCounter(name, PerformanceCounterType.NumberOfItems64, help);
}
/// <summary>
/// Adds a NumberOfItems64 performance counter to the category collection.
/// </summary>
/// <remarks>
/// Adds a NumberOfItems64 performance counter to the category collection.
/// </remarks>
/// <param name="name">Performance counter name.</param>
public void AddNumberCounter(string name)
{
AddCounter(name, PerformanceCounterType.NumberOfItems64, null);
}
/// <summary>
/// Adds a RateOfCountsPerSecond64 performance counter to the category collection.
/// </summary>
/// <remarks>
/// Adds a RateOfCountsPerSecond64 performance counter to the category collection.
/// </remarks>
/// <param name="name">Performance counter name.</param>
/// <param name="help">Performance counter help text.</param>
public void AddRateCounter(string name, string help)
{
AddCounter(name, PerformanceCounterType.RateOfCountsPerSecond64, help);
}
/// <summary>
/// Adds a RateOfCountsPerSecond64 performance counter to the category collection.
/// </summary>
/// <remarks>
/// Adds a RateOfCountsPerSecond64 performance counter to the category collection.
/// </remarks>
/// <param name="name">Performance counter name.</param>
public void AddRateCounter(string name)
{
AddCounter(name, PerformanceCounterType.RateOfCountsPerSecond64);
}
/// <summary>
/// Adds a AverageCount64 performance counter to the category collection.
/// </summary>
/// <remarks>
/// Also adds the required base counter.
/// </remarks>
/// <param name="name">Performance counter name.</param>
/// <param name="help">Performance counter help text.</param>
public void AddAverageCounter(string name, string help)
{
// add average counter
AddCounter(name, PerformanceCounterType.AverageCount64, help);
// add the corresponding base counter
AddCounter(name + "Base", PerformanceCounterType.AverageBase, null);
}
/// <summary>
/// Adds a AverageCount64 performance counter to the category collection.
/// </summary>
/// <remarks>
/// Also adds the required base counter.
/// </remarks>
/// <param name="name">Performance counter name.</param>
public void AddAverageCounter(string name)
{
AddAverageCounter(name, null);
}
/// <summary>
/// Adds a RawFraction performance counter to the category collection.
/// </summary>
/// <remarks>
/// Also adds the required base counter.
/// </remarks>
/// <param name="name">Performance counter name.</param>
/// <param name="help">Performance counter help text.</param>
public void AddFractionCounter(string name, string help)
{
// add average counter
AddCounter(name, PerformanceCounterType.RawFraction, help);
// add the corresponding base counter
AddCounter(name + "Base", PerformanceCounterType.RawBase, null);
}
/// <summary>
/// Adds a RawFraction performance counter to the category collection.
/// </summary>
/// <remarks>
/// Also adds the required base counter.
/// </remarks>
/// <param name="name">Performance counter name.</param>
public void AddFractionCounter(string name)
{
AddFractionCounter(name, null);
}
/// <summary>
/// Adds all performance counter for the specified Type to this category.
/// </summary>
/// <remarks>
/// Performance category name of the Type must match the one of this category instance.
/// </remarks>
/// <param name="type">Type</param>
public void AddCounters(Type type)
{
PerformanceCounterCategoryAttribute category = GetCategory(type);
if (category == null)
throw new ArgumentException(String.Format("Type {0} has no PerformanceCounterCategory attribute", type.Name), "type");
if (category.Name != this.categoryName)
throw new ArgumentException("Category names do not match");
_AddCounters(type);
}
/// <summary>
/// Indicates if the performance counter category exists.
/// </summary>
/// <remarks>
/// Indicates if the performance counter category exists.
/// </remarks>
/// <returns>True if it does exist, false otherwise.</returns>
public bool Exists()
{
return Exists(categoryName);
}
/// <summary>
/// Creates performance counter category and counters.
/// </summary>
/// <remarks>
/// If the category already exists only non existing counters are added.
/// </remarks>
public void Create()
{
if (!Exists(categoryName))
{
// category does not exist, create
PerformanceCounterCategory.Create(
categoryName,
categoryHelp != null ? categoryHelp : "",
categoryType,
counters);
}
}
/// <summary>
/// Deletes the performance counter category and counters.
/// </summary>
/// <remarks>
/// Deletes the performance counter category and counters.
/// </remarks>
public void Delete()
{
Delete(categoryName);
}
#endregion
#region Private Methods
/// <summary>
/// Gets the performance counter category attribute attached to the type.
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="type"></param>
/// <returns>The performance counter category attribute on success, null otherwise</returns>
private PerformanceCounterCategoryAttribute GetCategory(Type type)
{
// get category attribute
object[] attributes = type.GetCustomAttributes(typeof(PerformanceCounterCategoryAttribute), false);
// we don't have performance counter category, give up
if (attributes.Length < 1) return null;
return (PerformanceCounterCategoryAttribute)attributes[0];
}
/// <summary>
/// Populates the CounterCreationDataCollection for a given type.
/// </summary>
/// <param name="type">Type to search for performance counters.</param>
private void _AddCounters(Type type)
{
foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
{
// ignore member if it is not a performance counter
if (field.FieldType != typeof(PerformanceCounter)) continue;
// get the performance counter attribute
object[] attributes = field.GetCustomAttributes(typeof(PerformanceCounterAttribute), false);
// ignore it if it has no performance counter attribute set
if (attributes.Length < 1) continue;
// assign the performance counter
PerformanceCounterAttribute counter = (PerformanceCounterAttribute)attributes[0];
// only create a counter with multiple instances once
if (counter.Instance != string.Empty && Exists(counter))
continue;
AddCounter(counter.Name, counter.Type, counter.Help);
// create base counter if needed
switch (counter.Type)
{
case PerformanceCounterType.AverageCount64:
case PerformanceCounterType.AverageTimer32:
AddCounter(counter.Name + "Base", PerformanceCounterType.AverageBase);
break;
case PerformanceCounterType.RawFraction:
AddCounter(counter.Name + "Base", PerformanceCounterType.RawBase);
break;
case PerformanceCounterType.CounterMultiTimer:
case PerformanceCounterType.CounterMultiTimerInverse:
case PerformanceCounterType.CounterMultiTimer100Ns:
case PerformanceCounterType.CounterMultiTimer100NsInverse:
AddCounter(counter.Name + "Base", PerformanceCounterType.CounterMultiBase);
break;
case PerformanceCounterType.SampleCounter:
case PerformanceCounterType.SampleFraction:
AddCounter(counter.Name + "Base", PerformanceCounterType.SampleBase);
break;
default: break;
}
}
}
/// <summary>
/// Checks if a counter by the same name already exists in the counter creation data collection.
/// </summary>
/// <remarks>
/// Counter names must be unique within a category.
/// </remarks>
/// <param name="counter">Performance counter attribute.</param>
/// <returns><b>True</b> if the counter already exists, <b>false</b> otherwise.</returns>
private bool Exists(PerformanceCounterAttribute counter)
{
foreach (CounterCreationData creationData in counters)
{
if (creationData.CounterName == counter.Name)
return true;
}
return false;
}
#endregion
#region Static Methods
/// <summary>
/// Gets a performance counter instance for a given name and category.
/// </summary>
/// <remarks>
/// Gets a performance counter instance for a given name and category.
/// </remarks>
/// <param name="categoryName">Performance counter category.</param>
/// <param name="counterName">Performance counter name.</param>
/// <param name="instanceName">Performance counter instance name.</param>
/// <param name="readOnly">ReadOnly</param>
/// <returns>Performance counter instance.</returns>
public static PerformanceCounter GetCounter(string categoryName, string counterName, string instanceName, bool readOnly)
{
return new PerformanceCounter(categoryName, counterName, instanceName, readOnly);
}
/// <summary>
/// Checks if a performance counter category exists.
/// </summary>
/// <param name="categoryName">Performance counter category name.</param>
/// <returns>True if the category exists, false otherwise.</returns>
public static bool Exists(string categoryName)
{
return PerformanceCounterCategory.Exists(categoryName);
}
/// <summary>
/// Deletes the specified performance counter category and counters.
/// </summary>
/// <remarks>
/// Deletes the specified performance counter category and counters.
/// </remarks>
/// <param name="categoryName">Performance counter category name.</param>
public static void Delete(string categoryName)
{
PerformanceCounterCategory.Delete(categoryName);
}
/// <summary>
/// Creates instances for all performance counter members in the given type.
/// </summary>
/// <remarks>
/// The type must have the PerformanceCounterCategory attribute set. Each performance counter
/// member must be static and tagged with a PerformanceCounter attribute.
/// </remarks>
/// <param name="type">Type to instantiate counters</param>
/// <returns><b>True</b> if counters were created successfully, <b>false</b> otherwise.</returns>
public static bool CreateCounters(Type type)
{
return CreateCounters(type, null);
}
/// <summary>
/// Creates instances for all performance counter members in the given type.
/// </summary>
/// <remarks>
/// The type must have the PerformanceCounterCategory attribute set. Each performance counter
/// member must be static and tagged with a PerformanceCounter attribute.
/// </remarks>
/// <param name="type">Type to instantiate counters</param>
/// <param name="instance">Instance to assign performance counters to.</param>
/// <returns><b>True</b> if counters were created successfully, <b>false</b> otherwise.</returns>
public static bool CreateCounters(Type type, object instance)
{
Debug.Assert(instance == null || type == instance.GetType() || instance.GetType().IsSubclassOf(type));
// get category attribute
object[] attributes = type.GetCustomAttributes(typeof(PerformanceCounterCategoryAttribute), false);
// we don't have performance counter category, we are done
if (attributes.Length < 1) return false;
string categoryName = ((PerformanceCounterCategoryAttribute)attributes[0]).Name;
bool result = false;
try
{
if (PerformanceCounterCategory.Exists(categoryName))
{
// get the category type
PerformanceCounterCategory category = new PerformanceCounterCategory(categoryName);
PerformanceCounterCategoryType categoryType = category.CategoryType;
foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
{
// ignore member if it is not a performance counter or it is not static
if (field.FieldType != typeof(PerformanceCounter) || (instance == null && !field.IsStatic)) continue;
// get the performance counter attribute
attributes = field.GetCustomAttributes(typeof(PerformanceCounterAttribute), false);
// ignore it if it has no performance counter attribute set
if (attributes.Length < 1) continue;
// get the counter attribute data
PerformanceCounterAttribute counter = (PerformanceCounterAttribute)attributes[0];
// use a default instance name if the the counter does not have one and the category is marked MultiInstance
string instanceName = (counter.Instance == String.Empty && category.CategoryType == PerformanceCounterCategoryType.MultiInstance) ? "_default" : counter.Instance;
// assign the performance counter
field.SetValue(instance, new PerformanceCounter(categoryName, counter.Name, instanceName, false));
// create base counter if needed
switch (counter.Type)
{
case PerformanceCounterType.AverageCount64:
case PerformanceCounterType.RawFraction:
case PerformanceCounterType.SampleFraction:
FieldInfo baseCounter = type.GetField(field.Name + "Base", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
if (baseCounter != null)
baseCounter.SetValue(instance, new PerformanceCounter(categoryName, counter.Name + "Base", instanceName, false));
break;
default: break;
}
}
result = true;
}
}
catch (Exception)
{
result = false;
}
return result;
}
#endregion
}
}