Add generate crash dump command to diagnostics server (#24460)
[platform/upstream/coreclr.git] / src / vm / diagnosticserver.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 "diagnosticserver.h"
7 #include "eventpipeprotocolhelper.h"
8 #include "diagnosticprotocolhelper.h"
9
10 #ifdef FEATURE_PAL
11 #include "pal.h"
12 #endif // FEATURE_PAL
13
14 #ifdef FEATURE_PERFTRACING
15
16 IpcStream::DiagnosticsIpc *DiagnosticServer::s_pIpc = nullptr;
17
18 static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter)
19 {
20     CONTRACTL
21     {
22         NOTHROW;
23         GC_TRIGGERS;
24         MODE_PREEMPTIVE;
25         PRECONDITION(lpThreadParameter != nullptr);
26     }
27     CONTRACTL_END;
28
29     auto pIpc = reinterpret_cast<IpcStream::DiagnosticsIpc *>(lpThreadParameter);
30     if (pIpc == nullptr)
31     {
32         STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Diagnostics IPC listener was undefined\n");
33         return 1;
34     }
35
36     ErrorCallback LoggingCallback = [](const char *szMessage, uint32_t code) {
37         STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_WARNING, "warning (%d): %s.\n", code, szMessage);
38     };
39
40     EX_TRY
41     {
42         while (true)
43         {
44             // FIXME: Ideally this would be something like a std::shared_ptr
45             IpcStream *pStream = pIpc->Accept(LoggingCallback);
46             if (pStream == nullptr)
47                 continue;
48
49             // TODO: Read operation should happen in a loop.
50             uint32_t nNumberOfBytesRead = 0;
51             MessageHeader header;
52             bool fSuccess = pStream->Read(&header, sizeof(header), nNumberOfBytesRead);
53             if (!fSuccess || nNumberOfBytesRead != sizeof(header))
54             {
55                 delete pStream;
56                 continue;
57             }
58
59             switch (header.RequestType)
60             {
61             case DiagnosticMessageType::StopEventPipeTracing:
62                 EventPipeProtocolHelper::StopTracing(pStream);
63                 break;
64
65             case DiagnosticMessageType::CollectEventPipeTracing:
66                 EventPipeProtocolHelper::CollectTracing(pStream);
67                 break;
68
69 #ifdef FEATURE_PAL
70             case DiagnosticMessageType::GenerateCoreDump:
71                 DiagnosticProtocolHelper::GenerateCoreDump(pStream);
72                 break;
73 #endif
74
75             default:
76                 STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", header.RequestType);
77                 delete pStream;
78                 break;
79             }
80         }
81     }
82     EX_CATCH
83     {
84         STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Exception caught in diagnostic thread. Leaving thread now.\n");
85         _ASSERTE(!"Hit an error in the diagnostic server thread\n.");
86     }
87     EX_END_CATCH(SwallowAllExceptions);
88
89     return 0;
90 }
91
92 bool DiagnosticServer::Initialize()
93 {
94     CONTRACTL
95     {
96         NOTHROW;
97         GC_TRIGGERS;
98         MODE_ANY;
99     }
100     CONTRACTL_END;
101
102     bool fSuccess = false;
103
104     EX_TRY
105     {
106         auto ErrorCallback = [](const char *szMessage, uint32_t code) {
107             STRESS_LOG2(
108                 LF_DIAGNOSTICS_PORT,                                  // facility
109                 LL_ERROR,                                             // level
110                 "Failed to create diagnostic IPC: error (%d): %s.\n", // msg
111                 code,                                                 // data1
112                 szMessage);                                           // data2
113         };
114
115         // TODO: Should we handle/assert that (s_pIpc == nullptr)?
116         s_pIpc = IpcStream::DiagnosticsIpc::Create(
117             "dotnetcore-diagnostic", ErrorCallback);
118
119         if (s_pIpc != nullptr)
120         {
121             DWORD dwThreadId = 0;
122             HANDLE hThread = ::CreateThread( // TODO: Is it correct to have this "lower" level call here?
123                 nullptr,                     // no security attribute
124                 0,                           // default stack size
125                 DiagnosticsServerThread,     // thread proc
126                 (LPVOID)s_pIpc,              // thread parameter
127                 0,                           // not suspended
128                 &dwThreadId);                // returns thread ID
129
130             if (hThread == nullptr)
131             {
132                 // Failed to create IPC thread.
133                 STRESS_LOG1(
134                     LF_DIAGNOSTICS_PORT,                                 // facility
135                     LL_ERROR,                                            // level
136                     "Failed to create diagnostic server thread (%d).\n", // msg
137                     ::GetLastError());                                   // data1
138             }
139             else
140             {
141                 // FIXME: Maybe hold on to the thread to abort/cleanup at exit?
142                 ::CloseHandle(hThread);
143
144                 // TODO: Add error handling?
145                 fSuccess = true;
146             }
147         }
148     }
149     EX_CATCH
150     {
151         // TODO: Should we log anything here?
152     }
153     EX_END_CATCH(SwallowAllExceptions);
154
155     return fSuccess;
156 }
157
158 bool DiagnosticServer::Shutdown()
159 {
160     CONTRACTL
161     {
162         NOTHROW;
163         GC_TRIGGERS;
164         MODE_ANY;
165     }
166     CONTRACTL_END;
167
168     bool fSuccess = false;
169
170     EX_TRY
171     {
172         if (s_pIpc != nullptr)
173         {
174             auto ErrorCallback = [](const char *szMessage, uint32_t code) {
175                 STRESS_LOG2(
176                     LF_DIAGNOSTICS_PORT,                                  // facility
177                     LL_ERROR,                                             // level
178                     "Failed to unlink diagnostic IPC: error (%d): %s.\n", // msg
179                     code,                                                 // data1
180                     szMessage);                                           // data2
181             };
182             s_pIpc->Unlink(ErrorCallback);
183         }
184         fSuccess = true;
185     }
186     EX_CATCH
187     {
188         fSuccess = false;
189         // TODO: Should we log anything here?
190     }
191     EX_END_CATCH(SwallowAllExceptions);
192
193     return fSuccess;
194 }
195
196 #endif // FEATURE_PERFTRACING