// 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 #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* 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); /// /// 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* pc_offset_array, int skip_frames, std::function* 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_