Chromium Code Reviews| Index: runtime/lib/stacktrace.cc |
| diff --git a/runtime/lib/stacktrace.cc b/runtime/lib/stacktrace.cc |
| index d7dcfe01f2e4e81ac1c21632bff21caef543b435..a2be9c62b0dd96ed6b46cd6c2bc0edb906d72f29 100644 |
| --- a/runtime/lib/stacktrace.cc |
| +++ b/runtime/lib/stacktrace.cc |
| @@ -4,16 +4,170 @@ |
| #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) { |
|
rmacnak
2017/01/26 18:05:57
isolate parameter not used
siva
2017/01/26 19:26:23
Thread is available here, you could extract the zo
Cutch
2017/01/31 23:45:30
Done.
Cutch
2017/01/31 23:45:30
Done.
|
| + const Function& null_function = Function::Handle(); |
| + const intptr_t skip_frames = 1; |
|
siva
2017/01/26 19:26:22
maybe document which frame is skipped
Cutch
2017/01/31 23:45:30
Done.
|
| + |
| + // 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); |
|
siva
2017/01/26 19:26:23
Do you think there stack traces are going to be li
Cutch
2017/01/31 23:45:30
Acknowledged.
|
| +} |
| + |
| + |
| +static RawStackTrace* CurrentStackTrace( |
| + Thread* thread, |
| + Isolate* isolate, |
|
rmacnak
2017/01/26 18:05:57
isolate parameter not used
Cutch
2017/01/31 23:45:30
Done.
|
| + bool for_async_function, |
| + intptr_t skip_frames = 1, |
| + 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(); |
|
rmacnak
2017/01/26 18:05:57
Handle(zone)
Cutch
2017/01/31 23:45:30
Done (here and elsewhere).
|
| + 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 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)); |
|
rmacnak
2017/01/26 18:05:57
code ^= async_code_array.At(i)
Cutch
2017/01/31 23:45:30
Done.
|
| + offset = Smi::RawCast(async_pc_offset_array.At(i)); |
|
rmacnak
2017/01/26 18:05:57
offset ^=
Cutch
2017/01/31 23:45:30
Done.
|
| + code_array.SetAt(write_cursor, code); |
| + pc_offset_array.SetAt(write_cursor, offset); |
| + write_cursor++; |
| + } |
| + } |
| + |
| + return StackTrace::New(code_array, pc_offset_array); |
| +} |
| + |
| + |
| +RawStackTrace* GetStackTraceForException() { |
| + Thread* thread = Thread::Current(); |
| + Isolate* isolate = thread->isolate(); |
| + return CurrentStackTrace(thread, isolate, false, 0); |
| +} |
| + |
| + |
| +DEFINE_NATIVE_ENTRY(StackTrace_current, 0) { |
| + return CurrentStackTrace(thread, isolate, false); |
| +} |
| + |
| + |
| +DEFINE_NATIVE_ENTRY(StackTrace_forAsyncMethod, 0) { |
| + return CurrentStackTrace(thread, isolate, 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 +188,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 +197,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 +206,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 +217,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 |