1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "debugsession.h"
6
7#include "rawdebugsession.h"
8#include "runtimecfgprovider.h"
9#include "debugservice.h"
10#include "debuggersignals.h"
11#include "debuggerglobals.h"
12#include "debugmodel.h"
13#include "stackframe.h"
14#include "interface/variable.h"
15
16#include "dap/io.h"
17#include "dap/protocol.h"
18#include "dap/session.h"
19#include "dap/network.h"
20
21#include <QDebug>
22#include <QUuid>
23
24#include <chrono>
25#include <thread>
26#include <algorithm>
27
28namespace DEBUG_NAMESPACE {
29
30static constexpr const char *kLocals = "locals";
31
32using namespace dap;
33DebugSession::DebugSession(DebugModel *_model, QObject *parent)
34 : QObject(parent),
35 id(QUuid::createUuid().toString().toStdString()),
36 model(_model)
37{
38}
39
40DebugSession::~DebugSession()
41{
42 for (auto it : stoppedDetails) {
43 if (it) {
44 delete it;
45 it = nullptr;
46 }
47 }
48 stoppedDetails.clear();
49
50 for (auto it : threads) {
51 if (it.second) {
52 delete it.second;
53 it.second = nullptr;
54 }
55 }
56 threads.clear();
57
58 delete alertBox;
59}
60
61const Capabilities &DebugSession::capabilities() const
62{
63 return raw->capabilities();
64}
65
66bool DebugSession::initialize(const char *ip, int port, dap::InitializeRequest &iniRequest)
67{
68 shutdown();
69
70 if (!raw) {
71 // if there was already a connection make sure to remove old listeners
72 // TODO(mozart):crashed when re-start debug.
73 rtCfgProvider.reset(new RunTimeCfgProvider(/*this*/));
74
75 constexpr int kMaxAttempts = 10;
76 bool connected = false;
77 // The socket might take a while to open - retry connecting.
78 for (int attempt = 0; attempt < kMaxAttempts; attempt++) {
79 auto connection = net::connect(ip, port);
80 if (!connection) {
81 std::this_thread::sleep_for(std::chrono::milliseconds(100));
82 continue;
83 }
84 // Socket opened. Create the debugger session and bind.
85 session.reset();
86 session = dap::Session::create();
87 session->bind(connection);
88
89 raw.reset(new RawDebugSession(session /*, this*/));
90
91 connected = true;
92 break;
93 }
94
95 if (!connected) {
96 return false;
97 }
98 }
99
100 auto init_res = raw->initialize(iniRequest).get();
101 if (init_res.error) {
102 initialized = false;
103 shutdown();
104 qDebug() << init_res.error.message.c_str();
105 return false;
106 } else {
107 initialized = true;
108 }
109
110 // Notify outter register handlers.
111 emit sigRegisterHandlers();
112
113 return initialized;
114}
115
116bool DebugSession::launch(dap::LaunchRequest &config)
117{
118 if (!raw)
119 return false;
120
121 auto ret = raw->launch(config);
122 return ret.valid();
123}
124
125bool DebugSession::attach(dap::AttachRequest &config)
126{
127 if (!raw)
128 return false;
129
130 auto ret = raw->attach(config);
131 return ret.valid();
132}
133
134void DebugSession::restart()
135{
136 if (!raw)
137 return;
138
139 raw->restart({});
140}
141
142void DebugSession::terminate(bool restart)
143{
144 if (!raw)
145 return;
146
147 raw->terminate(restart);
148}
149
150void DebugSession::disconnect(bool terminateDebuggee, bool restart)
151{
152 if (!raw)
153 return;
154
155 DisconnectRequest request;
156 request.terminateDebuggee = terminateDebuggee;
157 request.restart = restart;
158 raw->disconnect(request);
159}
160
161void DebugSession::continueDbg(integer threadId)
162{
163 if (!raw)
164 return;
165
166 ContinueRequest request;
167 request.threadId = threadId;
168 raw->continueDbg(request);
169}
170
171void DebugSession::reverseContinue(integer threadId)
172{
173 if (!raw)
174 return;
175
176 dap::ReverseContinueRequest request;
177 request.threadId = threadId;
178 raw->reverseContinue(request).wait();
179}
180
181void DebugSession::pause(integer threadId)
182{
183 if (!raw)
184 return;
185
186 PauseRequest request;
187 request.threadId = threadId;
188 raw->pause(request).wait();
189}
190
191void DebugSession::terminateThreads(dap::array<integer> &threadIds)
192{
193 if (!raw)
194 return;
195
196 dap::TerminateThreadsRequest request;
197 request.threadIds = threadIds;
198 raw->terminateThreads(request);
199}
200
201dap::optional<SetVariableResponse> DebugSession::setVariable(
202 integer variablesReference, string &name, string &value)
203{
204 if (!raw)
205 return undefined;
206
207 dap::SetVariableRequest request;
208 request.variablesReference = variablesReference;
209 request.name = name;
210 request.value = value;
211 auto response = raw->setVariable(request);
212 if (response.valid())
213 return response.get().response;
214 return undefined;
215}
216
217dap::optional<SetExpressionResponse> DebugSession::setExpression(
218 integer frameId, string &expression, string &value)
219{
220 if (!raw)
221 return undefined;
222
223 dap::SetExpressionRequest request;
224 request.frameId = frameId;
225 request.expression = expression;
226 request.value = value;
227 auto response = raw->setExpression(request);
228 if (response.valid())
229 return response.get().response;
230
231 return undefined;
232}
233
234dap::optional<GotoTargetsResponse> DebugSession::gotoTargets(
235 dap::Source &source, integer line, integer column)
236{
237 if (!raw)
238 return undefined;
239
240 dap::GotoTargetsRequest request;
241 request.source = source;
242 request.line = line;
243 request.column = column;
244 auto response = raw->gotoTargets(request);
245 if (response.valid())
246 return response.get().response;
247
248 return undefined;
249}
250
251dap::optional<GotoResponse> DebugSession::goto_(integer threadId, integer targetId)
252{
253 if (!raw)
254 return undefined;
255
256 dap::GotoRequest request;
257 request.threadId = threadId;
258 request.targetId = targetId;
259 auto response = raw->goto_(request);
260 if (response.valid())
261 return response.get().response;
262
263 return undefined;
264}
265
266dap::optional<StepInTargetsResponse> DebugSession::stepInTargets(integer frameId)
267{
268 if (!raw)
269 return undefined;
270
271 dap::StepInTargetsRequest request;
272 request.frameId = frameId;
273 auto response = raw->stepInTargets(request);
274 if (response.valid())
275 return response.get().response;
276
277 return undefined;
278}
279
280dap::optional<CancelResponse> DebugSession::cancel(string &progressId)
281{
282 if (!raw)
283 return undefined;
284
285 dap::CancelRequest request;
286 request.progressId = progressId;
287 auto response = raw->cancel(request);
288 if (response.valid())
289 return response.get().response;
290
291 return undefined;
292}
293
294dap::optional<Thread *> DebugSession::getThread(integer threadId)
295{
296 auto it = threads.find(threadId);
297 if (it != threads.end())
298 return it->second;
299 return undefined;
300}
301
302dap::optional<dap::array<IThread *>> DebugSession::getAllThreads() const
303{
304 dap::array<IThread *> result;
305 for (auto id : threadIds) {
306 auto it = threads.find(id);
307 if (it != threads.end()) {
308 result.push_back(it->second);
309 }
310 }
311 dap::optional<dap::array<IThread *>> ret;
312 ret = result;
313 return ret;
314}
315
316void DebugSession::clearThreads(bool removeThreads, dap::optional<integer> reference)
317{
318 if (reference) {
319 auto thread = threads.find(reference.value());
320 if (thread != threads.end()) {
321 auto threadptr = thread->second;
322 threadptr->clearCallStack();
323 threadptr->stoppedDetails = undefined;
324 threadptr->stopped = false;
325
326 if (removeThreads) {
327 delete threadptr;
328 threadptr = nullptr;
329 }
330 }
331 } else {
332 for (auto thread : threads) {
333 auto threadptr = thread.second;
334 threadptr->clearCallStack();
335 threadptr->stoppedDetails = undefined;
336 threadptr->stopped = false;
337 }
338
339 if (removeThreads) {
340 threads.clear();
341 threadIds.clear();
342 // ExpressionContainer.allValues.clear();
343 }
344 }
345}
346
347void DebugSession::rawUpdate(IRawModelUpdate *data)
348{
349 threadIds.clear();
350
351 for (auto thread : data->threads) {
352 threadIds.push_back(thread.id);
353 if (threads.find(thread.id) == threads.end()) {
354 // save the new thread.
355 threads.insert(std::pair<dapNumber, Thread *>(thread.id, new Thread(this, thread.name, thread.id)));
356 } else if (!thread.name.empty()) {
357 // update thread name.
358 auto oldThread = threads.find(thread.id);
359 if (oldThread != threads.end()) {
360 oldThread->second->name = thread.name;
361 }
362 }
363 }
364
365 // Remove all old threads which are no longer part of the update
366 for (auto t = threads.begin(); t != threads.end();) {
367 auto it = std::find(threadIds.begin(), threadIds.end(), t->first);
368 if (it == threadIds.end()) {
369 t = threads.erase(t);
370 } else {
371 ++t;
372 }
373 }
374
375 auto stoppedDetails = data->stoppedDetails;
376 if (stoppedDetails) {
377 if (stoppedDetails->allThreadsStopped) {
378 for (auto thread : threads) {
379 if (thread.second->threadId == stoppedDetails->threadId.value()) {
380 thread.second->stoppedDetails = stoppedDetails;
381 thread.second->stopped = true;
382 thread.second->clearCallStack();
383 }
384 }
385 } else {
386 if (stoppedDetails->threadId) {
387 auto thread = threads.find(stoppedDetails->threadId.value());
388 if (thread != threads.end()) {
389 thread->second->stoppedDetails = stoppedDetails;
390 thread->second->clearCallStack();
391 thread->second->stopped = true;
392 }
393 }
394 }
395 }
396}
397
398dap::array<IRawStoppedDetails *> &DebugSession::getStoppedDetails()
399{
400 return stoppedDetails;
401}
402
403void DebugSession::fetchThreads(dap::optional<IRawStoppedDetails> stoppedDetails)
404{
405 if (raw) {
406 auto response = raw->threads();
407 if (response.valid()) {
408 IRawModelUpdate args { getId(), response.get().response.threads, stoppedDetails };
409 model->rawUpdate(&args);
410 }
411 }
412}
413
414dap::optional<dap::Source> DebugSession::getSourceForUri(QUrl &uri)
415{
416 Q_UNUSED(uri)
417 return undefined;
418}
419
420Source *DebugSession::getSource(dap::optional<dap::Source> raw)
421{
422 DEBUG::Source *source = new DEBUG::Source(raw, id);
423 dap::string uriKey = source->uri.toString().toStdString();
424 auto found = sources.find(uriKey);
425 if (found != sources.end()) {
426 found->second->raw.presentationHint = source->presentationHint();
427 delete source;
428 source = nullptr;
429 } else {
430 sources.insert(std::pair<dap::string, Source *>(uriKey, source));
431 }
432
433 return source;
434}
435
436void DebugSession::stepIn(dap::integer threadId,
437 dap::optional<integer> targetId,
438 dap::optional<dap::SteppingGranularity> granularity)
439{
440 if (!raw)
441 return;
442
443 StepInRequest request;
444 request.threadId = threadId;
445 request.targetId = targetId;
446 request.granularity = granularity;
447 setLastSteppingGranularity(threadId, granularity);
448 raw->stepIn(request);
449}
450
451void DebugSession::stepOut(integer threadId, dap::optional<dap::SteppingGranularity> granularity)
452{
453 StepOutRequest request;
454 request.threadId = threadId;
455 request.granularity = granularity;
456 setLastSteppingGranularity(threadId, granularity);
457 raw->stepOut(request);
458}
459
460void DebugSession::stepBack(integer threadId, dap::optional<SteppingGranularity> granularity)
461{
462 if (!raw)
463 return;
464
465 setLastSteppingGranularity(threadId, granularity);
466 dap::StepBackRequest request;
467 request.threadId = threadId;
468 request.granularity = granularity;
469 raw->stepBack(request).wait();
470}
471
472void DebugSession::next(integer threadId, dap::optional<dap::SteppingGranularity> granularity)
473{
474 NextRequest request;
475 request.threadId = threadId;
476 request.granularity = granularity;
477 raw->next(request);
478}
479
480void DebugSession::sendBreakpoints(const QString &sourcePath, dap::array<IBreakpoint> &breakpointsToSend)
481{
482 if (!raw || !raw->readyForBreakpoints())
483 return;
484
485 SetBreakpointsRequest request;
486 request.source.path = sourcePath.toStdString();
487 dap::array<SourceBreakpoint> breakpoints;
488 for (auto it : breakpointsToSend) {
489 dap::Source source;
490 source.path = it.uri.toString().toStdString();
491 source.name = undefined;
492 request.source = source;
493 SourceBreakpoint bt;
494 bt.line = it.lineNumber;
495 breakpoints.push_back(bt);
496 }
497
498 request.breakpoints = breakpoints;
499 auto response = raw->setBreakpoints(request);
500 if (response.valid()) {
501 response.wait();
502
503 auto data = std::map<dap::string, dap::Breakpoint>();
504 for (size_t i = 0; i < breakpointsToSend.size(); ++i) {
505 if (response.get().response.breakpoints.size() > i)
506 data.insert(std::pair<dap::string, dap::Breakpoint>(breakpointsToSend[i].getId(), response.get().response.breakpoints[i]));
507 }
508
509 model->setBreakpointSessionData(id, capabilities(), data);
510 }
511}
512
513void DebugSession::sendFunctionBreakpoints(dap::array<IFunctionBreakpoint> &fbpts)
514{
515 if (!raw)
516 return;
517
518 if (!raw->readyForBreakpoints()) {
519 qInfo() << "break point not ready!";
520 return;
521 }
522 SetFunctionBreakpointsRequest request;
523 if (raw->readyForBreakpoints()) {
524 auto response = raw->setFunctionBreakpoints(request);
525 if (response.valid()) {
526 auto data = std::map<dap::string, dap::Breakpoint>();
527 for (size_t i = 0; i < fbpts.size(); ++i) {
528 data.insert(std::pair<dap::string, dap::Breakpoint>(fbpts[i].getId(), response.get().response.breakpoints[i]));
529 }
530
531 model->setBreakpointSessionData(id, capabilities(), data);
532 }
533 }
534}
535
536void DebugSession::sendExceptionBreakpoints(dap::array<IExceptionBreakpoint> &exbpts) {
537 Q_UNUSED(exbpts)
538 // TODO(mozart)
539}
540
541dap::optional<DataBreakpointInfoResponse> DebugSession::dataBreakpointInfo(
542 string &name, dap::optional<integer> variablesReference)
543{
544 if (!raw)
545 return undefined;
546
547 if (!raw->readyForBreakpoints()) {
548 qInfo() << "break point not ready!";
549 return undefined;
550 }
551
552 DataBreakpointInfoRequest request;
553 request.name = name;
554 request.variablesReference = variablesReference;
555 auto response = raw->dataBreakpointInfo(request);
556 return response.get().response;
557}
558
559void convertTodbp(const array<IDataBreakpoint> &dataBreakpoints, array<DataBreakpoint> &result)
560{
561 for (auto bp : dataBreakpoints) {
562 DataBreakpoint dbp;
563 dbp.accessType = bp.accessType;
564 // An optional expression for conditional breakpoints.
565 dbp.condition = bp.condition;
566 // An id representing the data. This id is returned from the
567 // dataBreakpointInfo request.
568 dbp.dataId = bp.dataId;
569 // An optional expression that controls how many hits of the breakpoint are
570 // ignored. The backend is expected to interpret the expression as needed.
571 dbp.hitCondition = bp.hitCondition;
572 result.push_back(dbp);
573 }
574}
575
576void DebugSession::sendDataBreakpoints(dap::array<IDataBreakpoint> dataBreakpoints)
577{
578 if (!raw)
579 return;
580
581 if (!raw->readyForBreakpoints()) {
582 qInfo() << "break point not ready!";
583 return;
584 }
585
586 dap::SetDataBreakpointsRequest request;
587 convertTodbp(dataBreakpoints, request.breakpoints);
588 auto response = raw->setDataBreakpoints(request);
589 if (response.valid()) {
590 auto data = std::map<dap::string, dap::Breakpoint>();
591 for (size_t i = 0; i < dataBreakpoints.size(); ++i) {
592 data.insert(std::pair<dap::string, dap::Breakpoint>(dataBreakpoints[i].getId(), response.get().response.breakpoints[i]));
593 }
594 model->setBreakpointSessionData(id, capabilities(), data);
595 }
596}
597
598void DebugSession::sendInstructionBreakpoints(dap::array<IInstructionBreakpoint> instructionBreakpoints)
599{
600 if (!raw)
601 return;
602
603 if (!raw->readyForBreakpoints()) {
604 qInfo() << "break point not ready!";
605 return;
606 }
607
608 if (raw->readyForBreakpoints()) {
609 SetInstructionBreakpointsRequest request;
610 auto response = raw->setInstructionBreakpoints(request);
611 if (response.valid()) {
612 auto data = std::map<dap::string, dap::Breakpoint>();
613 for (size_t i = 0; i < instructionBreakpoints.size(); ++i) {
614 data.insert(std::pair<dap::string, dap::Breakpoint>(instructionBreakpoints[i].getId(), response.get().response.breakpoints[i]));
615 }
616
617 model->setBreakpointSessionData(id, capabilities(), data);
618 }
619 }
620}
621
622dap::optional<dap::Breakpoint> DebugSession::getDebugProtocolBreakpoint(string &breakpointId)
623{
624 auto id = getId();
625 return model->getDebugProtocolBreakpoint(breakpointId, id);
626}
627
628dap::optional<StackTraceResponse> DebugSession::stackTrace(integer threadId, integer startFrame, integer levels)
629{
630 if (!raw)
631 return undefined;
632
633 dap::StackTraceRequest request;
634 request.levels = levels;
635 request.startFrame = startFrame;
636 request.threadId = threadId;
637 return raw->stackTrace(request).get().response;
638}
639
640dap::optional<IExceptionInfo> DebugSession::exceptionInfo(integer threadId)
641{
642 if (!raw)
643 return undefined;
644
645 dap::ExceptionInfoRequest request;
646 request.threadId = threadId;
647
648 dap::optional<IExceptionInfo> ret;
649 auto response = raw->exceptionInfo(request);
650 if (response.valid()) {
651 auto body = response.get().response;
652
653 IExceptionInfo info;
654 info.id = body.exceptionId;
655 info.description = body.description;
656 info.breakMode = body.breakMode;
657 info.details = body.details;
658 ret = info;
659
660 return ret;
661 }
662 return undefined;
663}
664
665dap::optional<ScopesResponse> DebugSession::scopes(integer frameId, integer threadId)
666{
667 Q_UNUSED(threadId)
668
669 if (!raw)
670 return undefined;
671
672 dap::ScopesRequest request;
673 request.frameId = frameId;
674 return raw->scopes(request).get().response;
675}
676
677dap::optional<VariablesResponse> DebugSession::variables(
678 integer variablesReference,
679 dap::optional<integer> threadId,
680 dap::optional<string> filter,
681 dap::optional<integer> start,
682 dap::optional<integer> count)
683{
684 Q_UNUSED(threadId)
685
686 if (!raw)
687 return undefined;
688
689 VariablesRequest request;
690 request.count = count;
691 request.filter = filter;
692 request.start = start;
693 request.variablesReference = variablesReference;
694
695 auto response = raw->variables(request);
696 if (response.valid()) {
697 return response.get().response;
698 }
699 return undefined;
700}
701
702dap::optional<EvaluateResponse> DebugSession::evaluate(string &expression, integer frameId, dap::optional<string> context)
703{
704 if (!raw)
705 return undefined;
706
707 dap::EvaluateRequest request;
708 request.context = context;
709 request.expression = expression;
710 request.frameId = frameId;
711
712 auto response = raw->evaluate(request);
713 if (response.valid()) {
714 return response.get().response;
715 }
716 return undefined;
717}
718
719void DebugSession::restartFrame(integer frameId, integer threadId)
720{
721 Q_UNUSED(threadId)
722
723 if (!raw)
724 return;
725
726 dap::RestartFrameRequest request;
727 request.frameId = frameId;
728 raw->restartFrame(request);
729}
730
731void DebugSession::setLastSteppingGranularity(integer threadId,
732 dap::optional<SteppingGranularity> granularity)
733{
734 auto thread = getThread(threadId);
735 if (thread) {
736 thread.value()->lastSteppingGranularity = granularity;
737 }
738}
739
740string DebugSession::getId()
741{
742 return id;
743}
744
745integer DebugSession::getThreadId()
746{
747 return threadId;
748}
749
750string DebugSession::getLabel() const
751{
752 return "session";
753}
754
755void DebugSession::setName(string &_name)
756{
757 name = _name;
758 // fire event.
759}
760
761void DebugSession::shutdown()
762{
763 if (raw) {
764 raw->disconnect({});
765 raw.reset(nullptr);
766 }
767}
768
769// GetVariables fetches the fully traversed set of Variables from the debugger
770// for the given reference identifier.
771// Returns true on success, false on error.
772bool DebugSession::getVariables(dap::integer variablesRef, IVariables *out, dap::integer depth/* = 0*/)
773{
774 if (depth > 5)
775 return false;
776
777 dap::VariablesRequest request;
778 request.variablesReference = variablesRef;
779 auto response = raw->variables(request);
780 if (!response.valid()) {
781 return false;
782 }
783
784 array<Variable> &&variables = response.get().response.variables;
785 for (auto var : variables) {
786 IVariable v;
787 v.name = var.name;
788 v.var = var;
789 v.depth = depth + 1;
790 out->push_back(v);
791 if (var.variablesReference > 0) {
792 if (!getVariables(var.variablesReference, &v.children, v.depth)) {
793 return false;
794 }
795 }
796 }
797 return true;
798}
799
800// GetLocals fetches the fully traversed set of local Variables from the
801// debugger for the given stack frame.
802// Returns true on success, false on error.
803bool DebugSession::getLocals(dap::integer frameId, IVariables *out)
804{
805 dap::ScopesRequest scopeReq;
806 scopeReq.frameId = frameId;
807
808 if (!raw->scopes(scopeReq).valid()) {
809 return false;
810 }
811 auto scopeRes = raw->scopes(scopeReq).get().response;
812 for (auto scope : scopeRes.scopes) {
813 if (scope.presentationHint.value("") == kLocals
814 || scope.name == "Local") {
815 return getVariables(scope.variablesReference, out);
816 }
817 }
818
819 qInfo() << "Locals scope not found";
820 return false;
821}
822
823Session *DebugSession::getDapSession() const
824{
825 return session.get();
826}
827
828RawDebugSession *DebugSession::getRawSession() const
829{
830 return raw.get();
831}
832
833dap::array<dap::Thread> DebugSession::fetchThreads(IRawStoppedDetails *stoppedDetails)
834{
835 if (raw) {
836 auto response = raw->threads();
837 if (response.valid() && response.get().response.threads.size()) {
838 dap::optional<IRawStoppedDetails> details;
839 if (stoppedDetails) {
840 details = *stoppedDetails;
841 }
842 IRawModelUpdate args { getId(), response.get().response.threads, details };
843 model->rawUpdate(&args);
844 return response.get().response.threads;
845 }
846 }
847 return {};
848}
849
850void DebugSession::onBreakpointHit(const StoppedEvent &event)
851{
852}
853
854void DebugSession::onStep(const StoppedEvent &event)
855{
856}
857
858void DebugSession::closeSession()
859{
860 shutdown();
861}
862
863void DebugSession::disassemble(const dap::string &address)
864{
865 DisassembleRequest request;
866 request.memoryReference = address;
867 raw->disassemble(request);
868}
869
870} // endnamespace
871