Chromium Code Reviews| Index: runtime/lib/stacktrace.cc |
| diff --git a/runtime/lib/stacktrace.cc b/runtime/lib/stacktrace.cc |
| index d7dcfe01f2e4e81ac1c21632bff21caef543b435..4bf5dda336cd7b7d2ef82d72bf925fab415da069 100644 |
| --- a/runtime/lib/stacktrace.cc |
| +++ b/runtime/lib/stacktrace.cc |
| @@ -4,16 +4,140 @@ |
| #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"); |
|
siva
2017/02/08 18:46:28
These flags don't seem to be used anymore, can the
Cutch
2017/02/09 22:45:50
Removed.
|
| + |
| +static RawStackTrace* CurrentSyncStackTrace(Thread* thread) { |
| + Zone* zone = thread->zone(); |
| + const Function& null_function = Function::ZoneHandle(zone); |
| + // Skip the Dart exit frame. |
| + 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); |
|
siva
2017/02/08 18:46:28
Pass thread into this function too?
Cutch
2017/02/09 22:45:50
Done.
|
| + |
| + // 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); |
| + } |
| + |
| + 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(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_entry()->code(); |
| + 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) { |
| + return CurrentStackTrace(thread, false); |
| +} |
| + |
| + |
| +DEFINE_NATIVE_ENTRY(StackTrace_asyncStackTraceHelper, 0) { |
| + return CurrentStackTrace(thread, true); |
| +} |
| + |
| + |
| +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 +158,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 +167,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 +176,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 +187,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 |