| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_VM_STACK_TRACE_H_ |
| #define RUNTIME_VM_STACK_TRACE_H_ |
| |
| #include <functional> |
| |
| #include "vm/allocation.h" |
| #include "vm/flag_list.h" |
| #include "vm/object.h" |
| #include "vm/symbols.h" |
| |
| namespace dart { |
| |
| // Helper class for finding the closure of the caller. |
| class CallerClosureFinder { |
| public: |
| explicit CallerClosureFinder(Zone* zone); |
| |
| // Recursively follow any `_FutureListener.result`. |
| // If no `result`, then return (bottom) `_FutureListener.callback` |
| ClosurePtr GetCallerInFutureImpl(const Object& future_); |
| |
| // Get caller closure from _FutureListener. |
| // Returns closure found either via the `result` Future, or the `callback`. |
| ClosurePtr GetCallerInFutureListener(const Object& future_listener); |
| |
| // Find caller closure from an async* function receiver context. |
| // Returns either the `onData` or the Future awaiter. |
| ClosurePtr FindCallerInAsyncGenClosure(const Context& receiver_context); |
| |
| // Find caller closure from a function receiver closure. |
| // For async* functions, async functions, `Future.timeout` and `Future.wait`, |
| // we can do this by finding and following their awaited Futures. |
| ClosurePtr FindCaller(const Closure& receiver_closure); |
| |
| // Finds the awaited Future from an async function receiver closure. |
| ObjectPtr GetAsyncFuture(const Closure& receiver_closure); |
| |
| // Get sdk/lib/async/future_impl.dart:_FutureListener.state. |
| intptr_t GetFutureListenerState(const Object& future_listener); |
| |
| // Get sdk/lib/async/future_impl.dart:_FutureListener.callback. |
| ClosurePtr GetFutureListenerCallback(const Object& future_listener); |
| |
| // Get sdk/lib/async/future_impl.dart:_FutureListener.result. |
| ObjectPtr GetFutureListenerResult(const Object& future_listener); |
| |
| // Get sdk/lib/async/future_impl.dart:_Future._resultOrListeners. |
| ObjectPtr GetFutureFutureListener(const Object& future); |
| |
| bool HasCatchError(const Object& future_listener); |
| |
| static bool IsRunningAsync(const Closure& receiver_closure); |
| |
| private: |
| Context& receiver_context_; |
| Function& receiver_function_; |
| Function& parent_function_; |
| |
| Object& context_entry_; |
| Object& future_; |
| Object& listener_; |
| Object& callback_; |
| Object& controller_; |
| Object& state_; |
| Object& var_data_; |
| Object& callback_instance_; |
| |
| Class& future_impl_class; |
| Class& future_listener_class; |
| Class& async_start_stream_controller_class; |
| Class& stream_controller_class; |
| Class& async_stream_controller_class; |
| Class& controller_subscription_class; |
| Class& buffering_stream_subscription_class; |
| Class& stream_iterator_class; |
| |
| Field& future_result_or_listeners_field; |
| Field& callback_field; |
| Field& future_listener_state_field; |
| Field& future_listener_result_field; |
| Field& controller_controller_field; |
| Field& var_data_field; |
| Field& state_field; |
| Field& on_data_field; |
| Field& state_data_field; |
| |
| DISALLOW_COPY_AND_ASSIGN(CallerClosureFinder); |
| }; |
| |
| class StackTraceUtils : public AllStatic { |
| public: |
| // Find the async_op closure from the stack frame. |
| static ClosurePtr FindClosureInFrame(ObjectPtr* last_object_in_caller, |
| const Function& function); |
| |
| static ClosurePtr ClosureFromFrameFunction( |
| Zone* zone, |
| CallerClosureFinder* caller_closure_finder, |
| const DartFrameIterator& frames, |
| StackFrame* frame, |
| bool* skip_frame, |
| bool* is_async); |
| |
| static void UnwindAwaiterChain(Zone* zone, |
| const GrowableObjectArray& code_array, |
| GrowableArray<uword>* pc_offset_array, |
| CallerClosureFinder* caller_closure_finder, |
| const Closure& leaf_closure); |
| |
| /// Collects all frames on the current stack until an async/async* frame is |
| /// hit which has yielded before (i.e. is not in sync-async case). |
| /// |
| /// From there on finds the closure of the async/async* frame and starts |
| /// traversing the listeners: |
| /// while (closure != null) { |
| /// yield_index = closure.context[Context::kAsyncJumpVarIndex] |
| /// pc = closure.function.code.pc_descriptors.LookupPcFromYieldIndex( |
| /// yield_index); |
| /// <emit pc in frame> |
| /// closure = closure.context[Context::kAsyncCompleterVarIndex]._future |
| /// ._resultOrListeners.callback; |
| /// } |
| /// |
| /// If [on_sync_frames] is non-nullptr, it will be called for every |
| /// synchronous frame which is collected. |
| static void CollectFramesLazy( |
| Thread* thread, |
| const GrowableObjectArray& code_array, |
| GrowableArray<uword>* pc_offset_array, |
| int skip_frames, |
| std::function<void(StackFrame*)>* on_sync_frames = nullptr, |
| bool* has_async = nullptr); |
| |
| /// Counts the number of stack frames. |
| /// Skips over the first |skip_frames|. |
| /// If |async_function| is not null, stops at the function that has |
| /// |async_function| as its parent, and records in 'sync_async_end' whether |
| /// |async_function| was called synchronously. |
| static intptr_t CountFrames(Thread* thread, |
| int skip_frames, |
| const Function& async_function, |
| bool* sync_async_end); |
| |
| /// Collects |count| frames into |code_array| and |pc_offset_array|. |
| /// Writing begins at |array_offset|. |
| /// Skips over the first |skip_frames|. |
| /// Returns the number of frames collected. |
| static intptr_t CollectFrames(Thread* thread, |
| const Array& code_array, |
| const TypedData& pc_offset_array, |
| intptr_t array_offset, |
| intptr_t count, |
| int skip_frames); |
| }; |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_STACK_TRACE_H_ |