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.
25static ConditionLock configured;
26const int maxTryNum = 100;
27using namespace dap;
28
29class 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
51DapSession::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
61DapSession::~DapSession()
62{
63
64}
65
66void 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> &param) {
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
94void 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
103bool 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
145void DapSession::stop()
146{
147 d->server->stop();
148}
149
150void 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
160void 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
234void 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
589void DapSession::handleAsyncStopped(const dap::StoppedEvent &stoppedEvent)
590{
591 d->session->send(stoppedEvent);
592 Log("--> Server sent stopped event to client\n")
593}
594
595void DapSession::handleAsyncExited(const dap::ExitedEvent &exitedEvent)
596{
597 d->session->send(exitedEvent);
598 Log("--> Server sent exited event to client\n")
599}
600
601void 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
611void 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
624void 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
637void 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
647void 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
658dap::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
700InitializeResponse 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
721void DapSession::handleTerminateEvent()
722{
723 dap::TerminatedEvent terminatedEvent;
724 d->session->send(terminatedEvent);
725}
726
727void 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