| Index: runtime/lib/stacktrace.cc
|
| diff --git a/runtime/lib/stacktrace.cc b/runtime/lib/stacktrace.cc
|
| index d7dcfe01f2e4e81ac1c21632bff21caef543b435..255047f78ae6d0d8bb49a26e26ca7104bb7fe5b9 100644
|
| --- a/runtime/lib/stacktrace.cc
|
| +++ b/runtime/lib/stacktrace.cc
|
| @@ -4,16 +4,187 @@
|
|
|
| #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 {
|
|
|
| -static void IterateFrames(const GrowableObjectArray& code_list,
|
| - const GrowableObjectArray& pc_offset_list,
|
| - int skip_frames) {
|
| +DECLARE_FLAG(bool, show_invisible_frames);
|
| +DEFINE_FLAG(int,
|
| + max_async_stack_trace_frames,
|
| + 96,
|
| + "Maximum number of asynchronous stack traces frames remembered in "
|
| + "an asynchronous function activation");
|
| +
|
| +static RawStackTrace* CurrentSyncStackTrace(Thread* thread, Isolate* isolate) {
|
| + const Function& null_function = Function::Handle();
|
| + const intptr_t skip_frames = 1;
|
| +
|
| + // Determine how big the stack trace is.
|
| + const intptr_t stack_trace_length =
|
| + StackTraceUtils::CountFrames(skip_frames, null_function);
|
| +
|
| + // Allocate once.
|
| + const Array& code_array = Array::Handle(Array::New(stack_trace_length));
|
| + const Array& pc_offset_array = Array::Handle(Array::New(stack_trace_length));
|
| +
|
| + // Collect the frames.
|
| + const intptr_t collected_frames_count = StackTraceUtils::CollectFrames(
|
| + 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,
|
| + Isolate* isolate,
|
| + bool for_async_function,
|
| + bool sane_async_stacks = FLAG_sane_async_stacks) {
|
| + if (!sane_async_stacks) {
|
| + // Return the synchronous stack trace.
|
| + return CurrentSyncStackTrace(thread, isolate);
|
| + }
|
| +
|
| + Code& code = Code::Handle();
|
| + Smi& offset = Smi::Handle();
|
| + Function& async_function = Function::Handle();
|
| + Array& async_code_array = Array::Handle();
|
| + Array& async_pc_offset_array = Array::Handle();
|
| +
|
| + const intptr_t async_stack_trace_length =
|
| + StackTraceUtils::ExtractAsyncStackTraceInfo(
|
| + thread, &async_function, &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 skip_frames = 1;
|
| + const intptr_t synchronous_stack_trace_length =
|
| + StackTraceUtils::CountFrames(skip_frames, async_function);
|
| +
|
| + const intptr_t complete_length = async_stack_trace_length +
|
| + synchronous_stack_trace_length +
|
| + extra_frames; // For the asynchronous gap.
|
| +
|
| + // We only truncate the stack trace when collecting for asynchronous
|
| + // stack traces.
|
| + const bool truncate_stack_trace =
|
| + for_async_function || (async_stack_trace_length > 0);
|
| +
|
| + const intptr_t capacity =
|
| + (!truncate_stack_trace ||
|
| + (FLAG_max_async_stack_trace_frames > complete_length))
|
| + ? complete_length
|
| + : FLAG_max_async_stack_trace_frames;
|
| +
|
| + // Allocate memory for the stack trace.
|
| + const Array& code_array = Array::Handle(Array::New(capacity));
|
| + const Array& pc_offset_array = Array::Handle(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_entry()->code();
|
| + ASSERT(!code.IsNull());
|
| + offset = Smi::New(0);
|
| + code_array.SetAt(write_cursor, code);
|
| + pc_offset_array.SetAt(write_cursor, offset);
|
| + write_cursor++;
|
| + }
|
| +
|
| + const intptr_t sync_frames_to_collect =
|
| + (synchronous_stack_trace_length < (capacity - write_cursor))
|
| + ? synchronous_stack_trace_length
|
| + : capacity - write_cursor;
|
| +
|
| + // Append the synchronous stack trace.
|
| + const intptr_t collected_frames_count =
|
| + StackTraceUtils::CollectFrames(code_array, pc_offset_array, write_cursor,
|
| + sync_frames_to_collect, skip_frames);
|
| +
|
| + write_cursor += collected_frames_count;
|
| +
|
| + // Append the asynchronous stack trace.
|
| + if (async_stack_trace_length > 0) {
|
| + ASSERT(!async_code_array.IsNull());
|
| + ASSERT(!async_pc_offset_array.IsNull());
|
| + for (intptr_t i = 0; i < async_stack_trace_length; i++) {
|
| + if (write_cursor == capacity) {
|
| + break;
|
| + }
|
| + if (async_code_array.At(i) == Code::null()) {
|
| + break;
|
| + }
|
| + code = Code::RawCast(async_code_array.At(i));
|
| + offset = Smi::RawCast(async_pc_offset_array.At(i));
|
| + code_array.SetAt(write_cursor, code);
|
| + pc_offset_array.SetAt(write_cursor, offset);
|
| + write_cursor++;
|
| + }
|
| + }
|
| +
|
| + return StackTrace::New(code_array, pc_offset_array);
|
| +}
|
| +
|
| +
|
| +DEFINE_NATIVE_ENTRY(StackTrace_current, 0) {
|
| + return CurrentStackTrace(thread, isolate, false);
|
| +}
|
| +
|
| +
|
| +DEFINE_NATIVE_ENTRY(StackTrace_forAsyncMethod, 1) {
|
| + GET_NON_NULL_NATIVE_ARGUMENT(Closure, async_op, arguments->NativeArgAt(0));
|
| + Debugger* debugger = isolate->debugger();
|
| + if ((debugger != NULL) && debugger->IsSingleStepping()) {
|
| + // This native entry is only ever called from the async/async* entry
|
| + // function. If we are single stepping at this point, treat it as a request
|
| + // to single step into the async body.
|
| + debugger->AsyncStepInto(async_op);
|
| + }
|
| + return CurrentStackTrace(thread, isolate, true);
|
| +}
|
| +
|
| +
|
| +DEFINE_NATIVE_ENTRY(AsyncStarMoveNext_debuggerStepCheck, 1) {
|
| + GET_NON_NULL_NATIVE_ARGUMENT(Closure, async_op, arguments->NativeArgAt(0));
|
| + Debugger* debugger = isolate->debugger();
|
| + OS::PrintErr("*************************** 1\n");
|
| + if ((debugger != NULL) && debugger->IsSingleStepping()) {
|
| + OS::PrintErr("*************************** 2\n");
|
| + // This native entry is only ever called from the async/async* entry
|
| + // function. If we are single stepping at this point, treat it as a request
|
| + // to single step into the async body.
|
| + debugger->AsyncStepInto(async_op);
|
| + }
|
| + OS::PrintErr("*************************** 3\n");
|
| + return Object::null();
|
| +}
|
| +
|
| +
|
| +DEFINE_NATIVE_ENTRY(StackTrace_clearAsyncThreadStackTrace, 0) {
|
| + thread->clear_async_stack_trace();
|
| + return Object::null();
|
| +}
|
| +
|
| +
|
| +DEFINE_NATIVE_ENTRY(StackTrace_setAsyncThreadStackTrace, 1) {
|
| + 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) {
|
| StackFrameIterator frames(StackFrameIterator::kDontValidateFrames);
|
| StackFrame* frame = frames.NextFrame();
|
| ASSERT(frame != NULL); // We expect to find a dart invocation frame.
|
| @@ -34,6 +205,7 @@ static void IterateFrames(const GrowableObjectArray& code_list,
|
| }
|
| }
|
|
|
| +
|
| // Creates a StackTrace object from the current stack.
|
| //
|
| // Skips the first skip_frames Dart frames.
|
| @@ -42,7 +214,7 @@ const StackTrace& GetCurrentStackTrace(int skip_frames) {
|
| GrowableObjectArray::Handle(GrowableObjectArray::New());
|
| const GrowableObjectArray& pc_offset_list =
|
| GrowableObjectArray::Handle(GrowableObjectArray::New());
|
| - IterateFrames(code_list, pc_offset_list, skip_frames);
|
| + AppendFrames(code_list, pc_offset_list, skip_frames);
|
| const Array& code_array = Array::Handle(Array::MakeArray(code_list));
|
| const Array& pc_offset_array =
|
| Array::Handle(Array::MakeArray(pc_offset_list));
|
| @@ -51,6 +223,7 @@ const StackTrace& GetCurrentStackTrace(int skip_frames) {
|
| return stacktrace;
|
| }
|
|
|
| +
|
| // An utility method for convenient printing of dart stack traces when
|
| // inside 'gdb'. Note: This function will only work when there is a
|
| // valid exit frame information. It will not work when a breakpoint is
|
| @@ -61,19 +234,15 @@ void _printCurrentStackTrace() {
|
| OS::PrintErr("=== Current Trace:\n%s===\n", stacktrace.ToCString());
|
| }
|
|
|
| +
|
| // Like _printCurrentStackTrace, but works in a NoSafepointScope.
|
| void _printCurrentStackTraceNoSafepoint() {
|
| StackFrameIterator frames(StackFrameIterator::kDontValidateFrames);
|
| StackFrame* frame = frames.NextFrame();
|
| while (frame != NULL) {
|
| - OS::Print("%s\n", frame->ToCString());
|
| + OS::PrintErr("%s\n", frame->ToCString());
|
| frame = frames.NextFrame();
|
| }
|
| }
|
|
|
| -DEFINE_NATIVE_ENTRY(StackTrace_current, 0) {
|
| - const StackTrace& stacktrace = GetCurrentStackTrace(1);
|
| - return stacktrace.raw();
|
| -}
|
| -
|
| } // namespace dart
|
|
|