Index: runtime/vm/debugger.h |
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h |
index fa9c3a61466776e9cf319922d508f8806b11296c..53e98d2a8bf7a75ccfa4c86671ac477e80c57d62 100644 |
--- a/runtime/vm/debugger.h |
+++ b/runtime/vm/debugger.h |
@@ -11,6 +11,17 @@ |
#include "vm/port.h" |
#include "vm/service_event.h" |
+DECLARE_FLAG(bool, verbose_debug); |
+ |
+// 'Trace Debugger' TD_Print. |
+#if defined(_MSC_VER) |
+#define TD_Print(format, ...) \ |
+ if (FLAG_verbose_debug) Log::Current()->Print(format, __VA_ARGS__) |
+#else |
+#define TD_Print(format, ...) \ |
+ if (FLAG_verbose_debug) Log::Current()->Print(format, ##__VA_ARGS__) |
+#endif |
+ |
namespace dart { |
class CodeBreakpoint; |
@@ -204,6 +215,8 @@ class CodeBreakpoint { |
RawCode* OrigStubAddress() const; |
+ const char* ToCString(); |
+ |
private: |
void VisitObjectPointers(ObjectPointerVisitor* visitor); |
@@ -245,12 +258,26 @@ class CodeBreakpoint { |
// on the call stack. |
class ActivationFrame : public ZoneAllocated { |
public: |
+ enum Kind { |
+ kRegular, |
+ kAsyncLive, |
+ kAsyncSuspensionMarker, |
+ kAsyncHistorical, |
+ }; |
+ |
ActivationFrame(uword pc, |
uword fp, |
uword sp, |
const Code& code, |
const Array& deopt_frame, |
- intptr_t deopt_frame_offset); |
+ intptr_t deopt_frame_offset, |
+ Kind kind = kRegular); |
+ |
+ ActivationFrame(uword pc, const Code& code); |
+ |
+ explicit ActivationFrame(Kind kind); |
+ |
+ explicit ActivationFrame(const Closure& async_activation); |
uword pc() const { return pc_; } |
uword fp() const { return fp_; } |
@@ -272,6 +299,10 @@ class ActivationFrame : public ZoneAllocated { |
intptr_t LineNumber(); |
intptr_t ColumnNumber(); |
+ bool IsLiveFrame() const { |
+ return (kind_ == kRegular) || (kind_ == kAsyncLive); |
+ } |
+ |
// Returns true if this frame is for a function that is visible |
// to the user and can be debugged. |
bool IsDebuggable() const; |
@@ -279,6 +310,17 @@ class ActivationFrame : public ZoneAllocated { |
// Returns true if it is possible to rewind the debugger to this frame. |
bool IsRewindable() const; |
+ // Returns true if this function is _SyncCompleter._completeOnAsyncReturn |
+ // from the async library. |
+ bool IsCompleteOnAsyncReturn(); |
+ |
+ // Returns true if this function is from the AsyncStartStreamController class. |
+ bool IsInAsyncStarStreamController(); |
+ |
+ // Returns true if this function is part of the async implementation |
+ // machinery. |
+ bool IsInAsyncMachinery(); |
+ |
// The context level of a frame is the context level at the |
// PC/token index of the frame. It determines the depth of the context |
// chain that belongs to the function of this activation frame. |
@@ -302,6 +344,15 @@ class ActivationFrame : public ZoneAllocated { |
const Context& GetSavedCurrentContext(); |
RawObject* GetAsyncOperation(); |
+ RawObject* GetAsyncCompleter(); |
+ RawObject* GetAsyncCompleterAwaiter(const Object& completer); |
+ |
+ void ExtractTokenPositionFromAsyncClosure(); |
+ |
+ RawObject* GetAsyncStreamControllerStream(); |
+ RawObject* GetAsyncStreamControllerStreamAwaiter(const Object& stream); |
+ |
+ RawObject* GetAsyncAwaiter(); |
RawObject* Evaluate(const String& expr); |
@@ -311,6 +362,12 @@ class ActivationFrame : public ZoneAllocated { |
void PrintToJSONObject(JSONObject* jsobj, bool full = false); |
private: |
+ void PrintToJSONObjectAsync(JSONObject* jsobj, bool full = false); |
+ void PrintToJSONObjectRegular(JSONObject* jsobj, bool full = false); |
+ void PrintToJSONObjectHistoricalFrame(JSONObject* jsobj, bool full = false); |
+ |
+ void PrintToJSONObjectLiveFrame(JSONObject* jsobj, bool full = false); |
+ |
void PrintContextMismatchError(intptr_t ctx_slot, |
intptr_t frame_ctx_level, |
intptr_t var_ctx_level); |
@@ -320,6 +377,22 @@ class ActivationFrame : public ZoneAllocated { |
void GetVarDescriptors(); |
void GetDescIndices(); |
+ static const char* KindToCString(Kind kind) { |
+ switch (kind) { |
+ case kRegular: |
+ return "kRegular"; |
+ case kAsyncLive: |
+ return "kAsyncLive"; |
+ case kAsyncSuspensionMarker: |
+ return "kMarker"; |
+ case kAsyncHistorical: |
+ return "kAsyncHistorical"; |
+ default: |
+ UNREACHABLE(); |
+ return ""; |
+ } |
+ } |
+ |
RawObject* GetStackVar(intptr_t slot_index); |
RawObject* GetContextVar(intptr_t ctxt_level, intptr_t slot_index); |
@@ -329,8 +402,9 @@ class ActivationFrame : public ZoneAllocated { |
// The anchor of the context chain for this function. |
Context& ctx_; |
- const Code& code_; |
- const Function& function_; |
+ Code& code_; |
+ Function& function_; |
+ bool suspended_frame_; |
bool token_pos_initialized_; |
TokenPosition token_pos_; |
intptr_t try_index_; |
@@ -343,6 +417,8 @@ class ActivationFrame : public ZoneAllocated { |
const Array& deopt_frame_; |
const intptr_t deopt_frame_offset_; |
+ Kind kind_; |
+ |
bool vars_initialized_; |
LocalVarDescriptors& var_descriptors_; |
ZoneGrowableArray<intptr_t> desc_indices_; |
@@ -367,6 +443,9 @@ class DebuggerStackTrace : public ZoneAllocated { |
private: |
void AddActivation(ActivationFrame* frame); |
+ void AddMarker(ActivationFrame::Kind marker); |
+ void AddAsyncHistoricalFrame(uword pc, const Code& code); |
+ |
ZoneGrowableArray<ActivationFrame*> trace_; |
friend class Debugger; |
@@ -394,6 +473,8 @@ class Debugger { |
void NotifyIsolateCreated(); |
void Shutdown(); |
+ void WhenRunnable(); |
+ |
void NotifyCompilation(const Function& func); |
void NotifyDoneLoading(); |
@@ -430,8 +511,13 @@ class Debugger { |
bool IsStepping() const { return resume_action_ != kContinue; } |
+ bool IsSingleStepping() const { return resume_action_ == kStepInto; } |
+ |
bool IsPaused() const { return pause_event_ != NULL; } |
+ void AsyncStepInto(const Closure& async_op); |
+ void AsyncContinue(); |
+ |
// Put the isolate into single stepping mode when Dart code next runs. |
// |
// This is used by the vm service to allow the user to step while |
@@ -470,6 +556,17 @@ class Debugger { |
DebuggerStackTrace* StackTrace(); |
DebuggerStackTrace* CurrentStackTrace(); |
+ // Returns a stack trace with frames corresponding to invisible functions |
+ // omitted. CurrentAsyncStackTrace always returns a new trace on the current |
+ // stack. The trace returned by AsyncStackTrace may have been cached; it is |
+ // suitable for use when stepping, but otherwise may be out of sync with the |
+ // current stack. |
+ DebuggerStackTrace* AsyncStackTrace(); |
+ DebuggerStackTrace* CurrentAsyncStackTrace(); |
+ |
+ DebuggerStackTrace* AsyncReturnStack(); |
+ DebuggerStackTrace* CurrentAsyncReturnStack(); |
+ |
// Returns a debugger stack trace corresponding to a dart.core.StackTrace. |
// Frames corresponding to invisible functions are omitted. It is not valid |
// to query local variables in the returned stack. |
@@ -530,9 +627,23 @@ class Debugger { |
private: |
RawError* PauseRequest(ServiceEvent::EventKind kind); |
+ /// Finds the breakpoint we hit at |location|. |
+ Breakpoint* FindHitBreakpoint(BreakpointLocation* location, |
+ ActivationFrame* top_frame); |
+ |
+ // Returns true if the debugger had just taken a step after hitting a |
+ // synthetic async breakpoint. |
+ bool SteppedForSyntheticAsyncBreakpoint() const; |
+ |
+ RawError* StepForSyntheticAsyncBreakpoint(Breakpoint* bpt); |
+ |
+ void CleanupSyntheticAsyncBreakpoint(); |
+ |
// Will return false if we are not at an await. |
bool SetupStepOverAsyncSuspension(const char** error); |
+ uword GetAsyncActivationGeneratorSteppingFramePointer(); |
+ |
bool NeedsIsolateEvents(); |
bool NeedsDebugEvents(); |
void InvokeEventHandler(ServiceEvent* event); |
@@ -574,16 +685,21 @@ class Debugger { |
void SyncBreakpointLocation(BreakpointLocation* loc); |
ActivationFrame* TopDartFrame() const; |
- static ActivationFrame* CollectDartFrame(Isolate* isolate, |
- uword pc, |
- StackFrame* frame, |
- const Code& code, |
- const Array& deopt_frame, |
- intptr_t deopt_frame_offset); |
+ static ActivationFrame* CollectDartFrame( |
+ Isolate* isolate, |
+ uword pc, |
+ StackFrame* frame, |
+ const Code& code, |
+ const Array& deopt_frame, |
+ intptr_t deopt_frame_offset, |
+ ActivationFrame::Kind kind = ActivationFrame::kRegular); |
static RawArray* DeoptimizeToArray(Thread* thread, |
StackFrame* frame, |
const Code& code); |
+ static DebuggerStackTrace* CollectAsyncStackTrace(); |
static DebuggerStackTrace* CollectStackTrace(); |
+ static DebuggerStackTrace* CollectAsyncReturnCallStack(); |
+ |
void SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt); |
intptr_t nextId() { return next_id_++; } |
@@ -603,6 +719,11 @@ class Debugger { |
void HandleSteppingRequest(DebuggerStackTrace* stack_trace, |
bool skip_next_step = false); |
+ void CacheStackTraces(DebuggerStackTrace* stack_trace, |
+ DebuggerStackTrace* async_stack_trace, |
+ DebuggerStackTrace* async_return_stack_trace); |
+ void ClearCachedStackTraces(); |
+ |
// Can we rewind to the indicated frame? |
bool CanRewindFrame(intptr_t frame_index, const char** error) const; |
@@ -645,11 +766,25 @@ class Debugger { |
// Current stack trace. Valid only while IsPaused(). |
DebuggerStackTrace* stack_trace_; |
+ // Current async stack trace. Valid only while IsPaused(). |
+ DebuggerStackTrace* async_stack_trace_; |
+ |
+ // Current async return call trace. Valid only while IsPaused(). |
+ DebuggerStackTrace* async_return_call_stack_; |
+ |
// When stepping through code, only pause the program if the top |
// frame corresponds to this fp value, or if the top frame is |
// lower on the stack. |
uword stepping_fp_; |
+ // In order to support automatic step-out behaviour for asynchronous functions |
+ // we track the top frame's awaiter. This is where execution should begin |
+ // once we've left the current function. We keep track of the |
+ // async_stepping_fp_ and use it to determine if we have single stepped our |
+ // way out of the asynchronous function. |
+ RawObject* top_frame_awaiter_; |
+ uword async_stepping_fp_; |
+ |
// If we step while at a breakpoint, we would hit the same pc twice. |
// We use this field to let us skip the next single-step after a |
// breakpoint. |