| 1 | // Copyright (c) 2017, 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_STACK_TRACE_H_ |
| 6 | #define RUNTIME_VM_STACK_TRACE_H_ |
| 7 | |
| 8 | #include <functional> |
| 9 | |
| 10 | #include "vm/allocation.h" |
| 11 | #include "vm/flag_list.h" |
| 12 | #include "vm/object.h" |
| 13 | #include "vm/symbols.h" |
| 14 | |
| 15 | namespace dart { |
| 16 | |
| 17 | class StackTraceUtils : public AllStatic { |
| 18 | public: |
| 19 | /// Collects all frames on the current stack until an async/async* frame is |
| 20 | /// hit which has yielded before (i.e. is not in sync-async case). |
| 21 | /// |
| 22 | /// From there on finds the closure of the async/async* frame and starts |
| 23 | /// traversing the listeners: |
| 24 | /// while (closure != null) { |
| 25 | /// yield_index = closure.context[Context::kAsyncJumpVarIndex] |
| 26 | /// pc = closure.function.code.pc_descriptors.LookupPcFromYieldIndex( |
| 27 | /// yield_index); |
| 28 | /// <emit pc in frame> |
| 29 | /// closure = closure.context[Context::kAsyncCompleterVarIndex]._future |
| 30 | /// ._resultOrListeners.callback; |
| 31 | /// } |
| 32 | /// |
| 33 | /// If [on_sync_frames] is non-nullptr, it will be called for every |
| 34 | /// synchronous frame which is collected. |
| 35 | static void CollectFramesLazy( |
| 36 | Thread* thread, |
| 37 | const GrowableObjectArray& code_array, |
| 38 | const GrowableObjectArray& pc_offset_array, |
| 39 | int skip_frames, |
| 40 | std::function<void(StackFrame*)>* on_sync_frames = nullptr, |
| 41 | bool* has_async = nullptr); |
| 42 | |
| 43 | /// Counts the number of stack frames. |
| 44 | /// Skips over the first |skip_frames|. |
| 45 | /// If |async_function| is not null, stops at the function that has |
| 46 | /// |async_function| as its parent, and records in 'sync_async_end' whether |
| 47 | /// |async_function| was called synchronously. |
| 48 | static intptr_t CountFrames(Thread* thread, |
| 49 | int skip_frames, |
| 50 | const Function& async_function, |
| 51 | bool* sync_async_end); |
| 52 | |
| 53 | /// Collects |count| frames into |code_array| and |pc_offset_array|. |
| 54 | /// Writing begins at |array_offset|. |
| 55 | /// Skips over the first |skip_frames|. |
| 56 | /// Returns the number of frames collected. |
| 57 | static intptr_t CollectFrames(Thread* thread, |
| 58 | const Array& code_array, |
| 59 | const Array& pc_offset_array, |
| 60 | intptr_t array_offset, |
| 61 | intptr_t count, |
| 62 | int skip_frames); |
| 63 | |
| 64 | /// If |thread| has no async_stack_trace, does nothing. |
| 65 | /// Populates |async_function| with the top function of the async stack |
| 66 | /// trace. Populates |async_stack_trace|, |async_code_array|, and |
| 67 | /// |async_pc_offset_array| with the thread's async_stack_trace. |
| 68 | /// Returns the length of the asynchronous stack trace. |
| 69 | static intptr_t (Thread* thread, |
| 70 | Function* async_function, |
| 71 | StackTrace* async_stack_trace, |
| 72 | Array* async_code_array, |
| 73 | Array* async_pc_offset_array); |
| 74 | |
| 75 | // The number of frames involved in a "sync-async" gap: a synchronous initial |
| 76 | // invocation of an asynchronous function. See CheckAndSkipAsync. |
| 77 | static constexpr intptr_t kSyncAsyncFrameGap = 2; |
| 78 | |
| 79 | // A synchronous invocation of an async function involves the following |
| 80 | // frames: |
| 81 | // <async function>__<anonymous_closure> (0) |
| 82 | // _Closure.call (1) |
| 83 | // _AsyncAwaitCompleter.start (2) |
| 84 | // <async_function> (3) |
| 85 | // |
| 86 | // Alternatively, for bytecode or optimized frames, we may see: |
| 87 | // <async function>__<anonymous_closure> (0) |
| 88 | // _AsyncAwaitCompleter.start (1) |
| 89 | // <async_function> (2) |
| 90 | static bool CheckAndSkipAsync(int* skip_sync_async_frames_count, |
| 91 | const String& function_name) { |
| 92 | ASSERT(*skip_sync_async_frames_count > 0); |
| 93 | // Make sure any function objects for methods used here are marked for |
| 94 | // retention by the precompiler, even if otherwise not needed at runtime. |
| 95 | // |
| 96 | // _AsyncAwaitCompleter.start is marked with the vm:entry-point pragma. |
| 97 | if (function_name.Equals(Symbols::_AsyncAwaitCompleterStart())) { |
| 98 | *skip_sync_async_frames_count = 0; |
| 99 | return true; |
| 100 | } |
| 101 | // _Closure.call is explicitly checked in Precompiler::MustRetainFunction. |
| 102 | if (function_name.Equals(Symbols::_ClosureCall()) && |
| 103 | *skip_sync_async_frames_count == 2) { |
| 104 | (*skip_sync_async_frames_count)--; |
| 105 | return true; |
| 106 | } |
| 107 | return false; |
| 108 | } |
| 109 | }; |
| 110 | |
| 111 | } // namespace dart |
| 112 | |
| 113 | #endif // RUNTIME_VM_STACK_TRACE_H_ |
| 114 | |