Add check to prevent attaching a profiler when one is already present (#25520)
[platform/upstream/coreclr.git] / src / vm / profilerdiagnosticprotocolhelper.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 #include "common.h"
6 #include "fastserializer.h"
7 #include "profilerdiagnosticprotocolhelper.h"
8 #include "diagnosticsipc.h"
9 #include "diagnosticsprotocol.h"
10
11 #if defined(FEATURE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) && !defined(DACCESS_COMPILE)
12 #include "profilinghelper.h"
13 #include "profilinghelper.inl"
14
15 void ProfilerDiagnosticProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream)
16 {
17     CONTRACTL
18     {
19         THROWS;
20         GC_TRIGGERS;
21         MODE_PREEMPTIVE;
22         PRECONDITION(pStream != nullptr);
23     }
24     CONTRACTL_END;
25
26     switch ((ProfilerCommandId)message.GetHeader().CommandId)
27     {
28     case ProfilerCommandId::AttachProfiler:
29         ProfilerDiagnosticProtocolHelper::AttachProfiler(message, pStream);
30         break;
31
32     default:
33         STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", message.GetHeader().CommandSet);
34         DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, CORDIAGIPC_E_UNKNOWN_COMMAND);
35         delete pStream;
36         break;
37     }
38 }
39
40 const AttachProfilerCommandPayload* AttachProfilerCommandPayload::TryParse(BYTE* lpBuffer, uint16_t& BufferSize)
41 {
42     CONTRACTL
43     {
44         NOTHROW;
45         GC_TRIGGERS;
46         MODE_PREEMPTIVE;
47         PRECONDITION(lpBuffer != nullptr);
48     }
49     CONTRACTL_END;
50
51     AttachProfilerCommandPayload* payload = new (nothrow) AttachProfilerCommandPayload;
52     if (payload == nullptr)
53     {
54         // OOM
55         return nullptr;
56     }
57
58     payload->incomingBuffer = lpBuffer;
59     uint8_t* pBufferCursor = payload->incomingBuffer;
60     uint32_t bufferLen = BufferSize;
61     if (!::TryParse(pBufferCursor, bufferLen, payload->dwAttachTimeout) ||
62         !::TryParse(pBufferCursor, bufferLen, payload->profilerGuid) ||
63         !TryParseString(pBufferCursor, bufferLen, payload->pwszProfilerPath) ||
64         !::TryParse(pBufferCursor, bufferLen, payload->cbClientData) ||
65         !(bufferLen <= payload->cbClientData))
66     {
67         delete payload;
68         return nullptr;
69     }
70
71     payload->pClientData = pBufferCursor;
72
73     return payload;
74 }
75
76 void ProfilerDiagnosticProtocolHelper::AttachProfiler(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream)
77 {
78     CONTRACTL
79     {
80         THROWS;
81         GC_TRIGGERS;
82         MODE_PREEMPTIVE;
83         PRECONDITION(pStream != nullptr);
84     }
85     CONTRACTL_END;
86
87     if (pStream == nullptr)
88     {
89         return;
90     }
91
92     HRESULT hr = S_OK;
93     NewHolder<const AttachProfilerCommandPayload> payload = message.TryParsePayload<AttachProfilerCommandPayload>();
94     if (payload == nullptr)
95     {
96         hr = CORDIAGIPC_E_BAD_ENCODING;
97         goto ErrExit;
98     }
99
100     if (!g_profControlBlock.fProfControlBlockInitialized)
101     {
102         hr = CORPROF_E_RUNTIME_UNINITIALIZED;
103         goto ErrExit;
104     }
105
106     // Certain actions are only allowable during attach, and this flag is how we track it.
107     ClrFlsSetThreadType(ThreadType_ProfAPI_Attach);
108
109     EX_TRY
110     {
111         hr = ProfilingAPIUtility::LoadProfilerForAttach(&payload->profilerGuid,
112                                                         payload->pwszProfilerPath,
113                                                         payload->pClientData,
114                                                         payload->cbClientData,
115                                                         payload->dwAttachTimeout);
116     }
117     EX_CATCH_HRESULT(hr);
118
119     // Clear the flag so this thread isn't permanently marked as the attach thread.
120     ClrFlsClearThreadType(ThreadType_ProfAPI_Attach);
121
122 ErrExit:
123     if (hr != S_OK)
124     {
125         DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, hr);
126     }
127     else
128     {
129         DiagnosticsIpc::IpcMessage profilerAttachResponse;
130         if (profilerAttachResponse.Initialize(DiagnosticsIpc::GenericSuccessHeader, hr))
131             profilerAttachResponse.Send(pStream);
132     }
133     delete pStream;
134 }
135
136 #endif // defined(FEATURE_PERFTRACING) && defined(FEATURE_PROFAPI_ATTACH_DETACH) && !defined(DACCESS_COMPILE)