diff options
Diffstat (limited to 'src/vm/profattach.h')
-rw-r--r-- | src/vm/profattach.h | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/src/vm/profattach.h b/src/vm/profattach.h new file mode 100644 index 0000000000..0a06fae73f --- /dev/null +++ b/src/vm/profattach.h @@ -0,0 +1,425 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ProfAttach.h +// + +// +// Declaration of functions that help with attaching and detaching profilers, including +// message structures that are passed back and forth between the trigger (client) and +// the target profilee (server). For code specific to triggers and profilees, see +// code:ProfilingAPIAttachClient and code:ProfilingAPIAttachServer, respectively. +// + +// ====================================================================================== + +#ifndef __PROF_ATTACH_H__ +#define __PROF_ATTACH_H__ + +#include "internalunknownimpl.h" + +//--------------------------------------------------------------------------------------- +// Structure representing the runtime's version. Used to negotiate versions between the +// trigger and profilee. +// +// **** COMPATIBILITY WARNING *** +// +// You are not allowed to change the binary layout of this structure, or else the +// trigger & profilee will be unable to negotiate version information. Asserts in +// code:ProfilingAPIAttachDetach::VerifyMessageStructureLayout attempt to enforce this. +// +// **** COMPATIBILITY WARNING *** +// +struct VersionBlock +{ +public: + DWORD m_dwMajor; + DWORD m_dwMinor; + DWORD m_dwBuild; + DWORD m_dwQFE; + + VersionBlock(DWORD dwMajor, DWORD dwMinor, DWORD dwBuild, DWORD dwQFE); + VersionBlock(); + BOOL operator <(const VersionBlock & otherVersionBlock) const; +}; + + +//--------------------------------------------------------------------------------------- +// Types of request messages that may be sent from trigger across the pipe +// +enum RequestMessageType +{ + // Client (trigger) asks server (profilee) for server's version information. + // The message type must be code:ProfilingAPIAttachDetach::BaseRequestMessage + kMsgGetVersion, + + // Client (trigger) asks server (profilee) to attach the profiler. The message + // type must be code:ProfilingAPIAttachDetach::AttachRequestMessage or AttachRequestMessageV2 + kMsgAttach, + + kMsgCount +}; + + +// --------------------------------------------------------------------------------------- +// Base request message format. All request messages sent by trigger across pipe derive +// from this. +// +// **** COMPATIBILITY WARNING *** +// +// You are not allowed to change this structure in such a way as would modify the binary +// layout of derived type GetVersionRequestMessage, or else the trigger & profilee will +// be unable to negotiate version information. Asserts in +// code:ProfilingAPIAttachDetach::VerifyMessageStructureLayout attempt to enforce this. +// +// **** COMPATIBILITY WARNING *** +// +struct BaseRequestMessage +{ +public: + // Total size of the message (including size of derived type, client data, etc., if + // present in the message) + DWORD m_cbMessage; + + // What kind of message is this? + RequestMessageType m_requestMessageType; + + BaseRequestMessage(DWORD cbMessage, RequestMessageType requestMessageType); + +private: + // Use parameterized constructor above to initialize this struct + BaseRequestMessage(); +}; + + +// --------------------------------------------------------------------------------------- +// Message format for requesting version information from the target profilee +// +// **** COMPATIBILITY WARNING *** +// +// You are not allowed to change the binary layout of this structure, or else the trigger +// & profilee will be unable to negotiate version information. Asserts in +// code:ProfilingAPIAttachDetach::VerifyMessageStructureLayout attempt to enforce this. +// +// **** COMPATIBILITY WARNING *** +// +struct GetVersionRequestMessage : public BaseRequestMessage +{ +public: + GetVersionRequestMessage(); +}; + + +//--------------------------------------------------------------------------------------- +// Attach request message format. A kMsgAttach message sent by trigger must be of this +// type. +struct AttachRequestMessage : public BaseRequestMessage +{ +public: + // Trigger sends its version info here. This allows the target profilee to + // customize its response for the format expected by the trigger. + VersionBlock m_triggerVersion; + + // The GUID of the profiler's COM object to load + CLSID m_clsidProfiler; + + // The path to the profiler's COM object to load + WCHAR m_wszProfilerPath[MAX_LONGPATH]; + + // Client data is custom data that the profiler's + // trigger-process wishes to copy into this process. + // Profiler authors will typically use this as a way to + // communicate to the profiler DLL what options the profiler + // user has chosen. This will help the profiler DLL configure + // itself (e.g., to determine which callbacks to request). + // + // Since the client data is variable length, and we may + // want to tail-extend this structure in the future, we use + // an offset to point to the client data. Client data + // begins at this + m_dwClientDataStartOffset bytes. + DWORD m_dwClientDataStartOffset; + DWORD m_cbClientDataLength; + + AttachRequestMessage( + DWORD cbMessage, + const VersionBlock & triggerVersion, + const CLSID * pClsidProfiler, + LPCWSTR wszProfilerPath, + DWORD dwClientDataStartOffset, + DWORD cbClientDataLength); + +private: + // Use parameterized constructor above to initialize this struct + AttachRequestMessage(); +}; + +//--------------------------------------------------------------------------------------- +// Attach request message V2 +// Pass the timeout information from client (the trigger process) to server (the profilee) +struct AttachRequestMessageV2 : public AttachRequestMessage +{ + +public : + // Timeout for the wait operation for concurrent GC in server side + // Basically time out passed from AttachProfiler API minus the amount of time already + // elapsed in client side + DWORD m_dwConcurrentGCWaitTimeoutInMs; + +public : + AttachRequestMessageV2( + DWORD cbMessage, + const VersionBlock & triggerVersion, + const CLSID * pClsidProfiler, + LPCWSTR wszProfilerPath, + DWORD dwClientDataStartOffset, + DWORD cbClientDataLength, + DWORD dwConcurrentGCWaitTimeoutInMs); + + // Whether the attach request message is a V2 message (including V2+) + static BOOL CanCastTo(const AttachRequestMessage * pMsg); + +private: + // Use parameterized constructor above to initialize this struct + AttachRequestMessageV2(); +}; + +// --------------------------------------------------------------------------------------- +// Base response message format. All response messages returned by profilee across the +// pipe to the trigger derive from this. +// +// **** COMPATIBILITY WARNING *** +// +// You are not allowed to change this structure in such a way as would change the binary +// layout of derived type GetVersionResponseMessage, or else the trigger & profilee will +// be unable to negotiate version information. Asserts in +// code:ProfilingAPIAttachDetach::VerifyMessageStructureLayout attempt to enforce this. +// +// **** COMPATIBILITY WARNING *** +// +struct BaseResponseMessage +{ +public: + // HRESULT indicating success or failure of carrying out the request + HRESULT m_hr; + + BaseResponseMessage(HRESULT hr); + +protected: + // Use parameterized constructor above to initialize this struct + BaseResponseMessage(); +}; + +// --------------------------------------------------------------------------------------- +// GetVersion response message format. The server responds to a kMsgGetVersion message +// request with a message of this type. +// +// **** COMPATIBILITY WARNING *** +// +// You are not allowed to change the binary layout of this structure, or else the trigger +// & profilee will be unable to negotiate version information. Asserts in +// code:ProfilingAPIAttachDetach::VerifyMessageStructureLayout attempt to enforce this. +// +// **** COMPATIBILITY WARNING *** +// +struct GetVersionResponseMessage : public BaseResponseMessage +{ +public: + // The target profilee constructs this response by filling out the following two + // values. The trigger process uses these values to determine whether it's compatible + // with the target profilee. + + // Target profilee provides its version info here. If trigger determines that + // this number is too small, then trigger refuses the profilee as being too old. + VersionBlock m_profileeVersion; + + // Target profilee provides here the oldest version of a trigger process that it + // can communicate with. If trigger determines that this number is too big, + // then trigger refuses the profilee as being too new. + VersionBlock m_minimumAllowableTriggerVersion; + + GetVersionResponseMessage( + HRESULT hr, + const VersionBlock & profileeVersion, + const VersionBlock & minimumAllowableTriggerVersion); + + GetVersionResponseMessage(); +}; + + +// --------------------------------------------------------------------------------------- +// Attach response message format. The server responds to a kMsgAttach message +// request with a message of this type. +// +struct AttachResponseMessage : public BaseResponseMessage +{ +public: + AttachResponseMessage(HRESULT hr); +}; + +// --------------------------------------------------------------------------------------- +// Static-only class to handle attach request communication and detach functionality +// +// The target profilee app generally calls functions in ProfilingAPIAttachServer, while +// the trigger process (by way of the AttachProfiler API) generally calls functions in +// ProfilingAPIAttachClient. ProfilingAPIAttachDetach contains functionality common to +// target profilees and triggers, as well as initialization and other routines exposed to +// other parts of the EE. +// +class ProfilingAPIAttachDetach +{ +public: + // --------------------------------------------------------------------------------------- + // Indicates whether AttachThread is always available without the need for an event + // (that the finalizer thread listens to), or whether the AttachThread is only + // available on demand (when finalizer thread detects the attach event has been + // signaled). The mode used by default is determined by the gc mode (server vs. + // workstation). But this can be overridden in either case by setting + // COMPlus_AttachThreadAlwaysOn: 0=kOnDemand, nonzero=kAlwaysOn. + enum AttachThreadingMode + { + // Too early in startup to know the mode yet + kUninitialized, + + // Default GC-workstation mode: AttachThread is only created when the attach + // event is signaled. AttachThread automatically exits when pipe requests quiet + // down. + kOnDemand, + + // Default GC-server mode: AttachThread and attach pipe are created on startup, + // and they never go away. There is no need for an attach event in this mode, so + // the attach event is never created. + kAlwaysOn, + }; + + // --------------------------------------------------------------------------------------- + // Helper class used by both the target profilee app (server) and the trigger process + // (client) to create and dispose of an OVERLAPPED structure and to use it in a call + // to the OS API GetOverlappedResult (wrapped via + // code:ProfilingAPIAttachDetach::OverlappedResultHolder::Wait). The point of having + // this holder is to encapsulate the code that verifies when the OS is finished with + // the OVERLAPPED structure (usually when OverlappedResultHolder goes out of scope). + // See code:ProfilingAPIAttachDetach::OverlappedResultHolder::Wait for details. Since + // this class derives from NewHolder<OVERLAPPED>, users may automagically cast + // instances to OVERLAPPED* for use in passing to Windows OS APIs + class OverlappedResultHolder : public NewHolder<OVERLAPPED> + { + public: + HRESULT Initialize(); + HRESULT Wait( + DWORD dwMillisecondsMax, + HANDLE hPipe, + DWORD * pcbReceived); + }; + + static const VersionBlock kCurrentProcessVersion; + static const VersionBlock kMinimumAllowableTriggerVersion; + static const VersionBlock kMinimumAllowableProfileeVersion; + + static DWORD WINAPI ProfilingAPIAttachThreadStart(LPVOID lpParameter); + static void ProcessSignaledAttachEvent(); + static HANDLE GetAttachEvent(); + static HRESULT Initialize(); + static HRESULT InitSecurityAttributes(SECURITY_ATTRIBUTES * pSecAttrs, DWORD cbSecAttrs); + static AttachThreadingMode GetAttachThreadingMode(); + + static HRESULT GetAttachPipeName(HANDLE hProfileeProcess, SString * pAttachPipeName); + static void GetAttachPipeNameForPidAndVersion(HANDLE hProfileeProcess, LPCWSTR wszRuntimeVersion, SString * pAttachPipeName); + static HRESULT GetAttachEventName(HANDLE hProfileeProcess, SString * pAttachEventName); + static void GetAttachEventNameForPidAndVersion(HANDLE hProfileeProcess, LPCWSTR wszRuntimeVersion, SString * pAttachEventName); + static HRESULT GetAppContainerNamedObjectPath(HANDLE hProcess, __out_ecount(dwObjectPathSizeInChar) WCHAR * wszObjectPath, DWORD dwObjectPathSizeInChar); + static BOOL IsAppContainerProcess(HANDLE hProcess); + +private: + // This caches the security descriptor to be used when generating the + // SECURITY_ATTRIBUTES structure for the event and pipe objects. + // + // Technically, this should be freed via LocalFree() or HeapFree (with the current + // process heap), but there's only one of these per runtime, and it is used + // throughout the process's lifetime, and there isn't much point to freeing it when + // the process shuts down since the OS does that automatically. + static PSECURITY_DESCRIPTOR s_pSecurityDescriptor; + + // HANDLE to event object created on startup and listened to by finalizer thread + // (when running in code:ProfilingAPIAttachDetach::kOnDemand mode) + // + // Technically, this should be freed via CloseHandle(), but there's only one of these + // per runtime, and it is used throughout the process's lifetime, and there isn't + // much point to freeing it when the process shuts down since the OS does that + // automatically. + static HANDLE s_hAttachEvent; + + // See code:ProfilingAPIAttachDetach::AttachThreadingMode + static AttachThreadingMode s_attachThreadingMode; + + static BOOL s_fInitializeCalled; + + // Static-only class. Private constructor enforces you don't try to make an instance + ProfilingAPIAttachDetach() {} + + INDEBUG(static void VerifyMessageStructureLayout()); + static void InitializeAttachThreadingMode(); + static HRESULT InitializeForOnDemandMode(); + static HRESULT InitializeForAlwaysOnMode(); + static HRESULT ProfilingAPIAttachThreadMain(); + static void CreateAttachThread(); + + static HRESULT GetSecurityDescriptor(PSECURITY_DESCRIPTOR * ppsd); +}; + +// IClassFactory implementation for ICLRProfiling inteface. +class CLRProfilingClassFactoryImpl : public IUnknownCommon<IClassFactory> +{ +public: + CLRProfilingClassFactoryImpl() + { + LIMITED_METHOD_CONTRACT; + } + + ~CLRProfilingClassFactoryImpl() + { + LIMITED_METHOD_CONTRACT; + } + + // + // IClassFactory methods + // + STDMETHOD(CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppv)); + + STDMETHOD(LockServer( + BOOL fLock)); +}; + +// CLRProfiling implementation. +class CLRProfilingImpl : public IUnknownCommon<ICLRProfiling> +{ +public: + CLRProfilingImpl() + { + LIMITED_METHOD_CONTRACT; + } + + ~CLRProfilingImpl() + { + LIMITED_METHOD_CONTRACT; + } + + // + // ICLRProfiling method + // + STDMETHOD(AttachProfiler( + DWORD dwProfileeProcessID, + DWORD dwMillisecondsMax, + const CLSID * pClsidProfiler, + LPCWSTR wszProfilerPath, + void * pvClientData, + UINT cbClientData)); +}; + + +HRESULT ICLRProfilingGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv); + +#endif // __PROF_ATTACH_H__ |