// Copyright (c) 2012, 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 "vm/stack_frame.h" #include "vm/assembler.h" #include "vm/deopt_instructions.h" #include "vm/isolate.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/os.h" #include "vm/parser.h" #include "vm/raw_object.h" #include "vm/stub_code.h" #include "vm/visitor.h" namespace dart { bool StackFrame::IsStubFrame() const { ASSERT(!(IsEntryFrame() || IsExitFrame())); uword saved_pc = *(reinterpret_cast(fp() + EntrypointMarkerOffsetFromFp())); return (saved_pc == 0); } void StackFrame::Print() const { OS::Print("[%-8s : sp(%#"Px") ]\n", GetName(), sp()); } void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { // There are no objects to visit in this frame. } RawContext* EntryFrame::SavedContext() const { return *(reinterpret_cast(fp() + SavedContextOffset())); } void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { // Visit objects between SP and (FP - callee_save_area). ASSERT(visitor != NULL); RawObject** start = reinterpret_cast(sp()); RawObject** end = reinterpret_cast( fp() - kWordSize + ExitLinkOffset()); visitor->VisitPointers(start, end); } void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { // NOTE: This code runs while GC is in progress and runs within // a NoHandleScope block. Hence it is not ok to use regular Zone or // Scope handles. We use direct stack handles, the raw pointers in // these handles are not traversed. The use of handles is mainly to // be able to reuse the handle based code and avoid having to add // helper functions to the raw object interface. ASSERT(visitor != NULL); NoGCScope no_gc; RawObject** start_addr = reinterpret_cast(sp()); RawObject** end_addr = reinterpret_cast(fp()) + kFirstLocalSlotIndex; Code code; code = LookupDartCode(); if (!code.IsNull()) { // Visit the code object. RawObject* raw_code = code.raw(); visitor->VisitPointer(&raw_code); // Visit stack based on stack maps. Array maps; maps = Array::null(); Stackmap map; map = code.GetStackmap(pc(), &maps, &map); if (!map.IsNull()) { // A stack map is present in the code object, use the stack map to // visit frame slots which are marked as having objects. // // The layout of the frame is (lower addresses to the right): // | spill slots | outgoing arguments | saved registers | // |XXXXXXXXXXXXX|--------------------|XXXXXXXXXXXXXXXXX| // // The splill slots and any saved registers are described in the stack // map. The outgoing arguments are assumed to be tagged; the number // of outgoing arguments is not explicitly tracked. // // TODO(kmillikin): This does not handle slow path calls with // arguments, where the arguments are pushed after the live registers. // Enable such calls. intptr_t length = map.Length(); // Spill slots are at the 'bottom' of the frame. intptr_t spill_slot_count = length - map.RegisterBitCount(); for (intptr_t bit = 0; bit < spill_slot_count; ++bit) { if (map.IsObject(bit)) visitor->VisitPointer(end_addr); --end_addr; } // The live registers at the 'top' of the frame comprise the rest of the // stack map. for (intptr_t bit = length - 1; bit >= spill_slot_count; --bit) { if (map.IsObject(bit)) visitor->VisitPointer(start_addr); ++start_addr; } // The end address can be one slot (but not more) past the start // address in the case that all slots were covered by the stack map. ASSERT((end_addr + 1) >= start_addr); } } // Each slot between the start and end address are tagged objects. visitor->VisitPointers(start_addr, end_addr); } RawFunction* StackFrame::LookupDartFunction() const { const Code& code = Code::Handle(LookupDartCode()); if (!code.IsNull()) { return code.function(); } return Function::null(); } RawCode* StackFrame::LookupDartCode() const { // We add a no gc scope to ensure that the code below does not trigger // a GC as we are handling raw object references here. It is possible // that the code is called while a GC is in progress, that is ok. NoGCScope no_gc; RawCode* code = GetCodeObject(); ASSERT(code == Code::null() || code->ptr()->function_ != Function::null()); return code; } RawCode* StackFrame::GetCodeObject() const { // We add a no gc scope to ensure that the code below does not trigger // a GC as we are handling raw object references here. It is possible // that the code is called while a GC is in progress, that is ok. NoGCScope no_gc; uword saved_pc = *(reinterpret_cast(fp() + EntrypointMarkerOffsetFromFp())); if (saved_pc != 0) { uword entry_point = (saved_pc - Assembler::kOffsetOfSavedPCfromEntrypoint); RawInstructions* instr = Instructions::FromEntryPoint(entry_point); if (instr != Instructions::null()) { return instr->ptr()->code_; } } return Code::null(); } bool StackFrame::FindExceptionHandler(uword* handler_pc) const { const Code& code = Code::Handle(LookupDartCode()); if (code.IsNull()) { return false; // Stub frames do not have exception handlers. } // Find pc descriptor for the current pc. const PcDescriptors& descriptors = PcDescriptors::Handle(code.pc_descriptors()); for (intptr_t i = 0; i < descriptors.Length(); i++) { if ((static_cast(descriptors.PC(i)) == pc()) && (descriptors.TryIndex(i) != -1)) { const intptr_t try_index = descriptors.TryIndex(i); const ExceptionHandlers& handlers = ExceptionHandlers::Handle(code.exception_handlers()); *handler_pc = handlers.HandlerPC(try_index); return true; } } return false; } intptr_t StackFrame::GetTokenPos() const { const Code& code = Code::Handle(LookupDartCode()); if (code.IsNull()) { return -1; // Stub frames do not have token_pos. } const PcDescriptors& descriptors = PcDescriptors::Handle(code.pc_descriptors()); ASSERT(!descriptors.IsNull()); for (int i = 0; i < descriptors.Length(); i++) { if (static_cast(descriptors.PC(i)) == pc()) { return descriptors.TokenPos(i); } } return -1; } bool StackFrame::IsValid() const { if (IsEntryFrame() || IsExitFrame() || IsStubFrame()) { return true; } return (LookupDartCode() != Code::null()); } StackFrameIterator::StackFrameIterator(bool validate) : validate_(validate), entry_(), exit_(), current_frame_(NULL) { SetupLastExitFrameData(); // Setup data for last exit frame. } StackFrameIterator::StackFrameIterator(uword last_fp, bool validate) : validate_(validate), entry_(), exit_(), current_frame_(NULL) { frames_.fp_ = last_fp; } StackFrame* StackFrameIterator::NextFrame() { // When we are at the start of iteration after having created an // iterator object current_frame_ will be NULL as we haven't seen // any frames yet. At this point if NextFrame is called it tries // to set up the next exit frame by reading the top_exit_frame_info // from the isolate. If we do not have any dart invocations yet // top_exit_frame_info will be 0 and so we would return NULL. // current_frame_ will also be NULL, when we are at the end of having // iterated through all the frames. if NextFrame is called at this // point we will try and set up the next exit frame but since we are // at the end of the iteration fp_ will be 0 and we would return NULL. if (current_frame_ == NULL) { if (!HasNextFrame()) { return NULL; } current_frame_ = NextExitFrame(); return current_frame_; } ASSERT((validate_ == kDontValidateFrames) || current_frame_->IsValid()); if (current_frame_->IsEntryFrame()) { if (HasNextFrame()) { // We have another chained block. current_frame_ = NextExitFrame(); return current_frame_; } current_frame_ = NULL; // No more frames. return current_frame_; } ASSERT(current_frame_->IsExitFrame() || current_frame_->IsDartFrame() || current_frame_->IsStubFrame()); // Consume dart/stub frames using StackFrameIterator::FrameSetIterator // until we are out of dart/stub frames at which point we return the // corresponding entry frame for that set of dart/stub frames. current_frame_ = (frames_.HasNext()) ? frames_.NextFrame(validate_) : NextEntryFrame(); return current_frame_; } StackFrame* StackFrameIterator::FrameSetIterator::NextFrame(bool validate) { StackFrame* frame; ASSERT(HasNext()); frame = &stack_frame_; frame->sp_ = sp_; frame->fp_ = fp_; sp_ = frame->GetCallerSp(); fp_ = frame->GetCallerFp(); ASSERT((validate == kDontValidateFrames) || frame->IsValid()); return frame; } ExitFrame* StackFrameIterator::NextExitFrame() { exit_.sp_ = frames_.sp_; exit_.fp_ = frames_.fp_; frames_.sp_ = exit_.GetCallerSp(); frames_.fp_ = exit_.GetCallerFp(); ASSERT(exit_.IsValid()); return &exit_; } EntryFrame* StackFrameIterator::NextEntryFrame() { ASSERT(!frames_.HasNext()); entry_.sp_ = frames_.sp_; entry_.fp_ = frames_.fp_; SetupNextExitFrameData(); // Setup data for next exit frame in chain. ASSERT(entry_.IsValid()); return &entry_; } InlinedFunctionsIterator::InlinedFunctionsIterator(StackFrame* frame) : index_(0), code_(Code::Handle()), deopt_info_(DeoptInfo::Handle()), function_(Function::Handle()), pc_(0), deopt_instructions_(), object_table_(Array::Handle()) { ASSERT(frame != NULL); code_ = frame->LookupDartCode(); ASSERT(code_.is_optimized()); intptr_t deopt_reason = kDeoptUnknown; deopt_info_ = code_.GetDeoptInfoAtPc(frame->pc(), &deopt_reason); if (deopt_info_.IsNull()) { // This is the case when a call without deopt info in optimzed code // throws an exception. (e.g. in the parameter copying prologue). // In that case there won't be any inlined frames. function_ = code_.function(); pc_ = frame->pc(); ASSERT(pc_ != 0); } else { // Unpack deopt info into instructions (translate away suffixes). const Array& deopt_table = Array::Handle(code_.deopt_info_array()); ASSERT(!deopt_table.IsNull()); deopt_info_.ToInstructions(deopt_table, &deopt_instructions_); object_table_ = code_.object_table(); Advance(); } } void InlinedFunctionsIterator::Advance() { // Iterate over the deopt instructions and determine the inlined // functions if any and iterate over them. ASSERT(!Done()); if (deopt_info_.IsNull()) { SetDone(); return; } Function& func = Function::Handle(); ASSERT(deopt_instructions_.length() != 0); while (index_ < deopt_instructions_.length()) { DeoptInstr* deopt_instr = deopt_instructions_[index_++]; if (deopt_instr->kind() == DeoptInstr::kRetAddress) { pc_ = DeoptInstr::GetRetAddress(deopt_instr, object_table_, &func); code_ = func.unoptimized_code(); function_ = func.raw(); return; } } SetDone(); } } // namespace dart