Files
sdk/runtime/lib/stacktrace.cc
T
Ben Konyi 5bf9163e1b [ Service / dart:isolate ] Added getPorts RPC and 'debugName' optional
parameter for ReceivePort and RawReceivePort

This change collects additional information related to ReceivePort
allocation locations and an optional debug name that will be displayed
by tooling. ReceivePort is now a special InstanceKind and a ReceivePort
@Instance will include the port ID, allocation stack trace, and debug
name.

Change-Id: I003cfff2b7649218e37d9b653c0e953df5d992e7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167902
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2020-10-26 23:09:47 +00:00

236 lines
8.0 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 const intptr_t kDefaultStackAllocation = 8;
static StackTracePtr CurrentSyncStackTraceLazy(Thread* thread,
intptr_t skip_frames = 1) {
Zone* zone = thread->zone();
const auto& code_array = GrowableObjectArray::ZoneHandle(
zone, GrowableObjectArray::New(kDefaultStackAllocation));
const auto& pc_offset_array = GrowableObjectArray::ZoneHandle(
zone, GrowableObjectArray::New(kDefaultStackAllocation));
// Collect the frames.
StackTraceUtils::CollectFramesLazy(thread, code_array, pc_offset_array,
skip_frames);
const auto& code_array_fixed =
Array::Handle(zone, Array::MakeFixedLength(code_array));
const auto& pc_offset_array_fixed =
Array::Handle(zone, Array::MakeFixedLength(pc_offset_array));
return StackTrace::New(code_array_fixed, pc_offset_array_fixed);
}
static StackTracePtr 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, nullptr);
// 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 StackTracePtr CurrentStackTrace(
Thread* thread,
bool for_async_function,
intptr_t skip_frames = 1,
bool causal_async_stacks = FLAG_causal_async_stacks) {
if (FLAG_lazy_async_stacks) {
return CurrentSyncStackTraceLazy(thread, skip_frames);
}
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;
bool sync_async_end = false;
const intptr_t synchronous_stack_trace_length = StackTraceUtils::CountFrames(
thread, skip_frames, async_function, &sync_async_end);
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);
const StackTrace& result = StackTrace::Handle(
zone, StackTrace::New(code_array, pc_offset_array, async_stack_trace,
sync_async_end));
return result.raw();
}
StackTracePtr 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;
}
bool HasStack() {
Thread* thread = Thread::Current();
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* frame = frames.NextFrame();
return frame != nullptr;
}
} // namespace dart