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 | |