summaryrefslogtreecommitdiff
path: root/src/vm/profattach.inl
blob: 297c78e6d162e830f91ad8d5a3ac315a53e52162 (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
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
// 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.inl
// 

//
// Implementation of inlineable functions that help with attaching and detaching 
// profilers
//

// ======================================================================================

#ifndef __PROF_ATTACH_INL__
#define __PROF_ATTACH_INL__


// ----------------------------------------------------------------------------
// VersionBlock::VersionBlock
//
// Description: 
//    VersionBlock constructor with no arguments; just zeroes out fields
//

inline VersionBlock::VersionBlock()
{
    LIMITED_METHOD_CONTRACT;
    memset(this, 0, sizeof(*this)); 
}

// ----------------------------------------------------------------------------
// VersionBlock::VersionBlock
//
// Description: 
//    VersionBlock constructor with version number parameters
//
// Arguments:
//    * dwMajor - Major version number
//    * dwMinor - Minor version number
//    * dwBuild - Product build number
//    * dwQFE - Product build QFE number
//

inline VersionBlock::VersionBlock(
    DWORD dwMajor,
    DWORD dwMinor,
    DWORD dwBuild,
    DWORD dwQFE) :
        m_dwMajor(dwMajor),
        m_dwMinor(dwMinor),
        m_dwBuild(dwBuild),
        m_dwQFE(dwQFE)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// VersionBlock::operator <
// 
// Description:
//    Allows for in-fix comparison operator between two VersionBlocks. Compares fields
//    from most-significant to least-significant: m_dwMajor, m_dwMinor, m_dwBuild,
//    m_dwQFE
//    
// Arguments:
//    * otherVersionBlock - VersionBlock to compare against this (shown on RHS of <
//        operator)
//        
// Return Value:
//    Nonzero if this is strictly before otherVersionBlock, else 0.
//    

inline BOOL VersionBlock::operator <(const VersionBlock & otherVersionBlock) const
{
    LIMITED_METHOD_CONTRACT;

    // COMPARE MAJOR
    if (m_dwMajor < otherVersionBlock.m_dwMajor)
    {
        return TRUE;
    }
    if (m_dwMajor > otherVersionBlock.m_dwMajor)
    {
        return FALSE;
    }
    _ASSERTE(m_dwMajor == otherVersionBlock.m_dwMajor);

    // COMPARE MINOR
    if (m_dwMinor < otherVersionBlock.m_dwMinor)
    {
        return TRUE;
    }
    if (m_dwMinor > otherVersionBlock.m_dwMinor)
    {
        return FALSE;
    }
    _ASSERTE(m_dwMinor == otherVersionBlock.m_dwMinor);

    // COMPARE BUILD
    if (m_dwBuild < otherVersionBlock.m_dwBuild)
    {
        return TRUE;
    }
    if (m_dwBuild > otherVersionBlock.m_dwBuild)
    {
        return FALSE;
    }
    _ASSERTE(m_dwBuild == otherVersionBlock.m_dwBuild);

    // COMPARE QFE
    if (m_dwQFE < otherVersionBlock.m_dwQFE)
    {
        return TRUE;
    }
    if (m_dwQFE > otherVersionBlock.m_dwQFE)
    {
        return FALSE;
    }
    _ASSERTE(m_dwQFE == otherVersionBlock.m_dwQFE);

    return FALSE;
}

// ----------------------------------------------------------------------------
// BaseRequestMessage::BaseRequestMessage
// 
// Description:
//    Constructor for base class of all request messages sent from trigger (client) to
//    profilee (server).
//    
// Arguments:
//    * cbMessage - Size, in bytes, of the entire request message (including size of
//        derived type, client data, etc., if present in the message)
//    * requestMessageType - Enum representing type of request this constitutes
//

inline BaseRequestMessage::BaseRequestMessage(
    DWORD cbMessage, 
    RequestMessageType requestMessageType) :
        m_cbMessage(cbMessage),
        m_requestMessageType(requestMessageType)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// GetVersionRequestMessage::GetVersionRequestMessage
//
// Description: 
//    Constructor to create a fully initialized GetVersionRequestMessage
//

inline GetVersionRequestMessage::GetVersionRequestMessage()
    : BaseRequestMessage(sizeof(GetVersionRequestMessage), kMsgGetVersion)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// AttachRequestMessage::AttachRequestMessage
//
// Description: 
//    Constructor for request message of type kMsgAttach sent from trigger (client) to
//    profilee (server)
//
// Arguments:
//    * cbMessage - Size, in bytes, of the entire request message (including size of
//        derived type, client data, etc., if present in the message)
//    * triggerVersion - VersionBlock representing runtime version used by trigger
//    * pClsidProfiler - CLSID of profiler to attach
//    * wszProfilerPath - path to profiler DLL 
//    * dwClientDataStartOffset - see code:AttachRequestMessage
//    * cbClientDataLength - see code:AttachRequestMessage
//

inline AttachRequestMessage::AttachRequestMessage(
    DWORD cbMessage, 
    const VersionBlock & triggerVersion,
    const CLSID * pClsidProfiler,
    LPCWSTR wszProfilerPath,
    DWORD dwClientDataStartOffset,
    DWORD cbClientDataLength) :
        BaseRequestMessage(cbMessage, kMsgAttach),
        m_triggerVersion(triggerVersion),
        m_dwClientDataStartOffset(dwClientDataStartOffset),
        m_cbClientDataLength(cbClientDataLength)
{
    LIMITED_METHOD_CONTRACT;
    _ASSERT(cbMessage >= sizeof(AttachRequestMessage) + cbClientDataLength);
    memcpy(&m_clsidProfiler, pClsidProfiler, sizeof(m_clsidProfiler));
    if (wszProfilerPath != NULL)
    {
        _ASSERTE(wcslen(wszProfilerPath) < _countof(m_wszProfilerPath));
        wcscpy_s(m_wszProfilerPath, _countof(m_wszProfilerPath), wszProfilerPath);
    }
    else
    {
        m_wszProfilerPath[0] = L'\0';
    }
}

// ----------------------------------------------------------------------------
// AttachRequestMessageV2::AttachRequestMessageV2
//
// Description: 
//    Constructor for request message V2 of type kMsgAttach sent from trigger (client) to
//    profilee (server)
//
// Arguments:
//    * cbMessage - Size, in bytes, of the entire request message (including size of
//        derived type, client data, etc., if present in the message)
//    * triggerVersion - VersionBlock representing runtime version used by trigger
//    * pClsidProfiler - CLSID of profiler to attach
//    * wszProfilerPath - path to profiler DLL 
//    * dwClientDataStartOffset - see code:AttachRequestMessage
//    * cbClientDataLength - see code:AttachRequestMessage
//    * dwConcurrentGCWaitTimeoutInMs - the time out for wait operation on concurrent GC to finish. 
//                                      Attach scenario only.
//
inline AttachRequestMessageV2::AttachRequestMessageV2(
    DWORD cbMessage, 
    const VersionBlock & triggerVersion,
    const CLSID * pClsidProfiler,
    LPCWSTR wszProfilerPath,
    DWORD dwClientDataStartOffset,
    DWORD cbClientDataLength,
    DWORD dwConcurrentGCWaitTimeoutInMs)
    :AttachRequestMessage(
        cbMessage,
        triggerVersion,
        pClsidProfiler,
        wszProfilerPath,
        dwClientDataStartOffset,
        cbClientDataLength),
    m_dwConcurrentGCWaitTimeoutInMs(dwConcurrentGCWaitTimeoutInMs)
{
    LIMITED_METHOD_CONTRACT;
    _ASSERT(cbMessage >= sizeof(AttachRequestMessageV2) + cbClientDataLength);
}

// ----------------------------------------------------------------------------
// AttachRequestMessageV2::CanCastTo
inline BOOL AttachRequestMessageV2::CanCastTo(const AttachRequestMessage *pMsg)
{
    LIMITED_METHOD_CONTRACT;

    // We already have checks that the client data doesn't go beyond the message body. 
    // If someone creates a bad message that pretends to be a V2 message, the worst scenario 
    // is we got a bad time out.
    if (pMsg->m_cbMessage >= sizeof(AttachRequestMessageV2) + pMsg->m_cbClientDataLength)
        return TRUE;
    
    return FALSE;
}

// ----------------------------------------------------------------------------
// BaseResponseMessage::BaseResponseMessage
//
// Description: 
//    Constructor for base class of all response messages returned by profilee (server)
//    to trigger (client)
//
// Arguments:
//    * hr - HRESULT indicating success or failure of executing the request that the
//        trigger had made to the profilee
//

inline BaseResponseMessage::BaseResponseMessage(HRESULT hr) :
    m_hr(hr)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// BaseResponseMessage::BaseResponseMessage
//
// Description: 
//    Zero-parameter constructor for BaseResponseMessage for use when hr is not yet
//    known.
//

inline BaseResponseMessage::BaseResponseMessage() :
    m_hr(E_UNEXPECTED)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// GetVersionResponseMessage::GetVersionResponseMessage
//
// Description: 
//    Constructor to create a fully initialized GetVersionResponseMessage
//
// Arguments:
//    * hr - Success / failure of carrying out the GetVersion request
//    * profileeVersion - Version of the target profilee app's runtime (server)
//    * minimumAllowableTriggerVersion - Oldest version of a trigger process that this
//        target profilee app is willing to talk to.
//

inline GetVersionResponseMessage::GetVersionResponseMessage(
    HRESULT hr,
    const VersionBlock & profileeVersion,
    const VersionBlock & minimumAllowableTriggerVersion) :
        BaseResponseMessage(hr),
        m_profileeVersion(profileeVersion),
        m_minimumAllowableTriggerVersion(minimumAllowableTriggerVersion)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// GetVersionResponseMessage::GetVersionResponseMessage
//
// Description: 
//    Constructor to use for GetVersionResponseMessage when the data is not known yet. 
//    The trigger will typically use this constructor to create an empty
//    GetVersionResponseMessage as storage to receive the GetVersionResponseMessage data
//    that will come in over the pipe from the target profilee app.
//

inline GetVersionResponseMessage::GetVersionResponseMessage()
{
    LIMITED_METHOD_CONTRACT;
    memset(this, 0, sizeof(*this));
    m_hr = E_UNEXPECTED;
}

// ----------------------------------------------------------------------------
// AttachResponseMessage::AttachResponseMessage
//
// Description: 
//    Constructor for AttachResponseMessage
//
// Arguments:
//    * hr - Success / failure of carrying out the attach request
//

inline AttachResponseMessage::AttachResponseMessage(HRESULT hr)
    : BaseResponseMessage(hr)
{
    LIMITED_METHOD_CONTRACT;
}

// ----------------------------------------------------------------------------
// ProfilingAPIAttachDetach::GetAttachThreadingMode
// 
// Description:
//    Returns the profiling attach threading mode for this runtime instance. See
//    code:ProfilingAPIAttachDetach::AttachThreadingMode.
//    
// Return Value:
//    The profiling attach threading mode 
//    
// Assumptions:
//    * code:ProfilingAPIAttachDetach::Initialize must be called before this function.
//        

// static
inline ProfilingAPIAttachDetach::AttachThreadingMode ProfilingAPIAttachDetach::GetAttachThreadingMode()
{
    LIMITED_METHOD_CONTRACT;

    // ProfilingAPIAttachDetach::Initialize must be called before this function.
    _ASSERTE(s_fInitializeCalled);
    return s_attachThreadingMode; 
}

// ----------------------------------------------------------------------------
// ProfilingAPIAttachDetach::GetAttachEventNameForPidAndVersion
//
// Description: 
//    Generates name for Globally Named Attach Event, based on PID and the runtime version
//    Name looks like this:
//         CPFATE_nnnn_RuntimeVersion
//    CPFATE stands for CLR Profiling API attach trigger event
//    nnnn is decimal process ID
//    RuntimeVersion is the string of the runtime version
//
// Arguments:
//    * hProfileeProcess - The profilee process we want to attach to
//    * wszRuntimeVersion - runtime version string
//    * pAttachEventName - [in/out] SString to hold the generated name
//

// static
inline void ProfilingAPIAttachDetach::GetAttachEventNameForPidAndVersion(HANDLE hProfileeProcess, LPCWSTR wszRuntimeVersion, SString * pAttachEventName)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        CAN_TAKE_LOCK;
    }
    CONTRACTL_END;

    // Convert to lower case using invariant culture
    SString strRuntimeVersion(wszRuntimeVersion);
    strRuntimeVersion.LowerCase();
    
    DWORD dwProfileeProcessPid = ::GetProcessId(hProfileeProcess);
    
    if (IsAppContainerProcess(hProfileeProcess))
    {
        HANDLE hCurrentProcess = ::GetCurrentProcess();
        if (hProfileeProcess == hCurrentProcess || IsAppContainerProcess(::GetCurrentProcess()))
        {
            // App container to app container or the current process is the profilee process
            // In any case, use a local name
            pAttachEventName->Printf(L"CPFATE_%d_%s", dwProfileeProcessPid, strRuntimeVersion.GetUnicode());        
        }
        else
        {
            // Otherwise, we'll assume it is full-trust to lowbox, and in this case we need to prefix the name with app container path
            WCHAR wszObjectPath[MAX_PATH];
            HRESULT hr = GetAppContainerNamedObjectPath(hProfileeProcess, wszObjectPath, sizeof(wszObjectPath)/sizeof(WCHAR));
            IfFailThrow(hr);

            //
            // Retrieve the session ID
            //
            DWORD dwSessionId;
            if (!ProcessIdToSessionId(dwProfileeProcessPid, &dwSessionId))
            {
                COMPlusThrowHR(HRESULT_FROM_GetLastError());
            }

            pAttachEventName->Printf(L"Session\\%d\\%s\\CPFATE_%d_%s", dwSessionId, wszObjectPath, dwProfileeProcessPid, strRuntimeVersion.GetUnicode());        
        }        
    }
    else
    {
        // Non-app conatiner scenario
        // Create in global namespace
        pAttachEventName->Printf(L"Global\\CPFATE_%d_%s", dwProfileeProcessPid, strRuntimeVersion.GetUnicode());
    }
}


