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 |