| 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 | |