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
15namespace dart {
16
17class 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 ExtractAsyncStackTraceInfo(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