// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #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 { DECLARE_FLAG(bool, show_invisible_frames); static RawStackTrace* CurrentSyncStackTrace(Thread* thread, intptr_t skip_frames = 1) { Zone* zone = thread->zone(); const Function& null_function = Function::ZoneHandle(zone); // Determine how big the stack trace is. const intptr_t stack_trace_length = StackTraceUtils::CountFrames(thread, skip_frames, null_function); // 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, skip_frames); } 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(thread, 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().raw(); 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, 0) { return CurrentStackTrace(thread, false); } DEFINE_NATIVE_ENTRY(StackTrace_asyncStackTraceHelper, 0, 1) { if (!FLAG_causal_async_stacks) { return Object::null(); } #if !defined(PRODUCT) GET_NATIVE_ARGUMENT(Closure, async_op, arguments->NativeArgAt(0)); Debugger* debugger = isolate->debugger(); if (debugger != NULL) { debugger->MaybeAsyncStepInto(async_op); } #endif return CurrentStackTrace(thread, true); } DEFINE_NATIVE_ENTRY(StackTrace_clearAsyncThreadStackTrace, 0, 0) { thread->clear_async_stack_trace(); return Object::null(); } DEFINE_NATIVE_ENTRY(StackTrace_setAsyncThreadStackTrace, 0, 1) { if (!FLAG_causal_async_stacks) { return Object::null(); } 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) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread, StackFrameIterator::kNoCrossThreadIteration); StackFrame* frame = frames.NextFrame(); ASSERT(frame != NULL); // We expect to find a dart invocation frame. Code& code = Code::Handle(zone); Bytecode& bytecode = Bytecode::Handle(zone); Smi& offset = Smi::Handle(zone); for (; frame != NULL; frame = frames.NextFrame()) { if (!frame->IsDartFrame()) { continue; } if (skip_frames > 0) { skip_frames--; continue; } if (frame->is_interpreted()) { bytecode = frame->LookupDartBytecode(); if (bytecode.function() == Function::null()) { continue; } offset = Smi::New(frame->pc() - bytecode.PayloadStart()); code_list.Add(bytecode); } else { code = frame->LookupDartCode(); offset = Smi::New(frame->pc() - code.PayloadStart()); code_list.Add(code); } pc_offset_list.Add(offset); } } // Creates a StackTrace object from the current stack. // // Skips the first skip_frames Dart frames. const StackTrace& GetCurrentStackTrace(int skip_frames) { const GrowableObjectArray& code_list = GrowableObjectArray::Handle(GrowableObjectArray::New()); const GrowableObjectArray& pc_offset_list = GrowableObjectArray::Handle(GrowableObjectArray::New()); AppendFrames(code_list, pc_offset_list, skip_frames); const Array& code_array = Array::Handle(Array::MakeFixedLength(code_list)); const Array& pc_offset_array = Array::Handle(Array::MakeFixedLength(pc_offset_list)); const StackTrace& stacktrace = StackTrace::Handle(StackTrace::New(code_array, pc_offset_array)); return stacktrace; } } // namespace dart