mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Martin Kustermann](/assets/img/avatar_default.png)
Even though the FLAG_causal_async_stacks can be used to disable causal async stack traces, the generated code would still call to the runtime, which would simply return `null`. To avoid this runtime call we recognize the method in the 2 flow graph builders. Issue https://github.com/dart-lang/sdk/issues/37668 Change-Id: I896807060db911714d47a462c34696a0a3def62f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/110919 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
197 lines
6.6 KiB
C++
197 lines
6.6 KiB
C++
// Copyright (c) 2013, 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.
|
|
|
|
#include "lib/stacktrace.h"
|
|
#include "vm/bootstrap_natives.h"
|
|
#include "vm/debugger.h"
|
|
#include "vm/exceptions.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/runtime_entry.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/stack_trace.h"
|
|
|
|
namespace dart {
|
|
|
|
DECLARE_FLAG(bool, show_invisible_frames);
|
|
|
|
static RawStackTrace* CurrentSyncStackTrace(Thread* thread,
|
|
intptr_t skip_frames = 1) {
|
|
Zone* zone = thread->zone();
|
|
const Function& null_function = Function::ZoneHandle(zone);
|
|
|
|
// Determine how big the stack trace is.
|
|
const intptr_t stack_trace_length =
|
|
StackTraceUtils::CountFrames(thread, skip_frames, null_function);
|
|
|
|
// Allocate once.
|
|
const Array& code_array =
|
|
Array::ZoneHandle(zone, Array::New(stack_trace_length));
|
|
const Array& pc_offset_array =
|
|
Array::ZoneHandle(zone, Array::New(stack_trace_length));
|
|
|
|
// Collect the frames.
|
|
const intptr_t collected_frames_count = StackTraceUtils::CollectFrames(
|
|
thread, code_array, pc_offset_array, 0, stack_trace_length, skip_frames);
|
|
|
|
ASSERT(collected_frames_count == stack_trace_length);
|
|
|
|
return StackTrace::New(code_array, pc_offset_array);
|
|
}
|
|
|
|
static RawStackTrace* CurrentStackTrace(
|
|
Thread* thread,
|
|
bool for_async_function,
|
|
intptr_t skip_frames = 1,
|
|
bool causal_async_stacks = FLAG_causal_async_stacks) {
|
|
if (!causal_async_stacks) {
|
|
// Return the synchronous stack trace.
|
|
return CurrentSyncStackTrace(thread, skip_frames);
|
|
}
|
|
|
|
Zone* zone = thread->zone();
|
|
Code& code = Code::ZoneHandle(zone);
|
|
Smi& offset = Smi::ZoneHandle(zone);
|
|
Function& async_function = Function::ZoneHandle(zone);
|
|
StackTrace& async_stack_trace = StackTrace::ZoneHandle(zone);
|
|
Array& async_code_array = Array::ZoneHandle(zone);
|
|
Array& async_pc_offset_array = Array::ZoneHandle(zone);
|
|
|
|
StackTraceUtils::ExtractAsyncStackTraceInfo(
|
|
thread, &async_function, &async_stack_trace, &async_code_array,
|
|
&async_pc_offset_array);
|
|
|
|
// Determine the size of the stack trace.
|
|
const intptr_t extra_frames = for_async_function ? 1 : 0;
|
|
const intptr_t synchronous_stack_trace_length =
|
|
StackTraceUtils::CountFrames(thread, skip_frames, async_function);
|
|
|
|
const intptr_t capacity = synchronous_stack_trace_length +
|
|
extra_frames; // For the asynchronous gap.
|
|
|
|
// Allocate memory for the stack trace.
|
|
const Array& code_array = Array::ZoneHandle(zone, Array::New(capacity));
|
|
const Array& pc_offset_array = Array::ZoneHandle(zone, Array::New(capacity));
|
|
|
|
intptr_t write_cursor = 0;
|
|
if (for_async_function) {
|
|
// Place the asynchronous gap marker at the top of the stack trace.
|
|
code = StubCode::AsynchronousGapMarker().raw();
|
|
ASSERT(!code.IsNull());
|
|
offset = Smi::New(0);
|
|
code_array.SetAt(write_cursor, code);
|
|
pc_offset_array.SetAt(write_cursor, offset);
|
|
write_cursor++;
|
|
}
|
|
|
|
// Append the synchronous stack trace.
|
|
const intptr_t collected_frames_count = StackTraceUtils::CollectFrames(
|
|
thread, code_array, pc_offset_array, write_cursor,
|
|
synchronous_stack_trace_length, skip_frames);
|
|
|
|
write_cursor += collected_frames_count;
|
|
|
|
ASSERT(write_cursor == capacity);
|
|
|
|
return StackTrace::New(code_array, pc_offset_array, async_stack_trace);
|
|
}
|
|
|
|
RawStackTrace* GetStackTraceForException() {
|
|
Thread* thread = Thread::Current();
|
|
return CurrentStackTrace(thread, false, 0);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(StackTrace_current, 0, 0) {
|
|
return CurrentStackTrace(thread, false);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(StackTrace_asyncStackTraceHelper, 0, 1) {
|
|
if (!FLAG_causal_async_stacks) {
|
|
// If causal async stacks are not enabled we should recognize this method
|
|
// and never call to the NOP runtime.
|
|
// See kernel_to_il.cc/bytecode_reader.cc/interpreter.cc.
|
|
UNREACHABLE();
|
|
}
|
|
#if !defined(PRODUCT)
|
|
GET_NATIVE_ARGUMENT(Closure, async_op, arguments->NativeArgAt(0));
|
|
Debugger* debugger = isolate->debugger();
|
|
if (debugger != NULL) {
|
|
debugger->MaybeAsyncStepInto(async_op);
|
|
}
|
|
#endif
|
|
return CurrentStackTrace(thread, true);
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(StackTrace_clearAsyncThreadStackTrace, 0, 0) {
|
|
thread->clear_async_stack_trace();
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(StackTrace_setAsyncThreadStackTrace, 0, 1) {
|
|
if (!FLAG_causal_async_stacks) {
|
|
return Object::null();
|
|
}
|
|
|
|
GET_NON_NULL_NATIVE_ARGUMENT(StackTrace, stack_trace,
|
|
arguments->NativeArgAt(0));
|
|
thread->set_async_stack_trace(stack_trace);
|
|
return Object::null();
|
|
}
|
|
|
|
static void AppendFrames(const GrowableObjectArray& code_list,
|
|
const GrowableObjectArray& pc_offset_list,
|
|
int skip_frames) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
|
|
StackFrameIterator::kNoCrossThreadIteration);
|
|
StackFrame* frame = frames.NextFrame();
|
|
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
|
Code& code = Code::Handle(zone);
|
|
Bytecode& bytecode = Bytecode::Handle(zone);
|
|
Smi& offset = Smi::Handle(zone);
|
|
for (; frame != NULL; frame = frames.NextFrame()) {
|
|
if (!frame->IsDartFrame()) {
|
|
continue;
|
|
}
|
|
if (skip_frames > 0) {
|
|
skip_frames--;
|
|
continue;
|
|
}
|
|
|
|
if (frame->is_interpreted()) {
|
|
bytecode = frame->LookupDartBytecode();
|
|
if (bytecode.function() == Function::null()) {
|
|
continue;
|
|
}
|
|
offset = Smi::New(frame->pc() - bytecode.PayloadStart());
|
|
code_list.Add(bytecode);
|
|
} else {
|
|
code = frame->LookupDartCode();
|
|
offset = Smi::New(frame->pc() - code.PayloadStart());
|
|
code_list.Add(code);
|
|
}
|
|
pc_offset_list.Add(offset);
|
|
}
|
|
}
|
|
|
|
// Creates a StackTrace object from the current stack.
|
|
//
|
|
// Skips the first skip_frames Dart frames.
|
|
const StackTrace& GetCurrentStackTrace(int skip_frames) {
|
|
const GrowableObjectArray& code_list =
|
|
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
|
const GrowableObjectArray& pc_offset_list =
|
|
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
|
AppendFrames(code_list, pc_offset_list, skip_frames);
|
|
const Array& code_array = Array::Handle(Array::MakeFixedLength(code_list));
|
|
const Array& pc_offset_array =
|
|
Array::Handle(Array::MakeFixedLength(pc_offset_list));
|
|
const StackTrace& stacktrace =
|
|
StackTrace::Handle(StackTrace::New(code_array, pc_offset_array));
|
|
return stacktrace;
|
|
}
|
|
|
|
} // namespace dart
|