Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Services / Monitoring / system / Diagnosticts / SharedPerformanceCounter.cs / 3 / SharedPerformanceCounter.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Diagnostics { using System; using System.Text; using System.Threading; using System.Collections; using System.Runtime.ConstrainedExecution; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Security; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; using System.Globalization; using System.Security.Principal; using System.Security.AccessControl; using System.Collections.Generic; using System.Runtime.Versioning; [HostProtection(Synchronization=true, SharedState=true)] internal sealed class SharedPerformanceCounter { private const int MaxSpinCount = 5000; internal const int DefaultCountersFileMappingSize = 524288; internal const int MaxCountersFileMappingSize = 33554432; internal const int MinCountersFileMappingSize = 32768; internal const int InstanceNameMaxLength = 127; internal const int InstanceNameSlotSize = 256; internal const string SingleInstanceName = "systemdiagnosticssharedsingleinstance"; internal const string DefaultFileMappingName = "netfxcustomperfcounters.1.0"; internal static readonly int SingleInstanceHashCode = GetWstrHashCode(SingleInstanceName); private static Hashtable categoryDataTable = new Hashtable(StringComparer.Ordinal); private static readonly int CategoryEntrySize = Marshal.SizeOf(typeof(CategoryEntry)); private static readonly int InstanceEntrySize = Marshal.SizeOf(typeof(InstanceEntry)); private static readonly int CounterEntrySize = Marshal.SizeOf(typeof(CounterEntry)); private static readonly int ProcessLifetimeEntrySize = Marshal.SizeOf(typeof(ProcessLifetimeEntry)); private static long LastInstanceLifetimeSweepTick; private static long InstanceLifetimeSweepWindow = 30*10000000; //ticks private static ProcessData procData; private static ProcessData ProcessData { get { if (procData == null) { new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { int pid = NativeMethods.GetCurrentProcessId(); long startTime = -1; // Though we have asserted the required CAS permissions above, we may // still fail to query the process information if the user does not // have the necessary process access rights or privileges. // This might be the case if the current process was started by a // different user (primary token) than the current user // (impersonation token) that has less privilege/ACL rights. using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, false, pid)) { if (!procHandle.IsInvalid) { long temp; NativeMethods.GetProcessTimes(procHandle, out startTime, out temp, out temp, out temp); } } procData = new ProcessData(pid, startTime); } finally { SecurityPermission.RevertAssert(); } } return procData; } } // InitialOffset is the offset in our global shared memory where we put the first CategoryEntry. It needs to be 4 because in // v1.0 and v1.1 we used IntPtr.Size. That creates potential side-by-side issues on 64 bit machines using WOW64. // A v1.0 app running on WOW64 will assume the InitialOffset is 4. A true 64 bit app on the same machine will assume // the initial offset is 8. // However, using an offset of 4 means that our CounterEntry.Value is potentially misaligned. This is why we have SetValue // and other methods which split CounterEntry.Value into two ints. With separate shared memory blocks per // category, we can fix this and always use an inital offset of 8. internal int InitialOffset = 4; private CategoryData categoryData; private long baseAddress; private unsafe CounterEntry* counterEntryPointer; private string categoryName; private int categoryNameHashCode; private int thisInstanceOffset = -1; internal SharedPerformanceCounter(string catName, string counterName, string instanceName) : this (catName, counterName, instanceName, PerformanceCounterInstanceLifetime.Global) { } [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] internal unsafe SharedPerformanceCounter(string catName, string counterName, string instanceName, PerformanceCounterInstanceLifetime lifetime) { this.categoryName = catName; this.categoryNameHashCode = GetWstrHashCode(categoryName); categoryData = GetCategoryData(); // Check that the instance name isn't too long if we're using the new shared memory. // We allocate InstanceNameSlotSize bytes in the shared memory if (categoryData.UseUniqueSharedMemory) { if (instanceName != null && instanceName.Length > InstanceNameMaxLength) throw new InvalidOperationException(SR.GetString(SR.InstanceNameTooLong)); } else { if (lifetime != PerformanceCounterInstanceLifetime.Global) throw new InvalidOperationException(SR.GetString(SR.ProcessLifetimeNotValidInGlobal)); } if (counterName != null && instanceName != null) { if (!categoryData.CounterNames.Contains(counterName)) Debug.Assert(false, "Counter " + counterName + " does not exist in category " + catName); else this.counterEntryPointer = GetCounter(counterName, instanceName, categoryData.EnableReuse, lifetime); } } private FileMapping FileView { [ResourceExposure(ResourceScope.Machine)] get { return categoryData.FileMapping; } } internal unsafe long Value { get { if (counterEntryPointer == null) return 0; return GetValue(this.counterEntryPointer); } set { if (counterEntryPointer == null) return; SetValue(this.counterEntryPointer, value); } } private unsafe int CalculateAndAllocateMemory(int totalSize, out int alignmentAdjustment) { int newOffset; int oldOffset; alignmentAdjustment = 0; Debug.Assert(!categoryData.UseUniqueSharedMemory, "We should never be calling CalculateAndAllocateMemory in the unique shared memory"); do { oldOffset = *((int *) baseAddress); // we need to verify the oldOffset before we start using it. Otherwise someone could change // it to something bogus and we would write outside of the shared memory. ResolveOffset(oldOffset, 0); newOffset = CalculateMemory(oldOffset, totalSize, out alignmentAdjustment); // In the default shared mem we need to make sure that the end address is also aligned. This is because // in v1.1/v1.0 we just assumed that the next free offset was always properly aligned. int endAddressMod8 = (int) (baseAddress + newOffset) & 0x7; int endAlignmentAdjustment = (8 - endAddressMod8) & 0x7; newOffset += endAlignmentAdjustment; } while (SafeNativeMethods.InterlockedCompareExchange((IntPtr)baseAddress, newOffset, oldOffset) != oldOffset); return oldOffset; } private int CalculateMemory(int oldOffset, int totalSize, out int alignmentAdjustment) { int newOffset = CalculateMemoryNoBoundsCheck(oldOffset, totalSize, out alignmentAdjustment); if (newOffset > FileView.FileMappingSize || newOffset < 0) { throw new InvalidOperationException(SR.GetString(SR.CountersOOM)); } return newOffset; } [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private int CalculateMemoryNoBoundsCheck(int oldOffset, int totalSize, out int alignmentAdjustment) { int currentTotalSize = totalSize; Thread.MemoryBarrier(); // make sure the start address is 8 byte aligned int startAddressMod8 = (int) (baseAddress + oldOffset) & 0x7; alignmentAdjustment = (8 - startAddressMod8) & 0x7; currentTotalSize = currentTotalSize + alignmentAdjustment; int newOffset = oldOffset + currentTotalSize; return newOffset; } private unsafe int CreateCategory(CategoryEntry* lastCategoryPointer, int instanceNameHashCode, string instanceName, PerformanceCounterInstanceLifetime lifetime) { int categoryNameLength; int instanceNameLength; int alignmentAdjustment; int freeMemoryOffset; int newOffset = 0; int totalSize; categoryNameLength = (categoryName.Length + 1) * 2; totalSize = CategoryEntrySize + InstanceEntrySize + (CounterEntrySize * categoryData.CounterNames.Count) + categoryNameLength; for (int i=0; iIsConsistent = 0; } else { instanceNameLength = (instanceName.Length +1) * 2; totalSize += instanceNameLength; freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment); } long nextPtr = ResolveOffset(freeMemoryOffset, totalSize + alignmentAdjustment); CategoryEntry* newCategoryEntryPointer; InstanceEntry* newInstanceEntryPointer; // We need to decide where to put the padding returned in alignmentAdjustment. There are several things that // need to be aligned. First, we need to align each struct on a 4 byte boundary so we can use interlocked // operations on the int Spinlock field. Second, we need to align the CounterEntry on an 8 byte boundary so that // on 64 bit platforms we can use interlocked operations on the Value field. alignmentAdjustment guarantees 8 byte // alignemnt, so we use that for both. If we're creating the very first category, however, we can't move that // CategoryEntry. In this case we put the alignmentAdjustment before the InstanceEntry. if (freeMemoryOffset == InitialOffset) { newCategoryEntryPointer = (CategoryEntry*) nextPtr; nextPtr += CategoryEntrySize + alignmentAdjustment; newInstanceEntryPointer = (InstanceEntry*) nextPtr; } else { nextPtr += alignmentAdjustment; newCategoryEntryPointer = (CategoryEntry*) nextPtr; nextPtr += CategoryEntrySize; newInstanceEntryPointer = (InstanceEntry*) nextPtr; } nextPtr += InstanceEntrySize; // create the first CounterEntry and reserve space for all of the rest. We won't // finish creating them until the end CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr; nextPtr += CounterEntrySize * categoryData.CounterNames.Count; if (categoryData.UseUniqueSharedMemory) { ProcessLifetimeEntry* newLifetimeEntry = (ProcessLifetimeEntry*) nextPtr; nextPtr += ProcessLifetimeEntrySize; newCounterEntryPointer->LifetimeOffset = (int)((long)newLifetimeEntry - baseAddress); PopulateLifetimeEntry(newLifetimeEntry, lifetime); } newCategoryEntryPointer->CategoryNameHashCode = categoryNameHashCode; newCategoryEntryPointer->NextCategoryOffset = 0; newCategoryEntryPointer->FirstInstanceOffset = (int)((long)newInstanceEntryPointer - baseAddress); newCategoryEntryPointer->CategoryNameOffset = (int) (nextPtr - baseAddress); Marshal.Copy(categoryName.ToCharArray(), 0, (IntPtr) nextPtr, categoryName.Length); nextPtr += categoryNameLength; newInstanceEntryPointer->InstanceNameHashCode = instanceNameHashCode; newInstanceEntryPointer->NextInstanceOffset = 0; newInstanceEntryPointer->FirstCounterOffset = (int)((long)newCounterEntryPointer - baseAddress); newInstanceEntryPointer->RefCount = 1; newInstanceEntryPointer->InstanceNameOffset = (int) (nextPtr - baseAddress); Marshal.Copy(instanceName.ToCharArray(), 0, (IntPtr) nextPtr, instanceName.Length); nextPtr += instanceNameLength; string counterName = (string) categoryData.CounterNames[0]; newCounterEntryPointer->CounterNameHashCode = GetWstrHashCode(counterName); SetValue(newCounterEntryPointer, 0); newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress); Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length); nextPtr += (counterName.Length + 1) * 2; CounterEntry* previousCounterEntryPointer; for (int i=1; i CounterNameHashCode = GetWstrHashCode(counterName); SetValue(newCounterEntryPointer, 0); newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress); Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length); nextPtr += (counterName.Length + 1) * 2; previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress); } Debug.Assert(nextPtr - baseAddress == freeMemoryOffset + totalSize + alignmentAdjustment, "We should have used all of the space we requested at this point"); int offset = (int) ((long) newCategoryEntryPointer - baseAddress); lastCategoryPointer->IsConsistent = 0; // If not the first category node, link it. if (offset != InitialOffset) lastCategoryPointer->NextCategoryOffset = offset; if (categoryData.UseUniqueSharedMemory) { *((int*) baseAddress) = newOffset; lastCategoryPointer->IsConsistent = 1; } return offset; } private unsafe int CreateInstance(CategoryEntry* categoryPointer, int instanceNameHashCode, string instanceName, PerformanceCounterInstanceLifetime lifetime) { int instanceNameLength; int totalSize = InstanceEntrySize + (CounterEntrySize * categoryData.CounterNames.Count); int alignmentAdjustment; int freeMemoryOffset; int newOffset = 0; if (categoryData.UseUniqueSharedMemory) { instanceNameLength = InstanceNameSlotSize; totalSize += ProcessLifetimeEntrySize + instanceNameLength; // If we're in a separate shared memory, we need to do a two stage update of the free memory pointer. // First we calculate our alignment adjustment and where the new free offset is. Then we // write the new structs and data. The last two operations are to link the new structs into the // existing ones and update the next free offset. Our process could get killed in between those two, // leaving the memory in an inconsistent state. We use the "IsConsistent" flag to help determine // when that has happened. freeMemoryOffset = *((int *) baseAddress); newOffset = CalculateMemory(freeMemoryOffset, totalSize, out alignmentAdjustment); } else { instanceNameLength = (instanceName.Length +1) * 2; totalSize += instanceNameLength; // add in the counter names for the global shared mem. for (int i=0; i LifetimeOffset = (int)((long)newLifetimeEntry - baseAddress); PopulateLifetimeEntry(newLifetimeEntry, lifetime); } // set up the InstanceEntry newInstanceEntryPointer->InstanceNameHashCode = instanceNameHashCode; newInstanceEntryPointer->NextInstanceOffset = 0; newInstanceEntryPointer->FirstCounterOffset = (int)((long)newCounterEntryPointer - baseAddress); newInstanceEntryPointer->RefCount = 1; newInstanceEntryPointer->InstanceNameOffset = (int) (nextPtr - baseAddress); Marshal.Copy(instanceName.ToCharArray(), 0, (IntPtr) nextPtr, instanceName.Length); nextPtr += instanceNameLength; if (categoryData.UseUniqueSharedMemory) { // in the unique shared mem we'll assume that the CounterEntries of the first instance // are all created. Then we can just refer to the old counter name rather than copying in a new one. InstanceEntry* firstInstanceInCategoryPointer = (InstanceEntry*) ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize); CounterEntry* firstCounterInCategoryPointer = (CounterEntry*) ResolveOffset(firstInstanceInCategoryPointer->FirstCounterOffset, CounterEntrySize); newCounterEntryPointer->CounterNameHashCode = firstCounterInCategoryPointer->CounterNameHashCode; SetValue(newCounterEntryPointer, 0); newCounterEntryPointer->CounterNameOffset = firstCounterInCategoryPointer->CounterNameOffset; // now create the rest of the CounterEntrys CounterEntry* previousCounterEntryPointer; for (int i=1; i NextCounterOffset != 0, "The unique shared memory should have all of its counters created by the time we hit CreateInstance"); firstCounterInCategoryPointer = (CounterEntry*) ResolveOffset(firstCounterInCategoryPointer->NextCounterOffset, CounterEntrySize); newCounterEntryPointer->CounterNameHashCode = firstCounterInCategoryPointer->CounterNameHashCode; SetValue(newCounterEntryPointer, 0); newCounterEntryPointer->CounterNameOffset = firstCounterInCategoryPointer->CounterNameOffset; previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress); } } else { // now create the rest of the CounterEntrys CounterEntry* previousCounterEntryPointer = null; for (int i=0; i CounterNameHashCode = GetWstrHashCode(counterName); newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress); Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length); nextPtr += (counterName.Length + 1) * 2; SetValue(newCounterEntryPointer, 0); if (i != 0) previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress); previousCounterEntryPointer = newCounterEntryPointer; newCounterEntryPointer++; } } Debug.Assert(nextPtr - baseAddress == freeMemoryOffset + totalSize, "We should have used all of the space we requested at this point"); int offset = (int) ((long) newInstanceEntryPointer - baseAddress); categoryPointer->IsConsistent = 0; // prepend the new instance rather than append, helps with perf of hooking up subsequent counters newInstanceEntryPointer->NextInstanceOffset = categoryPointer->FirstInstanceOffset; categoryPointer->FirstInstanceOffset = offset; if (categoryData.UseUniqueSharedMemory) { *((int*) baseAddress) = newOffset; categoryPointer->IsConsistent = 1; } return freeMemoryOffset; } private unsafe int CreateCounter(CounterEntry* lastCounterPointer, int counterNameHashCode, string counterName) { int counterNameLength = (counterName.Length + 1) * 2; int totalSize = sizeof(CounterEntry) + counterNameLength; int alignmentAdjustment; int freeMemoryOffset; Debug.Assert(!categoryData.UseUniqueSharedMemory, "We should never be calling CreateCounter in the unique shared memory"); freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment); freeMemoryOffset += alignmentAdjustment; long nextPtr = ResolveOffset(freeMemoryOffset, totalSize); CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr; nextPtr += sizeof(CounterEntry); newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress); newCounterEntryPointer->CounterNameHashCode = counterNameHashCode; newCounterEntryPointer->NextCounterOffset = 0; SetValue(newCounterEntryPointer, 0); Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length); Debug.Assert(nextPtr + counterNameLength - baseAddress == freeMemoryOffset + totalSize, "We should have used all of the space we requested at this point"); lastCounterPointer->NextCounterOffset = (int) ((long) newCounterEntryPointer - baseAddress); return freeMemoryOffset; } private unsafe static void PopulateLifetimeEntry(ProcessLifetimeEntry *lifetimeEntry, PerformanceCounterInstanceLifetime lifetime) { if (lifetime == PerformanceCounterInstanceLifetime.Process) { lifetimeEntry->LifetimeType = (int) PerformanceCounterInstanceLifetime.Process; lifetimeEntry->ProcessId = ProcessData.ProcessId; lifetimeEntry->StartupTime = ProcessData.StartupTime; } else { lifetimeEntry->ProcessId = 0; lifetimeEntry->StartupTime = 0; } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private static unsafe void WaitAndEnterCriticalSection(int* spinLockPointer, out bool taken) { WaitForCriticalSection(spinLockPointer); // Note - we are taking a lock here, but it probably isn't // worthwhile to use Thread.BeginCriticalRegion & EndCriticalRegion. // These only really help the CLR escalate from a thread abort // to an appdomain unload, under the assumption that you may be // editing shared state within the appdomain. Here you are editing // shared state, but it is shared across processes. Unloading the // appdomain isn't exactly helping. The only thing that would help // would be if the CLR tells the host to ensure all allocations // have a higher chance of succeeding within this critical region, // but of course that's only a probabilisitic statement. // Must be able to assign to the out param. RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { int r = Interlocked.CompareExchange(ref *spinLockPointer, 1, 0); taken = (r == 0); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private static unsafe void WaitForCriticalSection(int* spinLockPointer) { int spinCount = MaxSpinCount; for (; spinCount > 0 && *spinLockPointer != 0; spinCount--) { // We suspect there are scenarios where the finalizer thread // will call this method. The finalizer thread runs with // a higher priority than the other code. Using SpinWait // isn't sufficient, since it only spins, but doesn't yield // to any lower-priority threads. Call Thread.Sleep(1). if (*spinLockPointer != 0) Thread.Sleep(1); } // if the lock still isn't free, most likely there's a deadlock caused by a process // getting killed while it held the lock. We'll just free the lock if (spinCount == 0 && *spinLockPointer != 0) *spinLockPointer = 0; } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static unsafe void ExitCriticalSection(int* spinLockPointer) { *spinLockPointer = 0; } // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING // This hashcode function is identical to the one in SharedPerformanceCounter.cpp. If // you change one without changing the other, perfcounters will break. internal static int GetWstrHashCode(string wstr) { uint hash = 5381; for(uint i=0; i < wstr.Length; i++) hash = ((hash << 5) + hash) ^ wstr[(int) i]; return (int)hash; } // Calculate the length of a string in the shared memory. If we reach the end of the shared memory // before we see a null terminator, we throw. [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private unsafe int GetStringLength(char* startChar) { char* currentChar = startChar; ulong endAddress = (ulong) (baseAddress + FileView.FileMappingSize); while((ulong) currentChar < (endAddress - 2)) { if (*currentChar == 0) return (int) (currentChar - startChar); currentChar++; } throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted)); } // Compare a managed string to a string located at a given offset. If we walk past the end of the // shared memory, we throw. [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private unsafe bool StringEquals(string stringA, int offset) { char* currentChar = (char*) ResolveOffset(offset, 0); ulong endAddress = (ulong) (baseAddress + FileView.FileMappingSize); int i; for (i=0; i (endAddress - 2)) throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted)); if (stringA[i] != currentChar[i]) return false; } // now check for the null termination. if ((ulong) (currentChar+i) > (endAddress - 2)) throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted)); return (currentChar[i] == 0); } [ResourceExposure(ResourceScope.Machine)] // Memory maps the perf counter data file [ResourceConsumption(ResourceScope.Machine)] private unsafe CategoryData GetCategoryData() { CategoryData data = (CategoryData) categoryDataTable[categoryName]; if (data == null) { lock(categoryDataTable) { data = (CategoryData) categoryDataTable[categoryName]; if (data == null) { data = new CategoryData(); data.FileMappingName = DefaultFileMappingName; data.MutexName = categoryName; RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); registryPermission.Assert(); RegistryKey categoryKey = null; try { categoryKey = Registry.LocalMachine.OpenSubKey(PerformanceCounterLib.ServicePath + "\\" + categoryName + "\\Performance"); // first read the options Object optionsObject = categoryKey.GetValue("CategoryOptions"); if (optionsObject != null) { int options = (int) optionsObject; data.EnableReuse = (((PerformanceCounterCategoryOptions) options & PerformanceCounterCategoryOptions.EnableReuse) != 0); if (((PerformanceCounterCategoryOptions) options & PerformanceCounterCategoryOptions.UseUniqueSharedMemory) != 0) { data.UseUniqueSharedMemory = true; InitialOffset = 8; data.FileMappingName = DefaultFileMappingName + categoryName; } } int fileMappingSize; object fileMappingSizeObject = categoryKey.GetValue("FileMappingSize"); if (fileMappingSizeObject != null && data.UseUniqueSharedMemory) { // we only use this reg value in the unique shared memory case. fileMappingSize = (int) fileMappingSizeObject; if (fileMappingSize < MinCountersFileMappingSize) fileMappingSize = MinCountersFileMappingSize; if (fileMappingSize > MaxCountersFileMappingSize) fileMappingSize = MaxCountersFileMappingSize; } else { fileMappingSize = GetFileMappingSizeFromConfig(); if (data.UseUniqueSharedMemory) fileMappingSize = fileMappingSize >> 2; // if we have a custom filemapping, only make it 25% as large. } // now read the counter names object counterNamesObject = categoryKey.GetValue("Counter Names"); byte[] counterNamesBytes = counterNamesObject as byte[]; if (counterNamesBytes != null) { ArrayList names = new ArrayList(); fixed (byte* counterNamesPtr = counterNamesBytes) { int start = 0; for (int i=0; i SpinLock), out sectionEntered); int newCategoryOffset; if (sectionEntered) { try { newCategoryOffset = CreateCategory(categoryPointer, instanceNameHashCode, instanceName, lifetime); } finally { if (!categoryData.UseUniqueSharedMemory) ExitCriticalSection(&(categoryPointer->SpinLock)); } categoryPointer = (CategoryEntry*)(ResolveOffset(newCategoryOffset, CategoryEntrySize)); instancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer); Debug.Assert(counterFound, "All counters should be created, so we should always find the counter"); return counterPointer; } } bool foundFreeInstance; while (!FindInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, true, lifetime, out foundFreeInstance)) { InstanceEntry* lockInstancePointer = instancePointer; // don't bother locking again if we're using a separate shared memory. bool sectionEntered; if (categoryData.UseUniqueSharedMemory) sectionEntered = true; else WaitAndEnterCriticalSection(&(lockInstancePointer->SpinLock), out sectionEntered); if (sectionEntered) { try { bool reused = false; if (enableReuse && foundFreeInstance) { reused = TryReuseInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, lifetime, lockInstancePointer); // 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 // we're looking for. } if (!reused) { int newInstanceOffset = CreateInstance(categoryPointer, instanceNameHashCode, instanceName, lifetime); instancePointer = (InstanceEntry*)(ResolveOffset(newInstanceOffset, InstanceEntrySize)); counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer); Debug.Assert(counterFound, "All counters should be created, so we should always find the counter"); return counterPointer; } } finally { if (!categoryData.UseUniqueSharedMemory) ExitCriticalSection(&(lockInstancePointer->SpinLock)); } } } if (categoryData.UseUniqueSharedMemory) { counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer); Debug.Assert(counterFound, "All counters should be created, so we should always find the counter"); return counterPointer; } else { while (!FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer)) { bool sectionEntered; WaitAndEnterCriticalSection(&(counterPointer->SpinLock), out sectionEntered); if (sectionEntered) { try { int newCounterOffset = CreateCounter(counterPointer, counterNameHashCode, counterName); return (CounterEntry*) (ResolveOffset(newCounterOffset, CounterEntrySize)); } finally { ExitCriticalSection(&(counterPointer->SpinLock)); } } } return counterPointer; } } finally { // cache this instance for reuse try { if (counterPointer != null && instancePointer != null) { this.thisInstanceOffset = ResolveAddress((long)instancePointer, InstanceEntrySize); } } catch (InvalidOperationException) { this.thisInstanceOffset = -1; } if (mutex != null) { mutex.ReleaseMutex(); mutex.Close(); } } } private unsafe bool FindCategory(CategoryEntry** returnCategoryPointerReference) { CategoryEntry* firstCategoryPointer = (CategoryEntry*)(ResolveOffset(InitialOffset, CategoryEntrySize)); CategoryEntry* currentCategoryPointer = firstCategoryPointer; CategoryEntry* previousCategoryPointer = firstCategoryPointer; for(;;) { if (currentCategoryPointer->IsConsistent == 0) Verify(currentCategoryPointer); if (currentCategoryPointer->CategoryNameHashCode == categoryNameHashCode) { if (StringEquals(categoryName, currentCategoryPointer->CategoryNameOffset)) { *returnCategoryPointerReference = currentCategoryPointer; return true; } } previousCategoryPointer = currentCategoryPointer; if (currentCategoryPointer->NextCategoryOffset != 0) currentCategoryPointer = (CategoryEntry*)(ResolveOffset(currentCategoryPointer->NextCategoryOffset, CategoryEntrySize)); else { *returnCategoryPointerReference = previousCategoryPointer; return false; } } } private unsafe bool FindCounter(int counterNameHashCode, string counterName, InstanceEntry* instancePointer, CounterEntry** returnCounterPointerReference) { CounterEntry* currentCounterPointer = (CounterEntry*)(ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize)); CounterEntry* previousCounterPointer = currentCounterPointer; for(;;) { if (currentCounterPointer->CounterNameHashCode == counterNameHashCode) { if (StringEquals(counterName, currentCounterPointer->CounterNameOffset)) { *returnCounterPointerReference = currentCounterPointer; return true; } } previousCounterPointer = currentCounterPointer; if (currentCounterPointer->NextCounterOffset != 0) currentCounterPointer = (CounterEntry*)(ResolveOffset(currentCounterPointer->NextCounterOffset, CounterEntrySize)); else { *returnCounterPointerReference = previousCounterPointer; return false; } } } private unsafe bool FindInstance(int instanceNameHashCode, string instanceName, CategoryEntry* categoryPointer, InstanceEntry** returnInstancePointerReference, bool activateUnusedInstances, PerformanceCounterInstanceLifetime lifetime, out bool foundFreeInstance) { InstanceEntry* currentInstancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); InstanceEntry* previousInstancePointer = currentInstancePointer; foundFreeInstance = false; // Look at the first instance to determine if this is single or multi instance. if (currentInstancePointer->InstanceNameHashCode == SingleInstanceHashCode) { if (StringEquals(SingleInstanceName, currentInstancePointer->InstanceNameOffset)){ if (instanceName != SingleInstanceName) throw new InvalidOperationException(SR.GetString(SR.SingleInstanceOnly, categoryName)); } else { if (instanceName == SingleInstanceName) throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, categoryName)); } } else { if (instanceName == SingleInstanceName) throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, categoryName)); } // // 1st pass find exact matching! // // We don't need to aggressively claim unused instances. For performance, we would proactively // verify lifetime of instances if activateUnusedInstances is specified and certain time // has elapsed since last sweep or we are running out of shared memory. bool verifyLifeTime = activateUnusedInstances; if (activateUnusedInstances) { int totalSize = InstanceEntrySize + ProcessLifetimeEntrySize + InstanceNameSlotSize + (CounterEntrySize * categoryData.CounterNames.Count); int freeMemoryOffset = *((int *) baseAddress); int alignmentAdjustment; int newOffset = CalculateMemoryNoBoundsCheck(freeMemoryOffset, totalSize, out alignmentAdjustment); if (!(newOffset > FileView.FileMappingSize || newOffset < 0)) { long tickDelta = (DateTime.Now.Ticks - LastInstanceLifetimeSweepTick); if (tickDelta < InstanceLifetimeSweepWindow) verifyLifeTime = false; } } new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { for(;;) { bool verifiedLifetimeOfThisInstance = false; if (verifyLifeTime && (currentInstancePointer->RefCount != 0)) { verifiedLifetimeOfThisInstance = true; VerifyLifetime(currentInstancePointer); } if (currentInstancePointer->InstanceNameHashCode == instanceNameHashCode) { if (StringEquals(instanceName, currentInstancePointer->InstanceNameOffset)){ // we found a matching instance. *returnInstancePointerReference = currentInstancePointer; CounterEntry* firstCounter = (CounterEntry*) ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize); ProcessLifetimeEntry* lifetimeEntry; if (categoryData.UseUniqueSharedMemory) lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(firstCounter->LifetimeOffset, ProcessLifetimeEntrySize); else lifetimeEntry = null; // ensure that we have verified the lifetime of the matched instance if (!verifiedLifetimeOfThisInstance && currentInstancePointer->RefCount != 0) VerifyLifetime(currentInstancePointer); if (currentInstancePointer->RefCount != 0) { if (lifetimeEntry != null && lifetimeEntry->ProcessId != 0) { if (lifetime != PerformanceCounterInstanceLifetime.Process) throw new InvalidOperationException(SR.GetString(SR.CantConvertProcessToGlobal)); // make sure only one process is using this instance. if (ProcessData.ProcessId != lifetimeEntry->ProcessId) throw new InvalidOperationException(SR.GetString(SR.InstanceAlreadyExists, instanceName)); // compare start time of the process, account for ACL issues in querying process information if ((lifetimeEntry->StartupTime != -1) && (ProcessData.StartupTime != -1)) { if (ProcessData.StartupTime != lifetimeEntry->StartupTime) throw new InvalidOperationException(SR.GetString(SR.InstanceAlreadyExists, instanceName)); } } else { if (lifetime == PerformanceCounterInstanceLifetime.Process) throw new InvalidOperationException(SR.GetString(SR.CantConvertGlobalToProcess)); } return true; } if (activateUnusedInstances) { Mutex mutex = null; RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex); ClearCounterValues(currentInstancePointer); if (lifetimeEntry != null) PopulateLifetimeEntry(lifetimeEntry, lifetime); currentInstancePointer->RefCount = 1; return true; } finally { if (mutex != null) { mutex.ReleaseMutex(); mutex.Close(); } } } else return false; } } if (currentInstancePointer->RefCount == 0) { foundFreeInstance = true; } previousInstancePointer = currentInstancePointer; if (currentInstancePointer->NextInstanceOffset != 0) currentInstancePointer = (InstanceEntry*)(ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize)); else { *returnInstancePointerReference = previousInstancePointer; return false; } } } finally { SecurityPermission.RevertAssert(); if (verifyLifeTime) LastInstanceLifetimeSweepTick = DateTime.Now.Ticks; } } private unsafe bool TryReuseInstance(int instanceNameHashCode, string instanceName, CategoryEntry* categoryPointer, InstanceEntry** returnInstancePointerReference, PerformanceCounterInstanceLifetime lifetime, InstanceEntry* lockInstancePointer) { // // 2nd pass find a free instance slot // InstanceEntry* currentInstancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); InstanceEntry* previousInstancePointer = currentInstancePointer; for (;;) { if (currentInstancePointer->RefCount == 0) { bool hasFit; long instanceNamePtr; // we need cache this to avoid race conditions. if (categoryData.UseUniqueSharedMemory) { instanceNamePtr = ResolveOffset(currentInstancePointer->InstanceNameOffset, InstanceNameSlotSize); // In the separate shared memory case we should always have enough space for instances. The // name slot size is fixed. Debug.Assert(((instanceName.Length + 1) * 2) <= InstanceNameSlotSize, "The instance name length should always fit in our slot size"); hasFit = true; } else { // we don't know the string length yet. instanceNamePtr = ResolveOffset(currentInstancePointer->InstanceNameOffset, 0); // In the global shared memory, we require names to be exactly the same length in order // to reuse them. This way we don't end up leaking any space and we don't need to // depend on the layout of the memory to calculate the space we have. int length = GetStringLength((char*) instanceNamePtr); hasFit = (length == instanceName.Length); } bool noSpinLock = (lockInstancePointer == currentInstancePointer) || categoryData.UseUniqueSharedMemory; // Instance name fit if (hasFit) { // don't bother locking again if we're using a separate shared memory. bool sectionEntered; if (noSpinLock) sectionEntered = true; else WaitAndEnterCriticalSection(&(currentInstancePointer->SpinLock), out sectionEntered); if (sectionEntered) { try { // Make copy with zero-term char[] tmp = new char[instanceName.Length + 1]; instanceName.CopyTo(0, tmp, 0, instanceName.Length); tmp[instanceName.Length] = '\0'; Marshal.Copy(tmp, 0, (IntPtr) instanceNamePtr, tmp.Length); currentInstancePointer->InstanceNameHashCode = instanceNameHashCode; // return *returnInstancePointerReference = currentInstancePointer; // clear the counter values. ClearCounterValues(*returnInstancePointerReference); if (categoryData.UseUniqueSharedMemory) { CounterEntry* counterPointer = (CounterEntry*)ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize); ProcessLifetimeEntry* lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(counterPointer->LifetimeOffset, ProcessLifetimeEntrySize); PopulateLifetimeEntry(lifetimeEntry, lifetime); } (*returnInstancePointerReference)->RefCount = 1; return true; } finally { if (!noSpinLock) ExitCriticalSection(&(currentInstancePointer->SpinLock)); } } } } previousInstancePointer = currentInstancePointer; if (currentInstancePointer->NextInstanceOffset != 0) currentInstancePointer = (InstanceEntry*)(ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize)); else { *returnInstancePointerReference = previousInstancePointer; return false; } } } private unsafe void Verify(CategoryEntry* currentCategoryPointer) { if (!categoryData.UseUniqueSharedMemory) return; Mutex mutex = null; RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex); VerifyCategory(currentCategoryPointer); } finally { if (mutex != null) { mutex.ReleaseMutex(); mutex.Close(); } } } private unsafe void VerifyCategory(CategoryEntry* currentCategoryPointer) { int freeOffset = *((int*)baseAddress); ResolveOffset(freeOffset, 0); // verify next free offset if (currentCategoryPointer->NextCategoryOffset > freeOffset) currentCategoryPointer->NextCategoryOffset = 0; else if (currentCategoryPointer->NextCategoryOffset != 0) VerifyCategory((CategoryEntry*) ResolveOffset(currentCategoryPointer->NextCategoryOffset, CategoryEntrySize)); if (currentCategoryPointer->FirstInstanceOffset != 0) { // In V3, we started prepending the new instances rather than appending (as in V2) for performance. // Check whether the recently added instance at the head of the list is committed. If not, rewire // the head of the list to point to the next instance if (currentCategoryPointer->FirstInstanceOffset > freeOffset) { InstanceEntry* currentInstancePointer = (InstanceEntry*) ResolveOffset(currentCategoryPointer->FirstInstanceOffset, InstanceEntrySize); currentCategoryPointer->FirstInstanceOffset = currentInstancePointer->NextInstanceOffset; if (currentCategoryPointer->FirstInstanceOffset > freeOffset) currentCategoryPointer->FirstInstanceOffset = 0; } // if (currentCategoryPointer->FirstInstanceOffset != 0) { Debug.Assert(currentCategoryPointer->FirstInstanceOffset <= freeOffset, "The head of the list is inconsistent - possible mismatch of V2 & V3 instances?"); VerifyInstance((InstanceEntry*) ResolveOffset(currentCategoryPointer->FirstInstanceOffset, InstanceEntrySize)); } } currentCategoryPointer->IsConsistent = 1; } private unsafe void VerifyInstance(InstanceEntry* currentInstancePointer) { int freeOffset = *((int*)baseAddress); ResolveOffset(freeOffset, 0); // verify next free offset if (currentInstancePointer->NextInstanceOffset > freeOffset) currentInstancePointer->NextInstanceOffset = 0; else if (currentInstancePointer->NextInstanceOffset != 0) VerifyInstance((InstanceEntry*) ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize)); } private unsafe void VerifyLifetime(InstanceEntry* currentInstancePointer) { Debug.Assert(currentInstancePointer->RefCount != 0, "RefCount must be 1 for instances passed to VerifyLifetime"); CounterEntry* counter = (CounterEntry*) ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize); if (counter->LifetimeOffset != 0) { ProcessLifetimeEntry* lifetime = (ProcessLifetimeEntry*) ResolveOffset(counter->LifetimeOffset, ProcessLifetimeEntrySize); if (lifetime->LifetimeType == (int) PerformanceCounterInstanceLifetime.Process) { int pid = lifetime->ProcessId; long startTime = lifetime->StartupTime; if (pid != 0) { // Optimize for this process if (pid == ProcessData.ProcessId) { if ((ProcessData.StartupTime != -1) && (startTime != -1) && (ProcessData.StartupTime != startTime)) { // Process id got recycled. Reclaim this instance. currentInstancePointer->RefCount = 0; return; } } else { long processStartTime; using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, false, pid)) { int error = Marshal.GetLastWin32Error(); if ((error == NativeMethods.ERROR_INVALID_PARAMETER) && procHandle.IsInvalid) { // The process is dead. Reclaim this instance. Note that we only clear the refcount here. // If we tried to clear the pid and startup time as well, we would have a race where // we could clear the pid/startup time but not the refcount. currentInstancePointer->RefCount = 0; return; } // Defer cleaning the instance when we had previously encountered errors in // recording process start time (i.e, when startTime == -1) until after the // process id is not valid (which will be caught in the if check above) if (!procHandle.IsInvalid && startTime != -1) { long temp; if (NativeMethods.GetProcessTimes(procHandle, out processStartTime, out temp, out temp, out temp)) { if (processStartTime != startTime) { // The process is dead but a new one is using the same pid. Reclaim this instance. currentInstancePointer->RefCount = 0; return; } } } } } } } } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal unsafe long IncrementBy(long value) { if (counterEntryPointer == null) return 0; CounterEntry* counterEntry = this.counterEntryPointer; return AddToValue(counterEntry, value); } internal unsafe long Increment() { if (counterEntryPointer == null) return 0; return IncrementUnaligned(this.counterEntryPointer); } internal unsafe long Decrement() { if (counterEntryPointer == null) return 0; return DecrementUnaligned(this.counterEntryPointer); } internal unsafe static void RemoveAllInstances(string categoryName) { SharedPerformanceCounter spc = new SharedPerformanceCounter(categoryName, null, null); spc.RemoveAllInstances(); RemoveCategoryData(categoryName); } private unsafe void RemoveAllInstances() { CategoryEntry* categoryPointer; if (!FindCategory(&categoryPointer)) return; InstanceEntry* instancePointer = (InstanceEntry *)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); Mutex mutex = null; RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex); for(;;) { RemoveOneInstance(instancePointer, true); if (instancePointer->NextInstanceOffset != 0) instancePointer = (InstanceEntry*)(ResolveOffset(instancePointer->NextInstanceOffset, InstanceEntrySize)); else { break; } } } finally { if (mutex != null) { mutex.ReleaseMutex(); mutex.Close(); } } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal unsafe void RemoveInstance(string instanceName, PerformanceCounterInstanceLifetime instanceLifetime) { if (instanceName == null || instanceName.Length == 0) return; int instanceNameHashCode = GetWstrHashCode(instanceName); CategoryEntry* categoryPointer; if (!FindCategory(&categoryPointer)) return; InstanceEntry* instancePointer = null; bool validatedCachedInstancePointer = false; bool temp; Mutex mutex = null; RuntimeHelpers.PrepareConstrainedRegions(); try { SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex); if (this.thisInstanceOffset != -1) { try { // validate whether the cached instance pointer is pointing at the right instance instancePointer = (InstanceEntry*)(ResolveOffset(this.thisInstanceOffset, InstanceEntrySize)); if (instancePointer->InstanceNameHashCode == instanceNameHashCode) { if (StringEquals(instanceName, instancePointer->InstanceNameOffset)){ validatedCachedInstancePointer = true; // this is probably overkill CounterEntry* firstCounter = (CounterEntry*) ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize); ProcessLifetimeEntry* lifetimeEntry; if (categoryData.UseUniqueSharedMemory) { lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(firstCounter->LifetimeOffset, ProcessLifetimeEntrySize); if (lifetimeEntry != null && lifetimeEntry->LifetimeType == (int)PerformanceCounterInstanceLifetime.Process && lifetimeEntry->ProcessId != 0) { validatedCachedInstancePointer &= (instanceLifetime == PerformanceCounterInstanceLifetime.Process); validatedCachedInstancePointer &= (ProcessData.ProcessId == lifetimeEntry->ProcessId); if ((lifetimeEntry->StartupTime != -1) && (ProcessData.StartupTime != -1)) validatedCachedInstancePointer &= (ProcessData.StartupTime == lifetimeEntry->StartupTime); } else validatedCachedInstancePointer &= (instanceLifetime != PerformanceCounterInstanceLifetime.Process); } } } } catch (InvalidOperationException) { validatedCachedInstancePointer = false; } if (!validatedCachedInstancePointer) this.thisInstanceOffset = -1; } if (!validatedCachedInstancePointer && !FindInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, false, instanceLifetime, out temp)) return ; if (instancePointer != null) RemoveOneInstance(instancePointer, false); } finally { if (mutex != null) { mutex.ReleaseMutex(); mutex.Close(); } } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private unsafe void RemoveOneInstance(InstanceEntry* instancePointer, bool clearValue) { bool sectionEntered = false; RuntimeHelpers.PrepareConstrainedRegions(); try { if (!categoryData.UseUniqueSharedMemory) { while (!sectionEntered) { WaitAndEnterCriticalSection(&(instancePointer->SpinLock), out sectionEntered); } } instancePointer->RefCount = 0; if (clearValue) ClearCounterValues(instancePointer); } finally { if (sectionEntered) ExitCriticalSection(&(instancePointer->SpinLock)); } } private unsafe void ClearCounterValues(InstanceEntry* instancePointer) { //Clear counter instance values CounterEntry* currentCounterPointer = null; if (instancePointer->FirstCounterOffset != 0) currentCounterPointer = (CounterEntry*)(ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize)); while(currentCounterPointer != null) { SetValue(currentCounterPointer, 0); if (currentCounterPointer->NextCounterOffset != 0) currentCounterPointer = (CounterEntry*)(ResolveOffset(currentCounterPointer->NextCounterOffset, CounterEntrySize)); else currentCounterPointer = null; } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static unsafe long AddToValue(CounterEntry* counterEntry, long addend) { // Called while holding a lock - shouldn't have to worry about // reading misaligned data & getting old vs. new parts of an Int64. if (IsMisaligned(counterEntry)) { ulong newvalue; CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry; newvalue = (uint)entry->Value_hi; newvalue <<= 32; newvalue |= (uint)entry->Value_lo; newvalue = (ulong) ((long) newvalue + addend); entry->Value_hi = (int) (newvalue >> 32); entry->Value_lo = (int) (newvalue & 0xffffffff); return (long) newvalue; } else return Interlocked.Add(ref counterEntry->Value, addend); } private static unsafe long DecrementUnaligned(CounterEntry* counterEntry) { if (IsMisaligned(counterEntry)) return AddToValue(counterEntry, -1); else return Interlocked.Decrement(ref counterEntry->Value); } private static unsafe long GetValue(CounterEntry* counterEntry) { if (IsMisaligned(counterEntry)) { ulong value; CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry; value = (uint)entry->Value_hi; value <<= 32; value |= (uint)entry->Value_lo; return (long) value; } else return counterEntry->Value; } private static unsafe long IncrementUnaligned(CounterEntry* counterEntry) { if (IsMisaligned(counterEntry)) return AddToValue(counterEntry, 1); else return Interlocked.Increment(ref counterEntry->Value); } private static unsafe void SetValue(CounterEntry* counterEntry, long value) { if (IsMisaligned(counterEntry)) { CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry; entry->Value_lo = (int) (value & 0xffffffff); entry->Value_hi = (int) (value >> 32); } else counterEntry->Value = value; } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static unsafe bool IsMisaligned(CounterEntry* counterEntry) { return (( (Int64)counterEntry & 0x7) != 0); } [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private long ResolveOffset(int offset, int sizeToRead) { //It is very important to check the integrity of the shared memory //everytime a new address is resolved. if (offset > (FileView.FileMappingSize - sizeToRead) || offset < 0) throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted)); long address = baseAddress + offset; return address; } [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private int ResolveAddress(long address, int sizeToRead) { int offset = (int)(address - baseAddress); //It is very important to check the integrity of the shared memory //everytime a new address is resolved. if (offset > (FileView.FileMappingSize - sizeToRead) || offset < 0) throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted)); return offset; } private class FileMapping { internal int FileMappingSize; private SafeFileMapViewHandle fileViewAddress = null; private SafeFileMappingHandle fileMappingHandle = null; //The version of the file mapping name is independent from the //assembly version. public FileMapping(string fileMappingName, int fileMappingSize, int initialOffset) { this.Initialize(fileMappingName, fileMappingSize, initialOffset); } internal IntPtr FileViewAddress { get { if (fileViewAddress.IsInvalid) throw new InvalidOperationException(SR.GetString(SR.SharedMemoryGhosted)); return fileViewAddress.DangerousGetHandle(); } } private unsafe void Initialize(string fileMappingName, int fileMappingSize, int initialOffset) { string mappingName = fileMappingName; SharedUtils.CheckEnvironment(); SafeLocalMemHandle securityDescriptorPointer = null; new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { // The sddl string consists of these parts: // D: it's a DACL // (A; this is an allow ACE // OICI; object inherit and container inherit // FRFWGRGW;;; allow file read, file write, generic read and generic write // AU) granted to Authenticated Users // ;S-1-5-33) the same permission granted to AU is also granted to restricted services string sddlString = "D:(A;OICI;FRFWGRGW;;;AU)(A;OICI;FRFWGRGW;;;S-1-5-33)"; if (!SafeLocalMemHandle.ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, NativeMethods.SDDL_REVISION_1, out securityDescriptorPointer, IntPtr.Zero)) throw new InvalidOperationException(SR.GetString(SR.SetSecurityDescriptorFailed)); NativeMethods.SECURITY_ATTRIBUTES securityAttributes = new NativeMethods.SECURITY_ATTRIBUTES(); securityAttributes.lpSecurityDescriptor = securityDescriptorPointer; securityAttributes.bInheritHandle = false; bool created = false; while (!created) { fileMappingHandle = NativeMethods.CreateFileMapping((IntPtr)(-1), securityAttributes, NativeMethods.PAGE_READWRITE, 0, fileMappingSize, mappingName); if ((Marshal.GetLastWin32Error() != NativeMethods.ERROR_ACCESS_DENIED) || !fileMappingHandle.IsInvalid) { created = true; } else { // Invalidate the old safehandle before we get rid of it. This prevents it from trying to finalize fileMappingHandle.SetHandleAsInvalid(); fileMappingHandle = NativeMethods.OpenFileMapping(NativeMethods.FILE_MAP_WRITE, false, mappingName); if ((Marshal.GetLastWin32Error() != NativeMethods.ERROR_FILE_NOT_FOUND) || !fileMappingHandle.IsInvalid) created = true; } } if (fileMappingHandle.IsInvalid) { throw new InvalidOperationException(SR.GetString(SR.CantCreateFileMapping)); } fileViewAddress = SafeFileMapViewHandle.MapViewOfFile(fileMappingHandle, NativeMethods.FILE_MAP_WRITE, 0,0, UIntPtr.Zero); if (fileViewAddress.IsInvalid) throw new InvalidOperationException(SR.GetString(SR.CantMapFileView)); // figure out what size the share memory really is. NativeMethods.MEMORY_BASIC_INFORMATION meminfo = new NativeMethods.MEMORY_BASIC_INFORMATION(); if (NativeMethods.VirtualQuery(fileViewAddress, ref meminfo, (IntPtr) sizeof(NativeMethods.MEMORY_BASIC_INFORMATION)) == IntPtr.Zero) throw new InvalidOperationException(SR.GetString(SR.CantGetMappingSize)); FileMappingSize = (int) meminfo.RegionSize; } finally { if (securityDescriptorPointer != null) securityDescriptorPointer.Close(); SecurityPermission.RevertAssert(); } SafeNativeMethods.InterlockedCompareExchange(fileViewAddress.DangerousGetHandle(), initialOffset, 0); } } // // The final tmpPadding field is needed to make the size of this structure 8-byte aligned. This is // necessary on IA64. // // Note that in V1.0 and v1.1 there was no explicit padding defined on any of these structs. That means that // sizeof(CategoryEntry) or Marshal.SizeOf(typeof(CategoryEntry)) returned 4 bytes less before Whidbey, // and the int we use as IsConsistent could actually overlap the InstanceEntry SpinLock. [StructLayout(LayoutKind.Sequential)] private struct CategoryEntry { public int SpinLock; public int CategoryNameHashCode; public int CategoryNameOffset; public int FirstInstanceOffset; public int NextCategoryOffset; public int IsConsistent; // this was 4 bytes of padding in v1.0/v1.1 } [StructLayout(LayoutKind.Sequential)] private struct InstanceEntry { public int SpinLock; public int InstanceNameHashCode; public int InstanceNameOffset; public int RefCount; public int FirstCounterOffset; public int NextInstanceOffset; } [StructLayout(LayoutKind.Sequential)] private struct CounterEntry { public int SpinLock; public int CounterNameHashCode; public int CounterNameOffset; public int LifetimeOffset; // this was 4 bytes of padding in v1.0/v1.1 public long Value; public int NextCounterOffset; public int padding2; } [StructLayout(LayoutKind.Sequential)] private struct CounterEntryMisaligned { public int SpinLock; public int CounterNameHashCode; public int CounterNameOffset; public int LifetimeOffset; // this was 4 bytes of padding in v1.0/v1.1 public int Value_lo; public int Value_hi; public int NextCounterOffset; public int padding2; // The compiler adds this only if there is an int64 in the struct - // ie only for CounterEntry. It really needs to be here. } [StructLayout(LayoutKind.Sequential)] private struct ProcessLifetimeEntry { public int LifetimeType; public int ProcessId; public Int64 StartupTime; } private class CategoryData { public FileMapping FileMapping; public bool EnableReuse; public bool UseUniqueSharedMemory; public string FileMappingName; public string MutexName; public ArrayList CounterNames; } } internal class ProcessData { public ProcessData(int pid, long startTime) { ProcessId = pid; StartupTime = startTime; } public int ProcessId; public long StartupTime; } }
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- NullRuntimeConfig.cs
- TimeSpanConverter.cs
- HeaderedContentControl.cs
- URIFormatException.cs
- DataMemberConverter.cs
- DBCSCodePageEncoding.cs
- CriticalExceptions.cs
- PersianCalendar.cs
- ToggleProviderWrapper.cs
- webclient.cs
- PinnedBufferMemoryStream.cs
- FrameworkName.cs
- SQlBooleanStorage.cs
- WorkflowMarkupSerializerMapping.cs
- Resources.Designer.cs
- safelink.cs
- SecurityToken.cs
- TextStore.cs
- AmbientProperties.cs
- SQLRoleProvider.cs
- PersonalizationProvider.cs
- AnonymousIdentificationSection.cs
- UnmanagedBitmapWrapper.cs
- XPathSingletonIterator.cs
- ping.cs
- OperatingSystemVersionCheck.cs
- UniqueEventHelper.cs
- Point3DCollection.cs
- XsdDataContractImporter.cs
- ImageListImageEditor.cs
- JoinTreeNode.cs
- Line.cs
- ConfigurationPermission.cs
- UnsafeNativeMethods.cs
- DependencyObjectValidator.cs
- SchemaCollectionCompiler.cs
- HostProtectionException.cs
- ExpressionBindings.cs
- SiteMapNodeItemEventArgs.cs
- SamlConstants.cs
- ObjectListCommandCollection.cs
- EventArgs.cs
- WebBrowser.cs
- Help.cs
- EventBuilder.cs
- InstanceStoreQueryResult.cs
- ClientClassGenerator.cs
- NamedObject.cs
- ExpressionBuilderContext.cs
- InteropAutomationProvider.cs
- ParameterToken.cs
- JpegBitmapEncoder.cs
- DateBoldEvent.cs
- DataSourceViewSchemaConverter.cs
- ConnectionInterfaceCollection.cs
- ISSmlParser.cs
- CodeExpressionStatement.cs
- CallContext.cs
- Timeline.cs
- DataGridViewAdvancedBorderStyle.cs
- PolicyLevel.cs
- KeyMatchBuilder.cs
- ActiveXHost.cs
- Util.cs
- UnicastIPAddressInformationCollection.cs
- FreezableOperations.cs
- ApplyHostConfigurationBehavior.cs
- MsmqIntegrationInputMessage.cs
- BamlResourceDeserializer.cs
- EmptyElement.cs
- WmiInstallComponent.cs
- XPathAxisIterator.cs
- BooleanAnimationUsingKeyFrames.cs
- DynamicPhysicalDiscoSearcher.cs
- AnonymousIdentificationSection.cs
- AdornerDecorator.cs
- Fonts.cs
- TemplateInstanceAttribute.cs
- ToolZoneDesigner.cs
- CategoryNameCollection.cs
- CookieProtection.cs
- StringResourceManager.cs
- ConfigurationManagerInternalFactory.cs
- StoreContentChangedEventArgs.cs
- CodeBinaryOperatorExpression.cs
- HandledMouseEvent.cs
- MimeXmlReflector.cs
- BitmapImage.cs
- DrawingGroup.cs
- DataTransferEventArgs.cs
- XmlAnyElementAttributes.cs
- KnownTypesHelper.cs
- CodeGen.cs
- SerTrace.cs
- Rfc2898DeriveBytes.cs
- ReaderWriterLockWrapper.cs
- WindowsContainer.cs
- SkewTransform.cs
- RequestQueryProcessor.cs
- ObjectParameter.cs