// ----------------------------------------------------------------------------
// ProfilingAPIAttachDetach::GetAttachPipeNameForPidAndVersion
//
// Description: 
//    Generates name for Globally Named Attach Pipe, based on PID and the runtime version
//    Name looks like this:
//         \\.\pipe\CPFATP_nnnn_RuntimeVersion
//    CPFATP stands for CLR Profiling API attach trigger pipe
//    nnnn is decimal process ID
//    RuntimeVersion is the string of the runtime version
//
// Arguments:
//    * hProfileeProcess - The profilee process we want to attach to
//    * wszRuntimeVersion - runtime version string
//    * pAttachPipeName - [in/out] SString to hold the generated name
//

// static
inline void ProfilingAPIAttachDetach::GetAttachPipeNameForPidAndVersion(HANDLE hProfileeProcess, LPCWSTR wszRuntimeVersion, SString * pAttachPipeName)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        CAN_TAKE_LOCK;
    }
    CONTRACTL_END;

    // Convert to lower case using invariant culture
    SString strRuntimeVersion(wszRuntimeVersion);
    strRuntimeVersion.LowerCase();
    
    DWORD dwProfileeProcessPid = ::GetProcessId(hProfileeProcess);
    
    if (IsAppContainerProcess(hProfileeProcess))
    {
    
        //
        // Retrieve low object path
        //
        WCHAR wszObjectPath[MAX_PATH];
        HRESULT hr = GetAppContainerNamedObjectPath(hProfileeProcess, wszObjectPath, sizeof(wszObjectPath)/sizeof(WCHAR));
        IfFailThrow(hr);

        //
        // Retrieve the session ID
        //
        DWORD dwSessionId;
        if (!ProcessIdToSessionId(dwProfileeProcessPid, &dwSessionId))
        {
            COMPlusThrowHR(HRESULT_FROM_GetLastError());
        }
            
        pAttachPipeName->Printf(L"\\\\.\\pipe\\Sessions\\%d\\%s\\CPFATP_%d_%s", dwSessionId, wszObjectPath, dwProfileeProcessPid, strRuntimeVersion.GetUnicode());    
    }
    else
    {
        pAttachPipeName->Printf(L"\\\\.\\pipe\\CPFATP_%d_%s", dwProfileeProcessPid, strRuntimeVersion.GetUnicode());    
    }    
}

