Initial commit
[mono.git] / mcs / class / referencesource / System / services / monitoring / system / diagnosticts / SharedPerformanceCounter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SharedPerformanceCounter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Diagnostics {
8     using System;
9     using System.Text;
10     using System.Threading;
11     using System.Collections;
12     using System.Runtime.ConstrainedExecution;
13     using System.Runtime.CompilerServices;
14     using System.Runtime.InteropServices;
15     using System.Security.Permissions;
16     using System.Security;
17     using Microsoft.Win32;
18     using Microsoft.Win32.SafeHandles;
19     using System.Globalization;
20     using System.Security.Principal;
21     using System.Security.AccessControl;
22     using System.Collections.Generic;
23     using System.Runtime.Versioning;
24     
25     [HostProtection(Synchronization=true, SharedState=true)]
26     internal sealed class SharedPerformanceCounter {
27         private const int MaxSpinCount = 5000;
28         internal const int DefaultCountersFileMappingSize = 524288;
29         internal const int MaxCountersFileMappingSize = 33554432;
30         internal const int MinCountersFileMappingSize = 32768;
31         internal const int InstanceNameMaxLength = 127;
32         internal const int InstanceNameSlotSize = 256;
33         internal const string SingleInstanceName = "systemdiagnosticssharedsingleinstance";
34         internal const string DefaultFileMappingName = "netfxcustomperfcounters.1.0";
35         internal static readonly int SingleInstanceHashCode = GetWstrHashCode(SingleInstanceName);
36         private static Hashtable categoryDataTable = new Hashtable(StringComparer.Ordinal);
37         private static readonly int CategoryEntrySize = Marshal.SizeOf(typeof(CategoryEntry));
38         private static readonly int InstanceEntrySize = Marshal.SizeOf(typeof(InstanceEntry));
39         private static readonly int CounterEntrySize = Marshal.SizeOf(typeof(CounterEntry));
40         private static readonly int ProcessLifetimeEntrySize = Marshal.SizeOf(typeof(ProcessLifetimeEntry));
41         
42         private static long LastInstanceLifetimeSweepTick;
43         private const long InstanceLifetimeSweepWindow = 30*10000000; //ticks
44         private static volatile ProcessData procData;
45         
46         private static ProcessData ProcessData {
47             get {
48                 if (procData == null) {
49                     new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
50                     try {
51                         int pid = NativeMethods.GetCurrentProcessId();
52                         long startTime = -1;    
53     
54                         // Though we have asserted the required CAS permissions above, we may
55                         // still fail to query the process information if the user does not 
56                         // have the necessary process access rights or privileges.
57                         // This might be the case if the current process was started by a 
58                         // different user (primary token) than the current user
59                         // (impersonation token) that has less privilege/ACL rights.
60                         using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, false, pid)) {
61                             if (!procHandle.IsInvalid) {
62                                 long temp;
63                                 NativeMethods.GetProcessTimes(procHandle, out startTime, out temp, out temp, out temp);
64                             }
65                         }
66                         procData = new ProcessData(pid, startTime);
67                     }
68                     finally  {
69                         SecurityPermission.RevertAssert();
70                     }
71                 }
72                 return procData;
73             }
74         }
75
76         // InitialOffset is the offset in our global shared memory where we put the first CategoryEntry.  It needs to be 4 because in 
77         // v1.0 and v1.1 we used IntPtr.Size.  That creates potential side-by-side issues on 64 bit machines using WOW64.
78         // A v1.0 app running on WOW64 will assume the InitialOffset is 4.  A true 64 bit app on the same machine will assume
79         // the initial offset is 8. 
80         // However, using an offset of 4 means that our CounterEntry.Value is potentially misaligned.  This is why we have SetValue 
81         // and other methods which split CounterEntry.Value into two ints.  With separate shared memory blocks per
82         // category, we can fix this and always use an inital offset of 8. 
83         internal int InitialOffset = 4;
84         
85         private CategoryData categoryData;
86         private long baseAddress;
87         private unsafe CounterEntry* counterEntryPointer;
88         private string categoryName;
89         private int categoryNameHashCode;
90         private int thisInstanceOffset = -1;
91
92         internal SharedPerformanceCounter(string catName, string counterName, string instanceName) : 
93             this (catName, counterName, instanceName, PerformanceCounterInstanceLifetime.Global) { }
94
95         [ResourceExposure(ResourceScope.None)]
96         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
97         internal unsafe SharedPerformanceCounter(string catName, string counterName, string instanceName, PerformanceCounterInstanceLifetime lifetime) {
98             this.categoryName = catName;
99             this.categoryNameHashCode = GetWstrHashCode(categoryName);
100
101             categoryData = GetCategoryData();
102
103             // Check that the instance name isn't too long if we're using the new shared memory.  
104             // We allocate InstanceNameSlotSize bytes in the shared memory
105             if (categoryData.UseUniqueSharedMemory) {
106                 if (instanceName != null && instanceName.Length > InstanceNameMaxLength)
107                     throw new InvalidOperationException(SR.GetString(SR.InstanceNameTooLong));
108             }
109             else {
110                 if (lifetime != PerformanceCounterInstanceLifetime.Global)
111                     throw new InvalidOperationException(SR.GetString(SR.ProcessLifetimeNotValidInGlobal));
112             }
113
114             if (counterName != null && instanceName != null) {
115                 if (!categoryData.CounterNames.Contains(counterName))
116                     Debug.Assert(false, "Counter " + counterName + " does not exist in category " + catName);
117                 else
118                     this.counterEntryPointer = GetCounter(counterName, instanceName, categoryData.EnableReuse, lifetime);
119             }
120         }
121
122         private FileMapping FileView {
123             [ResourceExposure(ResourceScope.Machine)]
124             get {
125                 return categoryData.FileMapping;
126             }
127         }
128
129         internal unsafe long Value {
130             get {
131                 if (counterEntryPointer == null)
132                     return 0;
133
134                 return GetValue(this.counterEntryPointer);
135             }
136
137             set {
138                 if (counterEntryPointer == null)
139                     return;
140
141                 SetValue(this.counterEntryPointer, value);
142             }
143         }
144
145         private unsafe int CalculateAndAllocateMemory(int totalSize, out int alignmentAdjustment) {
146             int newOffset; 
147             int oldOffset;
148             alignmentAdjustment = 0;
149
150             Debug.Assert(!categoryData.UseUniqueSharedMemory, "We should never be calling CalculateAndAllocateMemory in the unique shared memory");
151
152             do {
153                 oldOffset = *((int *) baseAddress);
154                 // we need to verify the oldOffset before we start using it.  Otherwise someone could change
155                 // it to something bogus and we would write outside of the shared memory. 
156                 ResolveOffset(oldOffset, 0);
157                 
158                 newOffset = CalculateMemory(oldOffset, totalSize, out alignmentAdjustment);
159
160                 // In the default shared mem we need to make sure that the end address is also aligned.  This is because 
161                 // in v1.1/v1.0 we just assumed that the next free offset was always properly aligned. 
162                 int endAddressMod8 = (int) (baseAddress + newOffset) & 0x7;
163                 int endAlignmentAdjustment = (8 - endAddressMod8) & 0x7;
164                 newOffset += endAlignmentAdjustment;
165                     
166             } while (SafeNativeMethods.InterlockedCompareExchange((IntPtr)baseAddress, newOffset, oldOffset) != oldOffset);
167             
168             return oldOffset;
169         }            
170
171         private int CalculateMemory(int oldOffset, int totalSize, out int alignmentAdjustment) {
172             int newOffset = CalculateMemoryNoBoundsCheck(oldOffset, totalSize, out alignmentAdjustment);
173             
174             if (newOffset > FileView.FileMappingSize || newOffset < 0) {
175                 throw new InvalidOperationException(SR.GetString(SR.CountersOOM));
176             }
177
178             return newOffset;
179         }
180
181         [ResourceExposure(ResourceScope.None)]
182         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
183         private int CalculateMemoryNoBoundsCheck(int oldOffset, int totalSize, out int alignmentAdjustment) {
184             int currentTotalSize = totalSize;
185             
186             Thread.MemoryBarrier();
187             
188             // make sure the start address is 8 byte aligned
189             int startAddressMod8 = (int) (baseAddress + oldOffset) & 0x7;
190             alignmentAdjustment = (8 - startAddressMod8) & 0x7;
191             currentTotalSize = currentTotalSize + alignmentAdjustment;
192             
193             int newOffset = oldOffset + currentTotalSize;
194             
195             return newOffset;
196         }
197         
198         private unsafe int CreateCategory(CategoryEntry* lastCategoryPointer, 
199                                             int instanceNameHashCode, string instanceName, 
200                                             PerformanceCounterInstanceLifetime lifetime) {
201             int categoryNameLength;
202             int instanceNameLength;
203             int alignmentAdjustment;
204             int freeMemoryOffset;
205             int newOffset = 0;
206             int totalSize;
207
208             categoryNameLength = (categoryName.Length + 1) * 2;
209             totalSize = CategoryEntrySize + InstanceEntrySize + (CounterEntrySize * categoryData.CounterNames.Count) + categoryNameLength;
210             for (int i=0; i<categoryData.CounterNames.Count; i++) {
211                 totalSize += (((string)categoryData.CounterNames[i]).Length + 1) * 2;
212             }
213
214             if (categoryData.UseUniqueSharedMemory) {
215                 instanceNameLength = InstanceNameSlotSize;
216                 totalSize += ProcessLifetimeEntrySize + instanceNameLength;
217
218                 // If we're in a separate shared memory, we need to do a two stage update of the free memory pointer.
219                 // First we calculate our alignment adjustment and where the new free offset is.  Then we 
220                 // write the new structs and data.  The last two operations are to link the new structs into the 
221                 // existing ones and update the next free offset.  Our process could get killed in between those two,
222                 // leaving the memory in an inconsistent state.  We use the "IsConsistent" flag to help determine 
223                 // when that has happened. 
224                 freeMemoryOffset = *((int *) baseAddress);
225                 newOffset = CalculateMemory(freeMemoryOffset, totalSize, out alignmentAdjustment);
226
227                 if (freeMemoryOffset == InitialOffset)
228                     lastCategoryPointer->IsConsistent = 0;
229             }
230             else {
231                 instanceNameLength = (instanceName.Length +1) * 2;
232                 totalSize += instanceNameLength;
233                 freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment);
234             }
235
236             long nextPtr = ResolveOffset(freeMemoryOffset, totalSize + alignmentAdjustment);
237             
238             CategoryEntry* newCategoryEntryPointer;
239             InstanceEntry* newInstanceEntryPointer;
240             // We need to decide where to put the padding returned in alignmentAdjustment.  There are several things that
241             // need to be aligned.  First, we need to align each struct on a 4 byte boundary so we can use interlocked 
242             // operations on the int Spinlock field.  Second, we need to align the CounterEntry on an 8 byte boundary so that
243             // on 64 bit platforms we can use interlocked operations on the Value field.  alignmentAdjustment guarantees 8 byte
244             // alignemnt, so we use that for both.  If we're creating the very first category, however, we can't move that 
245             // CategoryEntry.  In this case we put the alignmentAdjustment before the InstanceEntry. 
246             if (freeMemoryOffset == InitialOffset) {
247                 newCategoryEntryPointer = (CategoryEntry*) nextPtr;
248                 nextPtr += CategoryEntrySize + alignmentAdjustment;
249                 newInstanceEntryPointer = (InstanceEntry*) nextPtr;
250             }
251             else {
252                 nextPtr += alignmentAdjustment;
253                 newCategoryEntryPointer = (CategoryEntry*) nextPtr;
254                 nextPtr += CategoryEntrySize;
255                 newInstanceEntryPointer = (InstanceEntry*) nextPtr;
256             }
257             nextPtr += InstanceEntrySize;
258
259             // create the first CounterEntry and reserve space for all of the rest.  We won't 
260             // finish creating them until the end
261             CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr;
262             nextPtr += CounterEntrySize * categoryData.CounterNames.Count;
263
264             if (categoryData.UseUniqueSharedMemory) {
265                 ProcessLifetimeEntry* newLifetimeEntry = (ProcessLifetimeEntry*) nextPtr;
266                 nextPtr += ProcessLifetimeEntrySize;
267                 
268                 newCounterEntryPointer->LifetimeOffset = (int)((long)newLifetimeEntry - baseAddress);
269                 PopulateLifetimeEntry(newLifetimeEntry, lifetime);
270             }
271
272             newCategoryEntryPointer->CategoryNameHashCode = categoryNameHashCode;
273             newCategoryEntryPointer->NextCategoryOffset = 0;
274             newCategoryEntryPointer->FirstInstanceOffset = (int)((long)newInstanceEntryPointer - baseAddress);
275             newCategoryEntryPointer->CategoryNameOffset = (int) (nextPtr - baseAddress);
276             SafeMarshalCopy(categoryName, (IntPtr)nextPtr);
277             nextPtr += categoryNameLength;
278
279             newInstanceEntryPointer->InstanceNameHashCode = instanceNameHashCode;
280             newInstanceEntryPointer->NextInstanceOffset = 0;
281             newInstanceEntryPointer->FirstCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
282             newInstanceEntryPointer->RefCount = 1;
283             newInstanceEntryPointer->InstanceNameOffset = (int) (nextPtr - baseAddress);
284             SafeMarshalCopy(instanceName, (IntPtr)nextPtr);
285             nextPtr += instanceNameLength;
286
287             string counterName = (string) categoryData.CounterNames[0];
288             newCounterEntryPointer->CounterNameHashCode = GetWstrHashCode(counterName);
289             SetValue(newCounterEntryPointer, 0);
290             newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
291             SafeMarshalCopy(counterName, (IntPtr)nextPtr);
292             nextPtr += (counterName.Length + 1) * 2;
293                 
294             CounterEntry* previousCounterEntryPointer;
295             for (int i=1; i<categoryData.CounterNames.Count; i++) {
296                 previousCounterEntryPointer = newCounterEntryPointer;
297                 counterName = (string) categoryData.CounterNames[i];
298                 
299                 newCounterEntryPointer++;
300                 newCounterEntryPointer->CounterNameHashCode = GetWstrHashCode(counterName);
301                 SetValue(newCounterEntryPointer, 0);
302                 newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
303                 SafeMarshalCopy(counterName, (IntPtr)nextPtr);
304
305                 nextPtr += (counterName.Length + 1) * 2;
306                 previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
307             }
308             
309             Debug.Assert(nextPtr - baseAddress == freeMemoryOffset + totalSize + alignmentAdjustment, "We should have used all of the space we requested at this point");
310             
311             int offset = (int) ((long) newCategoryEntryPointer - baseAddress);
312             lastCategoryPointer->IsConsistent = 0;
313             // If not the first category node, link it.
314             if (offset != InitialOffset)
315                 lastCategoryPointer->NextCategoryOffset = offset;
316
317             if (categoryData.UseUniqueSharedMemory) {
318                 *((int*) baseAddress) = newOffset;
319                 lastCategoryPointer->IsConsistent = 1;
320             }
321             return offset;
322         }
323
324         private unsafe int CreateInstance(CategoryEntry* categoryPointer, 
325                                             int instanceNameHashCode, string instanceName, 
326                                             PerformanceCounterInstanceLifetime lifetime) {
327             int instanceNameLength;
328             int totalSize = InstanceEntrySize +  (CounterEntrySize * categoryData.CounterNames.Count);
329             int alignmentAdjustment;
330             int freeMemoryOffset;
331             int newOffset = 0;
332
333
334             if (categoryData.UseUniqueSharedMemory) {
335                 instanceNameLength = InstanceNameSlotSize;
336                 totalSize += ProcessLifetimeEntrySize + instanceNameLength;
337
338                 // If we're in a separate shared memory, we need to do a two stage update of the free memory pointer.
339                 // First we calculate our alignment adjustment and where the new free offset is.  Then we 
340                 // write the new structs and data.  The last two operations are to link the new structs into the 
341                 // existing ones and update the next free offset.  Our process could get killed in between those two,
342                 // leaving the memory in an inconsistent state.  We use the "IsConsistent" flag to help determine 
343                 // when that has happened. 
344                 freeMemoryOffset = *((int *) baseAddress);
345                 newOffset = CalculateMemory(freeMemoryOffset, totalSize, out alignmentAdjustment);
346             }
347             else {
348                 instanceNameLength = (instanceName.Length +1) * 2;
349                 totalSize += instanceNameLength;
350
351                 // add in the counter names for the global shared mem.
352                 for (int i=0; i<categoryData.CounterNames.Count; i++) {
353                     totalSize += (((string)categoryData.CounterNames[i]).Length + 1) * 2;
354                 }
355                 freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment);
356             }
357
358             freeMemoryOffset += alignmentAdjustment;
359             long nextPtr = ResolveOffset(freeMemoryOffset, totalSize);    // don't add alignmentAdjustment since it's already
360                                                                           // been added to freeMemoryOffset 
361
362             InstanceEntry* newInstanceEntryPointer = (InstanceEntry*) nextPtr;
363             nextPtr += InstanceEntrySize;
364
365             // create the first CounterEntry and reserve space for all of the rest.  We won't 
366             // finish creating them until the end
367             CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr;
368             nextPtr += CounterEntrySize * categoryData.CounterNames.Count;
369             
370             if (categoryData.UseUniqueSharedMemory) {
371                 ProcessLifetimeEntry* newLifetimeEntry = (ProcessLifetimeEntry*) nextPtr;
372                 nextPtr += ProcessLifetimeEntrySize;
373                 
374                 newCounterEntryPointer->LifetimeOffset = (int)((long)newLifetimeEntry - baseAddress);
375                 PopulateLifetimeEntry(newLifetimeEntry, lifetime);
376             }
377
378             // set up the InstanceEntry
379             newInstanceEntryPointer->InstanceNameHashCode = instanceNameHashCode;
380             newInstanceEntryPointer->NextInstanceOffset = 0;
381             newInstanceEntryPointer->FirstCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
382             newInstanceEntryPointer->RefCount = 1;
383             newInstanceEntryPointer->InstanceNameOffset = (int) (nextPtr - baseAddress);
384             SafeMarshalCopy(instanceName, (IntPtr)nextPtr);
385
386             nextPtr += instanceNameLength;
387
388
389             if (categoryData.UseUniqueSharedMemory) {
390                 // in the unique shared mem we'll assume that the CounterEntries of the first instance
391                 // are all created.  Then we can just refer to the old counter name rather than copying in a new one.
392                 InstanceEntry* firstInstanceInCategoryPointer = (InstanceEntry*) ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize);
393                 CounterEntry* firstCounterInCategoryPointer = (CounterEntry*) ResolveOffset(firstInstanceInCategoryPointer->FirstCounterOffset, CounterEntrySize);
394                 newCounterEntryPointer->CounterNameHashCode = firstCounterInCategoryPointer->CounterNameHashCode;
395                 SetValue(newCounterEntryPointer, 0);
396                 newCounterEntryPointer->CounterNameOffset = firstCounterInCategoryPointer->CounterNameOffset;
397
398                 // now create the rest of the CounterEntrys
399                 CounterEntry* previousCounterEntryPointer;
400                 for (int i=1; i<categoryData.CounterNames.Count; i++) {
401                     previousCounterEntryPointer = newCounterEntryPointer;
402                     
403                     newCounterEntryPointer++;
404                     Debug.Assert(firstCounterInCategoryPointer->NextCounterOffset != 0, "The unique shared memory should have all of its counters created by the time we hit CreateInstance");
405                     firstCounterInCategoryPointer = (CounterEntry*) ResolveOffset(firstCounterInCategoryPointer->NextCounterOffset, CounterEntrySize);
406                     newCounterEntryPointer->CounterNameHashCode = firstCounterInCategoryPointer->CounterNameHashCode;
407                     SetValue(newCounterEntryPointer, 0);
408                     newCounterEntryPointer->CounterNameOffset = firstCounterInCategoryPointer->CounterNameOffset;
409
410                     previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
411                 }
412             }
413             else {
414                 // now create the rest of the CounterEntrys
415                 CounterEntry* previousCounterEntryPointer = null;
416                 for (int i=0; i<categoryData.CounterNames.Count; i++) {
417                     string counterName = (string) categoryData.CounterNames[i];
418                     newCounterEntryPointer->CounterNameHashCode = GetWstrHashCode(counterName);
419                     newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
420                     SafeMarshalCopy(counterName, (IntPtr)nextPtr);
421                     nextPtr += (counterName.Length + 1) * 2;
422
423                     SetValue(newCounterEntryPointer, 0);
424
425                     if (i != 0)
426                         previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
427
428                     previousCounterEntryPointer = newCounterEntryPointer;
429                     newCounterEntryPointer++;
430                 }
431             }
432             
433             Debug.Assert(nextPtr - baseAddress == freeMemoryOffset + totalSize, "We should have used all of the space we requested at this point");
434
435             int offset = (int) ((long) newInstanceEntryPointer - baseAddress);
436             categoryPointer->IsConsistent = 0;
437             
438             // prepend the new instance rather than append, helps with perf of hooking up subsequent counters 
439             newInstanceEntryPointer->NextInstanceOffset = categoryPointer->FirstInstanceOffset;
440             categoryPointer->FirstInstanceOffset = offset;
441
442             if (categoryData.UseUniqueSharedMemory) {
443                 *((int*) baseAddress) = newOffset;
444                 categoryPointer->IsConsistent = 1;
445             }
446
447             return freeMemoryOffset;
448         }
449
450         private unsafe int CreateCounter(CounterEntry* lastCounterPointer, 
451                                            int counterNameHashCode, string counterName) {
452             int counterNameLength = (counterName.Length + 1) * 2;
453             int totalSize = sizeof(CounterEntry) + counterNameLength;
454             int alignmentAdjustment;
455             int freeMemoryOffset;
456
457             Debug.Assert(!categoryData.UseUniqueSharedMemory, "We should never be calling CreateCounter in the unique shared memory");
458             freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment);
459
460             freeMemoryOffset += alignmentAdjustment;
461
462             long nextPtr = ResolveOffset(freeMemoryOffset, totalSize);
463             CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr;
464             nextPtr += sizeof(CounterEntry);
465
466             newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
467             newCounterEntryPointer->CounterNameHashCode = counterNameHashCode;
468             newCounterEntryPointer->NextCounterOffset = 0;
469             SetValue(newCounterEntryPointer, 0);
470             SafeMarshalCopy(counterName, (IntPtr)nextPtr);
471
472             Debug.Assert(nextPtr + counterNameLength - baseAddress == freeMemoryOffset + totalSize, "We should have used all of the space we requested at this point");
473
474             lastCounterPointer->NextCounterOffset = (int) ((long) newCounterEntryPointer - baseAddress);
475             return freeMemoryOffset;
476         }
477
478
479         [ResourceExposure(ResourceScope.None)]
480         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
481         private unsafe static void PopulateLifetimeEntry(ProcessLifetimeEntry *lifetimeEntry, PerformanceCounterInstanceLifetime lifetime) {
482
483             if (lifetime == PerformanceCounterInstanceLifetime.Process) {
484
485                 lifetimeEntry->LifetimeType = (int) PerformanceCounterInstanceLifetime.Process;
486                 lifetimeEntry->ProcessId = ProcessData.ProcessId;
487                 lifetimeEntry->StartupTime = ProcessData.StartupTime;
488             }
489             else {
490                 lifetimeEntry->ProcessId = 0;
491                 lifetimeEntry->StartupTime = 0;
492             }
493         }
494
495
496         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
497         private static unsafe void WaitAndEnterCriticalSection(int* spinLockPointer, out bool taken) {
498             WaitForCriticalSection(spinLockPointer);
499             
500             // Note - we are taking a lock here, but it probably isn't 
501             // worthwhile to use Thread.BeginCriticalRegion & EndCriticalRegion.
502             // These only really help the CLR escalate from a thread abort
503             // to an appdomain unload, under the assumption that you may be
504             // editing shared state within the appdomain.  Here you are editing
505             // shared state, but it is shared across processes.  Unloading the
506             // appdomain isn't exactly helping.  The only thing that would help
507             // would be if the CLR tells the host to ensure all allocations 
508             // have a higher chance of succeeding within this critical region,
509             // but of course that's only a probabilisitic statement.
510
511             // Must be able to assign to the out param.
512             RuntimeHelpers.PrepareConstrainedRegions();
513             try {
514             }
515             finally {
516                 int r = Interlocked.CompareExchange(ref *spinLockPointer, 1, 0);
517                 taken = (r == 0);
518             }
519         }
520
521         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
522         private static unsafe void WaitForCriticalSection(int* spinLockPointer) {
523             int spinCount = MaxSpinCount;
524             for (; spinCount > 0 && *spinLockPointer != 0; spinCount--) {
525                 // We suspect there are scenarios where the finalizer thread
526                 // will call this method.  The finalizer thread runs with 
527                 // a higher priority than the other code.  Using SpinWait
528                 // isn't sufficient, since it only spins, but doesn't yield
529                 // to any lower-priority threads.  Call Thread.Sleep(1).
530                 if (*spinLockPointer != 0)
531                     Thread.Sleep(1);
532             }
533
534             // if the lock still isn't free, most likely there's a deadlock caused by a process
535             // getting killed while it held the lock.  We'll just free the lock
536             if (spinCount == 0 && *spinLockPointer != 0)
537                 *spinLockPointer = 0;
538         }
539
540         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
541         private static unsafe void ExitCriticalSection(int* spinLockPointer) {
542             *spinLockPointer = 0;
543         }
544
545         // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
546         // This hashcode function is identical to the one in SharedPerformanceCounter.cpp.  If
547         // you change one without changing the other, perfcounters will break.
548         internal static int GetWstrHashCode(string wstr)
549         {
550             uint hash = 5381;
551             for(uint i=0; i < wstr.Length; i++)
552                 hash = ((hash << 5) + hash) ^ wstr[(int) i];
553             return (int)hash;
554         }
555
556         // Calculate the length of a string in the shared memory.  If we reach the end of the shared memory 
557         // before we see a null terminator, we throw.
558         [ResourceExposure(ResourceScope.None)]
559         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
560         private unsafe int GetStringLength(char* startChar) {
561             char* currentChar = startChar;
562             ulong endAddress = (ulong) (baseAddress + FileView.FileMappingSize);
563
564             while((ulong) currentChar < (endAddress - 2)) {
565                 if (*currentChar == 0)
566                     return (int) (currentChar - startChar);
567                 
568                 currentChar++;
569             }
570
571             throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
572         }
573
574         // Compare a managed string to a string located at a given offset.  If we walk past the end of the 
575         // shared memory, we throw. 
576         [ResourceExposure(ResourceScope.None)]
577         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
578         private unsafe bool StringEquals(string stringA, int offset) {
579             char* currentChar = (char*) ResolveOffset(offset, 0);
580             ulong endAddress = (ulong) (baseAddress + FileView.FileMappingSize);
581             
582             int i;
583             for (i=0; i<stringA.Length; i++) {
584                 if ((ulong) (currentChar+i) > (endAddress - 2))
585                     throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
586
587                 if (stringA[i] != currentChar[i])
588                     return false;
589             }
590
591             // now check for the null termination. 
592             if ((ulong) (currentChar+i) > (endAddress - 2))
593                 throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
594
595             return (currentChar[i] == 0);
596         }
597         
598
599         [ResourceExposure(ResourceScope.None)]  // Memory maps the perf counter data file
600         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
601         private unsafe CategoryData GetCategoryData() {
602             CategoryData data = (CategoryData) categoryDataTable[categoryName];
603             
604             if (data == null) {
605                 lock(categoryDataTable) {
606                     data = (CategoryData) categoryDataTable[categoryName];
607                     if (data == null) {
608                         data = new CategoryData();
609                         data.FileMappingName = DefaultFileMappingName;
610                         data.MutexName = categoryName;
611
612                         RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
613                         registryPermission.Assert();
614                         RegistryKey categoryKey = null;
615                         try {
616                             categoryKey = Registry.LocalMachine.OpenSubKey(PerformanceCounterLib.ServicePath + "\\" + categoryName + "\\Performance");
617
618                             // first read the options
619                             Object optionsObject = categoryKey.GetValue("CategoryOptions");
620                             if (optionsObject != null) {
621                                 int options = (int) optionsObject;
622                                 data.EnableReuse = (((PerformanceCounterCategoryOptions) options & PerformanceCounterCategoryOptions.EnableReuse) != 0);
623                         
624                                 if (((PerformanceCounterCategoryOptions) options & PerformanceCounterCategoryOptions.UseUniqueSharedMemory) != 0) {
625                                     data.UseUniqueSharedMemory = true;
626                                     InitialOffset = 8;
627                                     data.FileMappingName = DefaultFileMappingName + categoryName;
628                                 }
629                             }
630
631                             int fileMappingSize;
632                             object fileMappingSizeObject = categoryKey.GetValue("FileMappingSize");
633                             if (fileMappingSizeObject != null && data.UseUniqueSharedMemory) {
634                                 // we only use this reg value in the unique shared memory case. 
635                                 fileMappingSize = (int) fileMappingSizeObject;
636                                 if (fileMappingSize < MinCountersFileMappingSize)
637                                     fileMappingSize = MinCountersFileMappingSize;
638                                                         
639                                 if (fileMappingSize > MaxCountersFileMappingSize)
640                                     fileMappingSize = MaxCountersFileMappingSize;
641                             }
642                             else {
643                                 fileMappingSize = GetFileMappingSizeFromConfig(); 
644                                 if (data.UseUniqueSharedMemory)
645                                     fileMappingSize = fileMappingSize >> 2;  // if we have a custom filemapping, only make it 25% as large. 
646                             }
647
648                             // now read the counter names
649                             object counterNamesObject = categoryKey.GetValue("Counter Names");
650                             byte[] counterNamesBytes = counterNamesObject as byte[];
651                             
652                             if (counterNamesBytes != null) {
653                                 ArrayList names = new ArrayList();
654                                 fixed (byte* counterNamesPtr = counterNamesBytes) {
655                                     int start = 0;
656                                     for (int i=0; i<counterNamesBytes.Length-1; i+=2) {
657                                         if (counterNamesBytes[i] == 0 && counterNamesBytes[i+1] == 0 && start != i) {
658                                             string counter = new String((sbyte*)counterNamesPtr, start, i-start, Encoding.Unicode);
659                                             names.Add(counter.ToLowerInvariant());
660                                             start = i+2;
661                                         }
662                                     }
663                                 }
664                                 data.CounterNames = names;
665                             }
666                             else {
667                                 string[] counterNames = (string[]) counterNamesObject;
668                                 for (int i=0; i<counterNames.Length; i++) 
669                                     counterNames[i] = counterNames[i].ToLowerInvariant();
670                                 data.CounterNames = new ArrayList(counterNames);
671                             }
672
673                             // figure out the shared memory name
674                             if (SharedUtils.CurrentEnvironment == SharedUtils.W2kEnvironment) {
675                                 data.FileMappingName = "Global\\" +  data.FileMappingName;
676                                 data.MutexName = "Global\\" + categoryName;
677                             }
678
679                             data.FileMapping = new FileMapping(data.FileMappingName, fileMappingSize, InitialOffset);
680                             categoryDataTable[categoryName] = data;
681                         }
682                         finally {
683                             if (categoryKey != null)
684                                 categoryKey.Close();
685                             RegistryPermission.RevertAssert();
686                         }
687                     }
688                 }
689             }
690             baseAddress = (long) data.FileMapping.FileViewAddress;
691
692             if (data.UseUniqueSharedMemory)
693                 InitialOffset = 8;
694                                     
695
696             return data;
697         }
698
699         [MethodImpl(MethodImplOptions.NoInlining)]
700         private static int GetFileMappingSizeFromConfig() {
701             return DiagnosticsConfiguration.PerfomanceCountersFileMappingSize;
702         }
703
704         private static void RemoveCategoryData(string categoryName) {
705             lock (categoryDataTable) {
706                 categoryDataTable.Remove(categoryName);
707             }
708         }
709
710         [ResourceExposure(ResourceScope.None)]
711         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
712         private unsafe CounterEntry* GetCounter(string counterName, string instanceName, bool enableReuse, PerformanceCounterInstanceLifetime lifetime) {
713             int counterNameHashCode = GetWstrHashCode(counterName);
714             int instanceNameHashCode;
715             if (instanceName != null && instanceName.Length != 0)
716                 instanceNameHashCode = GetWstrHashCode(instanceName);
717             else {
718                 instanceNameHashCode = SingleInstanceHashCode;
719                 instanceName = SingleInstanceName;
720             }
721
722             Mutex mutex = null;
723             CounterEntry* counterPointer = null;
724             InstanceEntry* instancePointer = null;
725             RuntimeHelpers.PrepareConstrainedRegions();
726             try {
727                 SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);
728                 CategoryEntry* categoryPointer;
729                 bool counterFound = false;
730                 while (!FindCategory(&categoryPointer)) {
731                     // don't bother locking again if we're using a separate shared memory.  
732                     bool sectionEntered;
733                     if (categoryData.UseUniqueSharedMemory)
734                         sectionEntered = true;
735                     else
736                         WaitAndEnterCriticalSection(&(categoryPointer->SpinLock), out sectionEntered);
737
738                     int newCategoryOffset;
739                     if (sectionEntered) {
740                         try {
741                             newCategoryOffset = CreateCategory(categoryPointer, instanceNameHashCode, instanceName, lifetime);
742                         }
743                         finally {
744                             if (!categoryData.UseUniqueSharedMemory)
745                                 ExitCriticalSection(&(categoryPointer->SpinLock));
746                         }
747
748                         categoryPointer = (CategoryEntry*)(ResolveOffset(newCategoryOffset, CategoryEntrySize));
749                         instancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize));
750                         counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer);
751                         Debug.Assert(counterFound, "All counters should be created, so we should always find the counter");
752                         return counterPointer;
753                     }
754                 }
755
756                 bool foundFreeInstance;
757                 while (!FindInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, true, lifetime, out foundFreeInstance)) {
758                     InstanceEntry* lockInstancePointer = instancePointer;
759                     
760                     // don't bother locking again if we're using a separate shared memory.  
761                     bool sectionEntered;
762                     if (categoryData.UseUniqueSharedMemory)
763                         sectionEntered = true;
764                     else
765                         WaitAndEnterCriticalSection(&(lockInstancePointer->SpinLock), out sectionEntered);
766                     
767                     if (sectionEntered) {
768                         try {
769                             bool reused = false;
770
771                             if (enableReuse && foundFreeInstance) {
772                                 reused = TryReuseInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, lifetime, lockInstancePointer);
773                                 // at this point we might have reused an instance that came from v1.1/v1.0.  We can't assume it will have the counter
774                                 // we're looking for. 
775                             }
776
777                             if (!reused) {
778                                 int newInstanceOffset = CreateInstance(categoryPointer, instanceNameHashCode, instanceName, lifetime);
779                                 instancePointer = (InstanceEntry*)(ResolveOffset(newInstanceOffset, InstanceEntrySize));
780
781                                 counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer);
782                                 Debug.Assert(counterFound, "All counters should be created, so we should always find the counter");
783                                 return counterPointer;
784                             }
785                         }
786                         finally {
787                             if (!categoryData.UseUniqueSharedMemory)
788                                 ExitCriticalSection(&(lockInstancePointer->SpinLock));
789                         }
790                     }
791                 }
792
793                 if (categoryData.UseUniqueSharedMemory) {
794                     counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer);
795                     Debug.Assert(counterFound, "All counters should be created, so we should always find the counter");
796                     return counterPointer;
797                 }
798                 else {
799                     while (!FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer)) {
800                         bool sectionEntered;
801                         WaitAndEnterCriticalSection(&(counterPointer->SpinLock), out sectionEntered);
802                         
803                         if (sectionEntered) {
804                             try {
805                                 int newCounterOffset =  CreateCounter(counterPointer, counterNameHashCode, counterName);
806                                 return (CounterEntry*) (ResolveOffset(newCounterOffset, CounterEntrySize));
807                             }
808                             finally {
809                                 ExitCriticalSection(&(counterPointer->SpinLock));
810                             }
811                         }
812                     }
813                     
814                     return counterPointer;
815                 }
816             }
817             finally {
818                 // cache this instance for reuse 
819                 try {
820                     if (counterPointer != null && instancePointer != null) {
821                         this.thisInstanceOffset = ResolveAddress((long)instancePointer, InstanceEntrySize);
822                     }
823                 }
824                 catch (InvalidOperationException) {
825                     this.thisInstanceOffset = -1;
826                 }
827
828                 if (mutex != null) {
829                     mutex.ReleaseMutex();
830                     mutex.Close();
831                 }
832             }
833         }
834
835         //
836         // FindCategory -
837         //
838         // * when the function returns true the returnCategoryPointerReference is set to the CategoryEntry
839         //   that matches 'categoryNameHashCode' and 'categoryName'
840         //
841         // * when the function returns false the returnCategoryPointerReference is set to the last CategoryEntry
842         //   in the linked list
843         // 
844         private unsafe bool FindCategory(CategoryEntry** returnCategoryPointerReference) {
845             CategoryEntry* firstCategoryPointer =  (CategoryEntry*)(ResolveOffset(InitialOffset, CategoryEntrySize));
846             CategoryEntry* currentCategoryPointer = firstCategoryPointer;
847             CategoryEntry* previousCategoryPointer = firstCategoryPointer;
848
849             for(;;) {
850                 if (currentCategoryPointer->IsConsistent == 0)
851                     Verify(currentCategoryPointer);
852                 
853                 if (currentCategoryPointer->CategoryNameHashCode == categoryNameHashCode) {
854                     if (StringEquals(categoryName,  currentCategoryPointer->CategoryNameOffset)) {
855                         *returnCategoryPointerReference = currentCategoryPointer;
856                         return true;
857                     }
858                 }
859
860                 previousCategoryPointer = currentCategoryPointer;
861                 if (currentCategoryPointer->NextCategoryOffset != 0)
862                     currentCategoryPointer = (CategoryEntry*)(ResolveOffset(currentCategoryPointer->NextCategoryOffset, CategoryEntrySize));
863                 else {
864                     *returnCategoryPointerReference = previousCategoryPointer;
865                     return false;
866                 }
867             }
868         }
869
870         private unsafe bool FindCounter(int counterNameHashCode, string counterName, InstanceEntry* instancePointer, CounterEntry** returnCounterPointerReference) {
871             CounterEntry* currentCounterPointer = (CounterEntry*)(ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize));
872             CounterEntry* previousCounterPointer = currentCounterPointer;
873             for(;;) {
874                 if (currentCounterPointer->CounterNameHashCode == counterNameHashCode) {
875                     if (StringEquals(counterName, currentCounterPointer->CounterNameOffset)) {
876                         *returnCounterPointerReference = currentCounterPointer;
877                         return true;
878                     }
879                 }
880
881                 previousCounterPointer = currentCounterPointer;
882                 if (currentCounterPointer->NextCounterOffset != 0)
883                     currentCounterPointer = (CounterEntry*)(ResolveOffset(currentCounterPointer->NextCounterOffset, CounterEntrySize));
884                 else {
885                     *returnCounterPointerReference = previousCounterPointer;
886                     return false;
887                 }
888             }
889         }
890
891         [ResourceExposure(ResourceScope.None)]
892         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
893         private unsafe bool FindInstance(int instanceNameHashCode, string instanceName, 
894                                            CategoryEntry* categoryPointer, InstanceEntry** returnInstancePointerReference, 
895                                            bool activateUnusedInstances, PerformanceCounterInstanceLifetime lifetime, 
896                                            out bool foundFreeInstance) {
897
898             InstanceEntry* currentInstancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize));
899             InstanceEntry* previousInstancePointer = currentInstancePointer;
900             foundFreeInstance = false;
901             // Look at the first instance to determine if this is single or multi instance. 
902             if (currentInstancePointer->InstanceNameHashCode == SingleInstanceHashCode) {
903                 if (StringEquals(SingleInstanceName, currentInstancePointer->InstanceNameOffset)){
904                     if (instanceName != SingleInstanceName)
905                         throw new InvalidOperationException(SR.GetString(SR.SingleInstanceOnly, categoryName));
906                 }
907                 else {
908                     if (instanceName == SingleInstanceName)
909                         throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, categoryName));
910                 }
911             }
912             else {
913                 if (instanceName == SingleInstanceName)
914                     throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, categoryName));
915             }
916
917             //
918             // 1st pass find exact matching!
919             // 
920             // We don't need to aggressively claim unused instances. For performance, we would proactively 
921             // verify lifetime of instances if activateUnusedInstances is specified and certain time 
922             // has elapsed since last sweep or we are running out of shared memory.  
923             bool verifyLifeTime = activateUnusedInstances;
924             if (activateUnusedInstances) {
925                 
926                 int totalSize = InstanceEntrySize + ProcessLifetimeEntrySize + InstanceNameSlotSize +  (CounterEntrySize * categoryData.CounterNames.Count);
927                 int freeMemoryOffset = *((int *) baseAddress);
928                 int alignmentAdjustment;
929                 int newOffset = CalculateMemoryNoBoundsCheck(freeMemoryOffset, totalSize, out alignmentAdjustment);
930
931                 if (!(newOffset > FileView.FileMappingSize || newOffset < 0)) {
932                     long tickDelta = (DateTime.Now.Ticks - Volatile.Read(ref LastInstanceLifetimeSweepTick));  
933                     if (tickDelta < InstanceLifetimeSweepWindow) 
934                         verifyLifeTime = false;    
935                 }
936             }
937
938             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
939             try {
940                 for(;;) {
941                     bool verifiedLifetimeOfThisInstance = false;
942                     if (verifyLifeTime && (currentInstancePointer->RefCount != 0)) {
943                         verifiedLifetimeOfThisInstance = true;
944                         VerifyLifetime(currentInstancePointer);
945                     }
946                     
947                     if (currentInstancePointer->InstanceNameHashCode == instanceNameHashCode) {
948                         if (StringEquals(instanceName, currentInstancePointer->InstanceNameOffset)){
949                             // we found a matching instance. 
950                             *returnInstancePointerReference = currentInstancePointer;
951     
952                             CounterEntry* firstCounter = (CounterEntry*) ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize);
953                             ProcessLifetimeEntry* lifetimeEntry;
954                             if (categoryData.UseUniqueSharedMemory) 
955                                 lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(firstCounter->LifetimeOffset, ProcessLifetimeEntrySize);
956                             else
957                                 lifetimeEntry = null;
958                             
959                             // ensure that we have verified the lifetime of the matched instance
960                             if (!verifiedLifetimeOfThisInstance && currentInstancePointer->RefCount != 0) 
961                                 VerifyLifetime(currentInstancePointer);
962                                 
963                             if (currentInstancePointer->RefCount != 0) {
964                                 if (lifetimeEntry != null && lifetimeEntry->ProcessId != 0) {
965                                     if (lifetime != PerformanceCounterInstanceLifetime.Process)
966                                         throw new InvalidOperationException(SR.GetString(SR.CantConvertProcessToGlobal));
967                                         
968                                     // make sure only one process is using this instance. 
969                                     if (ProcessData.ProcessId != lifetimeEntry->ProcessId)
970                                         throw new InvalidOperationException(SR.GetString(SR.InstanceAlreadyExists, instanceName));
971
972                                    // compare start time of the process, account for ACL issues in querying process information
973                                     if ((lifetimeEntry->StartupTime != -1) && (ProcessData.StartupTime != -1)) { 
974                                         if (ProcessData.StartupTime != lifetimeEntry->StartupTime)
975                                             throw new InvalidOperationException(SR.GetString(SR.InstanceAlreadyExists, instanceName));
976                                     }
977                                 }
978                                 else {
979                                     if (lifetime == PerformanceCounterInstanceLifetime.Process)
980                                         throw new InvalidOperationException(SR.GetString(SR.CantConvertGlobalToProcess));
981                                 }
982                                 return true;
983                             }
984                             
985                             if (activateUnusedInstances) {
986                                 Mutex mutex = null;
987                                 RuntimeHelpers.PrepareConstrainedRegions();
988                                 try {
989                                     SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);
990                                     ClearCounterValues(currentInstancePointer);
991                                     if (lifetimeEntry != null)
992                                         PopulateLifetimeEntry(lifetimeEntry, lifetime);
993                                     
994                                     currentInstancePointer->RefCount = 1;
995                                     return true;
996                                 }
997                                 finally {
998                                     if (mutex != null) {
999                                         mutex.ReleaseMutex();
1000                                         mutex.Close();
1001                                     }
1002                                 }
1003                             }
1004                             else
1005                                 return false;
1006                         }
1007                     }
1008     
1009                     if (currentInstancePointer->RefCount == 0) {
1010                         foundFreeInstance = true;
1011                     }
1012     
1013                     previousInstancePointer = currentInstancePointer;
1014                     if (currentInstancePointer->NextInstanceOffset != 0)
1015                         currentInstancePointer =  (InstanceEntry*)(ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize));
1016                     else {
1017                         *returnInstancePointerReference = previousInstancePointer;
1018                         return false;
1019                     }
1020                 }
1021             }
1022             finally  {
1023                 SecurityPermission.RevertAssert();
1024                 
1025                 if (verifyLifeTime) 
1026                     Volatile.Write(ref LastInstanceLifetimeSweepTick, DateTime.Now.Ticks);
1027             }
1028         }
1029
1030         private unsafe bool TryReuseInstance(int instanceNameHashCode, string instanceName, 
1031                                                CategoryEntry* categoryPointer, InstanceEntry** returnInstancePointerReference, 
1032                                                PerformanceCounterInstanceLifetime lifetime,
1033                                                InstanceEntry* lockInstancePointer) {
1034             //
1035             // 2nd pass find a free instance slot
1036             // 
1037             InstanceEntry* currentInstancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize));
1038             InstanceEntry* previousInstancePointer = currentInstancePointer;
1039             for (;;) {
1040                 if (currentInstancePointer->RefCount == 0) {
1041
1042                     bool hasFit;
1043                     long instanceNamePtr;       // we need cache this to avoid race conditions. 
1044                     
1045                     if (categoryData.UseUniqueSharedMemory) {
1046                         instanceNamePtr = ResolveOffset(currentInstancePointer->InstanceNameOffset, InstanceNameSlotSize);
1047                         // In the separate shared memory case we should always have enough space for instances.  The
1048                         // name slot size is fixed. 
1049                         Debug.Assert(((instanceName.Length + 1) * 2) <= InstanceNameSlotSize, "The instance name length should always fit in our slot size");
1050                         hasFit = true;
1051                     }
1052                     else {
1053                         // we don't know the string length yet. 
1054                         instanceNamePtr = ResolveOffset(currentInstancePointer->InstanceNameOffset, 0);
1055
1056                         // In the global shared memory, we require names to be exactly the same length in order
1057                         // to reuse them.  This way we don't end up leaking any space and we don't need to 
1058                         // depend on the layout of the memory to calculate the space we have. 
1059                         int length = GetStringLength((char*) instanceNamePtr);
1060                         hasFit = (length == instanceName.Length);
1061                     }
1062
1063                     bool noSpinLock = (lockInstancePointer == currentInstancePointer) || categoryData.UseUniqueSharedMemory;
1064                     // Instance name fit
1065                     if (hasFit) {
1066                         // don't bother locking again if we're using a separate shared memory.  
1067                         bool sectionEntered;
1068                         if (noSpinLock)
1069                             sectionEntered = true;
1070                         else
1071                             WaitAndEnterCriticalSection(&(currentInstancePointer->SpinLock), out sectionEntered);
1072
1073                         if (sectionEntered) {
1074                             try {
1075                                 // Make copy with zero-term
1076                                 SafeMarshalCopy(instanceName, (IntPtr)instanceNamePtr);
1077                                 currentInstancePointer->InstanceNameHashCode = instanceNameHashCode;
1078
1079                                 // return
1080                                 *returnInstancePointerReference = currentInstancePointer;
1081                                 // clear the counter values. 
1082                                 ClearCounterValues(*returnInstancePointerReference);
1083
1084                                 if (categoryData.UseUniqueSharedMemory) {
1085                                     CounterEntry* counterPointer = (CounterEntry*)ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize);
1086                                     ProcessLifetimeEntry* lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(counterPointer->LifetimeOffset, ProcessLifetimeEntrySize);
1087                                     PopulateLifetimeEntry(lifetimeEntry, lifetime);
1088                                 }
1089                                 
1090                                 (*returnInstancePointerReference)->RefCount = 1;
1091                                 return true;
1092                             }
1093                             finally {
1094                                 if (!noSpinLock)
1095                                     ExitCriticalSection(&(currentInstancePointer->SpinLock));
1096                             }
1097                         }
1098                     }
1099                  }
1100             
1101                 previousInstancePointer = currentInstancePointer;
1102                 if (currentInstancePointer->NextInstanceOffset != 0)
1103                     currentInstancePointer = (InstanceEntry*)(ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize));
1104                 else
1105                 {
1106                     *returnInstancePointerReference = previousInstancePointer;
1107                     return false;
1108                 }
1109             }
1110         }
1111
1112         [ResourceExposure(ResourceScope.None)]
1113         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1114         private unsafe void Verify(CategoryEntry* currentCategoryPointer) {
1115             if (!categoryData.UseUniqueSharedMemory)
1116                 return;
1117             
1118             Mutex mutex = null;
1119             RuntimeHelpers.PrepareConstrainedRegions();
1120             try {
1121                 SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);               
1122                 VerifyCategory(currentCategoryPointer);
1123             }
1124             finally {
1125                 if (mutex != null) {
1126                     mutex.ReleaseMutex();
1127                     mutex.Close();
1128                 }
1129             }
1130          }
1131
1132         private unsafe void VerifyCategory(CategoryEntry* currentCategoryPointer) {
1133             int freeOffset = *((int*)baseAddress);
1134             ResolveOffset(freeOffset, 0);        // verify next free offset
1135
1136             // begin by verifying the head node's offset
1137             int currentOffset = ResolveAddress((long)currentCategoryPointer, CategoryEntrySize);
1138             if (currentOffset >= freeOffset) {
1139                 // zero out the bad head node entry
1140                 currentCategoryPointer->SpinLock             = 0;
1141                 currentCategoryPointer->CategoryNameHashCode = 0;
1142                 currentCategoryPointer->CategoryNameOffset   = 0;
1143                 currentCategoryPointer->FirstInstanceOffset  = 0;
1144                 currentCategoryPointer->NextCategoryOffset   = 0;
1145                 currentCategoryPointer->IsConsistent         = 0;
1146                 return;
1147             }
1148
1149             if (currentCategoryPointer->NextCategoryOffset > freeOffset)
1150                 currentCategoryPointer->NextCategoryOffset = 0;
1151             else if (currentCategoryPointer->NextCategoryOffset != 0)
1152                 VerifyCategory((CategoryEntry*) ResolveOffset(currentCategoryPointer->NextCategoryOffset, CategoryEntrySize));
1153
1154             if (currentCategoryPointer->FirstInstanceOffset != 0) {
1155                 // In V3, we started prepending the new instances rather than appending (as in V2) for performance.
1156                 // Check whether the recently added instance at the head of the list is committed. If not, rewire 
1157                 // the head of the list to point to the next instance 
1158                 if (currentCategoryPointer->FirstInstanceOffset > freeOffset) {
1159                     InstanceEntry* currentInstancePointer = (InstanceEntry*) ResolveOffset(currentCategoryPointer->FirstInstanceOffset, InstanceEntrySize);
1160                     currentCategoryPointer->FirstInstanceOffset = currentInstancePointer->NextInstanceOffset; 
1161                     if (currentCategoryPointer->FirstInstanceOffset > freeOffset)
1162                         currentCategoryPointer->FirstInstanceOffset = 0;
1163                 }
1164                 // 
1165
1166
1167
1168
1169                 if (currentCategoryPointer->FirstInstanceOffset != 0) {
1170                     Debug.Assert(currentCategoryPointer->FirstInstanceOffset <= freeOffset, "The head of the list is inconsistent - possible mismatch of V2 & V3 instances?");
1171                     VerifyInstance((InstanceEntry*) ResolveOffset(currentCategoryPointer->FirstInstanceOffset, InstanceEntrySize));
1172                 }
1173             }
1174            
1175             currentCategoryPointer->IsConsistent = 1;
1176         }
1177         
1178         private unsafe void VerifyInstance(InstanceEntry* currentInstancePointer) {
1179             int freeOffset = *((int*)baseAddress);
1180             ResolveOffset(freeOffset, 0);        // verify next free offset
1181
1182             if (currentInstancePointer->NextInstanceOffset > freeOffset)
1183                 currentInstancePointer->NextInstanceOffset = 0;
1184             else if (currentInstancePointer->NextInstanceOffset != 0)
1185                 VerifyInstance((InstanceEntry*) ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize));
1186         }
1187
1188         [ResourceExposure(ResourceScope.None)]
1189         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1190         private unsafe void VerifyLifetime(InstanceEntry* currentInstancePointer) {
1191             Debug.Assert(currentInstancePointer->RefCount != 0, "RefCount must be 1 for instances passed to VerifyLifetime");
1192
1193             CounterEntry* counter = (CounterEntry*) ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize);
1194             if (counter->LifetimeOffset != 0) {
1195                 ProcessLifetimeEntry* lifetime = (ProcessLifetimeEntry*) ResolveOffset(counter->LifetimeOffset, ProcessLifetimeEntrySize);
1196                 if (lifetime->LifetimeType == (int) PerformanceCounterInstanceLifetime.Process) {
1197                     int pid = lifetime->ProcessId;
1198                     long startTime = lifetime->StartupTime;
1199
1200                     if (pid != 0) {
1201
1202                         // Optimize for this process
1203                         if (pid == ProcessData.ProcessId) {
1204                             if ((ProcessData.StartupTime != -1) && (startTime != -1) && (ProcessData.StartupTime != startTime)) {
1205                                 // Process id got recycled.  Reclaim this instance. 
1206                                 currentInstancePointer->RefCount = 0;
1207                                 return;
1208                             }
1209                         }
1210                         else {
1211                             long processStartTime;
1212                             using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, false, pid)) {
1213                                 int error = Marshal.GetLastWin32Error();
1214                                 if ((error == NativeMethods.ERROR_INVALID_PARAMETER) && procHandle.IsInvalid) {
1215                                     // The process is dead.  Reclaim this instance.  Note that we only clear the refcount here.  
1216                                     // If we tried to clear the pid and startup time as well, we would have a ---- where
1217                                     // we could clear the pid/startup time but not the refcount. 
1218                                     currentInstancePointer->RefCount = 0;
1219                                     return;
1220                                 }
1221
1222                                 // Defer cleaning the instance when we had previously encountered errors in 
1223                                 // recording process start time (i.e, when startTime == -1) until after the 
1224                                 // process id is not valid (which will be caught in the if check above)
1225                                 if (!procHandle.IsInvalid && startTime != -1) {
1226                                     long temp;
1227                                     if (NativeMethods.GetProcessTimes(procHandle, out processStartTime, out temp, out temp, out temp)) {
1228                                         if (processStartTime != startTime) {
1229                                             // The process is dead but a new one is using the same pid.  Reclaim this instance. 
1230                                             currentInstancePointer->RefCount = 0;
1231                                             return;
1232                                         }
1233                                     }
1234                                 }
1235                             }
1236
1237                             // Check to see if the process handle has been signaled by the kernel.  If this is the case then it's safe
1238                             // to reclaim the instance as the process is in the process of exiting.
1239                             using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.SYNCHRONIZE, false, pid)) {
1240                                 if (!procHandle.IsInvalid) {
1241                                     using (ProcessWaitHandle wh = new ProcessWaitHandle(procHandle)) {
1242                                         if (wh.WaitOne(0, false)) {
1243                                             // Process has exited
1244                                             currentInstancePointer->RefCount = 0;
1245                                             return;
1246                                         }
1247                                     }
1248                                 }
1249                             }
1250                         }
1251                     }
1252                     
1253                 }
1254             }
1255         }
1256
1257         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1258         internal unsafe long IncrementBy(long value) {
1259             if (counterEntryPointer == null)
1260                 return 0;
1261
1262             CounterEntry* counterEntry = this.counterEntryPointer;
1263
1264             return AddToValue(counterEntry, value);
1265         }
1266
1267         internal unsafe long Increment() {
1268             if (counterEntryPointer == null)
1269                 return 0;
1270
1271             return IncrementUnaligned(this.counterEntryPointer);
1272         }
1273
1274         internal unsafe long Decrement() {
1275             if (counterEntryPointer == null)
1276                 return 0;
1277
1278             return DecrementUnaligned(this.counterEntryPointer);
1279         }
1280
1281         internal unsafe static void RemoveAllInstances(string categoryName) {
1282             SharedPerformanceCounter spc = new SharedPerformanceCounter(categoryName, null, null);
1283             spc.RemoveAllInstances();
1284             RemoveCategoryData(categoryName);
1285         }
1286
1287         [ResourceExposure(ResourceScope.None)]
1288         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1289         private unsafe void RemoveAllInstances() {
1290             CategoryEntry* categoryPointer;
1291             if (!FindCategory(&categoryPointer))
1292                 return;
1293
1294             InstanceEntry* instancePointer = (InstanceEntry *)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize));
1295
1296             Mutex mutex = null;
1297             RuntimeHelpers.PrepareConstrainedRegions();
1298             try {
1299                 SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex); 
1300                 for(;;) {
1301                     RemoveOneInstance(instancePointer, true);
1302                     
1303                     if (instancePointer->NextInstanceOffset != 0)
1304                         instancePointer =  (InstanceEntry*)(ResolveOffset(instancePointer->NextInstanceOffset, InstanceEntrySize));
1305                     else {
1306                         break;
1307                     }
1308                 }
1309             }
1310             finally {
1311                 if (mutex != null) {
1312                     mutex.ReleaseMutex();
1313                     mutex.Close();
1314                 }
1315             }
1316         }
1317
1318         [ResourceExposure(ResourceScope.None)]
1319         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1320         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1321         internal unsafe void RemoveInstance(string instanceName, PerformanceCounterInstanceLifetime instanceLifetime) {
1322              if (instanceName == null || instanceName.Length == 0)
1323                 return;
1324
1325             int instanceNameHashCode = GetWstrHashCode(instanceName);
1326             
1327             CategoryEntry* categoryPointer;
1328             if (!FindCategory(&categoryPointer)) 
1329                 return;
1330             
1331             InstanceEntry* instancePointer = null;
1332             bool validatedCachedInstancePointer = false;
1333             bool temp;
1334
1335             Mutex mutex = null;
1336             RuntimeHelpers.PrepareConstrainedRegions();
1337             try {
1338                 SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex); 
1339                 
1340                 if (this.thisInstanceOffset != -1) {
1341                     try {
1342                         // validate whether the cached instance pointer is pointing at the right instance  
1343                         instancePointer = (InstanceEntry*)(ResolveOffset(this.thisInstanceOffset, InstanceEntrySize));
1344                         if (instancePointer->InstanceNameHashCode == instanceNameHashCode) {
1345                             if (StringEquals(instanceName, instancePointer->InstanceNameOffset)){
1346                                 validatedCachedInstancePointer = true;
1347
1348                                 // this is probably overkill
1349                                 CounterEntry* firstCounter = (CounterEntry*) ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize);
1350                                 ProcessLifetimeEntry* lifetimeEntry;
1351                                 if (categoryData.UseUniqueSharedMemory) {
1352                                     lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(firstCounter->LifetimeOffset, ProcessLifetimeEntrySize);
1353                                     if (lifetimeEntry != null 
1354                                         && lifetimeEntry->LifetimeType == (int)PerformanceCounterInstanceLifetime.Process 
1355                                         && lifetimeEntry->ProcessId != 0) {
1356                                         validatedCachedInstancePointer &= (instanceLifetime == PerformanceCounterInstanceLifetime.Process);
1357                                         validatedCachedInstancePointer &= (ProcessData.ProcessId == lifetimeEntry->ProcessId);
1358                                         if ((lifetimeEntry->StartupTime != -1) && (ProcessData.StartupTime != -1))  
1359                                             validatedCachedInstancePointer &= (ProcessData.StartupTime == lifetimeEntry->StartupTime);
1360                                     }
1361                                     else 
1362                                         validatedCachedInstancePointer &= (instanceLifetime != PerformanceCounterInstanceLifetime.Process);
1363                                 }
1364                             }
1365                         }
1366                     }
1367                     catch (InvalidOperationException) {
1368                         validatedCachedInstancePointer = false;
1369                     }
1370                     if (!validatedCachedInstancePointer) 
1371                         this.thisInstanceOffset = -1;
1372                 }
1373                 
1374                 if (!validatedCachedInstancePointer && !FindInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, false, instanceLifetime, out temp)) 
1375                     return ;
1376
1377                 if (instancePointer != null)
1378                     RemoveOneInstance(instancePointer, false);
1379             }
1380             finally {
1381                 if (mutex != null) {
1382                     mutex.ReleaseMutex();
1383                     mutex.Close();
1384                 }
1385             }
1386         }
1387
1388         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1389         private unsafe void RemoveOneInstance(InstanceEntry* instancePointer, bool clearValue) {
1390             bool sectionEntered = false;
1391
1392             RuntimeHelpers.PrepareConstrainedRegions();
1393             try {
1394                 if (!categoryData.UseUniqueSharedMemory) {
1395                     while (!sectionEntered) {
1396                         WaitAndEnterCriticalSection(&(instancePointer->SpinLock), out sectionEntered);
1397                     }
1398                 }
1399             
1400                 instancePointer->RefCount = 0;
1401
1402                 if (clearValue)
1403                     ClearCounterValues(instancePointer);
1404             }
1405             finally {
1406                 if (sectionEntered)
1407                     ExitCriticalSection(&(instancePointer->SpinLock));
1408             }
1409         }
1410
1411         private unsafe void ClearCounterValues(InstanceEntry* instancePointer) {
1412             //Clear counter instance values
1413             CounterEntry* currentCounterPointer = null;
1414             
1415             if (instancePointer->FirstCounterOffset != 0)
1416                 currentCounterPointer = (CounterEntry*)(ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize));
1417
1418             while(currentCounterPointer != null) {
1419                 SetValue(currentCounterPointer, 0);
1420                     
1421                 if (currentCounterPointer->NextCounterOffset != 0)
1422                     currentCounterPointer = (CounterEntry*)(ResolveOffset(currentCounterPointer->NextCounterOffset, CounterEntrySize));
1423                 else
1424                     currentCounterPointer = null;
1425             }
1426
1427         }
1428
1429         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
1430         private static unsafe long AddToValue(CounterEntry* counterEntry, long addend) {
1431             // Called while holding a lock - shouldn't have to worry about
1432             // reading misaligned data & getting old vs. new parts of an Int64.
1433             if (IsMisaligned(counterEntry)) {
1434                 ulong newvalue;
1435
1436                 CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry;
1437                 newvalue = (uint)entry->Value_hi;                
1438                 newvalue <<= 32;
1439                 newvalue |= (uint)entry->Value_lo;
1440
1441
1442                 newvalue = (ulong) ((long) newvalue + addend);
1443
1444                 entry->Value_hi = (int) (newvalue >> 32);
1445                 entry->Value_lo = (int) (newvalue & 0xffffffff);
1446
1447                 return (long) newvalue;
1448             }
1449             else 
1450                 return Interlocked.Add(ref counterEntry->Value, addend);
1451         }
1452
1453         private static unsafe long DecrementUnaligned(CounterEntry* counterEntry) {
1454             if (IsMisaligned(counterEntry)) 
1455                 return AddToValue(counterEntry, -1);
1456             else
1457                 return Interlocked.Decrement(ref counterEntry->Value);
1458         }
1459
1460         private static unsafe long GetValue(CounterEntry* counterEntry) {
1461             if (IsMisaligned(counterEntry)) {
1462                 ulong value;
1463                 CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry;
1464                 value = (uint)entry->Value_hi;                
1465                 value <<= 32;
1466                 value |= (uint)entry->Value_lo;
1467
1468                 return (long) value;
1469             }
1470             else
1471                 return counterEntry->Value;
1472         }
1473
1474         private static unsafe long IncrementUnaligned(CounterEntry* counterEntry) {
1475             if (IsMisaligned(counterEntry)) 
1476                 return AddToValue(counterEntry, 1);
1477             else
1478                 return Interlocked.Increment(ref counterEntry->Value);
1479         }
1480
1481         private static unsafe void SetValue(CounterEntry* counterEntry, long value) {
1482             if (IsMisaligned(counterEntry)) {
1483                 CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry;
1484                 entry->Value_lo = (int) (value & 0xffffffff);
1485                 entry->Value_hi = (int) (value >> 32);
1486             }
1487             else
1488                 counterEntry->Value = value;
1489         }
1490
1491         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
1492         private static unsafe bool IsMisaligned(CounterEntry* counterEntry) {
1493             return (( (Int64)counterEntry & 0x7) != 0);
1494         }
1495
1496         [ResourceExposure(ResourceScope.None)]
1497         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1498         private long ResolveOffset(int offset, int sizeToRead) {
1499             //It is very important to check the integrity of the shared memory
1500             //everytime a new address is resolved.
1501             if (offset > (FileView.FileMappingSize - sizeToRead) || offset < 0)
1502                 throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
1503
1504             long address = baseAddress + offset;
1505
1506             return address;
1507         }
1508
1509         [ResourceExposure(ResourceScope.None)]
1510         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1511         private int ResolveAddress(long address, int sizeToRead) {
1512             int offset = (int)(address - baseAddress);
1513
1514             //It is very important to check the integrity of the shared memory
1515             //everytime a new address is resolved.
1516             if (offset > (FileView.FileMappingSize - sizeToRead) || offset < 0)
1517                 throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
1518
1519             return offset;
1520         }
1521
1522
1523         private class FileMapping {
1524             internal int FileMappingSize;
1525             private SafeFileMapViewHandle fileViewAddress = null;
1526             private SafeFileMappingHandle fileMappingHandle = null;
1527             //The version of the file mapping name is independent from the
1528             //assembly version.
1529             
1530             [ResourceExposure(ResourceScope.Machine)]
1531             [ResourceConsumption(ResourceScope.Machine)]
1532             public FileMapping(string fileMappingName, int fileMappingSize, int initialOffset) {
1533                 this.Initialize(fileMappingName, fileMappingSize, initialOffset);
1534             }
1535
1536             internal IntPtr FileViewAddress {
1537                 get { 
1538                     if (fileViewAddress.IsInvalid)
1539                        throw new InvalidOperationException(SR.GetString(SR.SharedMemoryGhosted));
1540
1541                     return fileViewAddress.DangerousGetHandle(); 
1542                 }
1543             }
1544
1545             [ResourceExposure(ResourceScope.Machine)]
1546             [ResourceConsumption(ResourceScope.Machine)]
1547             private unsafe void Initialize(string fileMappingName, int fileMappingSize, int initialOffset) {
1548                 string mappingName = fileMappingName;
1549                 SharedUtils.CheckEnvironment();
1550
1551                 SafeLocalMemHandle securityDescriptorPointer = null;
1552                 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
1553                 try {
1554                     // The sddl string consists of these parts:
1555                     // D:           it's a DACL
1556                     // (A;          this is an allow ACE
1557                     // OICI;        object inherit and container inherit
1558                     // FRFWGRGW;;;  allow file read, file write, generic read and generic write
1559                     // AU)          granted to Authenticated Users
1560                     // ;S-1-5-33)   the same permission granted to AU is also granted to restricted services
1561                     string sddlString = "D:(A;OICI;FRFWGRGW;;;AU)(A;OICI;FRFWGRGW;;;S-1-5-33)";
1562
1563                     if (!SafeLocalMemHandle.ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, NativeMethods.SDDL_REVISION_1,
1564                                                                                                     out securityDescriptorPointer, IntPtr.Zero))
1565                         throw new InvalidOperationException(SR.GetString(SR.SetSecurityDescriptorFailed));
1566
1567                     NativeMethods.SECURITY_ATTRIBUTES securityAttributes = new NativeMethods.SECURITY_ATTRIBUTES();
1568                     securityAttributes.lpSecurityDescriptor = securityDescriptorPointer;
1569                     securityAttributes.bInheritHandle = false;
1570
1571                     //
1572                     //
1573                     // Here we call CreateFileMapping to create the memory mapped file.  When CreateFileMapping fails
1574                     // with ERROR_ACCESS_DENIED, we know the file mapping has been created and we then open it with OpenFileMapping.
1575                     //
1576                     // There is chance of a race condition between CreateFileMapping and OpenFileMapping; The memory mapped file
1577                     // may actually be closed in between these two calls.  When this happens, OpenFileMapping returns ERROR_FILE_NOT_FOUND.
1578                     // In this case, we need to loop back and retry creating the memory mapped file.
1579                     //
1580                     // This loop will timeout in approximately 1.4 minutes.  An InvalidOperationException is thrown in the timeout case. 
1581                     //
1582                     //
1583                     int waitRetries = 14;   //((2^13)-1)*10ms == approximately 1.4mins
1584                     int waitSleep = 0;
1585                     bool created = false;
1586                     while (!created && waitRetries > 0) {
1587                         fileMappingHandle = NativeMethods.CreateFileMapping((IntPtr)(-1), securityAttributes,
1588                                                                   NativeMethods.PAGE_READWRITE, 0, fileMappingSize, mappingName);
1589
1590                         if ((Marshal.GetLastWin32Error() != NativeMethods.ERROR_ACCESS_DENIED) || !fileMappingHandle.IsInvalid) {
1591                             created = true;
1592                         }
1593                         else {
1594                             // Invalidate the old safehandle before we get rid of it.  This prevents it from trying to finalize
1595                             fileMappingHandle.SetHandleAsInvalid();
1596                             fileMappingHandle = NativeMethods.OpenFileMapping(NativeMethods.FILE_MAP_WRITE, false, mappingName);
1597
1598                             if ((Marshal.GetLastWin32Error() != NativeMethods.ERROR_FILE_NOT_FOUND) || !fileMappingHandle.IsInvalid) {
1599                                 created = true;
1600                             }
1601                             else {
1602                                 --waitRetries;
1603                                 if (waitSleep == 0) {
1604                                     waitSleep = 10;
1605                                 }
1606                                 else {
1607                                     System.Threading.Thread.Sleep(waitSleep);
1608                                     waitSleep *= 2;
1609                                 }
1610                             }
1611                         }
1612                     }
1613                     if (fileMappingHandle.IsInvalid) {
1614                         throw new InvalidOperationException(SR.GetString(SR.CantCreateFileMapping));
1615                     }
1616
1617                     fileViewAddress = SafeFileMapViewHandle.MapViewOfFile(fileMappingHandle, NativeMethods.FILE_MAP_WRITE, 0,0, UIntPtr.Zero);
1618                     if (fileViewAddress.IsInvalid)
1619                         throw new InvalidOperationException(SR.GetString(SR.CantMapFileView));
1620
1621                     // figure out what size the share memory really is.
1622                     NativeMethods.MEMORY_BASIC_INFORMATION meminfo = new NativeMethods.MEMORY_BASIC_INFORMATION();
1623                     if (NativeMethods.VirtualQuery(fileViewAddress, ref meminfo, (IntPtr) sizeof(NativeMethods.MEMORY_BASIC_INFORMATION)) == IntPtr.Zero)
1624                         throw new InvalidOperationException(SR.GetString(SR.CantGetMappingSize));
1625                     
1626                     FileMappingSize = (int) meminfo.RegionSize;
1627                 }
1628                 finally {
1629                     if (securityDescriptorPointer != null) securityDescriptorPointer.Close();
1630                     SecurityPermission.RevertAssert();
1631                 }
1632
1633                 SafeNativeMethods.InterlockedCompareExchange(fileViewAddress.DangerousGetHandle(), initialOffset, 0);
1634             }
1635
1636         }
1637
1638         // SafeMarshalCopy always null terminates the char array
1639         // before copying it to native memory
1640         //
1641         private static void SafeMarshalCopy(string str, IntPtr nativePointer) {
1642             // convert str to a char array and copy it to the unmanaged memory pointer
1643             char[] tmp = new char[str.Length + 1];
1644             str.CopyTo(0, tmp, 0, str.Length);
1645             tmp[str.Length] = '\0';  // make sure the char[] is null terminated
1646             Marshal.Copy(tmp, 0, nativePointer, tmp.Length);
1647         }
1648
1649         // <WARNING>
1650         // The final tmpPadding field is needed to make the size of this structure 8-byte aligned.  This is
1651         // necessary on IA64.
1652         // </WARNING>
1653         // Note that in V1.0 and v1.1 there was no explicit padding defined on any of these structs.  That means that 
1654         // sizeof(CategoryEntry) or Marshal.SizeOf(typeof(CategoryEntry)) returned 4 bytes less before Whidbey, 
1655         // and the int we use as IsConsistent could actually overlap the InstanceEntry SpinLock.  
1656         
1657         [StructLayout(LayoutKind.Sequential)]
1658         private struct CategoryEntry {
1659             public int SpinLock;
1660             public int CategoryNameHashCode;
1661             public int CategoryNameOffset;
1662             public int FirstInstanceOffset;
1663             public int NextCategoryOffset;
1664             public int IsConsistent;         // this was 4 bytes of padding in v1.0/v1.1
1665         }
1666
1667         [StructLayout(LayoutKind.Sequential)]
1668         private struct InstanceEntry {
1669             public int SpinLock;
1670             public int InstanceNameHashCode;
1671             public int InstanceNameOffset;
1672             public int RefCount;
1673             public int FirstCounterOffset;
1674             public int NextInstanceOffset;
1675         }
1676
1677         [StructLayout(LayoutKind.Sequential)]
1678         private struct CounterEntry {
1679             public int SpinLock;
1680             public int CounterNameHashCode;
1681             public int CounterNameOffset;
1682             public int LifetimeOffset;          // this was 4 bytes of padding in v1.0/v1.1
1683             public long Value;
1684             public int NextCounterOffset;
1685             public int padding2;        
1686         }
1687
1688         [StructLayout(LayoutKind.Sequential)]
1689         private struct CounterEntryMisaligned {
1690             public int SpinLock;
1691             public int CounterNameHashCode;
1692             public int CounterNameOffset;
1693             public int LifetimeOffset;         // this was 4 bytes of padding in v1.0/v1.1
1694             public int Value_lo;
1695             public int Value_hi;
1696             public int NextCounterOffset;
1697             public int padding2;        // The compiler adds this only if there is an int64 in the struct - 
1698                                         // ie only for CounterEntry.  It really needs to be here.  
1699         }
1700
1701         [StructLayout(LayoutKind.Sequential)]
1702         private struct ProcessLifetimeEntry {
1703             public int LifetimeType;
1704             public int ProcessId;
1705             public Int64 StartupTime;
1706         }
1707         
1708         private class CategoryData {
1709             public FileMapping FileMapping;
1710             public bool EnableReuse;
1711             public bool UseUniqueSharedMemory;
1712             public string FileMappingName;
1713             public string MutexName;
1714             public ArrayList CounterNames;
1715         }
1716     }
1717
1718     internal class ProcessData {
1719         public ProcessData(int pid, long startTime) {
1720             ProcessId = pid;
1721             StartupTime = startTime;
1722         }
1723         public int ProcessId;
1724         public long StartupTime;
1725     }
1726
1727 }