1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #ifndef RUNTIME_VM_DEBUGGER_H_ |
6 | #define RUNTIME_VM_DEBUGGER_H_ |
7 | |
8 | #include "include/dart_tools_api.h" |
9 | |
10 | #include "vm/constants_kbc.h" |
11 | #include "vm/kernel_isolate.h" |
12 | #include "vm/object.h" |
13 | #include "vm/port.h" |
14 | #include "vm/scopes.h" |
15 | #include "vm/service_event.h" |
16 | #include "vm/simulator.h" |
17 | |
18 | DECLARE_FLAG(bool, verbose_debug); |
19 | |
20 | // 'Trace Debugger' TD_Print. |
21 | #if defined(_MSC_VER) |
22 | #define TD_Print(format, ...) \ |
23 | if (FLAG_verbose_debug) Log::Current()->Print(format, __VA_ARGS__) |
24 | #else |
25 | #define TD_Print(format, ...) \ |
26 | if (FLAG_verbose_debug) Log::Current()->Print(format, ##__VA_ARGS__) |
27 | #endif |
28 | |
29 | namespace dart { |
30 | |
31 | class CodeBreakpoint; |
32 | class Isolate; |
33 | class JSONArray; |
34 | class JSONStream; |
35 | class ObjectPointerVisitor; |
36 | class BreakpointLocation; |
37 | class StackFrame; |
38 | |
39 | // A user-defined breakpoint, which either fires once, for a particular closure, |
40 | // or always. The API's notion of a breakpoint corresponds to this object. |
41 | class Breakpoint { |
42 | public: |
43 | Breakpoint(intptr_t id, BreakpointLocation* bpt_location) |
44 | : id_(id), |
45 | kind_(Breakpoint::kNone), |
46 | next_(NULL), |
47 | closure_(Instance::null()), |
48 | bpt_location_(bpt_location), |
49 | is_synthetic_async_(false) {} |
50 | |
51 | intptr_t id() const { return id_; } |
52 | Breakpoint* next() const { return next_; } |
53 | void set_next(Breakpoint* n) { next_ = n; } |
54 | |
55 | BreakpointLocation* bpt_location() const { return bpt_location_; } |
56 | void set_bpt_location(BreakpointLocation* new_bpt_location); |
57 | |
58 | bool IsRepeated() const { return kind_ == kRepeated; } |
59 | bool IsSingleShot() const { return kind_ == kSingleShot; } |
60 | bool IsPerClosure() const { return kind_ == kPerClosure; } |
61 | InstancePtr closure() const { return closure_; } |
62 | |
63 | void SetIsRepeated() { |
64 | ASSERT(kind_ == kNone); |
65 | kind_ = kRepeated; |
66 | } |
67 | |
68 | void SetIsSingleShot() { |
69 | ASSERT(kind_ == kNone); |
70 | kind_ = kSingleShot; |
71 | } |
72 | |
73 | void SetIsPerClosure(const Instance& closure) { |
74 | ASSERT(kind_ == kNone); |
75 | kind_ = kPerClosure; |
76 | closure_ = closure.raw(); |
77 | } |
78 | |
79 | // Mark that this breakpoint is a result of a step OverAwait request. |
80 | void set_is_synthetic_async(bool is_synthetic_async) { |
81 | is_synthetic_async_ = is_synthetic_async; |
82 | } |
83 | bool is_synthetic_async() const { return is_synthetic_async_; } |
84 | |
85 | void PrintJSON(JSONStream* stream); |
86 | |
87 | private: |
88 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
89 | |
90 | enum ConditionKind { |
91 | kNone, |
92 | kRepeated, |
93 | kSingleShot, |
94 | kPerClosure, |
95 | }; |
96 | |
97 | intptr_t id_; |
98 | ConditionKind kind_; |
99 | Breakpoint* next_; |
100 | InstancePtr closure_; |
101 | BreakpointLocation* bpt_location_; |
102 | bool is_synthetic_async_; |
103 | |
104 | friend class BreakpointLocation; |
105 | DISALLOW_COPY_AND_ASSIGN(Breakpoint); |
106 | }; |
107 | |
108 | // BreakpointLocation represents a collection of breakpoint conditions at the |
109 | // same token position in Dart source. There may be more than one CodeBreakpoint |
110 | // object per BreakpointLocation. |
111 | // An unresolved breakpoint is one where the underlying code has not |
112 | // been compiled yet. Since the code has not been compiled, we don't know |
113 | // the definitive source location yet. The requested source location may |
114 | // change when the underlying code gets compiled. |
115 | // A latent breakpoint represents a breakpoint location in Dart source |
116 | // that is not loaded in the VM when the breakpoint is requested. |
117 | // When a script with matching url is loaded, a latent breakpoint |
118 | // becomes an unresolved breakpoint. |
119 | class BreakpointLocation { |
120 | public: |
121 | // Create a new unresolved breakpoint. |
122 | BreakpointLocation(const Script& script, |
123 | TokenPosition token_pos, |
124 | TokenPosition end_token_pos, |
125 | intptr_t requested_line_number, |
126 | intptr_t requested_column_number); |
127 | // Create a new latent breakpoint. |
128 | BreakpointLocation(const String& url, |
129 | intptr_t requested_line_number, |
130 | intptr_t requested_column_number); |
131 | |
132 | ~BreakpointLocation(); |
133 | |
134 | FunctionPtr function() const { return function_; } |
135 | TokenPosition token_pos() const { return token_pos_; } |
136 | TokenPosition end_token_pos() const { return end_token_pos_; } |
137 | |
138 | ScriptPtr script() const { return script_; } |
139 | StringPtr url() const { return url_; } |
140 | |
141 | intptr_t requested_line_number() const { return requested_line_number_; } |
142 | intptr_t requested_column_number() const { return requested_column_number_; } |
143 | |
144 | void GetCodeLocation(Script* script, TokenPosition* token_pos) const; |
145 | |
146 | Breakpoint* AddRepeated(Debugger* dbg); |
147 | Breakpoint* AddSingleShot(Debugger* dbg); |
148 | Breakpoint* AddPerClosure(Debugger* dbg, |
149 | const Instance& closure, |
150 | bool for_over_await); |
151 | |
152 | bool AnyEnabled() const; |
153 | bool IsResolved() const { |
154 | return bytecode_token_pos_.IsReal() || code_token_pos_.IsReal(); |
155 | } |
156 | bool IsResolved(bool in_bytecode) const { |
157 | return in_bytecode ? bytecode_token_pos_.IsReal() |
158 | : code_token_pos_.IsReal(); |
159 | } |
160 | bool IsLatent() const { return !token_pos_.IsReal(); } |
161 | |
162 | private: |
163 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
164 | |
165 | void SetResolved(bool in_bytecode, |
166 | const Function& func, |
167 | TokenPosition token_pos); |
168 | |
169 | BreakpointLocation* next() const { return this->next_; } |
170 | void set_next(BreakpointLocation* value) { next_ = value; } |
171 | |
172 | void AddBreakpoint(Breakpoint* bpt, Debugger* dbg); |
173 | |
174 | Breakpoint* breakpoints() const { return this->conditions_; } |
175 | void set_breakpoints(Breakpoint* head) { this->conditions_ = head; } |
176 | |
177 | ScriptPtr script_; |
178 | StringPtr url_; |
179 | TokenPosition token_pos_; |
180 | TokenPosition end_token_pos_; |
181 | BreakpointLocation* next_; |
182 | Breakpoint* conditions_; |
183 | intptr_t requested_line_number_; |
184 | intptr_t requested_column_number_; |
185 | |
186 | // Valid for resolved breakpoints: |
187 | FunctionPtr function_; |
188 | TokenPosition bytecode_token_pos_; |
189 | TokenPosition code_token_pos_; |
190 | |
191 | friend class Debugger; |
192 | DISALLOW_COPY_AND_ASSIGN(BreakpointLocation); |
193 | }; |
194 | |
195 | // CodeBreakpoint represents a location in compiled or interpreted code. |
196 | // There may be more than one CodeBreakpoint for one BreakpointLocation, |
197 | // e.g. when a function gets compiled as a regular function and as a closure. |
198 | class CodeBreakpoint { |
199 | public: |
200 | CodeBreakpoint(const Code& code, |
201 | TokenPosition token_pos, |
202 | uword pc, |
203 | PcDescriptorsLayout::Kind kind); |
204 | CodeBreakpoint(const Bytecode& bytecode, TokenPosition token_pos, uword pc); |
205 | ~CodeBreakpoint(); |
206 | |
207 | FunctionPtr function() const; |
208 | uword pc() const { return pc_; } |
209 | TokenPosition token_pos() const { return token_pos_; } |
210 | |
211 | ScriptPtr SourceCode(); |
212 | StringPtr SourceUrl(); |
213 | intptr_t LineNumber(); |
214 | |
215 | void Enable(); |
216 | void Disable(); |
217 | bool IsEnabled() const { return is_enabled_; } |
218 | bool IsInterpreted() const { return bytecode_ != Bytecode::null(); } |
219 | |
220 | CodePtr OrigStubAddress() const; |
221 | |
222 | private: |
223 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
224 | |
225 | BreakpointLocation* bpt_location() const { return bpt_location_; } |
226 | void set_bpt_location(BreakpointLocation* value) { bpt_location_ = value; } |
227 | |
228 | void set_next(CodeBreakpoint* value) { next_ = value; } |
229 | CodeBreakpoint* next() const { return this->next_; } |
230 | |
231 | void PatchCode(); |
232 | void RestoreCode(); |
233 | void SetBytecodeBreakpoint(); |
234 | void UnsetBytecodeBreakpoint(); |
235 | |
236 | CodePtr code_; |
237 | BytecodePtr bytecode_; |
238 | TokenPosition token_pos_; |
239 | uword pc_; |
240 | intptr_t line_number_; |
241 | bool is_enabled_; |
242 | |
243 | BreakpointLocation* bpt_location_; |
244 | CodeBreakpoint* next_; |
245 | |
246 | PcDescriptorsLayout::Kind breakpoint_kind_; |
247 | CodePtr saved_value_; |
248 | |
249 | friend class Debugger; |
250 | DISALLOW_COPY_AND_ASSIGN(CodeBreakpoint); |
251 | }; |
252 | |
253 | // ActivationFrame represents one dart function activation frame |
254 | // on the call stack. |
255 | class ActivationFrame : public ZoneAllocated { |
256 | public: |
257 | enum Kind { |
258 | kRegular, |
259 | kAsyncSuspensionMarker, |
260 | kAsyncCausal, |
261 | kAsyncActivation, |
262 | }; |
263 | |
264 | ActivationFrame(uword pc, |
265 | uword fp, |
266 | uword sp, |
267 | const Code& code, |
268 | const Array& deopt_frame, |
269 | intptr_t deopt_frame_offset, |
270 | Kind kind = kRegular); |
271 | |
272 | ActivationFrame(uword pc, const Code& code); |
273 | |
274 | #if !defined(DART_PRECOMPILED_RUNTIME) |
275 | ActivationFrame(uword pc, |
276 | uword fp, |
277 | uword sp, |
278 | const Bytecode& bytecode, |
279 | Kind kind = kRegular); |
280 | |
281 | ActivationFrame(uword pc, const Bytecode& bytecode); |
282 | #endif // !defined(DART_PRECOMPILED_RUNTIME) |
283 | |
284 | explicit ActivationFrame(Kind kind); |
285 | |
286 | explicit ActivationFrame(const Closure& async_activation); |
287 | |
288 | uword pc() const { return pc_; } |
289 | uword fp() const { return fp_; } |
290 | uword sp() const { return sp_; } |
291 | const Function& function() const { |
292 | return function_; |
293 | } |
294 | const Code& code() const { |
295 | ASSERT(!code_.IsNull()); |
296 | return code_; |
297 | } |
298 | const Bytecode& bytecode() const { |
299 | ASSERT(!bytecode_.IsNull()); |
300 | return bytecode_; |
301 | } |
302 | bool IsInterpreted() const { return !bytecode_.IsNull(); } |
303 | |
304 | enum Relation { |
305 | kCallee, |
306 | kSelf, |
307 | kCaller, |
308 | }; |
309 | |
310 | Relation CompareTo(uword other_fp, bool other_is_interpreted) const; |
311 | |
312 | StringPtr QualifiedFunctionName(); |
313 | StringPtr SourceUrl(); |
314 | ScriptPtr SourceScript(); |
315 | LibraryPtr Library(); |
316 | TokenPosition TokenPos(); |
317 | intptr_t LineNumber(); |
318 | intptr_t ColumnNumber(); |
319 | |
320 | // Returns true if this frame is for a function that is visible |
321 | // to the user and can be debugged. |
322 | bool IsDebuggable() const; |
323 | |
324 | // Returns true if it is possible to rewind the debugger to this frame. |
325 | bool IsRewindable() const; |
326 | |
327 | // The context level of a frame is the context level at the |
328 | // PC/token index of the frame. It determines the depth of the context |
329 | // chain that belongs to the function of this activation frame. |
330 | intptr_t ContextLevel(); |
331 | |
332 | const char* ToCString(); |
333 | |
334 | intptr_t NumLocalVariables(); |
335 | |
336 | void VariableAt(intptr_t i, |
337 | String* name, |
338 | TokenPosition* declaration_token_pos, |
339 | TokenPosition* visible_start_token_pos, |
340 | TokenPosition* visible_end_token_pos, |
341 | Object* value); |
342 | |
343 | ArrayPtr GetLocalVariables(); |
344 | ObjectPtr GetParameter(intptr_t index); |
345 | ObjectPtr GetClosure(); |
346 | ObjectPtr GetReceiver(); |
347 | |
348 | const Context& GetSavedCurrentContext(); |
349 | ObjectPtr GetAsyncOperation(); |
350 | |
351 | TypeArgumentsPtr BuildParameters( |
352 | const GrowableObjectArray& param_names, |
353 | const GrowableObjectArray& param_values, |
354 | const GrowableObjectArray& type_params_names); |
355 | |
356 | ObjectPtr EvaluateCompiledExpression(const ExternalTypedData& kernel_data, |
357 | const Array& arguments, |
358 | const Array& type_definitions, |
359 | const TypeArguments& type_arguments); |
360 | |
361 | void PrintToJSONObject(JSONObject* jsobj); |
362 | |
363 | ObjectPtr GetAsyncAwaiter(); |
364 | ObjectPtr GetCausalStack(); |
365 | |
366 | bool HandlesException(const Instance& exc_obj); |
367 | |
368 | private: |
369 | void PrintToJSONObjectRegular(JSONObject* jsobj); |
370 | void PrintToJSONObjectAsyncCausal(JSONObject* jsobj); |
371 | void PrintToJSONObjectAsyncSuspensionMarker(JSONObject* jsobj); |
372 | void PrintContextMismatchError(intptr_t ctx_slot, |
373 | intptr_t frame_ctx_level, |
374 | intptr_t var_ctx_level); |
375 | void PrintDescriptorsError(const char* message); |
376 | |
377 | intptr_t TryIndex(); |
378 | intptr_t DeoptId(); |
379 | void GetPcDescriptors(); |
380 | void GetVarDescriptors(); |
381 | void GetDescIndices(); |
382 | |
383 | ObjectPtr GetAsyncContextVariable(const String& name); |
384 | ObjectPtr GetAsyncStreamControllerStreamAwaiter(const Object& stream); |
385 | ObjectPtr GetAsyncStreamControllerStream(); |
386 | ObjectPtr GetAsyncCompleterAwaiter(const Object& completer); |
387 | ObjectPtr GetAsyncCompleter(); |
388 | intptr_t GetAwaitJumpVariable(); |
389 | void (); |
390 | |
391 | bool IsAsyncMachinery() const; |
392 | |
393 | static const char* KindToCString(Kind kind) { |
394 | switch (kind) { |
395 | case kRegular: |
396 | return "Regular" ; |
397 | case kAsyncCausal: |
398 | return "AsyncCausal" ; |
399 | case kAsyncSuspensionMarker: |
400 | return "AsyncSuspensionMarker" ; |
401 | case kAsyncActivation: |
402 | return "AsyncActivation" ; |
403 | default: |
404 | UNREACHABLE(); |
405 | return "" ; |
406 | } |
407 | } |
408 | |
409 | ObjectPtr GetStackVar(VariableIndex var_index); |
410 | ObjectPtr GetRelativeContextVar(intptr_t ctxt_level, |
411 | intptr_t slot_index, |
412 | intptr_t frame_ctx_level); |
413 | ObjectPtr GetContextVar(intptr_t ctxt_level, intptr_t slot_index); |
414 | |
415 | uword pc_; |
416 | uword fp_; |
417 | uword sp_; |
418 | |
419 | // The anchor of the context chain for this function. |
420 | Context& ctx_; |
421 | Code& code_; |
422 | Bytecode& bytecode_; |
423 | Function& function_; |
424 | bool live_frame_; // Is this frame a live frame? |
425 | bool token_pos_initialized_; |
426 | TokenPosition token_pos_; |
427 | intptr_t try_index_; |
428 | intptr_t deopt_id_; |
429 | |
430 | intptr_t line_number_; |
431 | intptr_t column_number_; |
432 | intptr_t context_level_; |
433 | |
434 | // Some frames are deoptimized into a side array in order to inspect them. |
435 | const Array& deopt_frame_; |
436 | const intptr_t deopt_frame_offset_; |
437 | |
438 | Kind kind_; |
439 | |
440 | bool vars_initialized_; |
441 | LocalVarDescriptors& var_descriptors_; |
442 | ZoneGrowableArray<intptr_t> desc_indices_; |
443 | PcDescriptors& pc_desc_; |
444 | |
445 | friend class Debugger; |
446 | friend class DebuggerStackTrace; |
447 | DISALLOW_COPY_AND_ASSIGN(ActivationFrame); |
448 | }; |
449 | |
450 | // Array of function activations on the call stack. |
451 | class DebuggerStackTrace : public ZoneAllocated { |
452 | public: |
453 | explicit DebuggerStackTrace(int capacity) : trace_(capacity) {} |
454 | |
455 | intptr_t Length() const { return trace_.length(); } |
456 | |
457 | ActivationFrame* FrameAt(int i) const { return trace_[i]; } |
458 | |
459 | ActivationFrame* GetHandlerFrame(const Instance& exc_obj) const; |
460 | |
461 | private: |
462 | void AddActivation(ActivationFrame* frame); |
463 | void AddMarker(ActivationFrame::Kind marker); |
464 | void AddAsyncCausalFrame(uword pc, const Code& code); |
465 | void AddAsyncCausalFrame(uword pc, const Bytecode& bytecode); |
466 | |
467 | ZoneGrowableArray<ActivationFrame*> trace_; |
468 | |
469 | friend class Debugger; |
470 | DISALLOW_COPY_AND_ASSIGN(DebuggerStackTrace); |
471 | }; |
472 | |
473 | // On which exceptions to pause. |
474 | typedef enum { |
475 | kNoPauseOnExceptions = 1, |
476 | kPauseOnUnhandledExceptions, |
477 | kPauseOnAllExceptions, |
478 | kInvalidExceptionPauseInfo |
479 | } Dart_ExceptionPauseInfo; |
480 | |
481 | class Debugger { |
482 | public: |
483 | enum ResumeAction { |
484 | kContinue, |
485 | kStepInto, |
486 | kStepOver, |
487 | kStepOut, |
488 | kStepRewind, |
489 | kStepOverAsyncSuspension, |
490 | }; |
491 | |
492 | explicit Debugger(Isolate* isolate); |
493 | ~Debugger(); |
494 | |
495 | void NotifyIsolateCreated(); |
496 | void Shutdown(); |
497 | |
498 | void OnIsolateRunnable(); |
499 | |
500 | void NotifyCompilation(const Function& func) { |
501 | HandleCodeChange(/* bytecode_loaded = */ false, func); |
502 | } |
503 | void NotifyBytecodeLoaded(const Function& func) { |
504 | HandleCodeChange(/* bytecode_loaded = */ true, func); |
505 | } |
506 | void NotifyDoneLoading(); |
507 | |
508 | // Set breakpoint at closest location to function entry. |
509 | Breakpoint* SetBreakpointAtEntry(const Function& target_function, |
510 | bool single_shot); |
511 | Breakpoint* SetBreakpointAtActivation(const Instance& closure, |
512 | bool for_over_await); |
513 | Breakpoint* BreakpointAtActivation(const Instance& closure); |
514 | |
515 | // TODO(turnidge): script_url may no longer be specific enough. |
516 | Breakpoint* SetBreakpointAtLine(const String& script_url, |
517 | intptr_t line_number); |
518 | Breakpoint* SetBreakpointAtLineCol(const String& script_url, |
519 | intptr_t line_number, |
520 | intptr_t column_number); |
521 | |
522 | BreakpointLocation* BreakpointLocationAtLineCol(const String& script_url, |
523 | intptr_t line_number, |
524 | intptr_t column_number); |
525 | |
526 | void RemoveBreakpoint(intptr_t bp_id); |
527 | Breakpoint* GetBreakpointById(intptr_t id); |
528 | |
529 | void MaybeAsyncStepInto(const Closure& async_op); |
530 | void AsyncStepInto(const Closure& async_op); |
531 | |
532 | void Continue(); |
533 | |
534 | bool SetResumeAction(ResumeAction action, |
535 | intptr_t frame_index = 1, |
536 | const char** error = NULL); |
537 | |
538 | bool IsStepping() const { return resume_action_ != kContinue; } |
539 | |
540 | bool IsSingleStepping() const { return resume_action_ == kStepInto; } |
541 | |
542 | bool IsPaused() const { return pause_event_ != NULL; } |
543 | |
544 | bool ignore_breakpoints() const { return ignore_breakpoints_; } |
545 | void set_ignore_breakpoints(bool ignore_breakpoints) { |
546 | ignore_breakpoints_ = ignore_breakpoints; |
547 | } |
548 | |
549 | bool HasEnabledBytecodeBreakpoints() const; |
550 | // Called from the interpreter. Note that pc already points to next bytecode. |
551 | bool HasBytecodeBreakpointAt(const KBCInstr* next_pc) const; |
552 | |
553 | // Put the isolate into single stepping mode when Dart code next runs. |
554 | // |
555 | // This is used by the vm service to allow the user to step while |
556 | // paused at isolate start. |
557 | void EnterSingleStepMode(); |
558 | |
559 | // Indicates why the debugger is currently paused. If the debugger |
560 | // is not paused, this returns NULL. Note that the debugger can be |
561 | // paused for breakpoints, isolate interruption, and (sometimes) |
562 | // exceptions. |
563 | const ServiceEvent* PauseEvent() const { return pause_event_; } |
564 | |
565 | void SetExceptionPauseInfo(Dart_ExceptionPauseInfo pause_info); |
566 | Dart_ExceptionPauseInfo GetExceptionPauseInfo() const; |
567 | |
568 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
569 | |
570 | // Returns true if there is at least one breakpoint set in func or code. |
571 | // Checks for both user-defined and internal temporary breakpoints. |
572 | // This may be called from different threads, therefore do not use the, |
573 | // debugger's zone. |
574 | bool HasBreakpoint(const Function& func, Zone* zone); |
575 | bool HasBreakpoint(const Code& code); |
576 | // A Bytecode version of HasBreakpoint is not needed. |
577 | |
578 | // Returns true if the call at address pc is patched to point to |
579 | // a debugger stub. |
580 | bool HasActiveBreakpoint(uword pc); |
581 | |
582 | // Returns a stack trace with frames corresponding to invisible functions |
583 | // omitted. CurrentStackTrace always returns a new trace on the current stack. |
584 | // The trace returned by StackTrace may have been cached; it is suitable for |
585 | // use when stepping, but otherwise may be out of sync with the current stack. |
586 | DebuggerStackTrace* StackTrace(); |
587 | DebuggerStackTrace* CurrentStackTrace(); |
588 | |
589 | DebuggerStackTrace* AsyncCausalStackTrace(); |
590 | DebuggerStackTrace* CurrentAsyncCausalStackTrace(); |
591 | |
592 | DebuggerStackTrace* AwaiterStackTrace(); |
593 | DebuggerStackTrace* CurrentAwaiterStackTrace(); |
594 | |
595 | // Returns a debugger stack trace corresponding to a dart.core.StackTrace. |
596 | // Frames corresponding to invisible functions are omitted. It is not valid |
597 | // to query local variables in the returned stack. |
598 | DebuggerStackTrace* StackTraceFrom(const class StackTrace& dart_stacktrace); |
599 | |
600 | // Utility functions. |
601 | static const char* QualifiedFunctionName(const Function& func); |
602 | |
603 | // Pause execution for a breakpoint. Called from generated code. |
604 | ErrorPtr PauseBreakpoint(); |
605 | |
606 | // Pause execution due to stepping. Called from generated code. |
607 | ErrorPtr PauseStepping(); |
608 | |
609 | // Pause execution due to isolate interrupt. |
610 | ErrorPtr PauseInterrupted(); |
611 | |
612 | // Pause after a reload request. |
613 | ErrorPtr PausePostRequest(); |
614 | |
615 | // Pause execution due to an uncaught exception. |
616 | void PauseException(const Instance& exc); |
617 | |
618 | // Pause execution due to a call to the debugger() function from |
619 | // Dart. |
620 | void PauseDeveloper(const String& msg); |
621 | |
622 | CodePtr GetPatchedStubAddress(uword breakpoint_address); |
623 | |
624 | void PrintBreakpointsToJSONArray(JSONArray* jsarr) const; |
625 | void PrintSettingsToJSONObject(JSONObject* jsobj) const; |
626 | |
627 | static bool IsDebuggable(const Function& func); |
628 | static bool IsDebugging(Thread* thread, const Function& func); |
629 | |
630 | intptr_t limitBreakpointId() { return next_id_; } |
631 | |
632 | // Callback to the debugger to continue frame rewind, post-deoptimization. |
633 | void RewindPostDeopt(); |
634 | |
635 | static DebuggerStackTrace* CollectAwaiterReturnStackTrace(); |
636 | |
637 | private: |
638 | ErrorPtr PauseRequest(ServiceEvent::EventKind kind); |
639 | |
640 | // Finds the breakpoint we hit at |location|. |
641 | Breakpoint* FindHitBreakpoint(BreakpointLocation* location, |
642 | ActivationFrame* top_frame); |
643 | |
644 | // Will return false if we are not at an await. |
645 | bool SetupStepOverAsyncSuspension(const char** error); |
646 | |
647 | bool NeedsIsolateEvents(); |
648 | bool NeedsDebugEvents(); |
649 | void InvokeEventHandler(ServiceEvent* event); |
650 | |
651 | void SendBreakpointEvent(ServiceEvent::EventKind kind, Breakpoint* bpt); |
652 | |
653 | bool IsAtAsyncJump(ActivationFrame* top_frame); |
654 | void FindCompiledFunctions(const Script& script, |
655 | TokenPosition start_pos, |
656 | TokenPosition end_pos, |
657 | GrowableObjectArray* bytecode_function_list, |
658 | GrowableObjectArray* code_function_list); |
659 | bool FindBestFit(const Script& script, |
660 | TokenPosition token_pos, |
661 | TokenPosition last_token_pos, |
662 | Function* best_fit); |
663 | FunctionPtr FindInnermostClosure(const Function& function, |
664 | TokenPosition token_pos); |
665 | TokenPosition ResolveBreakpointPos(bool in_bytecode, |
666 | const Function& func, |
667 | TokenPosition requested_token_pos, |
668 | TokenPosition last_token_pos, |
669 | intptr_t requested_column, |
670 | TokenPosition exact_token_pos); |
671 | void DeoptimizeWorld(); |
672 | void NotifySingleStepping(bool value) const; |
673 | BreakpointLocation* SetCodeBreakpoints(bool in_bytecode, |
674 | BreakpointLocation* loc, |
675 | const Script& script, |
676 | TokenPosition token_pos, |
677 | TokenPosition last_token_pos, |
678 | intptr_t requested_line, |
679 | intptr_t requested_column, |
680 | TokenPosition exact_token_pos, |
681 | const GrowableObjectArray& functions); |
682 | BreakpointLocation* SetBreakpoint(const Script& script, |
683 | TokenPosition token_pos, |
684 | TokenPosition last_token_pos, |
685 | intptr_t requested_line, |
686 | intptr_t requested_column, |
687 | const Function& function); |
688 | bool RemoveBreakpointFromTheList(intptr_t bp_id, BreakpointLocation** list); |
689 | Breakpoint* GetBreakpointByIdInTheList(intptr_t id, BreakpointLocation* list); |
690 | void RemoveUnlinkedCodeBreakpoints(); |
691 | void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location); |
692 | BreakpointLocation* GetLatentBreakpoint(const String& url, |
693 | intptr_t line, |
694 | intptr_t column); |
695 | void RegisterBreakpointLocation(BreakpointLocation* bpt); |
696 | void RegisterCodeBreakpoint(CodeBreakpoint* bpt); |
697 | BreakpointLocation* GetBreakpointLocation( |
698 | const Script& script, |
699 | TokenPosition token_pos, |
700 | intptr_t requested_line, |
701 | intptr_t requested_column, |
702 | TokenPosition bytecode_token_pos = TokenPosition::kNoSource, |
703 | TokenPosition code_token_pos = TokenPosition::kNoSource); |
704 | void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt); |
705 | // Returns NULL if no breakpoint exists for the given address. |
706 | CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address); |
707 | |
708 | void SyncBreakpointLocation(BreakpointLocation* loc); |
709 | void PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt, |
710 | JSONArray* jsarr) const; |
711 | |
712 | void HandleCodeChange(bool bytecode_loaded, const Function& func); |
713 | |
714 | ActivationFrame* TopDartFrame() const; |
715 | static ActivationFrame* CollectDartFrame( |
716 | Isolate* isolate, |
717 | uword pc, |
718 | StackFrame* frame, |
719 | const Code& code, |
720 | const Array& deopt_frame, |
721 | intptr_t deopt_frame_offset, |
722 | ActivationFrame::Kind kind = ActivationFrame::kRegular); |
723 | #if !defined(DART_PRECOMPILED_RUNTIME) |
724 | static ActivationFrame* CollectDartFrame( |
725 | Isolate* isolate, |
726 | uword pc, |
727 | StackFrame* frame, |
728 | const Bytecode& bytecode, |
729 | ActivationFrame::Kind kind = ActivationFrame::kRegular); |
730 | static ArrayPtr DeoptimizeToArray(Thread* thread, |
731 | StackFrame* frame, |
732 | const Code& code); |
733 | TokenPosition FindExactTokenPosition(const Script& script, |
734 | TokenPosition start_of_line, |
735 | intptr_t column_number); |
736 | #endif |
737 | // Appends at least one stack frame. Multiple frames will be appended |
738 | // if |code| at the frame's pc contains inlined functions. |
739 | static void AppendCodeFrames(Thread* thread, |
740 | Isolate* isolate, |
741 | Zone* zone, |
742 | DebuggerStackTrace* stack_trace, |
743 | StackFrame* frame, |
744 | Code* code, |
745 | Code* inlined_code, |
746 | Array* deopt_frame); |
747 | static DebuggerStackTrace* CollectStackTrace(); |
748 | static DebuggerStackTrace* CollectAsyncCausalStackTrace(); |
749 | static DebuggerStackTrace* CollectAsyncLazyStackTrace(); |
750 | void SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt); |
751 | |
752 | intptr_t nextId() { return next_id_++; } |
753 | |
754 | bool ShouldPauseOnException(DebuggerStackTrace* stack_trace, |
755 | const Instance& exc); |
756 | |
757 | // Handles any events which pause vm execution. Breakpoints, |
758 | // interrupts, etc. |
759 | void Pause(ServiceEvent* event); |
760 | |
761 | void HandleSteppingRequest(DebuggerStackTrace* stack_trace, |
762 | bool skip_next_step = false); |
763 | |
764 | void CacheStackTraces(DebuggerStackTrace* stack_trace, |
765 | DebuggerStackTrace* async_causal_stack_trace, |
766 | DebuggerStackTrace* awaiter_stack_trace); |
767 | void ClearCachedStackTraces(); |
768 | |
769 | // Can we rewind to the indicated frame? |
770 | bool CanRewindFrame(intptr_t frame_index, const char** error) const; |
771 | |
772 | void RewindToFrame(intptr_t frame_index); |
773 | void RewindToUnoptimizedFrame(StackFrame* frame, const Code& code); |
774 | void RewindToOptimizedFrame(StackFrame* frame, |
775 | const Code& code, |
776 | intptr_t post_deopt_frame_index); |
777 | void RewindToInterpretedFrame(StackFrame* frame, const Bytecode& bytecode); |
778 | |
779 | void ResetSteppingFramePointers(); |
780 | bool SteppedForSyntheticAsyncBreakpoint() const; |
781 | void CleanupSyntheticAsyncBreakpoint(); |
782 | void RememberTopFrameAwaiter(); |
783 | void SetAsyncSteppingFramePointer(DebuggerStackTrace* stack_trace); |
784 | void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace); |
785 | |
786 | Isolate* isolate_; |
787 | |
788 | // ID number generator. |
789 | intptr_t next_id_; |
790 | |
791 | BreakpointLocation* latent_locations_; |
792 | BreakpointLocation* breakpoint_locations_; |
793 | CodeBreakpoint* code_breakpoints_; |
794 | |
795 | // Tells debugger what to do when resuming execution after a breakpoint. |
796 | ResumeAction resume_action_; |
797 | intptr_t resume_frame_index_; |
798 | intptr_t post_deopt_frame_index_; |
799 | |
800 | // Do not call back to breakpoint handler if this flag is set. |
801 | // Effectively this means ignoring breakpoints. Set when Dart code may |
802 | // be run as a side effect of getting values of fields. |
803 | bool ignore_breakpoints_; |
804 | |
805 | // Indicates why the debugger is currently paused. If the debugger |
806 | // is not paused, this is NULL. Note that the debugger can be |
807 | // paused for breakpoints, isolate interruption, and (sometimes) |
808 | // exceptions. |
809 | ServiceEvent* pause_event_; |
810 | |
811 | // Current stack trace. Valid only while IsPaused(). |
812 | DebuggerStackTrace* stack_trace_; |
813 | DebuggerStackTrace* async_causal_stack_trace_; |
814 | DebuggerStackTrace* awaiter_stack_trace_; |
815 | |
816 | // When stepping through code, only pause the program if the top |
817 | // frame corresponds to this fp value, or if the top frame is |
818 | // lower on the stack. |
819 | uword stepping_fp_; |
820 | bool interpreted_stepping_; |
821 | |
822 | // When stepping through code, do not stop more than once in the same |
823 | // token position range. |
824 | uword last_stepping_fp_; |
825 | TokenPosition last_stepping_pos_; |
826 | |
827 | // Used to track the current async/async* function. |
828 | uword async_stepping_fp_; |
829 | bool interpreted_async_stepping_; |
830 | ObjectPtr top_frame_awaiter_; |
831 | |
832 | // If we step while at a breakpoint, we would hit the same pc twice. |
833 | // We use this field to let us skip the next single-step after a |
834 | // breakpoint. |
835 | bool skip_next_step_; |
836 | |
837 | bool needs_breakpoint_cleanup_; |
838 | |
839 | // We keep this breakpoint alive until after the debugger does the step over |
840 | // async continuation machinery so that we can report that we've stopped |
841 | // at the breakpoint. |
842 | Breakpoint* synthetic_async_breakpoint_; |
843 | |
844 | Dart_ExceptionPauseInfo exc_pause_info_; |
845 | |
846 | friend class Isolate; |
847 | friend class BreakpointLocation; |
848 | DISALLOW_COPY_AND_ASSIGN(Debugger); |
849 | }; |
850 | |
851 | class DisableBreakpointsScope : public ValueObject { |
852 | public: |
853 | DisableBreakpointsScope(Debugger* debugger, bool disable) |
854 | : debugger_(debugger) { |
855 | ASSERT(debugger_ != NULL); |
856 | initial_state_ = debugger_->ignore_breakpoints(); |
857 | debugger_->set_ignore_breakpoints(disable); |
858 | } |
859 | |
860 | ~DisableBreakpointsScope() { |
861 | debugger_->set_ignore_breakpoints(initial_state_); |
862 | } |
863 | |
864 | private: |
865 | Debugger* debugger_; |
866 | bool initial_state_; |
867 | |
868 | DISALLOW_COPY_AND_ASSIGN(DisableBreakpointsScope); |
869 | }; |
870 | |
871 | } // namespace dart |
872 | |
873 | #endif // RUNTIME_VM_DEBUGGER_H_ |
874 | |