9520235bfe
Running all existing tests with/without lazy async stacks ensures that
the existing functionality for --lazy-async-stacks does not regress.
There are still a few smaller things to be done in the debugger for lazy
async stacks.
This CL also ensures:
* The inner closure, i.e. AsyncClosure/AsyncGenClosure, is not
inlined if FLAG_lazy_async_stacks is on.
* Fixes a crash in dartkb mode, when the function of the Bytecode
object is null.
* Does a simple integration of the lazy async stacks in debugger.cc -
to ensure a stack is returned via vm-service.
Issue https://github.com/dart-lang/sdk/issues/37668
Change-Id: Ibc1e887a457e2c456ae65d9ed5fa92434f122a32
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131825
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
108 lines
4.4 KiB
C++
108 lines
4.4 KiB
C++
// 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 {
|
|
|
|
class StackTraceUtils : public AllStatic {
|
|
public:
|
|
/// 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,
|
|
const GrowableObjectArray& pc_offset_array,
|
|
int skip_frames,
|
|
std::function<void(StackFrame*)>* on_sync_frames = 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 Array& pc_offset_array,
|
|
intptr_t array_offset,
|
|
intptr_t count,
|
|
int skip_frames);
|
|
|
|
/// If |thread| has no async_stack_trace, does nothing.
|
|
/// Populates |async_function| with the top function of the async stack
|
|
/// trace. Populates |async_stack_trace|, |async_code_array|, and
|
|
/// |async_pc_offset_array| with the thread's async_stack_trace.
|
|
/// Returns the length of the asynchronous stack trace.
|
|
static intptr_t ExtractAsyncStackTraceInfo(Thread* thread,
|
|
Function* async_function,
|
|
StackTrace* async_stack_trace,
|
|
Array* async_code_array,
|
|
Array* async_pc_offset_array);
|
|
|
|
// The number of frames involved in a "sync-async" gap: a synchronous initial
|
|
// invocation of an asynchronous function. See CheckAndSkipAsync.
|
|
static constexpr intptr_t kSyncAsyncFrameGap = 2;
|
|
|
|
// A synchronous invocation of an async function involves the following
|
|
// frames:
|
|
// <async function>__<anonymous_closure> (0)
|
|
// _Closure.call (1)
|
|
// _AsyncAwaitCompleter.start (2)
|
|
// <async_function> (3)
|
|
//
|
|
// Alternatively, for bytecode or optimized frames, we may see:
|
|
// <async function>__<anonymous_closure> (0)
|
|
// _AsyncAwaitCompleter.start (1)
|
|
// <async_function> (2)
|
|
static bool CheckAndSkipAsync(int* skip_sync_async_frames_count,
|
|
const String& function_name) {
|
|
ASSERT(*skip_sync_async_frames_count > 0);
|
|
if (function_name.Equals(Symbols::_AsyncAwaitCompleterStart())) {
|
|
*skip_sync_async_frames_count = 0;
|
|
return true;
|
|
}
|
|
if (function_name.Equals(Symbols::_ClosureCall()) &&
|
|
*skip_sync_async_frames_count == 2) {
|
|
(*skip_sync_async_frames_count)--;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_STACK_TRACE_H_
|