diff options
Diffstat (limited to 'src/ipcman/ipcheader.h')
-rw-r--r-- | src/ipcman/ipcheader.h | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/src/ipcman/ipcheader.h b/src/ipcman/ipcheader.h new file mode 100644 index 0000000000..98e940699e --- /dev/null +++ b/src/ipcman/ipcheader.h @@ -0,0 +1,561 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// ==++== +// + +// +// ==--== +//***************************************************************************** +// File: IPCHeader.h +// +// Define the LegacyPrivate header format for COM+ memory mapped files. Everyone +// outside of IPCMan.lib will use the public header, IPCManagerInterface.h +// +//***************************************************************************** + +#ifndef _IPCManagerPriv_h_ +#define _IPCManagerPriv_h_ + + +//----------------------------------------------------------------------------- +// We must pull in the headers of all client blocks +// <TODO>@todo - resolve these directory links</TODO> +//----------------------------------------------------------------------------- +#include "../debug/inc/dbgappdomain.h" +#include "perfcounterdefs.h" + +#include "ndpversion.h" +#include "ipcenums.h" + +//----------------------------------------------------------------------------- +// Entry in the IPC Directory. Ensure binary compatibility across versions +// if we add (or remove) entries. If we remove an block, the entry should +// be EMPTY_ENTRY_OFFSET +//----------------------------------------------------------------------------- + +const DWORD EMPTY_ENTRY_OFFSET = 0xFFFFFFFF; +const DWORD EMPTY_ENTRY_SIZE = 0; + +struct IPCEntry +{ + DWORD m_Offset; // offset of the IPC Block from the end of the Full IPC Header + DWORD m_Size; // size (in bytes) of the block +}; + +// Newer versions of the CLR use m_Flags field +const USHORT IPC_FLAG_USES_FLAGS = 0x1; +const USHORT IPC_FLAG_INITIALIZED = 0x2; +const USHORT IPC_FLAG_X86 = 0x4; + +/****************************************************************************** + * The CLR opens memory mapped files to expose perfcounter values and other + * information to other processes. Historically there have been three memory + * mapped files: the BlockTable, the LegacyPrivateBlock, and the + * LegacyPublicBlock. + * + * BlockTable - The block table was designed to work with multiple runtimes + * running side by side in the same process (SxS in-proc). We have defined + * semantics using interlocked operations that allow a runtime to allocate + * a block from the block table in a thread-safe manner. + * + * LegacyPrivateBlock - The legacy private block was used by older versions + * of the runtime to expose various information to the debugger. The + * legacy private block is not compatible with in-proc SxS, and thus it + * must be removed in the near future. Currently it is being used to expose + * information about AppDomains to the debugger. We will need to keep the + * code that knows how to read the legacy private block as long as we + * continue to support .NET 3.5 SP1. + * + * LegacyPublicBlock - The legacy public block was used by older versions + * of the runtime to expose perfcounter values. The legacy public block is + * not compatible with in-proc SxS, and thus it has been removed. We will + * need to keep the code that knows how to read the legacy public block as + * long as we continue to support .NET 3.5 SP1. + ******************************************************************************/ + +/**************************************** BLOCK TABLE ****************************************/ + +const DWORD IPC_BLOCK_TABLE_SIZE = 65536; +const DWORD IPC_BLOCK_SIZE = 2048; +const DWORD IPC_NUM_BLOCKS_IN_TABLE = 32; + +static_assert_no_msg(IPC_BLOCK_TABLE_SIZE == IPC_NUM_BLOCKS_IN_TABLE * IPC_BLOCK_SIZE); + +#if defined(TIA64) +#define IA64MemoryBarrier() MemoryBarrier() +#else +#define IA64MemoryBarrier() +#endif + +struct IPCHeader +{ + // Chunk header + Volatile<LONG> m_Counter; // value of 0 is special; means that this block has never been touched before by a writer + DWORD m_RuntimeId; // value of 0 is special; means that chunk is currently free (runtime ids are always greater than 0) + DWORD m_Reserved1; + DWORD m_Reserved2; + + // Standard header (m_hInstance was removed) + USHORT m_Version; // version of the IPC Block + USHORT m_Flags; // flags field + DWORD m_blockSize; // Size of the entire shared memory block + USHORT m_BuildYear; // stamp for year built + USHORT m_BuildNumber; // stamp for Month/Day built + DWORD m_numEntries; // Number of entries in the table + + // Directory + IPCEntry m_table[eIPC_MAX]; // entry describing each client's block +}; + +extern BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount); + +class IPCHeaderLockHolder +{ + LONG m_Counter; + BOOL m_HaveLock; + IPCHeader & m_Header; + + public: + + IPCHeaderLockHolder(IPCHeader & header) : m_HaveLock(FALSE), m_Header(header) {} + + BOOL TryGetLock() + { + _ASSERTE(!m_HaveLock); + LONG oldCounter = m_Header.m_Counter; + if ((oldCounter & 1) != 0) + return FALSE; + m_Counter = oldCounter + 1; + if (InterlockedCompareExchange((LONG *)(&(m_Header.m_Counter)), m_Counter, oldCounter) != oldCounter) + return FALSE; + m_HaveLock = TRUE; + + return TRUE; + } + + BOOL TryGetLock(DWORD numRetries) + { + DWORD dwSwitchCount = 0; + + for (;;) + { + if (TryGetLock()) + return TRUE; + + if (numRetries == 0) + return FALSE; + + --numRetries; + __SwitchToThread(0, ++dwSwitchCount); + } + } + + void FreeLock() + { + _ASSERTE(m_HaveLock); + _ASSERTE(m_Header.m_Counter == m_Counter); + ++m_Counter; + m_Counter = (m_Counter == 0) ? 2 : m_Counter; + m_Header.m_Counter = m_Counter; + m_HaveLock = FALSE; + } + + ~IPCHeaderLockHolder() + { + if (m_HaveLock) + FreeLock(); + } +}; + +class IPCHeaderReadHelper +{ + IPCHeader m_CachedHeader; + IPCHeader * m_pUnreliableHeader; + BOOL m_IsOpen; + + public: + + IPCHeaderReadHelper() : m_pUnreliableHeader(NULL), m_IsOpen(FALSE) {} + + BOOL TryOpenHeader(IPCHeader * header) + { + _ASSERTE(!m_IsOpen); + + m_pUnreliableHeader = header; + + // Read the counter and the runtime ID from the header + m_CachedHeader.m_Counter = m_pUnreliableHeader->m_Counter; + if ((m_CachedHeader.m_Counter & 1) != 0) + return FALSE; + m_CachedHeader.m_RuntimeId = m_pUnreliableHeader->m_RuntimeId; + + // If runtime ID is 0, then this block is not allocated by + // a runtime, and thus there is no further work to do + if (m_CachedHeader.m_RuntimeId == 0) + { + m_IsOpen = TRUE; + return TRUE; + } + + // Read the rest of the values from the header + m_CachedHeader.m_Reserved1 = m_pUnreliableHeader->m_Reserved1; + m_CachedHeader.m_Reserved2 = m_pUnreliableHeader->m_Reserved2; + m_CachedHeader.m_Version = m_pUnreliableHeader->m_Version; + m_CachedHeader.m_Flags = m_pUnreliableHeader->m_Flags; + m_CachedHeader.m_blockSize = m_pUnreliableHeader->m_blockSize; + m_CachedHeader.m_BuildYear = m_pUnreliableHeader->m_BuildYear; + m_CachedHeader.m_BuildNumber = m_pUnreliableHeader->m_BuildNumber; + m_CachedHeader.m_numEntries = m_pUnreliableHeader->m_numEntries; + + // Verify that the header did not change during the read + LONG counter = m_pUnreliableHeader->m_Counter; + if (m_CachedHeader.m_Counter != counter) + return FALSE; + + // Since we know we got a clean read of numEntries, we + // should be able to assert this with confidence + if (m_CachedHeader.m_numEntries == 0) + { + _ASSERTE(!"numEntries from IPCBlock is zero"); + return FALSE; + } + else if (m_CachedHeader.m_numEntries > eIPC_MAX) + { + _ASSERTE(!"numEntries from IPCBlock is too big"); + return FALSE; + } + + if (m_CachedHeader.m_blockSize == 0) + { + _ASSERTE(!"blockSize from IPCBlock is zero"); + return FALSE; + } + else if (m_CachedHeader.m_blockSize > IPC_BLOCK_SIZE) + { + _ASSERTE(!"blockSize from IPCBlock is too big"); + return FALSE; + } + + // Copy the table + for (DWORD i = 0; i < m_CachedHeader.m_numEntries; ++i) + { + m_CachedHeader.m_table[i].m_Offset = m_pUnreliableHeader->m_table[i].m_Offset; + m_CachedHeader.m_table[i].m_Size = m_pUnreliableHeader->m_table[i].m_Size; + if (i == eIPC_PerfCounters) + { + if(!((SIZE_T)m_CachedHeader.m_table[i].m_Offset < IPC_BLOCK_SIZE) && ((SIZE_T)m_CachedHeader.m_table[i].m_Offset + m_CachedHeader.m_table[i].m_Size <= IPC_BLOCK_SIZE)) + { + _ASSERTE(!"PerfCounter section offset + size is too large"); + return FALSE; + } + } + } + + // If eIPC_MAX > numEntries, then mark the left over + // slots in m_table as "empty". + for (DWORD i = m_CachedHeader.m_numEntries; i < eIPC_MAX; ++i) + { + m_CachedHeader.m_table[i].m_Offset = EMPTY_ENTRY_OFFSET; + m_CachedHeader.m_table[i].m_Size = EMPTY_ENTRY_SIZE; + } + + // Verify again that the header did not change during the read + counter = m_pUnreliableHeader->m_Counter; + if (m_CachedHeader.m_Counter != counter) + return FALSE; + + m_IsOpen = TRUE; + return TRUE; + } + + void CloseHeader() + { + m_IsOpen = FALSE; + m_pUnreliableHeader = NULL; + } + + BOOL HeaderHasChanged() + { + _ASSERTE(m_IsOpen); + LONG counter = m_pUnreliableHeader->m_Counter; + return (m_CachedHeader.m_Counter != counter) ? TRUE : FALSE; + } + + BOOL IsSentinal() + { + _ASSERTE(m_IsOpen); + return (m_CachedHeader.m_Counter == 0); + } + + DWORD GetRuntimeId() + { + _ASSERTE(m_IsOpen); + return m_CachedHeader.m_RuntimeId; + } + + USHORT GetIPCVersion() + { + _ASSERTE(m_IsOpen); + return m_CachedHeader.m_Version; + } + + BOOL UseWow64Structs() + { + _ASSERTE(m_IsOpen); +#if !defined(_TARGET_X86_) + return ((m_CachedHeader.m_Flags & IPC_FLAG_X86) != 0) ? TRUE : FALSE; +#else + return FALSE; +#endif + } + + IPCHeader * GetCachedCopyOfHeader() + { + _ASSERTE(m_IsOpen); + return &m_CachedHeader; + } + + IPCHeader * GetUnreliableHeader() + { + _ASSERTE(m_IsOpen); + return m_pUnreliableHeader; + } + + private: + + void * GetUnreliableSection(EIPCClient eClient) + { + if (!m_IsOpen) + { + _ASSERTE(!"IPCHeaderReadHelper is not open"); + return NULL; + } + + if (eClient < 0 || eClient >= eIPC_MAX) + { + _ASSERTE(!"eClient is out of bounds"); + return NULL; + } + + if (m_CachedHeader.m_table[eClient].m_Offset == EMPTY_ENTRY_OFFSET) + { + _ASSERTE(!"Section is empty"); + return NULL; + } + + return (BYTE*)m_pUnreliableHeader + (SIZE_T)m_CachedHeader.m_table[eClient].m_Offset; + } + + public: + + // We opted to return void* instead of PerfCounterIPCControlBlock* because this + // forces the caller to do an explicit cast. If UseWow64Structs() returns FALSE, + // then the caller should cast to PerfCounterIPCControlBlock*. If UseWow64Structs() + // return TRUE, then the caller should cast to PerfCounterWow64IPCControlBlock* + + void * GetUnreliablePerfBlock() + { + return GetUnreliableSection(eIPC_PerfCounters); + } +}; + +const DWORD SXSPUBLIC_IPC_SIZE_NO_PADDING = sizeof(IPCHeader) + sizeof(struct PerfCounterIPCControlBlock); +const DWORD SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING = sizeof(IPCHeader) + sizeof(struct PerfCounterWow64IPCControlBlock); + +const DWORD SXSPUBLIC_IPC_PAD_SIZE = IPC_BLOCK_SIZE - SXSPUBLIC_IPC_SIZE_NO_PADDING; +const DWORD SXSPUBLIC_WOW64_IPC_PAD_SIZE = IPC_BLOCK_SIZE - SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING; + +struct IPCControlBlock +{ +// Header + IPCHeader m_Header; + +// Client blocks + struct PerfCounterIPCControlBlock m_perf; + +// Padding + BYTE m_Padding[SXSPUBLIC_IPC_PAD_SIZE]; +}; + +#pragma pack(push, 4) +struct IPCControlBlockWow64 +{ +// Header + IPCHeader m_Header; + +// Client blocks + PerfCounterWow64IPCControlBlock m_perf; + +// Padding + BYTE m_Padding[SXSPUBLIC_WOW64_IPC_PAD_SIZE]; +}; +#pragma pack(pop) + +static_assert_no_msg(sizeof(IPCControlBlock) == IPC_BLOCK_SIZE); +static_assert_no_msg(sizeof(IPCControlBlockWow64) == IPC_BLOCK_SIZE); + +struct IPCControlBlockTable +{ + IPCControlBlock m_blocks[IPC_NUM_BLOCKS_IN_TABLE]; + + IPCControlBlock * GetBlock(DWORD index) + { return &(m_blocks[index]); } +}; + +static_assert_no_msg(sizeof(IPCControlBlockTable) == IPC_BLOCK_TABLE_SIZE); + +typedef DPTR(IPCControlBlockTable) PTR_IPCControlBlockTable; +typedef DPTR(IPCControlBlock) PTR_IPCControlBlock; + + +/**************************************** LEGACY ****************************************/ + +//----------------------------------------------------------------------------- +// LegacyPrivate header - put in its own structure so we can easily get the +// size of the header. It will compile to the same thing either way. +// This header must remain completely binary compatible w/ older versions. +// Notes: +// This header contains a "module handle" field which is platform dependent in size. +// That means for a 64 bit process to read IPC header of a 32 bit process, we cannot +// use the same class definition. The class would be different on the two platforms. +// Hence LegacyPrivateIPCHeaderTemplate is templatized on the type of the module handle. The +// IPC writers always use HINSTANCE as the parameter. The IPC reader has to know +// whether it is reading IPC block of a 32 bit process. If so it uses DWORD as the +// parameter so that the resulting LegacyPrivateIPCHeader is similar in format to the one +// written by the 32 bit process at the other end. +// The DWORD 'm_dwVersion' was split into two two USHORTS named 'm_Version' and +// 'm_Flags'. The upper bits of 'm_dwVersion' were never used, nor was 'm_dwVersion' +// used to determine whether an IPC block was valid or compatible. Thus, splitting +// the 'm_dwVersion' into two USHORTs should not introduce any compatibility issues. +//----------------------------------------------------------------------------- +template <class TModuleHandle> +struct LegacyPrivateIPCHeaderTemplate +{ +// header + USHORT m_Version; // version of the IPC Block + USHORT m_Flags; // flags field + DWORD m_blockSize; // Size of the entire shared memory block + TModuleHandle m_hInstance; // instance of module that created this header + USHORT m_BuildYear; // stamp for year built + USHORT m_BuildNumber; // stamp for Month/Day built + DWORD m_numEntries; // Number of entries in the table +}; + +typedef LegacyPrivateIPCHeaderTemplate<HINSTANCE> LegacyPrivateIPCHeader; + +//----------------------------------------------------------------------------- +// This fixes alignment & packing issues. +// This header must remain completely binary compatible w/ older versions. +//----------------------------------------------------------------------------- +struct FullIPCHeaderLegacyPrivate +{ +// Header + LegacyPrivateIPCHeader m_header; + +// Directory + IPCEntry m_table[eLegacyPrivateIPC_MAX]; // entry describing each client's block + +}; + +//----------------------------------------------------------------------------- +// This fixes alignment & packing issues. +// This header must remain completely binary compatible w/ older versions. +//----------------------------------------------------------------------------- +template <class TModuleHandle> +struct FullIPCHeaderLegacyPublicTemplate +{ +// Header + struct LegacyPrivateIPCHeaderTemplate<TModuleHandle> m_header; + +// Directory + IPCEntry m_table[eLegacyPublicIPC_MAX]; // entry describing each client's block + +}; + +// In hindsight, we should have made the offsets be absolute, but we made them +// relative to the end of the FullIPCHeader. +// The problem is that as future versions added new Entries to the directory, +// the header size grew. +// Thus we make IPCEntry::m_Offset is relative to LEGACY_IPC_ENTRY_OFFSET_BASE, which +// corresponds to sizeof(LegacyPrivateIPCHeader) for an v1.0 /v1.1 build. +#ifdef _TARGET_X86_ + const DWORD LEGACY_IPC_ENTRY_OFFSET_BASE = 0x14; +#else + // On non-x86 platforms, we don't need to worry about backwards compat. + // But we do need to worry about alignment, so just pretend that everett was 0, + // and solve both problems. + const DWORD LEGACY_IPC_ENTRY_OFFSET_BASE = 0x0; +#endif + +// When a 64 bit process reads IPC block of a 32 bit process, we need to know the +// LEGACY_IPC_ENTRY_OFFSET_BASE of the latter from the former. So this constant is defined +const DWORD LEGACY_IPC_ENTRY_OFFSET_BASE_WOW64 = 0x14; + +// Size of LegacyPublicIPCControlBlock for Everett and Whidbey +const DWORD LEGACYPUBLIC_IPC_BLOCK_SIZE_32 = 0x134; +const DWORD LEGACYPUBLIC_IPC_BLOCK_SIZE_64 = 0x1a0; + +//----------------------------------------------------------------------------- +// LegacyPrivate (per process) IPC Block for COM+ apps +//----------------------------------------------------------------------------- +struct LegacyPrivateIPCControlBlock +{ + FullIPCHeaderLegacyPrivate m_FullIPCHeader; + + +// Client blocks + struct PerfCounterIPCControlBlock m_perf; // no longer used but kept for compat + struct AppDomainEnumerationIPCBlock m_appdomain; + WCHAR m_instancePath[MAX_PATH]; +}; + +typedef DPTR(LegacyPrivateIPCControlBlock) PTR_LegacyPrivateIPCControlBlock; + +#if defined(_TARGET_X86_) +// For perf reasons, we'd like to keep the IPC block small enough to fit on +// a single page. This assert ensures it won't silently grow past the page boundary +// w/o us knowing about it. If this assert fires, then either: +// - consciously adjust it to let the IPC block be 2 pages. +// - shrink the IPC blocks. +static_assert_no_msg(sizeof(LegacyPrivateIPCControlBlock) <= 4096); +#endif + +//----------------------------------------------------------------------------- +// LegacyPublic (per process) IPC Block for CLR apps +//----------------------------------------------------------------------------- +struct LegacyPublicIPCControlBlock +{ + FullIPCHeaderLegacyPublicTemplate<HINSTANCE> m_FullIPCHeaderLegacyPublic; + +// Client blocks + struct PerfCounterIPCControlBlock m_perf; +}; + +//----------------------------------------------------------------------------- +// LegacyPublicWow64IPCControlBlock is used by a 64 bit process to read the IPC block +// of a 32 bit process. This struct is similar to LegacyPublicIPCControlBlock, except +// that all pointer (ie platform dependent) sized fields are substituted with +// DWORDs, so as to match the exact layout of LegacyPublicIPCControlBlock in a 32 bit process +//----------------------------------------------------------------------------- +#pragma pack(push, 4) +struct LegacyPublicWow64IPCControlBlock +{ + FullIPCHeaderLegacyPublicTemplate<DWORD> m_FullIPCHeaderLegacyPublic; + +// Client blocks + PerfCounterWow64IPCControlBlock m_perf; +}; +#pragma pack(pop) + +typedef DPTR(LegacyPublicIPCControlBlock) PTR_LegacyPublicIPCControlBlock; + +//----------------------------------------------------------------------------- +// Inline definitions +//----------------------------------------------------------------------------- + +#include "ipcheader.inl" + +#endif // _IPCManagerPriv_h_ |