summaryrefslogtreecommitdiff
path: root/src/vm/profattach.h
blob: 0a06fae73f0b4518cd70e8b8bb99fe880632964d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
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__