1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Diagnostics
5 {
6     using System.Collections.Generic;
7     using System.Diagnostics;
8     using System.Diagnostics.PerformanceData;
9     using System.Globalization;
10     using System.Runtime;
11     using System.Threading;
12     using System.Linq;
13 
14     abstract class PerformanceCountersBase : IDisposable
15     {
16         internal abstract string InstanceName
17         {
18             get;
19         }
20 
21         internal abstract string[] CounterNames
22         {
23             get;
24         }
25 
26         internal abstract int PerfCounterStart
27         {
28             get;
29         }
30 
31         internal abstract int PerfCounterEnd
32         {
33             get;
34         }
35 
GetInstanceNameWithHash(string instanceName, string fullInstanceName)36         private static string GetInstanceNameWithHash(string instanceName, string fullInstanceName)
37         {
38             return String.Format("{0}{1}", instanceName, StringUtil.GetNonRandomizedHashCode(fullInstanceName).ToString("X", CultureInfo.InvariantCulture));
39         }
40 
EnsureUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)41         protected static string EnsureUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)
42         {
43             if (String.IsNullOrEmpty(categoryName))
44                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("categoryName");
45             if (String.IsNullOrEmpty(instanceName))
46                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("instanceName");
47             if (String.IsNullOrEmpty(fullInstanceName))
48                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("fullInstanceName");
49 
50             try
51             {
52                 // If the instance name is already used, append a hash of the full name to it.
53                 if (PerformanceCounterCategory.InstanceExists(instanceName, categoryName))
54                 {
55                     return GetInstanceNameWithHash(instanceName, fullInstanceName);
56                 }
57             }
58             catch
59             {
60                 // If an exception is thrown, return the instance name without modification.
61             }
62 
63             return instanceName;
64         }
65 
GetUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)66         protected static string GetUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)
67         {
68             if (String.IsNullOrEmpty(categoryName))
69                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("categoryName");
70             if (String.IsNullOrEmpty(instanceName))
71                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("instanceName");
72             if (String.IsNullOrEmpty(fullInstanceName))
73                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("fullInstanceName");
74 
75             try
76             {
77                 // If the instance name with the hash appended exists, return it.
78                 string nameWithHash = GetInstanceNameWithHash(instanceName, fullInstanceName);
79                 if (PerformanceCounterCategory.InstanceExists(nameWithHash, categoryName))
80                 {
81                     return nameWithHash;
82                 }
83             }
84             catch
85             {
86                 // If an exception is thrown, return the instance name without modification.
87             }
88 
89             return instanceName;
90         }
91 
92         // remove count chars from string and add a 2 char hash code to beginning or end, as specified.
GetHashedString(string str, int startIndex, int count, bool hashAtEnd)93         protected static string GetHashedString(string str, int startIndex, int count, bool hashAtEnd)
94         {
95             string returnVal = str.Remove(startIndex, count);
96             string hash = ((uint)StringUtil.GetNonRandomizedHashCode(str) % 99).ToString("00", CultureInfo.InvariantCulture);
97             return hashAtEnd ? returnVal + hash : hash + returnVal;
98         }
99 
100         internal abstract bool Initialized { get; }
101 
102         protected int disposed = 0;
103 
Dispose()104         public void Dispose()
105         {
106             if (Interlocked.Exchange(ref disposed, 1) == 0)
107             {
108                 Dispose(true);
109             }
110         }
111 
Dispose(bool disposing)112         protected virtual void Dispose(bool disposing)
113         {
114         }
115 
116         // A CounterSetInstance is not disposed immediately when a service, endpoint or operation perf counter is disposed. Because messages
117         // can be processed while a ServiceHost is being closed, and such messages can try to update perf counters data, resulting in AVs or
118         // corruptions (see bug 249132 @ CSDMain). So instead of disposing a CounterSetInstance, we hold a WeakReference to it, until either
119         // GC reclaims it or a new service/endpoint/operation perf counter is started with the same name (and re-uses the CounterSetInstance).
120         // The CounterSetInstance finalizer will free up the perf counters memory, so we don't have a leak.
121         protected class CounterSetInstanceCache
122         {
123             // instance name -> WeakReference of CounterSetInstance
124             private readonly Dictionary<string, WeakReference> cache = new Dictionary<string, WeakReference>();
125 
126             /// <summary>
127             /// Returns and removes the CounterSetInstance with the specified name from the cache. Returns null if not found.
128             /// </summary>
Get(string instanceName)129             internal CounterSetInstance Get(string instanceName)
130             {
131                 Fx.Assert(instanceName != null, "Invalid argument.");
132 
133                 lock (this.cache)
134                 {
135                     WeakReference wr;
136                     if (this.cache.TryGetValue(instanceName, out wr))
137                     {
138                         Fx.Assert(wr != null, "The values in 'availableCounterSetInstances' should not be null.");
139                         this.cache.Remove(instanceName);
140                         return (CounterSetInstance)wr.Target;
141                     }
142                     else
143                     {
144                         return null;
145                     }
146                 }
147             }
148 
149             /// <summary>
150             /// Adds a CounterSetInstance to the cache, from where it will be garbage collected or re-used by another performance counter (whichever occurs first).
151             /// </summary>
Add(string instanceName, CounterSetInstance instance)152             internal void Add(string instanceName, CounterSetInstance instance)
153             {
154                 Fx.Assert(instanceName != null, "Invalid argument.");
155                 Fx.Assert(instance != null, "Invalid argument.");
156 
157                 lock (this.cache)
158                 {
159                     this.cache[instanceName] = new WeakReference(instance);
160                 }
161             }
162 
163             /// <summary>
164             /// Clear the entries for CounterSetInstances that were garbage collected.
165             /// </summary>
Cleanup()166             internal void Cleanup()
167             {
168                 lock (this.cache)
169                 {
170                     foreach (var entry in this.cache.Where(pair => !pair.Value.IsAlive).ToList())
171                     {
172                         this.cache.Remove(entry.Key);
173                     }
174                 }
175             }
176         }
177     }
178 }
179