1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "dapsession.h" |
6 | |
7 | #include "backendglobal.h" |
8 | #include "debugmanager.h" |
9 | #include "dapproxy.h" |
10 | #include "locker.h" |
11 | |
12 | #include "dap/protocol.h" |
13 | #include "dap/session.h" |
14 | #include "dap/network.h" |
15 | |
16 | #include <QDebug> |
17 | #include <QThread> |
18 | #include <QProcess> |
19 | #include <QtDBus/QDBusConnection> |
20 | #include <QList> |
21 | #include <QMetaObject> |
22 | #include <QDBusMessage> |
23 | |
24 | // keep the initialization in order. |
25 | static ConditionLock configured; |
26 | const int maxTryNum = 100; |
27 | using namespace dap; |
28 | |
29 | class DapSessionPrivate |
30 | { |
31 | friend class DapSession; |
32 | |
33 | std::shared_ptr<dap::Session> session; |
34 | std::unique_ptr<dap::net::Server> server; |
35 | |
36 | DebugManager *debugger{nullptr}; |
37 | ServerInfo serverInfo; |
38 | |
39 | bool isDebuggeIsStartWithLaunchRequest = false; |
40 | bool isDebuggeIsStartWithAttachRequest = false; |
41 | bool isSupportsTerminateDebuggee = true; |
42 | bool isGDbProcessTerminated = false; |
43 | bool isThreadRequestReceived = false; |
44 | bool isInferiorStopped = true; |
45 | |
46 | QString uuid; |
47 | |
48 | ConditionLock startedLock; |
49 | }; |
50 | |
51 | DapSession::DapSession(QObject *parent) |
52 | : QObject(parent) |
53 | , d (new DapSessionPrivate()) |
54 | { |
55 | qRegisterMetaType<std::shared_ptr<dap::ReaderWriter>>("std::shared_ptr<dap::ReaderWriter>" ); |
56 | registerDBusConnect(); |
57 | |
58 | initializeDebugMgr(); |
59 | } |
60 | |
61 | DapSession::~DapSession() |
62 | { |
63 | |
64 | } |
65 | |
66 | void DapSession::registerDBusConnect() |
67 | { |
68 | QDBusConnection sessionBus = QDBusConnection::sessionBus(); |
69 | sessionBus.disconnect(QString("" ), |
70 | "/path" , |
71 | "com.deepin.unioncode.interface" , |
72 | "getDebugPort" , |
73 | this, SLOT(slotReceiveClientInfo(QString, QString, QString, QStringList))); |
74 | sessionBus.connect(QString("" ), |
75 | "/path" , |
76 | "com.deepin.unioncode.interface" , |
77 | "getDebugPort" , |
78 | this, SLOT(slotReceiveClientInfo(QString, QString, QString, QStringList))); |
79 | |
80 | |
81 | connect(this, &DapSession::sigSendToClient, [](const QString &uuid, int port, const QString &kit, |
82 | const QMap<QString, QVariant> ¶m) { |
83 | QDBusMessage msg = QDBusMessage::createSignal("/path" , |
84 | "com.deepin.unioncode.interface" , |
85 | "dapport" ); |
86 | msg << uuid |
87 | << port |
88 | << kit |
89 | << param; |
90 | QDBusConnection::sessionBus().send(msg); |
91 | }); |
92 | } |
93 | |
94 | void DapSession::slotReceiveClientInfo(const QString &uuid, const QString &kit, const QString &targetPath, const QStringList &arguments) |
95 | { |
96 | QMap<QString, QVariant> param; |
97 | param.insert("targetPath" , targetPath); |
98 | param.insert("arguments" , arguments); |
99 | |
100 | emit sigSendToClient(uuid, d->serverInfo.port(), kit, param); |
101 | } |
102 | |
103 | bool DapSession::start() |
104 | { |
105 | auto onClientConnected = [this](const std::shared_ptr<ReaderWriter> &socket) { |
106 | QMetaObject::invokeMethod(this, "initialize" , Qt::BlockingQueuedConnection, Q_ARG(std::shared_ptr<dap::ReaderWriter>, socket)); |
107 | }; |
108 | |
109 | auto onError = [](const char *errMessage) { |
110 | Log(errMessage) |
111 | }; |
112 | |
113 | // Create the network server |
114 | d->server = dap::net::Server::create(); |
115 | |
116 | auto checkPortFree = [](int port) { |
117 | QProcess process; |
118 | QString cmd = QString("fuser %1/tcp" ).arg(port); |
119 | process.start(cmd); |
120 | process.waitForFinished(); |
121 | QString ret = process.readAll(); |
122 | if (ret.isEmpty()) |
123 | return true; |
124 | return false; |
125 | }; |
126 | |
127 | bool ret = false; |
128 | int port = kPort; |
129 | while (port - kPort < maxTryNum) { |
130 | if (checkPortFree(port)) { |
131 | // Start listening on kPort. |
132 | // onClientConnected will be called when a client wants to connect. |
133 | // onError will be called on any connection errors. |
134 | ret = d->server->start(port, onClientConnected, onError); |
135 | if (ret) { |
136 | d->serverInfo.setPort(port); |
137 | break; |
138 | } |
139 | } |
140 | port++; |
141 | } |
142 | return ret; |
143 | } |
144 | |
145 | void DapSession::stop() |
146 | { |
147 | d->server->stop(); |
148 | } |
149 | |
150 | void DapSession::initialize(std::shared_ptr<dap::ReaderWriter> socket) |
151 | { |
152 | d->session.reset(); |
153 | d->session = dap::Session::create(); |
154 | registerHanlder(); |
155 | d->session->bind(socket); |
156 | |
157 | Log("DapSession initialized.\n" ) |
158 | } |
159 | |
160 | void DapSession::initializeDebugMgr() |
161 | { |
162 | // instance a debug manager object |
163 | if (!d->debugger) |
164 | d->debugger = DebugManager::instance(); |
165 | |
166 | Qt::ConnectionType SequentialExecution = Qt::BlockingQueuedConnection; |
167 | |
168 | // Output Event and Module Event |
169 | connect(d->debugger, &DebugManager::streamDebugInternal, [&](const QStringList &textList) mutable { |
170 | handleOutputTextEvent(textList); |
171 | }); |
172 | |
173 | connect(d->debugger, &DebugManager::asyncStopped, DapProxy::instance(), [this](const dap::StoppedEvent &stoppedEvent) mutable { |
174 | handleAsyncStopped(stoppedEvent); |
175 | d->isInferiorStopped = true; |
176 | }); |
177 | |
178 | connect(d->debugger, &DebugManager::asyncExited, DapProxy::instance(), [this](const dap::ExitedEvent &exitedEvent) mutable { |
179 | handleAsyncExited(exitedEvent); |
180 | d->isInferiorStopped = true; |
181 | }); |
182 | |
183 | connect(d->debugger, &DebugManager::asyncRunning, DapProxy::instance(), [this](const QString &) mutable { |
184 | d->isInferiorStopped = false; |
185 | }); |
186 | |
187 | connect(d->debugger, &DebugManager::libraryLoaded, DapProxy::instance(), [this](const dap::ModuleEvent &moduleEvent) mutable { |
188 | handleLibraryLoaded(moduleEvent); |
189 | }); |
190 | |
191 | connect(d->debugger, &DebugManager::libraryUnloaded, DapProxy::instance(), [this](const dap::ModuleEvent &moduleEvent) mutable { |
192 | handleLibraryUnloaded(moduleEvent); |
193 | }); |
194 | |
195 | QObject::connect(d->debugger, &DebugManager::streamConsole, [&](const QString &text) mutable { |
196 | handleStreamConsole(text); |
197 | }); |
198 | |
199 | connect(d->debugger, &DebugManager::terminated, DapProxy::instance(), [this]() mutable { |
200 | handleTerminateEvent(); |
201 | }); |
202 | |
203 | QObject::connect(d->debugger, &DebugManager::assemblerData, [&](const QStringList &data) mutable { |
204 | handleAssembleData(data); |
205 | }); |
206 | |
207 | connect(d->debugger, &DebugManager::dbgProcessStarted, [this]() { |
208 | d->startedLock.fire(); |
209 | }); |
210 | |
211 | connect(d->debugger, &DebugManager::dbgProcessFinished, [this]() { |
212 | d->startedLock.reset(); |
213 | }); |
214 | |
215 | connect(DapProxy::instance(), &DapProxy::sigQuit, d->debugger, &DebugManager::quit, SequentialExecution); |
216 | connect(DapProxy::instance(), &DapProxy::sigKill, d->debugger, &DebugManager::kill, SequentialExecution); |
217 | connect(DapProxy::instance(), &DapProxy::sigStart, d->debugger, &DebugManager::execute, SequentialExecution); |
218 | connect(DapProxy::instance(), &DapProxy::sigBreakInsert, d->debugger, &DebugManager::breakInsert, SequentialExecution); |
219 | connect(DapProxy::instance(), &DapProxy::sigLaunchLocal, d->debugger, &DebugManager::launchLocal, SequentialExecution); |
220 | connect(DapProxy::instance(), &DapProxy::sigContinue, d->debugger, &DebugManager::commandContinue, SequentialExecution); |
221 | connect(DapProxy::instance(), &DapProxy::sigPause, d->debugger, &DebugManager::commandPause, SequentialExecution); |
222 | connect(DapProxy::instance(), &DapProxy::sigNext, d->debugger, &DebugManager::commandNext, SequentialExecution); |
223 | connect(DapProxy::instance(), &DapProxy::sigStepin, d->debugger, &DebugManager::commandStep, SequentialExecution); |
224 | connect(DapProxy::instance(), &DapProxy::sigStepout, d->debugger, &DebugManager::commandFinish, SequentialExecution); |
225 | connect(DapProxy::instance(), &DapProxy::sigThreads, d->debugger, &DebugManager::threadInfo, SequentialExecution); |
226 | connect(DapProxy::instance(), &DapProxy::sigSelectThread, d->debugger, &DebugManager::threadSelect, SequentialExecution); |
227 | connect(DapProxy::instance(), &DapProxy::sigStackTrace, d->debugger, &DebugManager::stackListFrames, SequentialExecution); |
228 | connect(DapProxy::instance(), &DapProxy::sigVariables, d->debugger, &DebugManager::stackListVariables, SequentialExecution); |
229 | connect(DapProxy::instance(), &DapProxy::sigSource, d->debugger, &DebugManager::listSourceFiles, SequentialExecution); |
230 | connect(DapProxy::instance(), &DapProxy::sigBreakRemoveAll, d->debugger, &DebugManager::breakRemoveAll, SequentialExecution); |
231 | |
232 | } |
233 | |
234 | void DapSession::registerHanlder() |
235 | { |
236 | // The Initialize request is the first message sent from the client and |
237 | // the response reports debugger capabilities. |
238 | d->session->registerHandler([&](const dap::InitializeRequest &request) { |
239 | return handleInitializeReq(request); |
240 | }); |
241 | |
242 | // The SetExceptionBreakpoints request instructs the debugger to set a exception breakpoints |
243 | d->session->registerHandler([&](const dap::SetExceptionBreakpointsRequest &request) { |
244 | Q_UNUSED(request) |
245 | Log("<-- Server received setExceptionBreakpoints request from client\n" ) |
246 | dap::SetExceptionBreakpointsResponse response; |
247 | |
248 | Log("--> Server sent setExceptionBreakpoints response to client\n" ) |
249 | return response; |
250 | }); |
251 | |
252 | // The SetBreakpoints request instructs the debugger to clear and set a number |
253 | // of line breakpoints for a specific source file. |
254 | // |
255 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints |
256 | d->session->registerHandler([&](const dap::SetBreakpointsRequest &request) { |
257 | return handleBreakpointReq(request); |
258 | }); |
259 | |
260 | // The SetFunctionBreakpointBreakpoints request instructs the debugger to set a function breakpoints |
261 | d->session->registerHandler([&](const dap::SetFunctionBreakpointsRequest &request) { |
262 | Q_UNUSED(request) |
263 | dap::SetFunctionBreakpointsResponse response; |
264 | Log("<-- Server received setFunctionBreakpoints request from client\n" ) |
265 | auto breakpoints = request.breakpoints; |
266 | for (auto &breakpoint : breakpoints) { |
267 | auto functionName = breakpoint.name; |
268 | qInfo() << functionName.c_str(); |
269 | if (!functionName.empty()) { |
270 | d->debugger->breakInsert(functionName.c_str()); |
271 | } |
272 | } |
273 | // Generic setFunctionBreakpointResponse |
274 | Log("--> Server sent setFunctionBreakpoints response to client\n" ) |
275 | return response; |
276 | }); |
277 | |
278 | // The SetDataBreakpoints request instructs the debugger to set a data breakpoints |
279 | d->session->registerHandler([&](const dap::SetDataBreakpointsRequest &request) { |
280 | Q_UNUSED(request) |
281 | dap::SetDataBreakpointsResponse response; |
282 | Log("<-- Server received SetDataBreakpoints request from client\n" ) |
283 | |
284 | Log("--> Server sent SetDataBreakpoints response to client\n" ) |
285 | return response; |
286 | }); |
287 | |
288 | // Signal used to configurate the server session when ConfigurationDoneReqeust |
289 | // The ConfigurationDone request is made by the client once all configuration |
290 | // requests have been made. |
291 | // |
292 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_ConfigurationDone |
293 | d->session->registerHandler([&](const dap::ConfigurationDoneRequest &request) { |
294 | Q_UNUSED(request) |
295 | Log("<-- Server received configurationDone request from client\n" ) |
296 | auto response = dap::ConfigurationDoneResponse(); |
297 | Log("--> Server sent configurationDone response to client\n" ) |
298 | return response; |
299 | }); |
300 | |
301 | // execute debugger and debuggee after configurate done response |
302 | d->session->registerSentHandler([&](const dap::ResponseOrError<dap::ConfigurationDoneResponse> &response) { |
303 | Q_UNUSED(response) |
304 | connect(d->debugger, &DebugManager::asyncRunning, DapProxy::instance(), |
305 | [this](const QString& processName, const QString& theadId) mutable { |
306 | // TODO(Any):multi-thread condition should be done. |
307 | Q_UNUSED(theadId) |
308 | dap::integer pointerSize; |
309 | dap::ProcessEvent processEvent; |
310 | processEvent.name = processName.toStdString(); |
311 | processEvent.startMethod = "launch" ; |
312 | processEvent.systemProcessId = d->debugger->getProcessId(); |
313 | processEvent.pointerSize = pointerSize; |
314 | d->session->send(processEvent); |
315 | Log("--> Server sent process Event to client\n" ) |
316 | |
317 | configured.fire(); |
318 | }, Qt::UniqueConnection); |
319 | |
320 | emit DapProxy::instance()->sigLaunchLocal(); |
321 | }); |
322 | |
323 | // The Launch request is made when the client instructs the debugger adapter |
324 | // to start the debuggee. This request contains the launch arguments. |
325 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Launch |
326 | d->session->registerHandler([&](const dap::LaunchRequest &request) { |
327 | Q_UNUSED(request) |
328 | dap::LaunchResponse response {}; |
329 | Log("<-- Server received launch request from client\n" ) |
330 | d->isDebuggeIsStartWithLaunchRequest = true; |
331 | |
332 | if (request.name.has_value() && request.program.has_value() ) { |
333 | QStringList arguments; |
334 | arguments.push_back(request.program.value().c_str()); |
335 | if (request.args.has_value()) { |
336 | foreach(auto arg, request.args.value()) { |
337 | arguments.push_back(arg.c_str()); |
338 | } |
339 | } |
340 | d->debugger->initDebugger(request.name.value().c_str(), arguments); |
341 | } |
342 | emit DapProxy::instance()->sigStart(); |
343 | auto message = QString{"gdb process started with pid: %1\n" }.arg(d->debugger->getProcessId()); |
344 | Log(message.toStdString().c_str()) |
345 | Log("--> Server sent launch response to client\n" ) |
346 | return response; |
347 | }); |
348 | |
349 | d->session->registerSentHandler([&](const dap::ResponseOrError<dap::LaunchResponse> &response) { |
350 | Q_UNUSED(response) |
351 | Log("--> Server sent initialized event to client\n" ) |
352 | d->session->send(dap::InitializedEvent()); |
353 | }); |
354 | |
355 | // The Attach request |
356 | d->session->registerHandler([=](const dap::AttachRequest &request) { |
357 | Q_UNUSED(request) |
358 | Log("<-- Server received attach reqeust from client\n" ) |
359 | d->isDebuggeIsStartWithAttachRequest = true; |
360 | dap::AttachResponse response; |
361 | Log("--> Server sent attach response to client\n" ) |
362 | return response; |
363 | }); |
364 | |
365 | // The Restart request |
366 | // Restarts a debug session. Clients should only call this request if the capability |
367 | // ‘supportsRestartRequest’ is true |
368 | d->session->registerHandler([=](const dap::RestartRequest &request) { |
369 | Q_UNUSED(request) |
370 | dap::RestartResponse response; |
371 | Log("<-- Server received restart request from client\n" ) |
372 | |
373 | d->isDebuggeIsStartWithLaunchRequest = true; |
374 | // kill current process. |
375 | emit DapProxy::instance()->sigKill(); |
376 | // re-launch local. |
377 | emit DapProxy::instance()->sigLaunchLocal(); |
378 | |
379 | Log("--> Server sent restart response to client\n" ) |
380 | return response; |
381 | }); |
382 | |
383 | // The Terminate requestis sent from the client to the debug adapter |
384 | //in order to give the debuggee a chance for terminating itself. |
385 | // map to Debug Plugin Menu: Abort Debugging |
386 | d->session->registerHandler([=](const dap::TerminateRequest &request) { |
387 | Q_UNUSED(request) |
388 | dap::TerminateResponse response; |
389 | Log("<-- Server received terminate request from client\n" ) |
390 | // send quit command to debugger |
391 | DebugManager::instance()->terminate(); |
392 | |
393 | Log("--> Server sent terminate response to client\n" ) |
394 | return response; |
395 | }); |
396 | |
397 | // The Pause request instructs the debugger to pause execution of one or all |
398 | // threads. |
399 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Pause |
400 | d->session->registerHandler([&](const dap::PauseRequest &request) { |
401 | Q_UNUSED(request) |
402 | Log("<-- Server received pause request from client\n" ) |
403 | emit DapProxy::instance()->sigPause(); |
404 | Log("--> Server sent pause response to client\n" ) |
405 | return dap::PauseResponse(); |
406 | }); |
407 | |
408 | // The Continue request instructs the debugger to resume execution of one or |
409 | // all threads. |
410 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Continue |
411 | d->session->registerHandler([&](const dap::ContinueRequest &request) { |
412 | Q_UNUSED(request) |
413 | // auto threadId = request.threadId; |
414 | Log("<-- Server received continue request from client\n" ) |
415 | emit DapProxy::instance()->sigContinue(); |
416 | Log("--> Server received continue request from client\n" ) |
417 | return dap::ContinueResponse(); |
418 | }); |
419 | |
420 | // The Next request instructs the debugger to single line step for a specific |
421 | // thread. |
422 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Next |
423 | d->session->registerHandler([&](const dap::NextRequest &request) { |
424 | Q_UNUSED(request) |
425 | Log("<-- Server received next request from client\n" ) |
426 | emit DapProxy::instance()->sigNext(); |
427 | Log("--> Server sent to next response client\n" ) |
428 | return dap::NextResponse(); |
429 | }); |
430 | |
431 | // The StepIn request instructs the debugger to step-in for a specific thread. |
432 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepIn |
433 | d->session->registerHandler([&](const dap::StepInRequest &request) { |
434 | Q_UNUSED(request) |
435 | // auto targetId = request.targetId; |
436 | // auto threadId = request.threadId; |
437 | // auto granularity = request.granularity; |
438 | Log("<-- Server received stepin request from client\n" ) |
439 | emit DapProxy::instance()->sigStepin(); |
440 | Log("--> Server sent stepin response to client\n" ) |
441 | return dap::StepInResponse(); |
442 | }); |
443 | |
444 | // The StepOut request instructs the debugger to step-out for a specific |
445 | // thread. |
446 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepOut |
447 | d->session->registerHandler([&](const dap::StepOutRequest &request) { |
448 | Q_UNUSED(request) |
449 | // auto threadId = request.threadId; |
450 | // auto granularity = request.granularity; |
451 | Log("<-- Server received stepout request from client\n" ) |
452 | emit DapProxy::instance()->sigStepout(); |
453 | Log("--> Server sent stepout response to client\n" ) |
454 | return dap::StepOutResponse(); |
455 | }); |
456 | |
457 | // The BreakpointLocations request returns all possible locations for source breakpoints in a range |
458 | d->session->registerHandler([&](const dap::BreakpointLocationsRequest &request) { |
459 | Q_UNUSED(request) |
460 | Log("<-- Server received BreakpointLocations request from client\n" ) |
461 | dap::BreakpointLocationsResponse response; |
462 | Log("--> Server sent BreakpointLocations response to client\n" ) |
463 | return response; |
464 | }); |
465 | |
466 | // The Threads request queries the debugger's list of active threads. |
467 | // |
468 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Threads |
469 | d->session->registerHandler([&](const dap::ThreadsRequest &request) { |
470 | //configured.wait(); //TODO |
471 | Q_UNUSED(request) |
472 | // wait for debug process started. |
473 | d->startedLock.wait(); |
474 | d->isThreadRequestReceived = true; |
475 | dap::ThreadsResponse response; |
476 | Log("<-- Server recevied Thread request from client\n" ) |
477 | d->debugger->threadInfo(); |
478 | |
479 | Log("--> Server sent Thread response to client\n" ) |
480 | response.threads = d->debugger->allThreadList(); |
481 | return response; |
482 | }); |
483 | |
484 | // The request returns a stacktrace from the current execution state of a given thread. |
485 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StackTrace |
486 | d->session->registerHandler([&](const dap::StackTraceRequest &request) |
487 | -> dap::StackTraceResponse { |
488 | |
489 | auto threadid = request.threadId.operator long(); |
490 | |
491 | // select a given thread id |
492 | // -thread-select 3 |
493 | emit DapProxy::instance()->sigSelectThread(static_cast<int>(threadid)); |
494 | |
495 | dap::StackTraceResponse response; |
496 | Log("<-- Server received StackTrace request from the client\n" ) |
497 | if (d->isInferiorStopped) { |
498 | d->debugger->stackListFrames(); |
499 | response.stackFrames = d->debugger->allStackframes(); |
500 | Log("--> Server sent StackTrace response to the client\n" ) |
501 | } |
502 | |
503 | return response; |
504 | }); |
505 | |
506 | // The Scopes(all variables in selected thread and frame) request reports all the scopes of the given stack frame. |
507 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Scopes |
508 | d->session->registerHandler([&](const dap::ScopesRequest &request) |
509 | -> dap::ScopesResponse { |
510 | Q_UNUSED(request) |
511 | auto frameId = request.frameId; |
512 | static dap::ScopesResponse response; |
513 | dap::array<dap::Scope> scopes; |
514 | Log("<-- Server received Scopes request from the client\n" ) |
515 | //emit DapProxy::instance()->sigScopes(frameId); |
516 | |
517 | // locals |
518 | dap::Scope scopeLocals; |
519 | scopeLocals.presentationHint = "locals" ; |
520 | scopeLocals.name = "Locals" ; |
521 | scopeLocals.expensive = false; |
522 | scopeLocals.variablesReference = frameId; |
523 | scopes.push_back(scopeLocals); |
524 | |
525 | // register |
526 | dap::Scope scopeRegisters; |
527 | scopeRegisters.presentationHint = "registers" ; |
528 | scopeRegisters.name = "Registers" ; |
529 | scopeRegisters.expensive = false; |
530 | scopeRegisters.variablesReference = frameId + 1; |
531 | scopes.push_back(scopeRegisters); |
532 | response.scopes = scopes; |
533 | Log("--> Server sent Scopes response to the client\n" ) |
534 | return response; |
535 | }); |
536 | |
537 | // Retrieves all child variables for the given variable reference(the given scope). |
538 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Variables |
539 | d->session->registerHandler([&](const dap::VariablesRequest &request) |
540 | -> dap::ResponseOrError<dap::VariablesResponse> { |
541 | Q_UNUSED(request) |
542 | Log("<-- Server received Variables request from the client\n" ) |
543 | ResponseOrError<dap::VariablesResponse> response; |
544 | d->debugger->stackListVariables(); |
545 | response.response.variables = d->debugger->allVariableList(); |
546 | Log("--> Server sent Variables response to the client\n" ) |
547 | return response; |
548 | }); |
549 | |
550 | // The Source request retrieves the source code for a given source file. |
551 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Source |
552 | d->session->registerHandler([&](const dap::SourceRequest &request) |
553 | -> dap::ResponseOrError<dap::SourceResponse> { |
554 | Q_UNUSED(request) |
555 | dap::ResponseOrError<dap::SourceResponse> response; |
556 | Log("<-- Server received Source request from the client\n" ) |
557 | emit DapProxy::instance()->sigSource(); |
558 | Log("--> Server sent Source response to the client\n" ) |
559 | return response; |
560 | }); |
561 | |
562 | // The Disconnect request is made by the client before it disconnects |
563 | // from the server. |
564 | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect |
565 | // map to Debug Plugin Menu: Deattach |
566 | d->session->registerHandler([&](const dap::DisconnectRequest &request) { |
567 | Q_UNUSED(request) |
568 | Log("<-- Server received disconnnect request from client\n" ) |
569 | if (d->isSupportsTerminateDebuggee) { |
570 | if (d->isDebuggeIsStartWithLaunchRequest) { |
571 | emit DapProxy::instance()->sigKill(); |
572 | } else if (d->isDebuggeIsStartWithAttachRequest) { |
573 | qInfo() << "no supports terminate with attach target\n" ; |
574 | } |
575 | } |
576 | return dap::DisconnectResponse {}; |
577 | }); |
578 | |
579 | d->session->registerHandler([&](const dap::DisassembleRequest &request) { |
580 | Q_UNUSED(request) |
581 | Log("<-- Server received disassemble request from client\n" ) |
582 | if (!request.memoryReference.empty()) |
583 | d->debugger->disassemble(request.memoryReference.c_str()); |
584 | Log("--> Server sent disassemble response to client\n" ) |
585 | return dap::DisassembleResponse(); |
586 | }); |
587 | } |
588 | |
589 | void DapSession::handleAsyncStopped(const dap::StoppedEvent &stoppedEvent) |
590 | { |
591 | d->session->send(stoppedEvent); |
592 | Log("--> Server sent stopped event to client\n" ) |
593 | } |
594 | |
595 | void DapSession::handleAsyncExited(const dap::ExitedEvent &exitedEvent) |
596 | { |
597 | d->session->send(exitedEvent); |
598 | Log("--> Server sent exited event to client\n" ) |
599 | } |
600 | |
601 | void DapSession::handleThreadExited(const int threadId, const QString &groupId) |
602 | { |
603 | Q_UNUSED(groupId) |
604 | dap::ThreadEvent threadEvent; |
605 | threadEvent.reason = "exited" ; |
606 | threadEvent.threadId = threadId; |
607 | d->session->send(threadEvent); |
608 | Log("--> Server sent thread event to client\n" ) |
609 | } |
610 | |
611 | void DapSession::handleLibraryLoaded(const dap::ModuleEvent &moduleEvent) |
612 | { |
613 | dap::OutputEvent outputEvent; |
614 | outputEvent.category = "console" ; |
615 | QString targetName = QString::fromStdString(moduleEvent.module.name); |
616 | outputEvent.output = QString("Loaded %1. Symbols loaded.\n" ) |
617 | .arg(targetName).toStdString(); |
618 | d->session->send(outputEvent); |
619 | Log("--> Server sent output event to client\n" ) |
620 | d->session->send(moduleEvent); |
621 | Log("--> Server sent module event to client\n" ) |
622 | } |
623 | |
624 | void DapSession::handleLibraryUnloaded(const dap::ModuleEvent &moduleEvent) |
625 | { |
626 | dap::OutputEvent outputEvent; |
627 | outputEvent.category = "console" ; |
628 | QString targetName = QString::fromStdString(moduleEvent.module.name); |
629 | outputEvent.output = QString("Unloaded %1. Symbols loaded.\n" ) |
630 | .arg(targetName).toStdString(); |
631 | d->session->send(outputEvent); |
632 | Log("--> Server sent output event to client\n" ) |
633 | d->session->send(moduleEvent); |
634 | Log("--> Server sent module event to client\n" ) |
635 | } |
636 | |
637 | void DapSession::handleStreamConsole(const QString &text) |
638 | { |
639 | dap::OutputEvent outputEvent; |
640 | QString output = text; |
641 | outputEvent.category = "stdout" ; |
642 | outputEvent.output = output.toStdString(); |
643 | d->session->send(outputEvent); |
644 | // do not output log here, may cause block. |
645 | } |
646 | |
647 | void DapSession::handleOutputTextEvent(const QStringList &textList) |
648 | { |
649 | foreach (QString text, textList) { |
650 | dap::OutputEvent outputEvent; |
651 | outputEvent.output = text.toStdString(); |
652 | d->session->send(outputEvent); |
653 | qInfo() << "OutputText" << text; |
654 | Log("--> Server sent output event to client.\n" ) |
655 | } |
656 | } |
657 | |
658 | dap::SetBreakpointsResponse DapSession::handleBreakpointReq(const SetBreakpointsRequest &request) |
659 | { |
660 | dap::SetBreakpointsResponse response; |
661 | |
662 | Log("<-- Server received setBreakpoints request from client\n" ) |
663 | if (request.source.path->empty()) { |
664 | emit DapProxy::instance()->sigBreakRemoveAll(); |
665 | return response; |
666 | } |
667 | |
668 | const char *sourcePath = request.source.path->c_str(); |
669 | d->debugger->removeBreakpointInFile(sourcePath); |
670 | if (request.breakpoints.has_value()) { |
671 | dap::array<dap::Breakpoint> breakpoints; |
672 | for (auto &breakpoint : request.breakpoints.value()) { |
673 | dap::Breakpoint bp; |
674 | dap::Source source; |
675 | QString location; |
676 | if (request.source.path.has_value()) { |
677 | source = request.source; |
678 | } |
679 | location = source.path.value().c_str(); |
680 | location.append(":" ); |
681 | location.append(QString::number(breakpoint.line)); |
682 | emit DapProxy::instance()->sigBreakInsert(location); |
683 | |
684 | bp.line = breakpoint.line; |
685 | bp.source = request.source; |
686 | breakpoints.push_back(bp); |
687 | } |
688 | |
689 | // Generic response |
690 | Log("--> Server sent setBreakpoints response to client\n" ) |
691 | response.breakpoints = breakpoints; |
692 | } |
693 | |
694 | // return empty response to client, when lines and breakpoints all empty. |
695 | // Generic response |
696 | Log("--> Server sent setBreakpoints response to client\n" ) |
697 | return response; |
698 | } |
699 | |
700 | InitializeResponse DapSession::handleInitializeReq(const InitializeRequest &request) |
701 | { |
702 | Q_UNUSED(request) //! TODO(Mozart):get more info from here. |
703 | |
704 | Log("<-- Server received initialize request from client\n" ) |
705 | dap::InitializeResponse response {}; |
706 | response.supportsConfigurationDoneRequest = true; |
707 | response.supportsFunctionBreakpoints = true; |
708 | response.supportsInstructionBreakpoints = true; |
709 | response.supportsDataBreakpoints = true; |
710 | response.supportsConditionalBreakpoints = true; |
711 | response.supportsRestartRequest = true; |
712 | response.supportsTerminateRequest = true; |
713 | response.supportTerminateDebuggee = true; |
714 | response.supportsCompletionsRequest = true; |
715 | response.supportsDisassembleRequest = true; |
716 | |
717 | Log("--> Server sent initialize response to client\n" ) |
718 | return response; |
719 | } |
720 | |
721 | void DapSession::handleTerminateEvent() |
722 | { |
723 | dap::TerminatedEvent terminatedEvent; |
724 | d->session->send(terminatedEvent); |
725 | } |
726 | |
727 | void DapSession::handleAssembleData(const QStringList &data) |
728 | { |
729 | dap::OutputEvent outputEvent; |
730 | outputEvent.category = "assembler" ; |
731 | QString output; |
732 | foreach (auto line, data) { |
733 | output += line; |
734 | } |
735 | outputEvent.output = output.toStdString(); |
736 | d->session->send(outputEvent); |
737 | } |
738 | |