// Simple wrapper around code:ProfilingAPIAttachDetach::GetAttachEventNameForPidAndVersion using
// current process's PID and current runtime directory
// static
inline HRESULT ProfilingAPIAttachDetach::GetAttachEventName(HANDLE hProfileeProcess, SString * pAttachEventName)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        CAN_TAKE_LOCK;
    }
    CONTRACTL_END;

    WCHAR wszRuntimeVersion[MAX_PATH];
    wszRuntimeVersion[0] = L'\0';

    // Note: CoreCLR can have the same version as Desktop CLR.  And it's possible to have mutilple 
    // instances of the same version of the CoreCLR in the process.  We need to come up with 
    // something other than version When Attach is enabled for CoreCLR.
    DWORD dwSize = _countof(wszRuntimeVersion); 
    HRESULT hr = GetCORVersionInternal(wszRuntimeVersion, dwSize, &dwSize);
    if (FAILED(hr))
    {
        return hr;
    }

    GetAttachEventNameForPidAndVersion(hProfileeProcess, wszRuntimeVersion, pAttachEventName);
    return S_OK;
}

// Simple wrapper around code:ProfilingAPIAttachDetach::GetAttachPipeNameForPidAndVersion using
// current process's PID and current runtime directory
// static
inline HRESULT ProfilingAPIAttachDetach::GetAttachPipeName(HANDLE hProfileeProcess, SString * pAttachPipeName)
{
    CONTRACTL
    {
        THROWS;
        GC_TRIGGERS;
        MODE_PREEMPTIVE;
        CAN_TAKE_LOCK;
    }
    CONTRACTL_END;

    WCHAR wszRuntimeVersion[MAX_PATH];
    wszRuntimeVersion[0] = L'\0';

    // Note: CoreCLR can have the same version as Desktop CLR.  And it's possible to have mutilple 
    // instances of the same version of the CoreCLR in the process.  We need to come up with 
    // something other than version When Attach is enabled for CoreCLR.
    DWORD dwSize = _countof(wszRuntimeVersion); 
    HRESULT hr = GetCORVersionInternal(wszRuntimeVersion, dwSize, &dwSize);
    if (FAILED(hr))
    {
        return hr;
    }

    GetAttachPipeNameForPidAndVersion(hProfileeProcess, wszRuntimeVersion, pAttachPipeName);
    return S_OK;
}

#endif // __PROF_ATTACH_INL__