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 | |
28 | namespace DEBUG_NAMESPACE { |
29 | |
30 | static constexpr const char *kLocals = "locals" ; |
31 | |
32 | using namespace dap; |
33 | DebugSession::DebugSession(DebugModel *_model, QObject *parent) |
34 | : QObject(parent), |
35 | id(QUuid::createUuid().toString().toStdString()), |
36 | model(_model) |
37 | { |
38 | } |
39 | |
40 | DebugSession::~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 | |
61 | const Capabilities &DebugSession::capabilities() const |
62 | { |
63 | return raw->capabilities(); |
64 | } |
65 | |
66 | bool 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 | |
116 | bool 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 | |
125 | bool 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 | |
134 | void DebugSession::restart() |
135 | { |
136 | if (!raw) |
137 | return; |
138 | |
139 | raw->restart({}); |
140 | } |
141 | |
142 | void DebugSession::terminate(bool restart) |
143 | { |
144 | if (!raw) |
145 | return; |
146 | |
147 | raw->terminate(restart); |
148 | } |
149 | |
150 | void 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 | |
161 | void DebugSession::continueDbg(integer threadId) |
162 | { |
163 | if (!raw) |
164 | return; |
165 | |
166 | ContinueRequest request; |
167 | request.threadId = threadId; |
168 | raw->continueDbg(request); |
169 | } |
170 | |
171 | void 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 | |
181 | void 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 | |
191 | void 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 | |
201 | dap::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 | |
217 | dap::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 | |
234 | dap::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 | |
251 | dap::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 | |
266 | dap::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 | |
280 | dap::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 | |
294 | dap::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 | |
302 | dap::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 | |
316 | void 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 | |
347 | void 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 | |
398 | dap::array<IRawStoppedDetails *> &DebugSession::getStoppedDetails() |
399 | { |
400 | return stoppedDetails; |
401 | } |
402 | |
403 | void 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 | |
414 | dap::optional<dap::Source> DebugSession::getSourceForUri(QUrl &uri) |
415 | { |
416 | Q_UNUSED(uri) |
417 | return undefined; |
418 | } |
419 | |
420 | Source *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 | |
436 | void 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 | |
451 | void 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 | |
460 | void 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 | |
472 | void 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 | |
480 | void 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 | |
513 | void 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 | |
536 | void DebugSession::sendExceptionBreakpoints(dap::array<IExceptionBreakpoint> &exbpts) { |
537 | Q_UNUSED(exbpts) |
538 | // TODO(mozart) |
539 | } |
540 | |
541 | dap::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 | |
559 | void 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 | |
576 | void 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 | |
598 | void 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 | |
622 | dap::optional<dap::Breakpoint> DebugSession::getDebugProtocolBreakpoint(string &breakpointId) |
623 | { |
624 | auto id = getId(); |
625 | return model->getDebugProtocolBreakpoint(breakpointId, id); |
626 | } |
627 | |
628 | dap::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 | |
640 | dap::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 | |
665 | dap::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 | |
677 | dap::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 | |
702 | dap::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 | |
719 | void 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 | |
731 | void 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 | |
740 | string DebugSession::getId() |
741 | { |
742 | return id; |
743 | } |
744 | |
745 | integer DebugSession::getThreadId() |
746 | { |
747 | return threadId; |
748 | } |
749 | |
750 | string DebugSession::getLabel() const |
751 | { |
752 | return "session" ; |
753 | } |
754 | |
755 | void DebugSession::setName(string &_name) |
756 | { |
757 | name = _name; |
758 | // fire event. |
759 | } |
760 | |
761 | void 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. |
772 | bool 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. |
803 | bool 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 | |
823 | Session *DebugSession::getDapSession() const |
824 | { |
825 | return session.get(); |
826 | } |
827 | |
828 | RawDebugSession *DebugSession::getRawSession() const |
829 | { |
830 | return raw.get(); |
831 | } |
832 | |
833 | dap::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 | |
850 | void DebugSession::onBreakpointHit(const StoppedEvent &event) |
851 | { |
852 | } |
853 | |
854 | void DebugSession::onStep(const StoppedEvent &event) |
855 | { |
856 | } |
857 | |
858 | void DebugSession::closeSession() |
859 | { |
860 | shutdown(); |
861 | } |
862 | |
863 | void DebugSession::disassemble(const dap::string &address) |
864 | { |
865 | DisassembleRequest request; |
866 | request.memoryReference = address; |
867 | raw->disassemble(request); |
868 | } |
869 | |
870 | } // endnamespace |
871 | |