38049f3cb6
Handle bytecode without a function in stack trace collection. Bug: https://github.com/dart-lang/sdk/issues/36410 Change-Id: I828fbc11fb0c7ad617ff7a31f44adb021f9b6edf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99378 Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
137 lines
4.9 KiB
C++
137 lines
4.9 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.
|
|
|
|
#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);
|
|
Code& code = Code::Handle(zone);
|
|
Bytecode& bytecode = Bytecode::Handle(zone);
|
|
const bool async_function_is_null = async_function.IsNull();
|
|
for (; frame != NULL; frame = frames.NextFrame()) {
|
|
if (!frame->IsDartFrame()) {
|
|
continue;
|
|
}
|
|
if (skip_frames > 0) {
|
|
skip_frames--;
|
|
continue;
|
|
}
|
|
if (frame->is_interpreted()) {
|
|
bytecode = frame->LookupDartBytecode();
|
|
function = bytecode.function();
|
|
if (function.IsNull()) {
|
|
continue;
|
|
}
|
|
} else {
|
|
code = frame->LookupDartCode();
|
|
function = code.function();
|
|
}
|
|
frame_count++;
|
|
if (!async_function_is_null &&
|
|
(async_function.raw() == function.parent_function())) {
|
|
return frame_count;
|
|
}
|
|
}
|
|
// 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;
|
|
for (; (frame != NULL) && (collected_frames_count < count);
|
|
frame = frames.NextFrame()) {
|
|
if (!frame->IsDartFrame()) {
|
|
continue;
|
|
}
|
|
if (skip_frames > 0) {
|
|
skip_frames--;
|
|
continue;
|
|
}
|
|
if (frame->is_interpreted()) {
|
|
bytecode = frame->LookupDartBytecode();
|
|
function = bytecode.function();
|
|
if (function.IsNull()) {
|
|
continue;
|
|
}
|
|
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++;
|
|
}
|
|
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
|