/src/hermes/include/hermes/VM/Debugger/Debugger.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #ifndef HERMES_VM_DEBUGGER_DEBUGGER_H |
9 | | #define HERMES_VM_DEBUGGER_DEBUGGER_H |
10 | | |
11 | | #ifdef HERMES_ENABLE_DEBUGGER |
12 | | |
13 | | #include "hermes/BCGen/HBC/BytecodeInstructionGenerator.h" |
14 | | #include "hermes/BCGen/HBC/DebugInfo.h" |
15 | | #include "hermes/Inst/Inst.h" |
16 | | #include "hermes/Public/DebuggerTypes.h" |
17 | | #include "hermes/Support/OptValue.h" |
18 | | #include "hermes/VM/Debugger/DebugCommand.h" |
19 | | #include "hermes/VM/HermesValue.h" |
20 | | #include "hermes/VM/InterpreterState.h" |
21 | | #include "hermes/VM/RuntimeModule.h" |
22 | | #include "llvh/ADT/DenseSet.h" |
23 | | #include "llvh/ADT/MapVector.h" |
24 | | |
25 | | #include <atomic> |
26 | | #include <cstdint> |
27 | | #include <string> |
28 | | |
29 | | namespace hermes { |
30 | | namespace vm { |
31 | | class HermesValue; |
32 | | class CodeBlock; |
33 | | class Runtime; |
34 | | } // namespace vm |
35 | | } // namespace hermes |
36 | | |
37 | | namespace hermes { |
38 | | namespace vm { |
39 | | |
40 | | /// Main debugger object that receives commands from an external source, |
41 | | /// and passes them back to the interpreter loop, which handles them. |
42 | | class Debugger { |
43 | | public: |
44 | | struct EvalResultMetadata; |
45 | | |
46 | | using DidPauseCallback = std::function<DebugCommand( |
47 | | InterpreterState state, |
48 | | ::facebook::hermes::debugger::PauseReason pauseReason, |
49 | | HermesValue evalResult, |
50 | | const EvalResultMetadata &evalResultMetadata, |
51 | | ::facebook::hermes::debugger::BreakpointID)>; |
52 | | |
53 | | using BreakpointResolvedCallback = |
54 | | std::function<void(facebook::hermes::debugger::BreakpointID)>; |
55 | | |
56 | | private: |
57 | | friend DebugCommand; |
58 | | |
59 | | using PauseOnThrowMode = ::facebook::hermes::debugger::PauseOnThrowMode; |
60 | | using BreakpointID = ::facebook::hermes::debugger::BreakpointID; |
61 | | using BreakpointInfo = ::facebook::hermes::debugger::BreakpointInfo; |
62 | | using CallFrameInfo = ::facebook::hermes::debugger::CallFrameInfo; |
63 | | using ExceptionDetails = ::facebook::hermes::debugger::ExceptionDetails; |
64 | | using PauseReason = ::facebook::hermes::debugger::PauseReason; |
65 | | using SourceLocation = ::facebook::hermes::debugger::SourceLocation; |
66 | | using StackTrace = ::facebook::hermes::debugger::StackTrace; |
67 | | using StepMode = ::facebook::hermes::debugger::StepMode; |
68 | | using String = ::facebook::hermes::debugger::String; |
69 | | using LexicalInfo = ::facebook::hermes::debugger::LexicalInfo; |
70 | | using ScriptID = ::facebook::hermes::debugger::ScriptID; |
71 | | using AsyncPauseKind = ::facebook::hermes::debugger::AsyncPauseKind; |
72 | | |
73 | | Runtime &runtime_; |
74 | | |
75 | | /// Function handling pauses. |
76 | | DidPauseCallback didPauseCallback_; |
77 | | |
78 | | /// Function handling breakpoint resolution. |
79 | | BreakpointResolvedCallback breakpointResolvedCallback_; |
80 | | |
81 | | /// Logical breakpoint. |
82 | | struct Breakpoint { |
83 | | CodeBlock *codeBlock; |
84 | | uint32_t offset; |
85 | | |
86 | | /// Whether the breakpoint is "enabled" from the user's perspective. |
87 | | /// Note that this is set independently of resolved. |
88 | | bool enabled; |
89 | | |
90 | | /// Condition on which the breakpoint should trigger. |
91 | | /// If empty, the breakpoint will always trigger at the location it's set. |
92 | | std::string condition{}; |
93 | | |
94 | | /// Requested location of the breakpoint. |
95 | | SourceLocation requestedLocation; |
96 | | /// Resolved location of the breakpoint. |
97 | | /// Optionally filled in for resolved user breakpoints. |
98 | | llvh::Optional<SourceLocation> resolvedLocation{llvh::None}; |
99 | | |
100 | 0 | bool isResolved() const { |
101 | 0 | return resolvedLocation.hasValue(); |
102 | 0 | } |
103 | | }; |
104 | | |
105 | | /// Breakpoints that were set by the user. |
106 | | /// Every breakpoint has a unique ID that is never reused. |
107 | | /// Use a MapVector to iterate in insertion order. |
108 | | llvh::MapVector<BreakpointID, Breakpoint> userBreakpoints_{}; |
109 | | BreakpointID nextBreakpointId_{1}; |
110 | | |
111 | | /// One-shot breakpoints that are used for stepping commands. |
112 | | /// These are typically cleared immediately after breaking. |
113 | | std::vector<Breakpoint> tempBreakpoints_{}; |
114 | | |
115 | | /// One-shot breakpoints that are used for restoring breakpoint after using \p |
116 | | /// processInstUnderDebuggerOpCode(). These are cleared immediately after |
117 | | /// breaking. |
118 | | std::vector<Breakpoint> restorationBreakpoints_{}; |
119 | | |
120 | | /// Physical breakpoint location. |
121 | | struct BreakpointLocation { |
122 | | /// Opcode that was replaced. |
123 | | hbc::opcode_atom_t opCode; |
124 | | |
125 | | /// If this location has a user breakpoint set, |
126 | | /// then this is set to the ID of the user breakpoint. |
127 | | /// Else, it's set to None. |
128 | | OptValue<BreakpointID> user{llvh::None}; |
129 | | |
130 | | /// Whether this location has an on-load breakpoint set. |
131 | | bool onLoad{false}; |
132 | | /// Whether this location has a Step breakpoint set. |
133 | | bool hasStepBreakpoint{false}; |
134 | | /// Whether this location has a Restoration breakpoint set. |
135 | | bool hasRestorationBreakpoint{false}; |
136 | | |
137 | | /// This is the set of callStackDepths at which we are supposed to stop |
138 | | /// on this breakpoint for Step breakpoints. |
139 | | /// The size of this set is the number of Step breakpoints. |
140 | | /// We enforce that there is only one Step breakpoint |
141 | | /// per physical location, because Step breakpoints |
142 | | /// should be set in one location and cleared immediately when hit. |
143 | | /// If a depth != 0, then only break when the runtime call stack |
144 | | /// has exactly depth elements in it, and if a depth == 0, |
145 | | /// then break on any runtime call stack size. |
146 | | llvh::DenseSet<uint32_t> callStackDepths{}; |
147 | | |
148 | 0 | BreakpointLocation(hbc::opcode_atom_t opCode) : opCode(opCode) {} |
149 | | |
150 | | /// Total number of logical breakpoints set at this location. |
151 | 0 | uint32_t count() const { |
152 | 0 | return callStackDepths.size() + (user ? 1 : 0) + (onLoad ? 1 : 0); |
153 | 0 | } |
154 | | }; |
155 | | |
156 | | llvh::DenseMap<const inst::Inst *, BreakpointLocation> breakpointLocations_{}; |
157 | | |
158 | | /// The debugger is currently executing instructions. |
159 | | bool isDebugging_{false}; |
160 | | |
161 | | /// If not None, the debugger was issued a STEP instruction of some sort, |
162 | | /// and it hasn't completed yet. |
163 | | /// The value indicates the type of step we're trying to take. |
164 | | OptValue<StepMode> curStepMode_{llvh::None}; |
165 | | |
166 | | /// If true, all code blocks are breakpointed, |
167 | | /// and the debugger should stop on entering any code blocks. |
168 | | bool pauseOnAllCodeBlocks_{false}; |
169 | | |
170 | | /// Similar to \p pauseOnAllCodeBlocks_, except this flag specifically |
171 | | /// indicates whether Restoration breakpoint should be created. This can be |
172 | | /// set at the same time as \p pauseOnAllCodeBlocks_. If both are set, then in |
173 | | /// \p setEntryBreakpointForCodeBlock(), both a Step breakpoint and a |
174 | | /// Restoration breakpoint will be created at the same location. In \p |
175 | | /// runDebugger(), the Restoration breakpoint will be handled first and be |
176 | | /// cleared out, then if there is a Step breakpoint due to \p |
177 | | /// pauseOnAllCodeBlocks_, that will be handled as if the Restoration |
178 | | /// breakpoint was never there. |
179 | | bool pauseOnAllCodeBlocksToRestoreBreakpoint_{false}; |
180 | | |
181 | | /// Stores the CodeBlock and offset where in \p |
182 | | /// processInstUnderDebuggerOpCode() we purposely keep breakpoint uninstalled |
183 | | /// to reuse the Interpreter loop. |
184 | | std::pair<CodeBlock *, uint32_t> breakpointToRestore_{nullptr, 0}; |
185 | | |
186 | | /// What conditions the debugger needs to stop on exceptions. |
187 | | PauseOnThrowMode pauseOnThrowMode_{PauseOnThrowMode::None}; |
188 | | |
189 | | // When true, debugger is moving on after reporting an exception. |
190 | | // This is used to ensure we don't report an exception to the user twice |
191 | | // when pauseOnThrowMode_ is not None. The Interpreter can't distinguish |
192 | | // between exceptions thrown in native functions vs. thrown by users |
193 | | // when there's a native stack frame in the middle, so we need to account for |
194 | | // that by setting this flag to not stop between a caught exception |
195 | | // and its exception handler. This works because there should be no ability |
196 | | // to throw a new exception between reporting it and stepping to its handler. |
197 | | bool isUnwindingException_{false}; |
198 | | |
199 | | /// Whether the Debugger should pause after a script has loaded, before it |
200 | | /// begins executing. |
201 | | bool pauseOnScriptLoad_{false}; |
202 | | |
203 | | /// The last location we were at before we resumed execution in mid-step. |
204 | | /// This is only valid if curStepMode_ is not None. |
205 | | InterpreterState preStepState_{}; |
206 | | |
207 | | // Whether an user has attached to any Inspector. |
208 | | // It is exposed to JS via a property %DebuggerInternal.isDebuggerAttached |
209 | | std::atomic<bool> isDebuggerAttached_{false}; |
210 | | |
211 | | public: |
212 | 113 | explicit Debugger(Runtime &runtime) : runtime_(runtime) {} |
213 | | |
214 | | /// Reasons why the interpreter may invoke the debugger. Note this is a more |
215 | | /// limited set than PauseReason, because the Interpreter cannot distinguish |
216 | | /// between debugger opcodes as part of the Debugger command versus those |
217 | | /// inserted for breakpoints. |
218 | | enum class RunReason { |
219 | | /// The Interpreter hit a Debugger opcode. |
220 | | Opcode, |
221 | | |
222 | | /// The Interpreter hit an exception. |
223 | | Exception, |
224 | | |
225 | | /// The Interpreter is reacting to an async break request from the user. |
226 | | /// Any current stepping state should be cleared. |
227 | | AsyncBreakExplicit, |
228 | | |
229 | | /// The Interpreter is reacting to an async break request from the |
230 | | /// inspector. |
231 | | /// Any current stepping state should be *preserved*. |
232 | | AsyncBreakImplicit, |
233 | | }; |
234 | | |
235 | | /// An EvalResultMetadata is a subset of EvalResult in DebuggerAPI.h, lacking |
236 | | /// a jsi::Value field. |
237 | | struct EvalResultMetadata { |
238 | | bool isException = false; |
239 | | ExceptionDetails exceptionDetails; |
240 | | }; |
241 | | |
242 | | /// Runs debugger commands until execution continues. |
243 | | /// \param runReason the reason that the interpreter invoked the debugger. |
244 | | /// \param[in,out] state the debugging state. |
245 | | /// \return status after running the debugger. |
246 | | /// Ensures that if we stopped on an exception, the state doesn't change and |
247 | | /// the status is RETURNED. |
248 | | /// If the status is EXCEPTION, then the state is the state at the instruction |
249 | | /// that threw the exception - this allows the interpreter to handle the |
250 | | /// exception directly from the instruction that threw. |
251 | | ExecutionStatus runDebugger(RunReason runReason, InterpreterState &state); |
252 | | |
253 | 0 | bool isDebugging() const { |
254 | 0 | return isDebugging_; |
255 | 0 | } |
256 | | |
257 | | /// Should only be called when there is a current frame. Do not call while not |
258 | | /// in the interpreter loop. |
259 | | /// \return the current stack trace. |
260 | | StackTrace getStackTrace() const; |
261 | | |
262 | | llvh::Optional<const BreakpointLocation> getBreakpointLocation( |
263 | 0 | const inst::Inst *ip) const { |
264 | 0 | auto it = breakpointLocations_.find(ip); |
265 | 0 | if (it == breakpointLocations_.end()) { |
266 | 0 | return llvh::None; |
267 | 0 | } |
268 | 0 | return {it->second}; |
269 | 0 | } |
270 | | llvh::Optional<const BreakpointLocation> getBreakpointLocation( |
271 | | CodeBlock *codeBlock, |
272 | | uint32_t offset) const; |
273 | | |
274 | 113 | void setDidPauseCallback(DidPauseCallback callback) { |
275 | 113 | didPauseCallback_ = std::move(callback); |
276 | 113 | } |
277 | | |
278 | 113 | void setBreakpointResolvedCallback(BreakpointResolvedCallback callback) { |
279 | 113 | breakpointResolvedCallback_ = std::move(callback); |
280 | 113 | } |
281 | | |
282 | 0 | void setShouldPauseOnScriptLoad(bool flag) { |
283 | 0 | pauseOnScriptLoad_ = flag; |
284 | 0 | } |
285 | | |
286 | 260 | bool getShouldPauseOnScriptLoad() const { |
287 | 260 | return pauseOnScriptLoad_; |
288 | 260 | } |
289 | | |
290 | 0 | void setPauseOnThrowMode(PauseOnThrowMode mode) { |
291 | 0 | pauseOnThrowMode_ = mode; |
292 | 0 | } |
293 | | |
294 | 41 | PauseOnThrowMode getPauseOnThrowMode() const { |
295 | 41 | return pauseOnThrowMode_; |
296 | 41 | } |
297 | | |
298 | | /// Sets the property %isDebuggerAttached in the %DebuggerInternal object. Can |
299 | | /// be called from any thread. |
300 | 0 | void setIsDebuggerAttached(bool isAttached) { |
301 | 0 | isDebuggerAttached_ = isAttached; |
302 | 0 | } |
303 | | |
304 | | /// Gets the property %isDebuggerAttached in the %DebuggerInternal object. Can |
305 | | /// be called from any thread. |
306 | 0 | bool getIsDebuggerAttached() const { |
307 | 0 | return isDebuggerAttached_; |
308 | 0 | } |
309 | | |
310 | | /// Signal to the debugger that we are done unwinding an exception. |
311 | | /// This means that we can begin reporting exceptions to the user again |
312 | | /// if the user has requested them. |
313 | 0 | void finishedUnwindingException() { |
314 | 0 | isUnwindingException_ = false; |
315 | 0 | } |
316 | | |
317 | | /// Request an async pause. This may be called from any thread, or a signal |
318 | | /// handler. |
319 | | void triggerAsyncPause(AsyncPauseKind kind); |
320 | | |
321 | | /// Creates a user breakpoint given filename, line, and column. |
322 | | /// \param loc the location to set the breakpoint. |
323 | | /// \return the id of the new breakpoint, kInvalidBreakpoint if none was |
324 | | /// created. |
325 | | BreakpointID createBreakpoint(const SourceLocation &loc); |
326 | | |
327 | | /// Sets the condition on a breakpoint. |
328 | | /// \param id the breakpoint to change the condition on. |
329 | | /// \param condition if None, unset the condition, else set the condition. |
330 | | void setBreakpointCondition(BreakpointID id, std::string condition); |
331 | | |
332 | | /// Deletes the breakpoint given. |
333 | | void deleteBreakpoint(BreakpointID id); |
334 | | |
335 | | /// Deletes all breakpoints. |
336 | | void deleteAllBreakpoints(); |
337 | | |
338 | | /// Enables/disables the breakpoint given. |
339 | | /// \param id the id of the breakpoint to edit. |
340 | | /// \param enable if true, enable breakpoint \p id. |
341 | | void setBreakpointEnabled(BreakpointID id, bool enable); |
342 | | |
343 | | /// \return the breakpoint information for breakpoint \p id. |
344 | | /// If the supplied id is not valid, then the result has id == |
345 | | /// kInvalidBreakpoint. |
346 | 0 | BreakpointInfo getBreakpointInfo(BreakpointID id) const { |
347 | 0 | auto it = userBreakpoints_.find(id); |
348 | 0 | if (it == userBreakpoints_.end()) { |
349 | | // Invalid input. |
350 | 0 | BreakpointInfo result{}; |
351 | 0 | result.id = ::facebook::hermes::debugger::kInvalidBreakpoint; |
352 | 0 | return result; |
353 | 0 | } |
354 | | |
355 | 0 | const auto &breakpoint = it->second; |
356 | 0 | BreakpointInfo result{}; |
357 | 0 | result.id = id; |
358 | 0 | result.enabled = breakpoint.enabled; |
359 | 0 | result.resolved = breakpoint.isResolved(); |
360 | 0 | result.requestedLocation = breakpoint.requestedLocation; |
361 | 0 | if (breakpoint.isResolved()) { |
362 | 0 | result.resolvedLocation = *breakpoint.resolvedLocation; |
363 | 0 | } |
364 | 0 | return result; |
365 | 0 | } |
366 | | |
367 | | /// \return a list of valid user breakpoint IDs. |
368 | 0 | std::vector<BreakpointID> getBreakpoints() const { |
369 | 0 | std::vector<BreakpointID> result{}; |
370 | 0 | result.reserve(userBreakpoints_.size()); |
371 | 0 | for (const auto &it : userBreakpoints_) { |
372 | 0 | result.push_back(it.first); |
373 | 0 | } |
374 | 0 | return result; |
375 | 0 | } |
376 | | |
377 | | /// \return the number of variables in the given frame \p frame. |
378 | | LexicalInfo getLexicalInfoInFrame(uint32_t frame) const; |
379 | | |
380 | | /// \return the variable at \p variableIndex at scope depth \p scopeDepth in |
381 | | /// frame \p frame. 0 is the topmost frame, corresponding to \p topBlock. If |
382 | | /// \p outName is not null, populate it with the variable's name. |
383 | | HermesValue getVariableInFrame( |
384 | | uint32_t frame, |
385 | | uint32_t scopeDepth, |
386 | | uint32_t variableIndex, |
387 | | std::string *outName = nullptr) const; |
388 | | |
389 | | /// \param frame the index of the frame, with 0 being the topmost. |
390 | | /// \return the 'this' value at \p frame. |
391 | | HermesValue getThisValue(uint32_t frame) const; |
392 | | |
393 | | /// Report to the debugger that the runtime will execute a module given by \p |
394 | | /// module. The debugger may propagate a pause to the client. |
395 | | void willExecuteModule(RuntimeModule *module, CodeBlock *codeBlock); |
396 | | |
397 | | /// Report to the debugger that the runtime will unload a RuntimeModule. |
398 | | /// The debugger should unresolve breakpoints in that module. |
399 | | void willUnloadModule(RuntimeModule *module); |
400 | | |
401 | | /// Report to the debugger that the runtime will execute a codeBlock given by |
402 | | /// \p codeBlock. |
403 | | /// For example, this is used to set up the codeBlock while stepping. |
404 | 1.95k | void willEnterCodeBlock(CodeBlock *codeBlock) { |
405 | 1.95k | if (LLVM_UNLIKELY(pauseOnAllCodeBlocks_)) { |
406 | 0 | setEntryBreakpointForCodeBlock(codeBlock); |
407 | 0 | } |
408 | 1.95k | } |
409 | | |
410 | | /// Resolve any unresolved breakpoints. |
411 | | /// Intended for use following compilation or loading of a new function. |
412 | | /// \param codeBlock the code block that was compiled before this call. |
413 | | void resolveBreakpoints(CodeBlock *codeBlock); |
414 | | |
415 | | /// \return the source map URL for \p scriptId, empty string if non exists. |
416 | | String getSourceMappingUrl(ScriptID scriptId) const; |
417 | | |
418 | | /// \return list of loaded scripts that haven't been garbage collected |
419 | | std::vector<SourceLocation> getLoadedScripts() const; |
420 | | |
421 | | /// Find the handler for an exception thrown at \p state. |
422 | | /// \return llvh::None if no handler is found, else return the state of the |
423 | | /// handler and the offset of its frame. |
424 | | llvh::Optional<std::pair<InterpreterState, uint32_t>> findCatchTarget( |
425 | | const InterpreterState &state) const; |
426 | | |
427 | | /// Attempt to resolve the \p filenameId to a script ID based on the table. |
428 | | /// \return the ScriptID of the given filenameId. |
429 | | ScriptID resolveScriptId(RuntimeModule *runtimeModule, uint32_t filenameId) |
430 | | const; |
431 | | |
432 | | /// Get the actual OpCode produced from the source without being affected by |
433 | | /// any user installed breakpoint "Debugger" OpCode overrides. |
434 | | inst::OpCode getRealOpCode(CodeBlock *block, uint32_t offset) const; |
435 | | |
436 | | /// Processes an instruction that had Debugger OpCode installed on top of it. |
437 | | /// This is meant to be done after Interpreter encounters Debugger OpCode. It |
438 | | /// differs from \p stepInstruction() in that this function will handle Ret. |
439 | | /// Ret requires special logic in the case if it's returning to native code. |
440 | | /// The Interpreter has special logic to exit out of the Interpreter if we're |
441 | | /// returning to native code. But when \p stepFunction() is used, we have |
442 | | /// recursive Interpreter loop and the expectation is to exit out of both. |
443 | | /// This function will avoid using stepFunction() for Ret. It will instead |
444 | | /// keep breakpoint uninstalled and re-use the same Interpreter loop. |
445 | | /// \param[in,out] InterpreterState for where the Interpreter is currently at, |
446 | | /// and gets filled with where Interpreter should resume executing |
447 | | /// \return status after processing |
448 | | ExecutionStatus processInstUnderDebuggerOpCode(InterpreterState &state); |
449 | | |
450 | | private: |
451 | | /// The primary debugger command loop. |
452 | | ExecutionStatus debuggerLoop( |
453 | | InterpreterState &state, |
454 | | PauseReason pauseReason, |
455 | | BreakpointID breakpoint); |
456 | | |
457 | | /// Gets a BreakpointLocation from breakpointLocations_ if it exists |
458 | | /// for the given codeBlock and offset, else creates one. |
459 | | /// Used by other functions which should be called to set breakpoints. |
460 | | /// Installs a breakpoint at that location, doesn't modify it. |
461 | | /// \return the location at which the breakpoint was installed. |
462 | | BreakpointLocation &installBreakpoint(CodeBlock *codeBlock, uint32_t offset); |
463 | | |
464 | | /// Helper function to uninstall a breakpoint. Always use this function to |
465 | | /// uninstall breakpoints. This takes care of the case when we purposely keep |
466 | | /// breakpoint uninstalled in \p processInstUnderDebuggerOpCode(). |
467 | | void uninstallBreakpoint( |
468 | | CodeBlock *codeBlock, |
469 | | uint32_t offset, |
470 | | hbc::opcode_atom_t opCode); |
471 | | |
472 | | /// Set a user breakpoint at \p offset in \p codeBlock. |
473 | | /// Increments the count at a breakpoint if it already exists. |
474 | | /// If the physical breakpoint isn't enabled yet, patches the debugger |
475 | | /// instruction in. |
476 | | /// Sets the breakpoint ID to \p id. |
477 | | void |
478 | | setUserBreakpoint(CodeBlock *codeBlock, uint32_t offset, BreakpointID id); |
479 | | |
480 | | /// Should not be called directly except from \p setStepBreakpoint() or \p |
481 | | /// setRestorationBreakpoint(). This function sets a non-user breakpoint at \p |
482 | | /// offset in \p codeBlock. Increments the count at a breakpoint if it already |
483 | | /// exists. If the physical breakpoint isn't enabled yet, patches the debugger |
484 | | /// instruction in. |
485 | | /// The breakpoint is set to be a one-shot. |
486 | | /// If \p callStackDepth != 0, then the Interpreter should only |
487 | | /// break if the callStack is exactly \p callStackDepth frames long. |
488 | | /// If \p callStackDepth == 0, then the Step breakpoint should break |
489 | | /// regardless of the actual depth of the call stack. |
490 | | /// /param isStepBreakpoint true if creating a Step breakpoint, false for |
491 | | /// Restoration breakpoint. |
492 | | void doSetNonUserBreakpoint( |
493 | | CodeBlock *codeBlock, |
494 | | uint32_t offset, |
495 | | uint32_t callStackDepth, |
496 | | bool isStepBreakpoint); |
497 | | |
498 | | /// Set a stepping breakpoint at \p offset in \p codeBlock. |
499 | | void setStepBreakpoint( |
500 | | CodeBlock *codeBlock, |
501 | | uint32_t offset, |
502 | | uint32_t callStackDepth); |
503 | | |
504 | | /// Set an on-load breakpoint at \p offset in \p codeBlock. |
505 | | /// Increments the count at a breakpoint if it already exists. |
506 | | /// If the physical breakpoint isn't enabled yet, patches the debugger |
507 | | /// instruction in. |
508 | | /// The breakpoint is set to be a one-shot. |
509 | | void setOnLoadBreakpoint(CodeBlock *codeBlock, uint32_t offset); |
510 | | |
511 | | /// Unset user breakpoint \p breakpoint. |
512 | | /// Removes the location from breakpointLocations_ if no longer needed. |
513 | | void unsetUserBreakpoint(const Breakpoint &breakpoint); |
514 | | |
515 | | /// Creates a step breakpoint at offset 0 of \p codeBlock, |
516 | | /// to be used while in the middle of a step and entering a |
517 | | /// codeBlock for the first time. |
518 | | /// Requires that getShouldPauseOnAllCodeBlocks is true. |
519 | | /// The breakpoint will clear on the next call to clearTempBreakpoints. |
520 | | void setEntryBreakpointForCodeBlock(CodeBlock *codeBlock); |
521 | | |
522 | | /// Add a non-user breakpoint to the last interpreted function on the |
523 | | /// call stack, at the next offset after the saved return address. |
524 | | /// This allows stepping out of the currently executing function. |
525 | | /// \param forRestorationBreakpoint whether to create as a restoration |
526 | | /// breakpoint or a Step breakpoint |
527 | | void breakpointCaller(bool forRestorationBreakpoint); |
528 | | |
529 | | /// Add a Step breakpoint at the exception handler for the current state. |
530 | | /// Does nothing if the exception is uncaught. |
531 | | void breakpointExceptionHandler(const InterpreterState &state); |
532 | | |
533 | | /// Should not be called directly except from \p clearTempBreakpoints() or \p |
534 | | /// clearRestorationBreakpoints(). This function clears all of the given type |
535 | | /// of non-user breakpoint that have been set. |
536 | | /// /param isStepBreakpoint true to clear Step breakpoints, false to clear |
537 | | /// Restoration breakpoints. |
538 | | void doClearNonUserBreakpoints(bool isStepBreakpoint); |
539 | | |
540 | | /// Clear all the Step and OnLoad breakpoints that have been set. |
541 | | void clearTempBreakpoints(); |
542 | | |
543 | | /// Set a Restoration breakpoint at \p offset in \p codeBlock. |
544 | | void setRestorationBreakpoint( |
545 | | CodeBlock *codeBlock, |
546 | | uint32_t offset, |
547 | | uint32_t callStackDepth); |
548 | | |
549 | | /// Re-install any breakpoint stored by \p breakpointToRestore_. |
550 | | /// \return true if there was a breakpoint to restore, false otherwise. |
551 | | bool restoreBreakpointIfAny(); |
552 | | |
553 | | /// Clear all the Restoration breakpoints that have been set. |
554 | | void clearRestorationBreakpoints(); |
555 | | |
556 | | /// Steps a single instruction. |
557 | | /// Requires that the current instruction steps to somewhere else in the |
558 | | /// current function. |
559 | | /// If the current instruction is a call or a return, don't use this function. |
560 | | ExecutionStatus stepInstruction(InterpreterState &state); |
561 | | |
562 | | /// Evaluate \p src rooted in the stack frame specified in \p args. 0 is the |
563 | | /// topmost frame, corresponding to \p state. Populate \p outMetadata |
564 | | /// with metadata for the result. |
565 | | /// \return the resulting HermesValue. |
566 | | HermesValue evalInFrame( |
567 | | const EvalArgs &args, |
568 | | const std::string &src, |
569 | | const InterpreterState &state, |
570 | | EvalResultMetadata *outMetadata); |
571 | | |
572 | | /// Given that the runtime threw an exception, clear the thrown value, and |
573 | | /// populate the \p outMetadata. \return the thrown value. |
574 | | HermesValue getExceptionAsEvalResult(EvalResultMetadata *outMetadata); |
575 | | |
576 | | /// Requires that we're currently debugging. |
577 | | /// If the queue is empty, invoke the didPauseCallback_ (or command line, |
578 | | /// provided that stdin is interactive), with the given interpeter state \p |
579 | | /// state and pause reason \p pause. |
580 | | /// If the pause is EvalComplete, the evalResult is given in \p evalResult; |
581 | | /// othewrise the evalResult should be ignored. |
582 | | /// \return the next command to run in a paused interpreter loop. |
583 | | DebugCommand getNextCommand( |
584 | | InterpreterState state, |
585 | | PauseReason pauseReason, |
586 | | HermesValue evalResult, |
587 | | const EvalResultMetadata &evalMetadata, |
588 | 0 | BreakpointID breakpoint) { |
589 | | // If we have a didPauseCallback, invoke it. If we don't have a callback, |
590 | | // fall back to simply continuing. |
591 | 0 | if (didPauseCallback_) |
592 | 0 | return didPauseCallback_( |
593 | 0 | state, pauseReason, evalResult, evalMetadata, breakpoint); |
594 | | |
595 | 0 | return DebugCommand::makeContinue(); |
596 | 0 | } |
597 | | |
598 | | /// \return true if the states are on different instructions within the same |
599 | | /// statement. |
600 | | /// Ensure different instructions to allow for stepping to the same |
601 | | /// statement within a loop. |
602 | | inline bool sameStatementDifferentInstruction( |
603 | | const InterpreterState &a, |
604 | 0 | const InterpreterState &b) const { |
605 | 0 | auto aLoc = getLocationForState(a); |
606 | 0 | auto bLoc = getLocationForState(b); |
607 | | |
608 | | // Same statement in the same codeBlock, but different offsets. |
609 | 0 | return a.codeBlock == b.codeBlock && aLoc->statement == bLoc->statement && |
610 | 0 | a.offset != b.offset; |
611 | 0 | } |
612 | | |
613 | | OptValue<hbc::DebugSourceLocation> getLocationForState( |
614 | 0 | const InterpreterState &state) const { |
615 | 0 | return state.codeBlock->getSourceLocation(state.offset); |
616 | 0 | } |
617 | | |
618 | | /// Attempt to resolve the requestedLocation in \p breakpoint. |
619 | | /// If successful, sets breakpoint.resolvedLocation to the resolved location. |
620 | | /// Requires that \p breakpoint is not yet resolved. |
621 | | /// \return true if the breakpoint is successfully resolved. |
622 | | bool resolveBreakpointLocation(Breakpoint &breakpoint) const; |
623 | | |
624 | | /// Unresolves the breakpoint. |
625 | | void unresolveBreakpointLocation(Breakpoint &breakpoint); |
626 | | |
627 | | /// \return a CallFrameInfo for a given code block \p codeBlock and IP offset |
628 | | /// into it \p ipOffset. |
629 | | CallFrameInfo getCallFrameInfo(const CodeBlock *codeBlock, uint32_t offset) |
630 | | const; |
631 | | |
632 | | /// Get the jump target for an instruction (if it is a jump). |
633 | | llvh::Optional<uint32_t> findJumpTarget(CodeBlock *block, uint32_t offset); |
634 | | |
635 | | /// Set breakpoints at all possible next instructions after the current one. |
636 | | void breakAtPossibleNextInstructions(const InterpreterState &state); |
637 | | }; |
638 | | |
639 | | } // namespace vm |
640 | | } // namespace hermes |
641 | | |
642 | | #endif // HERMES_ENABLE_DEBUGGER |
643 | | #endif |