Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(694)

Unified Diff: runtime/lib/stacktrace.cc

Issue 2603383004: Sane asynchronous debugging and stack traces (Closed)
Patch Set: rebase Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/lib/async_patch.dart ('k') | runtime/observatory/lib/src/elements/debugger.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « runtime/lib/async_patch.dart ('k') | runtime/observatory/lib/src/elements/debugger.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698