blob: 24415e145c9cd92d2bce35cb849e917a15cf63d6 [file] [log] [blame] [edit]
// 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.
#include "vm/stack_trace.h"
#include "vm/stack_frame.h"
namespace dart {
// Count the number of frames that are on the stack.
intptr_t StackTraceUtils::CountFrames(Thread* thread,
int skip_frames,
const Function& async_function) {
Zone* zone = thread->zone();
intptr_t frame_count = 0;
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* frame = frames.NextFrame();
ASSERT(frame != NULL); // We expect to find a dart invocation frame.
Function& function = Function::Handle(zone);
const bool async_function_is_null = async_function.IsNull();
while (frame != NULL) {
if (frame->IsDartFrame()) {
if (skip_frames > 0) {
skip_frames--;
} else {
function = frame->LookupDartFunction();
frame_count++;
if (!async_function_is_null &&
(async_function.raw() == function.parent_function())) {
return frame_count;
}
}
}
frame = frames.NextFrame();
}
// We hit the sentinel.
ASSERT(async_function_is_null);
return frame_count;
}
intptr_t StackTraceUtils::CollectFrames(Thread* thread,
const Array& code_array,
const Array& pc_offset_array,
intptr_t array_offset,
intptr_t count,
int skip_frames) {
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.
Function& function = Function::Handle(zone);
Code& code = Code::Handle(zone);
Bytecode& bytecode = Bytecode::Handle(zone);
Smi& offset = Smi::Handle(zone);
intptr_t collected_frames_count = 0;
while ((frame != NULL) && (collected_frames_count < count)) {
if (frame->IsDartFrame()) {
if (skip_frames > 0) {
skip_frames--;
} else {
if (frame->is_interpreted()) {
bytecode = frame->LookupDartBytecode();
function = bytecode.function();
offset = Smi::New(frame->pc() - bytecode.PayloadStart());
code_array.SetAt(array_offset, bytecode);
} else {
code = frame->LookupDartCode();
function = code.function();
offset = Smi::New(frame->pc() - code.PayloadStart());
code_array.SetAt(array_offset, code);
}
pc_offset_array.SetAt(array_offset, offset);
array_offset++;
collected_frames_count++;
}
}
frame = frames.NextFrame();
}
return collected_frames_count;
}
intptr_t StackTraceUtils::ExtractAsyncStackTraceInfo(
Thread* thread,
Function* async_function,
StackTrace* async_stack_trace_out,
Array* async_code_array,
Array* async_pc_offset_array) {
if (thread->async_stack_trace() == StackTrace::null()) {
return 0;
}
*async_stack_trace_out = thread->async_stack_trace();
ASSERT(!async_stack_trace_out->IsNull());
const StackTrace& async_stack_trace =
StackTrace::Handle(thread->async_stack_trace());
const intptr_t async_stack_trace_length = async_stack_trace.Length();
// At least two entries (0: gap marker, 1: async function).
ASSERT(async_stack_trace_length >= 2);
// Validate the structure of this stack trace.
*async_code_array = async_stack_trace.code_array();
ASSERT(!async_code_array->IsNull());
*async_pc_offset_array = async_stack_trace.pc_offset_array();
ASSERT(!async_pc_offset_array->IsNull());
// We start with the asynchronous gap marker.
ASSERT(async_code_array->At(0) != Code::null());
ASSERT(async_code_array->At(0) == StubCode::AsynchronousGapMarker().raw());
const Object& code_object = Object::Handle(async_code_array->At(1));
if (code_object.IsCode()) {
*async_function = Code::Cast(code_object).function();
} else {
ASSERT(code_object.IsBytecode());
*async_function = Bytecode::Cast(code_object).function();
}
ASSERT(!async_function->IsNull());
ASSERT(async_function->IsAsyncFunction() ||
async_function->IsAsyncGenerator());
return async_stack_trace_length;
}
} // namespace dart