nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
debug_session.hpp
1// include/debug_session.hpp
2//
3// DebugSession: owns compilation + VM state for pausable debug execution.
4// Replaces the WASM replay hack with a clean start/resume/snapshot API.
5//
6// Usage:
7// DebugSession session(engine);
8// session.setBreakpoints({5, 10});
9// auto status = session.start("x = 1;\ny = 2;\n");
10// if (status == ExecStatus::Paused) {
11// auto snap = session.snapshot();
12// // show snap.line, snap.variables to the user
13// status = session.resume(DebugAction::Continue);
14// }
15//
16#pragma once
17
18#include <nk/core/ast.hpp>
19#include <nk/core/bytecode.hpp>
20#include <nk/core/debugger.hpp>
21#include <nk/core/debug_workspace.hpp>
22#include <nk/value/value.hpp>
23#include <nk/core/vm.hpp>
24
25#include <memory>
26#include <string>
27#include <vector>
28
29namespace nk {
30
31class Engine;
32class Compiler;
33struct BytecodeChunk;
34
35class DebugSession
36{
37public:
38 explicit DebugSession(Engine &engine);
39 ~DebugSession();
40
41 DebugSession(const DebugSession &) = delete;
42 DebugSession &operator=(const DebugSession &) = delete;
43
44 // Set breakpoints (line numbers). Must be called before start().
45 void setBreakpoints(const std::vector<uint16_t> &lines);
46
47 // Compile code and begin debug execution.
48 // Returns Paused if a breakpoint/step was hit, Completed if finished.
49 ExecStatus start(const std::string &code);
50
51 // Resume from pause with the given action.
52 ExecStatus resume(DebugAction action);
53
54 // Stop the debug session (cleans up state).
55 void stop();
56
57 // ── dbstop if error ──────────────────────────────────────
58 // When enabled, an uncaught error during the run pauses at the failing
59 // line (frames intact) instead of ending the session, so state can be
60 // inspected. Resuming lets the error finally propagate (session ends with
61 // it). Set before start().
62 void setStopOnError(bool enabled);
63 bool atErrorPause() const;
64 std::string errorPauseMessage() const;
65
66 // ── Frame navigation (dbup / dbdown) ─────────────────────
67 // Move the inspection focus one frame toward the base (frameUp / dbup) or
68 // back toward the current frame (frameDown / dbdown), WITHOUT resuming.
69 // snapshot() and eval() that follow operate on the newly selected frame —
70 // so a caller's locals can be inspected and edited at a pause inside a
71 // nested call. Returns true if the focus moved (false at the stack ends).
72 // The focus resets to the deepest frame on every pause.
73 bool frameUp();
74 bool frameDown();
75 // Current focus depth (0 = deepest/current frame) and the live frame count.
76 size_t selectedFrame() const { return selectedDepth_; }
77 size_t frameCount() const;
78
79 // ── Watch expressions ────────────────────────────────────
80 // Expressions re-evaluated at each pause for an IDE watch panel.
82 {
83 std::string expr;
84 std::string result; // eval display, or "Error: ..." if it failed
85 };
86 void addWatch(const std::string &expr);
87 void removeWatch(size_t index);
88 void clearWatches();
89 const std::vector<std::string> &watchExpressions() const { return watches_; }
90 // Evaluate every watch in the currently focused frame. Valid while paused.
91 std::vector<WatchResult> evalWatches();
92
93 // ── State inspection (valid when paused) ─────────────────
94
95 struct Variable
96 {
97 std::string name;
98 const Value *value = nullptr;
99 };
100
101 struct Snapshot
102 {
103 uint16_t line = 0;
104 uint16_t col = 0;
105 std::string functionName;
106 std::vector<Variable> variables;
107 std::vector<StackFrame> callStack;
108
109 // Owned backing store for variables[].value. snapshot() copies each
110 // value here (COW — cheap) and points Variable.value into it, so the
111 // snapshot is self-contained: its pointers stay valid for the
112 // Snapshot's own lifetime — including across a resume() that reuses the
113 // VM registers the live values came from. shared_ptr so copies/moves of
114 // the Snapshot keep the same backing alive.
115 std::shared_ptr<std::vector<Value>> ownedValues;
116 };
117
118 Snapshot snapshot() const;
119
120 // Evaluate an expression in the paused context (preserves debug state).
121 // Returns the captured output (e.g. "x = 42").
122 std::string eval(const std::string &code);
123
124 // Get accumulated output since last start/resume.
125 std::string takeOutput();
126
127 // Get error message (valid when start/resume returned after an exception).
128 const std::string &errorMessage() const { return errorMsg_; }
129 int errorLine() const { return errorLine_; }
130
131 bool isActive() const { return active_; }
132
133private:
134 // Tear down the shared "session is live" state (observer, script
135 // scope, AST). Called from every exit path — natural completion,
136 // error unwind, explicit stop.
137 void deactivate();
138
139 // One-line "In <fn> (line N)" status for the currently focused frame, and
140 // the MATLAB-style dbstack listing (deepest first, '>' marks the focus).
141 std::string frameFocusLine() const;
142 std::string formatDbStack() const;
143
144 // After a run lands on a breakpoint, transparently keep running past any
145 // breakpoint whose condition evaluates false (only when `running` — a step
146 // always stops). Returns the status of the real stop / completion.
147 ExecStatus skipFalseConditionalBreakpoints(ExecStatus status, bool running);
148
149 // Core of eval(): inject the paused frame's variables into the workspace,
150 // run `code`, restore. When applyChanges is true (console input) the
151 // changes are diffed back into the DebugWorkspace; when false (read-only
152 // condition / watch evaluation) they are NOT — so a watch/condition never
153 // pollutes the workspace (e.g. with `ans`). Sets lastEvalValue_; returns the
154 // captured display output. Assumes the session is active.
155 std::string runInDebugScope(const std::string &code, bool applyChanges);
156
157 Engine &engine_;
158 bool active_ = false;
159
160 // Compiled chunk (must outlive VM execution)
161 BytecodeChunk chunk_;
162
163 // Parsed AST. Held for the lifetime of the debug session so the
164 // engine's scriptLocalFuncs_ (populated in start()) keeps valid
165 // pointers into it — clearUserFunctions walks that list whenever
166 // a `clear all` fires from inside the paused script.
167 ASTNodePtr ast_;
168
169 // Error state
170 std::string errorMsg_;
171 int errorLine_ = 0;
172
173 // Observer — always returns Stop so the VM pauses at every event
174 class SessionObserver : public DebugObserver
175 {
176 public:
177 DebugAction onLine(const DebugContext &) override { return DebugAction::Stop; }
178 DebugAction onBreakpoint(const DebugContext &) override { return DebugAction::Stop; }
179 void onError(const DebugContext &, const std::string &) override {}
180 void onFunctionEntry(const DebugContext &) override {}
181 void onFunctionExit(const DebugContext &) override {}
182 };
183 std::shared_ptr<SessionObserver> observer_;
184
185 // Live view of variables at the pause point: frame pointers + overlay.
186 DebugWorkspace ws_;
187
188 // Inspection focus for dbup/dbdown: 0 = deepest (current) frame, increasing
189 // toward the base. Reset to 0 on every pause; ws_ is bound to this frame.
190 size_t selectedDepth_ = 0;
191
192 // Watch expressions, re-evaluated at each pause (see evalWatches()).
193 std::vector<std::string> watches_;
194
195 // dbstop-if-error mode requested for this session (applied to the VM).
196 bool stopOnError_ = false;
197
198 // Result Value of the most recent eval() — used to read a breakpoint
199 // condition's / watch's truthiness without re-parsing the display string.
200 Value lastEvalValue_;
201
202 // Output capture
203 std::string outputBuf_;
204 std::function<void(const std::string &)> savedOutputFunc_;
205};
206
207} // namespace nk
Core MATLAB-compatible multidimensional array and scalar value type.
Definition value.hpp:61