e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
179 lines
7.1 KiB
C#
179 lines
7.1 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
namespace System.ServiceModel.Diagnostics
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.PerformanceData;
|
|
using System.Globalization;
|
|
using System.Runtime;
|
|
using System.Threading;
|
|
using System.Linq;
|
|
|
|
abstract class PerformanceCountersBase : IDisposable
|
|
{
|
|
internal abstract string InstanceName
|
|
{
|
|
get;
|
|
}
|
|
|
|
internal abstract string[] CounterNames
|
|
{
|
|
get;
|
|
}
|
|
|
|
internal abstract int PerfCounterStart
|
|
{
|
|
get;
|
|
}
|
|
|
|
internal abstract int PerfCounterEnd
|
|
{
|
|
get;
|
|
}
|
|
|
|
private static string GetInstanceNameWithHash(string instanceName, string fullInstanceName)
|
|
{
|
|
return String.Format("{0}{1}", instanceName, StringUtil.GetNonRandomizedHashCode(fullInstanceName).ToString("X", CultureInfo.InvariantCulture));
|
|
}
|
|
|
|
protected static string EnsureUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)
|
|
{
|
|
if (String.IsNullOrEmpty(categoryName))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("categoryName");
|
|
if (String.IsNullOrEmpty(instanceName))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("instanceName");
|
|
if (String.IsNullOrEmpty(fullInstanceName))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("fullInstanceName");
|
|
|
|
try
|
|
{
|
|
// If the instance name is already used, append a hash of the full name to it.
|
|
if (PerformanceCounterCategory.InstanceExists(instanceName, categoryName))
|
|
{
|
|
return GetInstanceNameWithHash(instanceName, fullInstanceName);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// If an exception is thrown, return the instance name without modification.
|
|
}
|
|
|
|
return instanceName;
|
|
}
|
|
|
|
protected static string GetUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)
|
|
{
|
|
if (String.IsNullOrEmpty(categoryName))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("categoryName");
|
|
if (String.IsNullOrEmpty(instanceName))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("instanceName");
|
|
if (String.IsNullOrEmpty(fullInstanceName))
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("fullInstanceName");
|
|
|
|
try
|
|
{
|
|
// If the instance name with the hash appended exists, return it.
|
|
string nameWithHash = GetInstanceNameWithHash(instanceName, fullInstanceName);
|
|
if (PerformanceCounterCategory.InstanceExists(nameWithHash, categoryName))
|
|
{
|
|
return nameWithHash;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// If an exception is thrown, return the instance name without modification.
|
|
}
|
|
|
|
return instanceName;
|
|
}
|
|
|
|
// remove count chars from string and add a 2 char hash code to beginning or end, as specified.
|
|
protected static string GetHashedString(string str, int startIndex, int count, bool hashAtEnd)
|
|
{
|
|
string returnVal = str.Remove(startIndex, count);
|
|
string hash = ((uint)StringUtil.GetNonRandomizedHashCode(str) % 99).ToString("00", CultureInfo.InvariantCulture);
|
|
return hashAtEnd ? returnVal + hash : hash + returnVal;
|
|
}
|
|
|
|
internal abstract bool Initialized { get; }
|
|
|
|
protected int disposed = 0;
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Interlocked.Exchange(ref disposed, 1) == 0)
|
|
{
|
|
Dispose(true);
|
|
}
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
}
|
|
|
|
// A CounterSetInstance is not disposed immediately when a service, endpoint or operation perf counter is disposed. Because messages
|
|
// can be processed while a ServiceHost is being closed, and such messages can try to update perf counters data, resulting in AVs or
|
|
// corruptions (see bug 249132 @ CSDMain). So instead of disposing a CounterSetInstance, we hold a WeakReference to it, until either
|
|
// GC reclaims it or a new service/endpoint/operation perf counter is started with the same name (and re-uses the CounterSetInstance).
|
|
// The CounterSetInstance finalizer will free up the perf counters memory, so we don't have a leak.
|
|
protected class CounterSetInstanceCache
|
|
{
|
|
// instance name -> WeakReference of CounterSetInstance
|
|
private readonly Dictionary<string, WeakReference> cache = new Dictionary<string, WeakReference>();
|
|
|
|
/// <summary>
|
|
/// Returns and removes the CounterSetInstance with the specified name from the cache. Returns null if not found.
|
|
/// </summary>
|
|
internal CounterSetInstance Get(string instanceName)
|
|
{
|
|
Fx.Assert(instanceName != null, "Invalid argument.");
|
|
|
|
lock (this.cache)
|
|
{
|
|
WeakReference wr;
|
|
if (this.cache.TryGetValue(instanceName, out wr))
|
|
{
|
|
Fx.Assert(wr != null, "The values in 'availableCounterSetInstances' should not be null.");
|
|
this.cache.Remove(instanceName);
|
|
return (CounterSetInstance)wr.Target;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a CounterSetInstance to the cache, from where it will be garbage collected or re-used by another performance counter (whichever occurs first).
|
|
/// </summary>
|
|
internal void Add(string instanceName, CounterSetInstance instance)
|
|
{
|
|
Fx.Assert(instanceName != null, "Invalid argument.");
|
|
Fx.Assert(instance != null, "Invalid argument.");
|
|
|
|
lock (this.cache)
|
|
{
|
|
this.cache[instanceName] = new WeakReference(instance);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the entries for CounterSetInstances that were garbage collected.
|
|
/// </summary>
|
|
internal void Cleanup()
|
|
{
|
|
lock (this.cache)
|
|
{
|
|
foreach (var entry in this.cache.Where(pair => !pair.Value.IsAlive).ToList())
|
|
{
|
|
this.cache.Remove(entry.Key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|