1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #ifndef DEBUG_H |
6 | #define DEBUG_H |
7 | |
8 | #include "dap/protocol.h" |
9 | #include "debuggerglobals.h" |
10 | #include "interface/variable.h" |
11 | |
12 | #include <QString> |
13 | #include <QUrl> |
14 | #include <QUuid> |
15 | |
16 | #include <map> |
17 | |
18 | namespace DEBUG_NAMESPACE { |
19 | |
20 | #define undefined \ |
21 | { \ |
22 | } |
23 | #define dapNumber dap::integer |
24 | #define ReadonlyArray dap::array |
25 | |
26 | struct IDebugSession; |
27 | struct IStackFrame; |
28 | struct IExpression; |
29 | struct Thread; |
30 | struct Source; |
31 | |
32 | struct IRawStoppedDetails |
33 | { |
34 | dap::optional<dap::string> reason; |
35 | dap::optional<dap::string> description; |
36 | dap::optional<dapNumber> threadId; |
37 | dap::optional<dap::string> text; |
38 | dap::optional<dapNumber> totalFrames; |
39 | dap::optional<bool> allThreadsStopped; |
40 | dap::optional<dap::string> framesErrorMessage; |
41 | dap::optional<dap::array<dapNumber>> hitBreakpointIds; |
42 | }; |
43 | |
44 | struct IRawModelUpdate |
45 | { |
46 | dap::string sessionId; |
47 | dap::array<dap::Thread> threads; |
48 | dap::optional<IRawStoppedDetails> stoppedDetails; |
49 | }; |
50 | |
51 | struct IBreakpointData |
52 | { |
53 | dap::optional<dap::string> id; |
54 | dap::optional<dapNumber> lineNumber; |
55 | dap::optional<dapNumber> column; |
56 | bool enabled = true; |
57 | dap::optional<dap::string> condition; |
58 | dap::optional<dap::string> logMessage; |
59 | dap::optional<dap::string> hitCondition; |
60 | }; |
61 | |
62 | struct IBreakpointUpdateData |
63 | { |
64 | dap::optional<dap::string> condition; |
65 | dap::optional<dap::string> hitCondition; |
66 | dap::optional<dap::string> logMessage; |
67 | dap::optional<dapNumber> lineNumber; |
68 | dap::optional<dapNumber> column; |
69 | }; |
70 | |
71 | struct ITreeElement |
72 | { |
73 | virtual dap::string getId() { return "" ; } |
74 | }; |
75 | |
76 | struct IEnablement : public ITreeElement |
77 | { |
78 | bool enabled = true; |
79 | }; |
80 | |
81 | struct IBaseBreakpoint : public IEnablement |
82 | { |
83 | dap::optional<dap::string> condition; |
84 | dap::optional<dap::string> hitCondition; |
85 | dap::optional<dap::string> logMessage; |
86 | bool verified = false; |
87 | bool support = false; // suported. |
88 | dap::optional<dap::string> message; |
89 | dap::array<dap::string> sessionsThatVerified; |
90 | dap::optional<dapNumber> getIdFromAdapter(dap::string sessionId); |
91 | }; |
92 | |
93 | struct IInnerBreakpoint |
94 | { |
95 | QUrl uri; |
96 | dapNumber lineNumber = 0; |
97 | dap::optional<dapNumber> endLineNumber; |
98 | dap::optional<dapNumber> column; |
99 | dap::optional<dapNumber> endColumn; |
100 | dap::any adapterData; |
101 | }; |
102 | |
103 | struct IBreakpoint : public IBaseBreakpoint, public IInnerBreakpoint |
104 | { |
105 | }; |
106 | |
107 | struct IFunctionBreakpoint : public IBaseBreakpoint |
108 | { |
109 | dap::string name; |
110 | }; |
111 | |
112 | struct IExceptionBreakpoint : public IBaseBreakpoint |
113 | { |
114 | dap::string filter; |
115 | dap::string label; |
116 | dap::string description; |
117 | }; |
118 | |
119 | struct IDataBreakpoint : IBaseBreakpoint |
120 | { |
121 | dap::string description; |
122 | dap::string dataId; |
123 | bool canPersist = false; |
124 | dap::DataBreakpointAccessType accessType; |
125 | }; |
126 | |
127 | struct IInstructionBreakpoint : public IBaseBreakpoint |
128 | { |
129 | // instructionReference is the instruction 'address' from the debugger. |
130 | dap::string instructionReference; |
131 | dapNumber offset = 0; |
132 | }; |
133 | |
134 | struct IExceptionInfo |
135 | { |
136 | dap::optional<dap::string> id; |
137 | dap::optional<dap::string> description; |
138 | dap::string breakMode; |
139 | dap::optional<dap::ExceptionDetails> details; |
140 | }; |
141 | |
142 | /** |
143 | * Base structs. |
144 | */ |
145 | |
146 | struct Enablement : public IEnablement |
147 | { |
148 | Enablement(bool _enabled, dap::string &_id) |
149 | : id(_id) |
150 | { |
151 | enabled = _enabled; |
152 | } |
153 | |
154 | dap::string getId() override |
155 | { |
156 | return id; |
157 | } |
158 | |
159 | private: |
160 | dap::string id; |
161 | }; |
162 | |
163 | struct IBreakpointSessionData : public dap::Breakpoint |
164 | { |
165 | bool supportsConditionalBreakpoints; |
166 | bool supportsHitConditionalBreakpoints; |
167 | bool supportsLogPoints; |
168 | bool supportsFunctionBreakpoints; |
169 | bool supportsDataBreakpoints; |
170 | bool supportsInstructionBreakpoints; |
171 | dap::string sessionId; |
172 | }; |
173 | |
174 | struct BaseBreakpoint : public IBaseBreakpoint |
175 | { |
176 | BaseBreakpoint( |
177 | bool _enabled, |
178 | dap::optional<dap::string> _hitCondition, |
179 | dap::optional<dap::string> _condition, |
180 | dap::optional<dap::string> _logMessage, |
181 | const dap::string &_id) |
182 | : id(_id) |
183 | { |
184 | enabled = _enabled; |
185 | hitCondition = _hitCondition; |
186 | condition = _condition; |
187 | logMessage = _logMessage; |
188 | } |
189 | |
190 | virtual ~BaseBreakpoint() {} |
191 | |
192 | void setSessionData(dap::string &sessionId, dap::optional<IBreakpointSessionData> data) |
193 | { |
194 | if (!data) { |
195 | auto it = sessionData.begin(); |
196 | for (; it != sessionData.end(); ++it) { |
197 | if (sessionId == it->first) { |
198 | it = sessionData.erase(it); |
199 | } |
200 | } |
201 | } else { |
202 | data.value().sessionId = sessionId; |
203 | sessionData.insert(std::pair<dap::string, IBreakpointSessionData>(sessionId, data.value())); |
204 | } |
205 | |
206 | // TODO(mozart) |
207 | } |
208 | |
209 | dap::optional<dap::string> message() |
210 | { |
211 | if (!data) { |
212 | return undefined; |
213 | } |
214 | return data.value().message; |
215 | } |
216 | |
217 | bool verified() |
218 | { |
219 | if (data) { |
220 | return data.value().verified; |
221 | } |
222 | return true; |
223 | } |
224 | |
225 | dap::array<dap::string> sessionsThatVerified() |
226 | { |
227 | dap::array<dap::string> sessionIds; |
228 | |
229 | auto it = sessionData.begin(); |
230 | for (; it != sessionData.end(); ++it) { |
231 | if (it->second.verified) { |
232 | sessionIds.push_back(it->first); |
233 | } |
234 | } |
235 | return sessionIds; |
236 | } |
237 | |
238 | dap::optional<dapNumber> getIdFromAdapter(dap::string &sessionId) |
239 | { |
240 | dap::optional<IBreakpointSessionData> data = getData(sessionId); |
241 | if (data) { |
242 | return data.value().id; |
243 | } |
244 | return undefined; |
245 | } |
246 | |
247 | dap::optional<IBreakpointSessionData> getData(dap::string &sessionId) |
248 | { |
249 | auto it = sessionData.begin(); |
250 | bool bFound = false; |
251 | for (; it != sessionData.end(); ++it) { |
252 | if (it->first == sessionId) { |
253 | data = it->second; |
254 | bFound = true; |
255 | break; |
256 | } |
257 | } |
258 | if (bFound) { |
259 | return data; |
260 | } |
261 | return undefined; |
262 | } |
263 | |
264 | dap::optional<dap::Breakpoint> getDebugProtocolBreakpoint(dap::string &sessionId) |
265 | { |
266 | dap::optional<dap::Breakpoint> bp; |
267 | auto data = getData(sessionId); |
268 | if (data) { |
269 | bp.value().id = data->id; |
270 | bp.value().verified = data->verified; |
271 | bp.value().message = data->message; |
272 | bp.value().source = data->source; |
273 | bp.value().line = data->line; |
274 | bp.value().column = data->column; |
275 | bp.value().endLine = data->endLine; |
276 | bp.value().endColumn = data->endColumn; |
277 | bp.value().instructionReference = data->instructionReference; |
278 | bp.value().offset = data->offset; |
279 | } |
280 | return bp; |
281 | } |
282 | |
283 | protected: |
284 | dap::optional<IBreakpointSessionData> data; |
285 | |
286 | std::map<dap::string, IBreakpointSessionData> sessionData; |
287 | dap::string id; |
288 | }; |
289 | |
290 | struct Breakpoint : public BaseBreakpoint, public IInnerBreakpoint |
291 | { |
292 | Breakpoint( |
293 | QUrl &uri, |
294 | dapNumber lineNumber, |
295 | dap::optional<dapNumber> column, |
296 | bool enabled, |
297 | dap::optional<dap::string> condition, |
298 | dap::optional<dap::string> hitCondition, |
299 | dap::optional<dap::string> logMessage, |
300 | dap::any adapterData, |
301 | const std::string &id = QUuid::createUuid().toString().toStdString()) |
302 | : BaseBreakpoint(enabled, hitCondition, condition, logMessage, id), _uri(uri), _lineNumber(lineNumber), _column(column), _adapterData(adapterData) |
303 | { |
304 | } |
305 | |
306 | bool isDirty(QUrl uri) |
307 | { |
308 | Q_UNUSED(uri) |
309 | // Not support dirty check now. |
310 | return false; |
311 | } |
312 | |
313 | dapNumber lineNumber() |
314 | { |
315 | if (verified() && data && data.value().line) { |
316 | return data.value().line.value(); |
317 | } |
318 | return _lineNumber; |
319 | } |
320 | |
321 | bool verified() |
322 | { |
323 | if (data) { |
324 | return data.value().verified && !isDirty(_uri); |
325 | } |
326 | return true; |
327 | } |
328 | |
329 | QUrl getUriFromSource(dap::Source source, dap::optional<dap::string> path, dap::string sessionId) |
330 | { |
331 | Q_UNUSED(path) |
332 | Q_UNUSED(sessionId) |
333 | return QUrl(source.path->c_str()); |
334 | } |
335 | |
336 | QUrl uri() |
337 | { |
338 | if (verified() && data && data.value().source) { |
339 | return getUriFromSource(data.value().source.value(), data.value().source.value().path, data->sessionId); |
340 | } |
341 | return _uri; |
342 | } |
343 | |
344 | dap::optional<dap::integer> column() |
345 | { |
346 | if (verified() && data && data.value().column) { |
347 | return data.value().column; |
348 | } |
349 | return _column; |
350 | } |
351 | |
352 | dap::optional<dap::string> message() |
353 | { |
354 | if (isDirty(uri())) { |
355 | return "Unverified breakpoint. File is modified, please restart debug session." ; |
356 | } |
357 | return BaseBreakpoint::message(); |
358 | } |
359 | |
360 | dap::any adapterData() |
361 | { |
362 | if (data && data.value().source && data.value().source.value().adapterData) { |
363 | return data.value().source.value().adapterData.value(); |
364 | } |
365 | return _adapterData; |
366 | } |
367 | |
368 | dap::optional<dapNumber> endLineNumber() |
369 | { |
370 | if (verified() && data) { |
371 | return data.value().endLine; |
372 | } |
373 | return undefined; |
374 | } |
375 | |
376 | dap::optional<dapNumber> endColumn() |
377 | { |
378 | if (verified() && data) { |
379 | return data.value().endColumn; |
380 | } |
381 | return undefined; |
382 | } |
383 | |
384 | bool supported() |
385 | { |
386 | if (!data) { |
387 | return true; |
388 | } |
389 | if (logMessage && !data.value().supportsLogPoints) { |
390 | return false; |
391 | } |
392 | if (condition && !data.value().supportsConditionalBreakpoints) { |
393 | return false; |
394 | } |
395 | if (hitCondition && !data.value().supportsHitConditionalBreakpoints) { |
396 | return false; |
397 | } |
398 | |
399 | return true; |
400 | } |
401 | |
402 | void setSessionData(dap::string &sessionId, dap::optional<IBreakpointSessionData> data) |
403 | { |
404 | BaseBreakpoint::setSessionData(sessionId, data); |
405 | if (_adapterData.is<std::nullptr_t>()) { |
406 | _adapterData = adapterData(); |
407 | } |
408 | } |
409 | |
410 | dap::string toString() |
411 | { |
412 | return undefined; |
413 | } |
414 | |
415 | void update(IBreakpointUpdateData &data) |
416 | { |
417 | if (!data.lineNumber) { |
418 | _lineNumber = data.lineNumber.value(); |
419 | } |
420 | if (!data.column) { |
421 | _column = data.column; |
422 | } |
423 | if (!data.condition) { |
424 | condition = data.condition; |
425 | } |
426 | if (!data.hitCondition) { |
427 | hitCondition = data.hitCondition; |
428 | } |
429 | if (!data.logMessage) { |
430 | logMessage = data.logMessage; |
431 | } |
432 | } |
433 | |
434 | private: |
435 | QUrl _uri; |
436 | dapNumber _lineNumber = 0; |
437 | dap::optional<dapNumber> _column; |
438 | dap::any _adapterData; |
439 | }; |
440 | |
441 | struct IThread : public ITreeElement |
442 | { |
443 | public: |
444 | /** |
445 | * Process the thread belongs to |
446 | */ |
447 | IDebugSession *session = nullptr; |
448 | |
449 | /** |
450 | * Id of the thread generated by the debug adapter backend. |
451 | */ |
452 | dapNumber threadId = 0; |
453 | |
454 | /** |
455 | * Name of the thread. |
456 | */ |
457 | dap::string name; |
458 | |
459 | /** |
460 | * Information about the current thread stop event. Undefined if thread is not stopped. |
461 | */ |
462 | dap::optional<IRawStoppedDetails> stoppedDetails; |
463 | |
464 | /** |
465 | * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise undefined. |
466 | */ |
467 | dap::optional<IExceptionInfo> exceptionInfo; |
468 | |
469 | dap::string stateLabel; |
470 | |
471 | /** |
472 | * Gets the callstack if it has already been received from the debug |
473 | * adapter. |
474 | */ |
475 | ReadonlyArray<IStackFrame *> getCallStack(); |
476 | |
477 | /** |
478 | * Gets the top stack frame that is not hidden if the callstack has already been received from the debug adapter |
479 | */ |
480 | dap::optional<IStackFrame *> getTopStackFrame(); |
481 | |
482 | /** |
483 | * Invalidates the callstack cache |
484 | */ |
485 | void clearCallStack(); |
486 | |
487 | /** |
488 | * Indicates whether this thread is stopped. The callstack for stopped |
489 | * threads can be retrieved from the debug adapter. |
490 | */ |
491 | bool stopped = false; |
492 | |
493 | dap::any next(dap::SteppingGranularity granularity); |
494 | dap::any stepIn(dap::optional<dap::SteppingGranularity> granularity); |
495 | dap::any stepOut(dap::SteppingGranularity granularity); |
496 | dap::any stepBack(dap::SteppingGranularity granularity); |
497 | dap::any continue_(); |
498 | dap::any pause(); |
499 | dap::any terminate(); |
500 | dap::any reverseContinue(); |
501 | }; |
502 | |
503 | struct IExpressionContainer : public ITreeElement |
504 | { |
505 | bool hasChildren = false; |
506 | dap::array<IExpression *> getChildren(); |
507 | dap::optional<dapNumber> reference; |
508 | dap::string value; |
509 | dap::string type; |
510 | dap::optional<bool> valueChanged; |
511 | }; |
512 | |
513 | struct IExpression : public IExpressionContainer |
514 | { |
515 | dap::string name; |
516 | }; |
517 | |
518 | struct IRange |
519 | { |
520 | /** |
521 | * Line number on which the range starts (starts at 1). |
522 | */ |
523 | dapNumber startLineNumber = 0; |
524 | /** |
525 | * Column on which the range starts in line `startLineNumber` (starts at 1). |
526 | */ |
527 | dapNumber startColumn = 0; |
528 | /** |
529 | * Line number on which the range ends. |
530 | */ |
531 | dapNumber endLineNumber = 0; |
532 | /** |
533 | * Column on which the range ends in line `endLineNumber`. |
534 | */ |
535 | dapNumber endColumn = 0; |
536 | }; |
537 | |
538 | struct IScope : public IExpressionContainer |
539 | { |
540 | dap::string name; |
541 | bool expensive = false; |
542 | dap::optional<IRange> range; |
543 | }; |
544 | |
545 | struct IStackFrame : public ITreeElement |
546 | { |
547 | virtual ~IStackFrame() {} |
548 | IThread *thread = nullptr; |
549 | dap::string name; |
550 | dap::optional<dap::string> presentationHint; |
551 | dapNumber frameId = 0; |
552 | IRange range; |
553 | Source *source = nullptr; |
554 | bool canRestart; |
555 | dap::optional<dap::string> instructionPointerReference; |
556 | dap::array<IScope> getScopes(); |
557 | ReadonlyArray<IScope> getMostSpecificScopes(IRange range); |
558 | void forgetScopes(); |
559 | dap::any restart(); |
560 | dap::string toString(); |
561 | // openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined>; |
562 | bool equals(IStackFrame other); |
563 | }; |
564 | |
565 | enum State { |
566 | kInactive, |
567 | kInitializing, |
568 | kStopped, |
569 | kRunning |
570 | }; |
571 | |
572 | struct IEnvConfig |
573 | { |
574 | // 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart' |
575 | dap::optional<dap::string> internalConsoleOptions; |
576 | dap::optional<dap::string> preRestartTask; |
577 | dap::optional<dap::string> postRestartTask; |
578 | dap::optional<dap::string> preLaunchTaskr; |
579 | dap::optional<dap::string> postDebugTask; |
580 | dap::optional<dapNumber> debugServer; |
581 | dap::optional<bool> noDebug; |
582 | }; |
583 | |
584 | struct IConfigPresentation |
585 | { |
586 | dap::optional<bool> hidden; |
587 | dap::optional<dap::string> group; |
588 | dap::optional<dapNumber> order; |
589 | }; |
590 | |
591 | struct IConfig : public IEnvConfig |
592 | { |
593 | // fundamental attributes |
594 | dap::string type; |
595 | dap::string request; |
596 | dap::string name; |
597 | dap::optional<IConfigPresentation *> presentation; |
598 | |
599 | // internals |
600 | dap::optional<dap::string> __sessionId; |
601 | dap::optional<dap::any> __restart; |
602 | dap::optional<bool> __autoAttach; |
603 | dap::optional<dapNumber> port; |
604 | }; |
605 | |
606 | struct IDebugSession : public ITreeElement |
607 | { |
608 | virtual ~IDebugSession() {} |
609 | virtual const dap::Capabilities &capabilities() const = 0; |
610 | |
611 | virtual bool initialize(const char *ip, int port, dap::InitializeRequest &iniRequest) = 0; |
612 | |
613 | virtual bool launch(dap::LaunchRequest &config) = 0; |
614 | virtual bool attach(dap::AttachRequest &config) = 0; |
615 | |
616 | virtual void restart() = 0; |
617 | virtual void terminate(bool restart = false) = 0; |
618 | virtual void disconnect(bool terminateDebuggee = true, bool restart = false) = 0; |
619 | |
620 | virtual void sendBreakpoints(const QString &sourcePath, dap::array<IBreakpoint> &breakpointsToSend) = 0; |
621 | virtual void sendFunctionBreakpoints(dap::array<IFunctionBreakpoint> &fbpts) = 0; |
622 | virtual void sendExceptionBreakpoints(dap::array<IExceptionBreakpoint> &exbpts) = 0; |
623 | virtual dap::optional<dap::DataBreakpointInfoResponse> dataBreakpointInfo( |
624 | dap::string &name, dap::optional<dapNumber> variablesReference) = 0; |
625 | virtual void sendDataBreakpoints(dap::array<IDataBreakpoint> dataBreakpoints) = 0; |
626 | virtual void sendInstructionBreakpoints(dap::array<IInstructionBreakpoint> instructionBreakpoints) = 0; |
627 | // dap::array<IPosition> breakpointsLocations(URI uri, number lineNumber); |
628 | virtual dap::optional<dap::Breakpoint> getDebugProtocolBreakpoint(dap::string &breakpointId) = 0; |
629 | // dap::optional<dap::Response> customRequest(dap::string &request, dap::any args); |
630 | virtual dap::optional<dap::StackTraceResponse> stackTrace(dapNumber threadId, dapNumber startFrame, dapNumber levels) = 0; |
631 | virtual dap::optional<IExceptionInfo> exceptionInfo(dapNumber threadId) = 0; |
632 | virtual dap::optional<dap::ScopesResponse> scopes(dapNumber frameId, dapNumber threadId) = 0; |
633 | virtual dap::optional<dap::VariablesResponse> variables(dapNumber variablesReference, |
634 | dap::optional<dapNumber> threadId, |
635 | dap::optional<dap::string> filter, |
636 | dap::optional<dapNumber> start, |
637 | dap::optional<dapNumber> count) = 0; |
638 | virtual dap::optional<dap::EvaluateResponse> evaluate( |
639 | dap::string &expression, dapNumber frameId, dap::optional<dap::string> context) = 0; |
640 | virtual void restartFrame(dapNumber frameId, dapNumber threadId) = 0; |
641 | virtual void setLastSteppingGranularity(dapNumber threadId, dap::optional<dap::SteppingGranularity> granularity) = 0; |
642 | |
643 | virtual void next(dap::integer threadId, dap::optional<dap::SteppingGranularity> granularity) = 0; |
644 | virtual void stepIn(dap::integer threadId, dap::optional<dap::integer> targetId, |
645 | dap::optional<dap::SteppingGranularity> granularity) = 0; |
646 | virtual void stepOut(dap::integer threadId, dap::optional<dap::SteppingGranularity> granularity) = 0; |
647 | virtual void stepBack(dapNumber threadId, dap::optional<dap::SteppingGranularity> granularity) = 0; |
648 | virtual void continueDbg(dap::integer threadId) = 0; |
649 | virtual void reverseContinue(dapNumber threadId) = 0; |
650 | virtual void pause(dap::integer threadId) = 0; |
651 | virtual void terminateThreads(dap::array<dapNumber> &threadIds) = 0; |
652 | virtual dap::optional<dap::SetVariableResponse> setVariable( |
653 | dapNumber variablesReference, dap::string &name, dap::string &value) = 0; |
654 | virtual dap::optional<dap::SetExpressionResponse> setExpression( |
655 | dapNumber frameId, dap::string &expression, dap::string &value) = 0; |
656 | virtual dap::optional<dap::GotoTargetsResponse> gotoTargets(dap::Source &source, dapNumber line, dapNumber column) = 0; |
657 | virtual dap::optional<dap::GotoResponse> goto_(dapNumber threadId, dapNumber targetId) = 0; |
658 | // dap::optional<dap::SourceResponse> loadSource(QUrl &resource); |
659 | // dap::array<dap::Source> getLoadedSources(); |
660 | // dap::optional<dap::CompletionsResponse> completions( |
661 | // dap::optional<number> frameId, |
662 | // dap::optional<number> threadId, |
663 | // dap::string &text, |
664 | // dap::Position &position, |
665 | // number overwriteBefore); |
666 | virtual dap::optional<dap::StepInTargetsResponse> stepInTargets(dapNumber frameId) = 0; |
667 | virtual dap::optional<dap::CancelResponse> cancel(dap::string &progressId) = 0; |
668 | // dap::optional<dap::array<dap::DisassembledInstruction>> disassemble(dap::string &memoryReference, number offset, number instructionOffset, number instructionCount); |
669 | // dap::optional<dap::ReadMemoryResponse> readMemory(dap::string &memoryReference, number offset, number count); |
670 | // dap::optional<dap::WriteMemoryResponse> writeMemory(dap::string &memoryReference, number offset, dap::string &data, dap::optional<bool> allowPartial); |
671 | // threads. |
672 | virtual dap::optional<Thread *> getThread(dapNumber threadId) = 0; |
673 | virtual dap::optional<dap::array<IThread *>> getAllThreads() const = 0; |
674 | virtual void rawUpdate(IRawModelUpdate *data) = 0; |
675 | virtual void clearThreads(bool removeThreads, dap::optional<dapNumber> reference) = 0; |
676 | virtual dap::array<IRawStoppedDetails *> &getStoppedDetails() = 0; |
677 | virtual void fetchThreads(dap::optional<IRawStoppedDetails> stoppedDetails) = 0; |
678 | virtual dap::optional<dap::Source> getSourceForUri(QUrl &uri) = 0; |
679 | virtual Source *getSource(dap::optional<dap::Source> raw) = 0; |
680 | virtual dap::string getLabel() const = 0; |
681 | |
682 | virtual dap::integer getThreadId() = 0; |
683 | virtual void setName(dap::string &name) = 0; |
684 | |
685 | virtual bool getLocals(dap::integer frameId, IVariables *out) = 0; |
686 | |
687 | State state = kInactive; |
688 | IConfig *configuration = nullptr; |
689 | }; |
690 | |
691 | struct ExpressionContainer : public IExpressionContainer |
692 | { |
693 | }; |
694 | |
695 | struct Range : public IRange |
696 | { |
697 | Range(dapNumber _startLineNumber, dapNumber _startColumn, dapNumber _endLineNumber, dapNumber _endColumn) |
698 | { |
699 | startLineNumber = _startLineNumber; |
700 | startColumn = _startColumn; |
701 | endLineNumber = _endLineNumber; |
702 | endColumn = _endColumn; |
703 | } |
704 | }; |
705 | |
706 | struct Source |
707 | { |
708 | Source(dap::optional<dap::Source> raw_, dap::string &sessionId) |
709 | { |
710 | dap::string path; |
711 | if (raw_) { |
712 | raw = raw_.value(); |
713 | if (raw.path) |
714 | path = raw.path.value(); |
715 | else if (raw.name) { |
716 | path = raw.name.value(); |
717 | } else { |
718 | path = "" ; |
719 | } |
720 | available = true; |
721 | } else { |
722 | raw.name = "Unknown Source" ; |
723 | available = false; |
724 | path = "debug:Unknown Source" ; |
725 | } |
726 | uri = getUriFromSource(raw, path, sessionId); |
727 | } |
728 | |
729 | dap::optional<dap::string> name() const |
730 | { |
731 | return raw.name; |
732 | } |
733 | |
734 | dap::optional<dap::string> origin() const |
735 | { |
736 | return raw.origin; |
737 | } |
738 | |
739 | dap::optional<dap::string> path() const |
740 | { |
741 | return raw.path; |
742 | } |
743 | |
744 | dap::optional<dap::string> presentationHint() const |
745 | { |
746 | return raw.presentationHint; |
747 | } |
748 | |
749 | dap::optional<dap::integer> reference() const |
750 | { |
751 | return raw.sourceReference; |
752 | } |
753 | |
754 | bool inMemory() const |
755 | { |
756 | return uri.scheme() == "debug" ; |
757 | } |
758 | |
759 | QUrl getUriFromSource(dap::Source &raw, dap::optional<dap::string> path, dap::string &sessionId) |
760 | { |
761 | if (raw.sourceReference && raw.sourceReference.value() > 0) { |
762 | QUrl url; |
763 | url.setPath(path->c_str()); |
764 | url.setScheme("debug" ); |
765 | QString query = QString("session=%s&ref=%d" ).arg(sessionId.c_str()).arg(raw.sourceReference.value()); |
766 | url.setQuery(query); |
767 | return url; |
768 | } |
769 | return undefined; |
770 | } |
771 | |
772 | QUrl uri; |
773 | bool available = false; |
774 | dap::Source raw; |
775 | }; |
776 | |
777 | // Thread |
778 | struct Thread : public IThread |
779 | { |
780 | dap::array<dap::StackFrame> callStack; |
781 | dap::array<dap::StackFrame> staleCallStack; |
782 | // dap::optional<IRawStoppedDetails> stoppedDetails; |
783 | // bool stopped = false; |
784 | bool reachedEndOfCallStack = false; |
785 | dap::optional<dap::SteppingGranularity> lastSteppingGranularity; |
786 | // dap::string name; |
787 | |
788 | Thread(IDebugSession *_session, dap::string _name, dapNumber _threadId) |
789 | { |
790 | name = _name; |
791 | threadId = _threadId; |
792 | session = _session; |
793 | stopped = false; |
794 | } |
795 | |
796 | virtual ~Thread() |
797 | { |
798 | } |
799 | |
800 | dap::string getId() override |
801 | { |
802 | return session->getId(); |
803 | } |
804 | |
805 | void clearCallStack() |
806 | { |
807 | if (callStack.size()) { |
808 | staleCallStack = callStack; |
809 | } |
810 | callStack.clear(); |
811 | } |
812 | |
813 | dap::array<dap::StackFrame> getCallStack() |
814 | { |
815 | return callStack; |
816 | } |
817 | |
818 | ReadonlyArray<dap::StackFrame> getStaleCallStack() |
819 | { |
820 | return staleCallStack; |
821 | } |
822 | |
823 | dap::optional<IStackFrame> getTopStackFrame() |
824 | { |
825 | // TODO(mozart) |
826 | return undefined; |
827 | } |
828 | |
829 | dap::string stateLabel() |
830 | { |
831 | if (stoppedDetails) { |
832 | return stoppedDetails.value().description.value(); |
833 | } |
834 | |
835 | return undefined; |
836 | } |
837 | |
838 | /** |
839 | * Queries the debug adapter for the callstack and returns a promise |
840 | * which completes once the call stack has been retrieved. |
841 | * If the thread is not stopped, it returns a promise to an empty array. |
842 | * Only fetches the first stack frame for performance reasons. Calling this method consecutive times |
843 | * gets the remainder of the call stack. |
844 | */ |
845 | void fetchCallStack(dapNumber levels = 20) |
846 | { |
847 | // if (stopped) { |
848 | callStack.clear(); |
849 | auto start = callStack.size(); |
850 | auto callStack = getCallStackImpl(static_cast<int>(start), levels); |
851 | reachedEndOfCallStack = static_cast<int>(callStack.size()) < levels; |
852 | if (start < this->callStack.size()) { |
853 | size_t endIndex = callStack.size() - start; |
854 | this->callStack.erase(callStack.begin() + static_cast<int>(start), callStack.begin() + static_cast<int>(endIndex)); |
855 | } |
856 | this->callStack.insert(this->callStack.end(), callStack.begin(), callStack.end()); |
857 | // if (stoppedDetails.value().totalFrames && stoppedDetails.value().totalFrames.value() == static_cast<int>(callStack.size())) { |
858 | // reachedEndOfCallStack = true; |
859 | // } |
860 | // } |
861 | } |
862 | |
863 | private: |
864 | dap::array<dap::StackFrame> getCallStackImpl(dapNumber startFrame, dapNumber levels) |
865 | { |
866 | auto response = session->stackTrace(threadId, startFrame, levels); |
867 | if (!response) { |
868 | return undefined; |
869 | } |
870 | |
871 | if (stoppedDetails && response && response->totalFrames) { |
872 | stoppedDetails.value().totalFrames = response.value().totalFrames.value(); |
873 | } |
874 | |
875 | auto stackFrames = response.value().stackFrames; |
876 | return stackFrames; |
877 | } |
878 | |
879 | /** |
880 | * Returns exception info promise if the exception was thrown, otherwise undefined |
881 | */ |
882 | #if 0 // TODO(mozart):not used. |
883 | dap::optional<IExceptionInfo> exceptionInfo() |
884 | { |
885 | // TODO(mozart) |
886 | return undefined; |
887 | } |
888 | #endif |
889 | |
890 | void next(dap::SteppingGranularity granularity) |
891 | { |
892 | session->next(threadId, granularity); |
893 | } |
894 | |
895 | void stepIn(dap::SteppingGranularity granularity) |
896 | { |
897 | session->stepIn(threadId, undefined, granularity); |
898 | } |
899 | |
900 | void stepOut(dap::SteppingGranularity granularity) |
901 | { |
902 | session->stepOut(threadId, granularity); |
903 | } |
904 | |
905 | void stepBack(dap::SteppingGranularity granularity) |
906 | { |
907 | session->stepBack(threadId, granularity); |
908 | } |
909 | #if 0 // TODO(mozart):not used. |
910 | void continue_() |
911 | { |
912 | session->continueDbg(threadId); |
913 | } |
914 | #endif |
915 | |
916 | void pause() |
917 | { |
918 | session->pause(threadId); |
919 | } |
920 | #if 0 // TODO(mozart):not used. |
921 | void terminate() |
922 | { |
923 | dap::array<number> threadIds; |
924 | threadIds.push_back(threadId); |
925 | session->terminateThreads(threadIds); |
926 | } |
927 | #endif |
928 | |
929 | void reverseContinue() |
930 | { |
931 | session->reverseContinue(threadId); |
932 | } |
933 | }; |
934 | |
935 | } // end namespace. |
936 | |
937 | #endif // DEBUG_H |
938 | |