diff options
Diffstat (limited to 'src/3rdparty/v8/src/frames.cc')
-rw-r--r-- | src/3rdparty/v8/src/frames.cc | 1273 |
1 files changed, 1273 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/frames.cc b/src/3rdparty/v8/src/frames.cc new file mode 100644 index 0000000..1672b1d --- /dev/null +++ b/src/3rdparty/v8/src/frames.cc @@ -0,0 +1,1273 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "ast.h" +#include "deoptimizer.h" +#include "frames-inl.h" +#include "full-codegen.h" +#include "mark-compact.h" +#include "safepoint-table.h" +#include "scopeinfo.h" +#include "string-stream.h" + +namespace v8 { +namespace internal { + +// Iterator that supports traversing the stack handlers of a +// particular frame. Needs to know the top of the handler chain. +class StackHandlerIterator BASE_EMBEDDED { + public: + StackHandlerIterator(const StackFrame* frame, StackHandler* handler) + : limit_(frame->fp()), handler_(handler) { + // Make sure the handler has already been unwound to this frame. + ASSERT(frame->sp() <= handler->address()); + } + + StackHandler* handler() const { return handler_; } + + bool done() { + return handler_ == NULL || handler_->address() > limit_; + } + void Advance() { + ASSERT(!done()); + handler_ = handler_->next(); + } + + private: + const Address limit_; + StackHandler* handler_; +}; + + +// ------------------------------------------------------------------------- + + +#define INITIALIZE_SINGLETON(type, field) field##_(this), +StackFrameIterator::StackFrameIterator() + : isolate_(Isolate::Current()), + STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON) + frame_(NULL), handler_(NULL), + thread_(isolate_->thread_local_top()), + fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) { + Reset(); +} +StackFrameIterator::StackFrameIterator(Isolate* isolate) + : isolate_(isolate), + STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON) + frame_(NULL), handler_(NULL), + thread_(isolate_->thread_local_top()), + fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) { + Reset(); +} +StackFrameIterator::StackFrameIterator(Isolate* isolate, ThreadLocalTop* t) + : isolate_(isolate), + STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON) + frame_(NULL), handler_(NULL), thread_(t), + fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) { + Reset(); +} +StackFrameIterator::StackFrameIterator(Isolate* isolate, + bool use_top, Address fp, Address sp) + : isolate_(isolate), + STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON) + frame_(NULL), handler_(NULL), + thread_(use_top ? isolate_->thread_local_top() : NULL), + fp_(use_top ? NULL : fp), sp_(sp), + advance_(use_top ? &StackFrameIterator::AdvanceWithHandler : + &StackFrameIterator::AdvanceWithoutHandler) { + if (use_top || fp != NULL) { + Reset(); + } +} + +#undef INITIALIZE_SINGLETON + + +void StackFrameIterator::AdvanceWithHandler() { + ASSERT(!done()); + // Compute the state of the calling frame before restoring + // callee-saved registers and unwinding handlers. This allows the + // frame code that computes the caller state to access the top + // handler and the value of any callee-saved register if needed. + StackFrame::State state; + StackFrame::Type type = frame_->GetCallerState(&state); + + // Unwind handlers corresponding to the current frame. + StackHandlerIterator it(frame_, handler_); + while (!it.done()) it.Advance(); + handler_ = it.handler(); + + // Advance to the calling frame. + frame_ = SingletonFor(type, &state); + + // When we're done iterating over the stack frames, the handler + // chain must have been completely unwound. + ASSERT(!done() || handler_ == NULL); +} + + +void StackFrameIterator::AdvanceWithoutHandler() { + // A simpler version of Advance which doesn't care about handler. + ASSERT(!done()); + StackFrame::State state; + StackFrame::Type type = frame_->GetCallerState(&state); + frame_ = SingletonFor(type, &state); +} + + +void StackFrameIterator::Reset() { + StackFrame::State state; + StackFrame::Type type; + if (thread_ != NULL) { + type = ExitFrame::GetStateForFramePointer( + Isolate::c_entry_fp(thread_), &state); + handler_ = StackHandler::FromAddress( + Isolate::handler(thread_)); + } else { + ASSERT(fp_ != NULL); + state.fp = fp_; + state.sp = sp_; + state.pc_address = + reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp_)); + type = StackFrame::ComputeType(isolate(), &state); + } + if (SingletonFor(type) == NULL) return; + frame_ = SingletonFor(type, &state); +} + + +StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type, + StackFrame::State* state) { + if (type == StackFrame::NONE) return NULL; + StackFrame* result = SingletonFor(type); + ASSERT(result != NULL); + result->state_ = *state; + return result; +} + + +StackFrame* StackFrameIterator::SingletonFor(StackFrame::Type type) { +#define FRAME_TYPE_CASE(type, field) \ + case StackFrame::type: result = &field##_; break; + + StackFrame* result = NULL; + switch (type) { + case StackFrame::NONE: return NULL; + STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE) + default: break; + } + return result; + +#undef FRAME_TYPE_CASE +} + + +// ------------------------------------------------------------------------- + + +StackTraceFrameIterator::StackTraceFrameIterator() { + if (!done() && !IsValidFrame()) Advance(); +} + + +StackTraceFrameIterator::StackTraceFrameIterator(Isolate* isolate) + : JavaScriptFrameIterator(isolate) { + if (!done() && !IsValidFrame()) Advance(); +} + + +void StackTraceFrameIterator::Advance() { + while (true) { + JavaScriptFrameIterator::Advance(); + if (done()) return; + if (IsValidFrame()) return; + } +} + +bool StackTraceFrameIterator::IsValidFrame() { + if (!frame()->function()->IsJSFunction()) return false; + Object* script = JSFunction::cast(frame()->function())->shared()->script(); + // Don't show functions from native scripts to user. + return (script->IsScript() && + Script::TYPE_NATIVE != Script::cast(script)->type()->value()); +} + + +// ------------------------------------------------------------------------- + + +bool SafeStackFrameIterator::ExitFrameValidator::IsValidFP(Address fp) { + if (!validator_.IsValid(fp)) return false; + Address sp = ExitFrame::ComputeStackPointer(fp); + if (!validator_.IsValid(sp)) return false; + StackFrame::State state; + ExitFrame::FillState(fp, sp, &state); + if (!validator_.IsValid(reinterpret_cast<Address>(state.pc_address))) { + return false; + } + return *state.pc_address != NULL; +} + + +SafeStackFrameIterator::ActiveCountMaintainer::ActiveCountMaintainer( + Isolate* isolate) + : isolate_(isolate) { + isolate_->set_safe_stack_iterator_counter( + isolate_->safe_stack_iterator_counter() + 1); +} + + +SafeStackFrameIterator::ActiveCountMaintainer::~ActiveCountMaintainer() { + isolate_->set_safe_stack_iterator_counter( + isolate_->safe_stack_iterator_counter() - 1); +} + + +SafeStackFrameIterator::SafeStackFrameIterator( + Isolate* isolate, + Address fp, Address sp, Address low_bound, Address high_bound) : + maintainer_(isolate), + stack_validator_(low_bound, high_bound), + is_valid_top_(IsValidTop(isolate, low_bound, high_bound)), + is_valid_fp_(IsWithinBounds(low_bound, high_bound, fp)), + is_working_iterator_(is_valid_top_ || is_valid_fp_), + iteration_done_(!is_working_iterator_), + iterator_(isolate, is_valid_top_, is_valid_fp_ ? fp : NULL, sp) { +} + +bool SafeStackFrameIterator::is_active(Isolate* isolate) { + return isolate->safe_stack_iterator_counter() > 0; +} + + +bool SafeStackFrameIterator::IsValidTop(Isolate* isolate, + Address low_bound, Address high_bound) { + ThreadLocalTop* top = isolate->thread_local_top(); + Address fp = Isolate::c_entry_fp(top); + ExitFrameValidator validator(low_bound, high_bound); + if (!validator.IsValidFP(fp)) return false; + return Isolate::handler(top) != NULL; +} + + +void SafeStackFrameIterator::Advance() { + ASSERT(is_working_iterator_); + ASSERT(!done()); + StackFrame* last_frame = iterator_.frame(); + Address last_sp = last_frame->sp(), last_fp = last_frame->fp(); + // Before advancing to the next stack frame, perform pointer validity tests + iteration_done_ = !IsValidFrame(last_frame) || + !CanIterateHandles(last_frame, iterator_.handler()) || + !IsValidCaller(last_frame); + if (iteration_done_) return; + + iterator_.Advance(); + if (iterator_.done()) return; + // Check that we have actually moved to the previous frame in the stack + StackFrame* prev_frame = iterator_.frame(); + iteration_done_ = prev_frame->sp() < last_sp || prev_frame->fp() < last_fp; +} + + +bool SafeStackFrameIterator::CanIterateHandles(StackFrame* frame, + StackHandler* handler) { + // If StackIterator iterates over StackHandles, verify that + // StackHandlerIterator can be instantiated (see StackHandlerIterator + // constructor.) + return !is_valid_top_ || (frame->sp() <= handler->address()); +} + + +bool SafeStackFrameIterator::IsValidFrame(StackFrame* frame) const { + return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp()); +} + + +bool SafeStackFrameIterator::IsValidCaller(StackFrame* frame) { + StackFrame::State state; + if (frame->is_entry() || frame->is_entry_construct()) { + // See EntryFrame::GetCallerState. It computes the caller FP address + // and calls ExitFrame::GetStateForFramePointer on it. We need to be + // sure that caller FP address is valid. + Address caller_fp = Memory::Address_at( + frame->fp() + EntryFrameConstants::kCallerFPOffset); + ExitFrameValidator validator(stack_validator_); + if (!validator.IsValidFP(caller_fp)) return false; + } else if (frame->is_arguments_adaptor()) { + // See ArgumentsAdaptorFrame::GetCallerStackPointer. It assumes that + // the number of arguments is stored on stack as Smi. We need to check + // that it really an Smi. + Object* number_of_args = reinterpret_cast<ArgumentsAdaptorFrame*>(frame)-> + GetExpression(0); + if (!number_of_args->IsSmi()) { + return false; + } + } + frame->ComputeCallerState(&state); + return IsValidStackAddress(state.sp) && IsValidStackAddress(state.fp) && + iterator_.SingletonFor(frame->GetCallerState(&state)) != NULL; +} + + +void SafeStackFrameIterator::Reset() { + if (is_working_iterator_) { + iterator_.Reset(); + iteration_done_ = false; + } +} + + +// ------------------------------------------------------------------------- + + +#ifdef ENABLE_LOGGING_AND_PROFILING +SafeStackTraceFrameIterator::SafeStackTraceFrameIterator( + Isolate* isolate, + Address fp, Address sp, Address low_bound, Address high_bound) : + SafeJavaScriptFrameIterator(isolate, fp, sp, low_bound, high_bound) { + if (!done() && !frame()->is_java_script()) Advance(); +} + + +void SafeStackTraceFrameIterator::Advance() { + while (true) { + SafeJavaScriptFrameIterator::Advance(); + if (done()) return; + if (frame()->is_java_script()) return; + } +} +#endif + + +Code* StackFrame::GetSafepointData(Isolate* isolate, + Address pc, + SafepointEntry* safepoint_entry, + unsigned* stack_slots) { + PcToCodeCache::PcToCodeCacheEntry* entry = + isolate->pc_to_code_cache()->GetCacheEntry(pc); + SafepointEntry cached_safepoint_entry = entry->safepoint_entry; + if (!entry->safepoint_entry.is_valid()) { + entry->safepoint_entry = entry->code->GetSafepointEntry(pc); + ASSERT(entry->safepoint_entry.is_valid()); + } else { + ASSERT(entry->safepoint_entry.Equals(entry->code->GetSafepointEntry(pc))); + } + + // Fill in the results and return the code. + Code* code = entry->code; + *safepoint_entry = entry->safepoint_entry; + *stack_slots = code->stack_slots(); + return code; +} + + +bool StackFrame::HasHandler() const { + StackHandlerIterator it(this, top_handler()); + return !it.done(); +} + + +void StackFrame::IteratePc(ObjectVisitor* v, + Address* pc_address, + Code* holder) { + Address pc = *pc_address; + ASSERT(holder->contains(pc)); + unsigned pc_offset = static_cast<unsigned>(pc - holder->instruction_start()); + Object* code = holder; + v->VisitPointer(&code); + if (code != holder) { + holder = reinterpret_cast<Code*>(code); + pc = holder->instruction_start() + pc_offset; + *pc_address = pc; + } +} + + +StackFrame::Type StackFrame::ComputeType(Isolate* isolate, State* state) { + ASSERT(state->fp != NULL); + if (StandardFrame::IsArgumentsAdaptorFrame(state->fp)) { + return ARGUMENTS_ADAPTOR; + } + // The marker and function offsets overlap. If the marker isn't a + // smi then the frame is a JavaScript frame -- and the marker is + // really the function. + const int offset = StandardFrameConstants::kMarkerOffset; + Object* marker = Memory::Object_at(state->fp + offset); + if (!marker->IsSmi()) { + // If we're using a "safe" stack iterator, we treat optimized + // frames as normal JavaScript frames to avoid having to look + // into the heap to determine the state. This is safe as long + // as nobody tries to GC... + if (SafeStackFrameIterator::is_active(isolate)) return JAVA_SCRIPT; + Code::Kind kind = GetContainingCode(isolate, *(state->pc_address))->kind(); + ASSERT(kind == Code::FUNCTION || kind == Code::OPTIMIZED_FUNCTION); + return (kind == Code::OPTIMIZED_FUNCTION) ? OPTIMIZED : JAVA_SCRIPT; + } + return static_cast<StackFrame::Type>(Smi::cast(marker)->value()); +} + + + +StackFrame::Type StackFrame::GetCallerState(State* state) const { + ComputeCallerState(state); + return ComputeType(isolate(), state); +} + + +Code* EntryFrame::unchecked_code() const { + return HEAP->raw_unchecked_js_entry_code(); +} + + +void EntryFrame::ComputeCallerState(State* state) const { + GetCallerState(state); +} + + +void EntryFrame::SetCallerFp(Address caller_fp) { + const int offset = EntryFrameConstants::kCallerFPOffset; + Memory::Address_at(this->fp() + offset) = caller_fp; +} + + +StackFrame::Type EntryFrame::GetCallerState(State* state) const { + const int offset = EntryFrameConstants::kCallerFPOffset; + Address fp = Memory::Address_at(this->fp() + offset); + return ExitFrame::GetStateForFramePointer(fp, state); +} + + +Code* EntryConstructFrame::unchecked_code() const { + return HEAP->raw_unchecked_js_construct_entry_code(); +} + + +Object*& ExitFrame::code_slot() const { + const int offset = ExitFrameConstants::kCodeOffset; + return Memory::Object_at(fp() + offset); +} + + +Code* ExitFrame::unchecked_code() const { + return reinterpret_cast<Code*>(code_slot()); +} + + +void ExitFrame::ComputeCallerState(State* state) const { + // Setup the caller state. + state->sp = caller_sp(); + state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset); + state->pc_address + = reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset); +} + + +void ExitFrame::SetCallerFp(Address caller_fp) { + Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset) = caller_fp; +} + + +void ExitFrame::Iterate(ObjectVisitor* v) const { + // The arguments are traversed as part of the expression stack of + // the calling frame. + IteratePc(v, pc_address(), LookupCode()); + v->VisitPointer(&code_slot()); +} + + +Address ExitFrame::GetCallerStackPointer() const { + return fp() + ExitFrameConstants::kCallerSPDisplacement; +} + + +StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) { + if (fp == 0) return NONE; + Address sp = ComputeStackPointer(fp); + FillState(fp, sp, state); + ASSERT(*state->pc_address != NULL); + return EXIT; +} + + +void ExitFrame::FillState(Address fp, Address sp, State* state) { + state->sp = sp; + state->fp = fp; + state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize); +} + + +Address StandardFrame::GetExpressionAddress(int n) const { + const int offset = StandardFrameConstants::kExpressionsOffset; + return fp() + offset - n * kPointerSize; +} + + +int StandardFrame::ComputeExpressionsCount() const { + const int offset = + StandardFrameConstants::kExpressionsOffset + kPointerSize; + Address base = fp() + offset; + Address limit = sp(); + ASSERT(base >= limit); // stack grows downwards + // Include register-allocated locals in number of expressions. + return static_cast<int>((base - limit) / kPointerSize); +} + + +void StandardFrame::ComputeCallerState(State* state) const { + state->sp = caller_sp(); + state->fp = caller_fp(); + state->pc_address = reinterpret_cast<Address*>(ComputePCAddress(fp())); +} + + +void StandardFrame::SetCallerFp(Address caller_fp) { + Memory::Address_at(fp() + StandardFrameConstants::kCallerFPOffset) = + caller_fp; +} + + +bool StandardFrame::IsExpressionInsideHandler(int n) const { + Address address = GetExpressionAddress(n); + for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) { + if (it.handler()->includes(address)) return true; + } + return false; +} + + +void OptimizedFrame::Iterate(ObjectVisitor* v) const { +#ifdef DEBUG + // Make sure that optimized frames do not contain any stack handlers. + StackHandlerIterator it(this, top_handler()); + ASSERT(it.done()); +#endif + + // Make sure that we're not doing "safe" stack frame iteration. We cannot + // possibly find pointers in optimized frames in that state. + ASSERT(!SafeStackFrameIterator::is_active(isolate())); + + // Compute the safepoint information. + unsigned stack_slots = 0; + SafepointEntry safepoint_entry; + Code* code = StackFrame::GetSafepointData( + isolate(), pc(), &safepoint_entry, &stack_slots); + unsigned slot_space = stack_slots * kPointerSize; + + // Visit the outgoing parameters. This is usually dealt with by the + // callee, but while GC'ing we artificially lower the number of + // arguments to zero and let the caller deal with it. + Object** parameters_base = &Memory::Object_at(sp()); + Object** parameters_limit = &Memory::Object_at( + fp() + JavaScriptFrameConstants::kFunctionOffset - slot_space); + + // Visit the parameters that may be on top of the saved registers. + if (safepoint_entry.argument_count() > 0) { + v->VisitPointers(parameters_base, + parameters_base + safepoint_entry.argument_count()); + parameters_base += safepoint_entry.argument_count(); + } + + // Skip saved double registers. + if (safepoint_entry.has_doubles()) { + parameters_base += DoubleRegister::kNumAllocatableRegisters * + kDoubleSize / kPointerSize; + } + + // Visit the registers that contain pointers if any. + if (safepoint_entry.HasRegisters()) { + for (int i = kNumSafepointRegisters - 1; i >=0; i--) { + if (safepoint_entry.HasRegisterAt(i)) { + int reg_stack_index = MacroAssembler::SafepointRegisterStackIndex(i); + v->VisitPointer(parameters_base + reg_stack_index); + } + } + // Skip the words containing the register values. + parameters_base += kNumSafepointRegisters; + } + + // We're done dealing with the register bits. + uint8_t* safepoint_bits = safepoint_entry.bits(); + safepoint_bits += kNumSafepointRegisters >> kBitsPerByteLog2; + + // Visit the rest of the parameters. + v->VisitPointers(parameters_base, parameters_limit); + + // Visit pointer spill slots and locals. + for (unsigned index = 0; index < stack_slots; index++) { + int byte_index = index >> kBitsPerByteLog2; + int bit_index = index & (kBitsPerByte - 1); + if ((safepoint_bits[byte_index] & (1U << bit_index)) != 0) { + v->VisitPointer(parameters_limit + index); + } + } + + // Visit the context and the function. + Object** fixed_base = &Memory::Object_at( + fp() + JavaScriptFrameConstants::kFunctionOffset); + Object** fixed_limit = &Memory::Object_at(fp()); + v->VisitPointers(fixed_base, fixed_limit); + + // Visit the return address in the callee and incoming arguments. + IteratePc(v, pc_address(), code); + IterateArguments(v); +} + + +Object* JavaScriptFrame::GetParameter(int index) const { + ASSERT(index >= 0 && index < ComputeParametersCount()); + const int offset = JavaScriptFrameConstants::kParam0Offset; + return Memory::Object_at(caller_sp() + offset - (index * kPointerSize)); +} + + +int JavaScriptFrame::ComputeParametersCount() const { + Address base = caller_sp() + JavaScriptFrameConstants::kReceiverOffset; + Address limit = fp() + JavaScriptFrameConstants::kLastParameterOffset; + return static_cast<int>((base - limit) / kPointerSize); +} + + +bool JavaScriptFrame::IsConstructor() const { + Address fp = caller_fp(); + if (has_adapted_arguments()) { + // Skip the arguments adaptor frame and look at the real caller. + fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset); + } + return IsConstructFrame(fp); +} + + +Code* JavaScriptFrame::unchecked_code() const { + JSFunction* function = JSFunction::cast(this->function()); + return function->unchecked_code(); +} + + +Address JavaScriptFrame::GetCallerStackPointer() const { + int arguments; + if (SafeStackFrameIterator::is_active(isolate()) || + isolate()->heap()->gc_state() != Heap::NOT_IN_GC) { + // If the we are currently iterating the safe stack the + // arguments for frames are traversed as if they were + // expression stack elements of the calling frame. The reason for + // this rather strange decision is that we cannot access the + // function during mark-compact GCs when objects may have been marked. + // In fact accessing heap objects (like function->shared() below) + // at all during GC is problematic. + arguments = 0; + } else { + // Compute the number of arguments by getting the number of formal + // parameters of the function. We must remember to take the + // receiver into account (+1). + JSFunction* function = JSFunction::cast(this->function()); + arguments = function->shared()->formal_parameter_count() + 1; + } + const int offset = StandardFrameConstants::kCallerSPOffset; + return fp() + offset + (arguments * kPointerSize); +} + + +void JavaScriptFrame::GetFunctions(List<JSFunction*>* functions) { + ASSERT(functions->length() == 0); + functions->Add(JSFunction::cast(function())); +} + + +void JavaScriptFrame::Summarize(List<FrameSummary>* functions) { + ASSERT(functions->length() == 0); + Code* code_pointer = LookupCode(); + int offset = static_cast<int>(pc() - code_pointer->address()); + FrameSummary summary(receiver(), + JSFunction::cast(function()), + code_pointer, + offset, + IsConstructor()); + functions->Add(summary); +} + + +void FrameSummary::Print() { + PrintF("receiver: "); + receiver_->ShortPrint(); + PrintF("\nfunction: "); + function_->shared()->DebugName()->ShortPrint(); + PrintF("\ncode: "); + code_->ShortPrint(); + if (code_->kind() == Code::FUNCTION) PrintF(" NON-OPT"); + if (code_->kind() == Code::OPTIMIZED_FUNCTION) PrintF(" OPT"); + PrintF("\npc: %d\n", offset_); +} + + +void OptimizedFrame::Summarize(List<FrameSummary>* frames) { + ASSERT(frames->length() == 0); + ASSERT(is_optimized()); + + int deopt_index = Safepoint::kNoDeoptimizationIndex; + DeoptimizationInputData* data = GetDeoptimizationData(&deopt_index); + + // BUG(3243555): Since we don't have a lazy-deopt registered at + // throw-statements, we can't use the translation at the call-site of + // throw. An entry with no deoptimization index indicates a call-site + // without a lazy-deopt. As a consequence we are not allowed to inline + // functions containing throw. + if (deopt_index == Safepoint::kNoDeoptimizationIndex) { + JavaScriptFrame::Summarize(frames); + return; + } + + TranslationIterator it(data->TranslationByteArray(), + data->TranslationIndex(deopt_index)->value()); + Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); + ASSERT(opcode == Translation::BEGIN); + int frame_count = it.Next(); + + // We create the summary in reverse order because the frames + // in the deoptimization translation are ordered bottom-to-top. + int i = frame_count; + while (i > 0) { + opcode = static_cast<Translation::Opcode>(it.Next()); + if (opcode == Translation::FRAME) { + // We don't inline constructor calls, so only the first, outermost + // frame can be a constructor frame in case of inlining. + bool is_constructor = (i == frame_count) && IsConstructor(); + + i--; + int ast_id = it.Next(); + int function_id = it.Next(); + it.Next(); // Skip height. + JSFunction* function = + JSFunction::cast(data->LiteralArray()->get(function_id)); + + // The translation commands are ordered and the receiver is always + // at the first position. Since we are always at a call when we need + // to construct a stack trace, the receiver is always in a stack slot. + opcode = static_cast<Translation::Opcode>(it.Next()); + ASSERT(opcode == Translation::STACK_SLOT); + int input_slot_index = it.Next(); + + // Get the correct receiver in the optimized frame. + Object* receiver = NULL; + // Positive index means the value is spilled to the locals area. Negative + // means it is stored in the incoming parameter area. + if (input_slot_index >= 0) { + receiver = GetExpression(input_slot_index); + } else { + // Index -1 overlaps with last parameter, -n with the first parameter, + // (-n - 1) with the receiver with n being the number of parameters + // of the outermost, optimized frame. + int parameter_count = ComputeParametersCount(); + int parameter_index = input_slot_index + parameter_count; + receiver = (parameter_index == -1) + ? this->receiver() + : this->GetParameter(parameter_index); + } + + Code* code = function->shared()->code(); + DeoptimizationOutputData* output_data = + DeoptimizationOutputData::cast(code->deoptimization_data()); + unsigned entry = Deoptimizer::GetOutputInfo(output_data, + ast_id, + function->shared()); + unsigned pc_offset = + FullCodeGenerator::PcField::decode(entry) + Code::kHeaderSize; + ASSERT(pc_offset > 0); + + FrameSummary summary(receiver, function, code, pc_offset, is_constructor); + frames->Add(summary); + } else { + // Skip over operands to advance to the next opcode. + it.Skip(Translation::NumberOfOperandsFor(opcode)); + } + } +} + + +DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData( + int* deopt_index) { + ASSERT(is_optimized()); + + JSFunction* opt_function = JSFunction::cast(function()); + Code* code = opt_function->code(); + + // The code object may have been replaced by lazy deoptimization. Fall + // back to a slow search in this case to find the original optimized + // code object. + if (!code->contains(pc())) { + code = isolate()->pc_to_code_cache()->GcSafeFindCodeForPc(pc()); + } + ASSERT(code != NULL); + ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); + + SafepointEntry safepoint_entry = code->GetSafepointEntry(pc()); + *deopt_index = safepoint_entry.deoptimization_index(); + ASSERT(*deopt_index != Safepoint::kNoDeoptimizationIndex); + + return DeoptimizationInputData::cast(code->deoptimization_data()); +} + + +void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) { + ASSERT(functions->length() == 0); + ASSERT(is_optimized()); + + int deopt_index = Safepoint::kNoDeoptimizationIndex; + DeoptimizationInputData* data = GetDeoptimizationData(&deopt_index); + + TranslationIterator it(data->TranslationByteArray(), + data->TranslationIndex(deopt_index)->value()); + Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); + ASSERT(opcode == Translation::BEGIN); + int frame_count = it.Next(); + + // We insert the frames in reverse order because the frames + // in the deoptimization translation are ordered bottom-to-top. + while (frame_count > 0) { + opcode = static_cast<Translation::Opcode>(it.Next()); + if (opcode == Translation::FRAME) { + frame_count--; + it.Next(); // Skip ast id. + int function_id = it.Next(); + it.Next(); // Skip height. + JSFunction* function = + JSFunction::cast(data->LiteralArray()->get(function_id)); + functions->Add(function); + } else { + // Skip over operands to advance to the next opcode. + it.Skip(Translation::NumberOfOperandsFor(opcode)); + } + } +} + + +Address ArgumentsAdaptorFrame::GetCallerStackPointer() const { + const int arguments = Smi::cast(GetExpression(0))->value(); + const int offset = StandardFrameConstants::kCallerSPOffset; + return fp() + offset + (arguments + 1) * kPointerSize; +} + + +Address InternalFrame::GetCallerStackPointer() const { + // Internal frames have no arguments. The stack pointer of the + // caller is at a fixed offset from the frame pointer. + return fp() + StandardFrameConstants::kCallerSPOffset; +} + + +Code* ArgumentsAdaptorFrame::unchecked_code() const { + return isolate()->builtins()->builtin( + Builtins::kArgumentsAdaptorTrampoline); +} + + +Code* InternalFrame::unchecked_code() const { + const int offset = InternalFrameConstants::kCodeOffset; + Object* code = Memory::Object_at(fp() + offset); + ASSERT(code != NULL); + return reinterpret_cast<Code*>(code); +} + + +void StackFrame::PrintIndex(StringStream* accumulator, + PrintMode mode, + int index) { + accumulator->Add((mode == OVERVIEW) ? "%5d: " : "[%d]: ", index); +} + + +void JavaScriptFrame::Print(StringStream* accumulator, + PrintMode mode, + int index) const { + HandleScope scope; + Object* receiver = this->receiver(); + Object* function = this->function(); + + accumulator->PrintSecurityTokenIfChanged(function); + PrintIndex(accumulator, mode, index); + Code* code = NULL; + if (IsConstructor()) accumulator->Add("new "); + accumulator->PrintFunction(function, receiver, &code); + + Handle<SerializedScopeInfo> scope_info(SerializedScopeInfo::Empty()); + + if (function->IsJSFunction()) { + Handle<SharedFunctionInfo> shared(JSFunction::cast(function)->shared()); + scope_info = Handle<SerializedScopeInfo>(shared->scope_info()); + Object* script_obj = shared->script(); + if (script_obj->IsScript()) { + Handle<Script> script(Script::cast(script_obj)); + accumulator->Add(" ["); + accumulator->PrintName(script->name()); + + Address pc = this->pc(); + if (code != NULL && code->kind() == Code::FUNCTION && + pc >= code->instruction_start() && pc < code->instruction_end()) { + int source_pos = code->SourcePosition(pc); + int line = GetScriptLineNumberSafe(script, source_pos) + 1; + accumulator->Add(":%d", line); + } else { + int function_start_pos = shared->start_position(); + int line = GetScriptLineNumberSafe(script, function_start_pos) + 1; + accumulator->Add(":~%d", line); + } + + accumulator->Add("] "); + } + } + + accumulator->Add("(this=%o", receiver); + + // Get scope information for nicer output, if possible. If code is + // NULL, or doesn't contain scope info, info will return 0 for the + // number of parameters, stack slots, or context slots. + ScopeInfo<PreallocatedStorage> info(*scope_info); + + // Print the parameters. + int parameters_count = ComputeParametersCount(); + for (int i = 0; i < parameters_count; i++) { + accumulator->Add(","); + // If we have a name for the parameter we print it. Nameless + // parameters are either because we have more actual parameters + // than formal parameters or because we have no scope information. + if (i < info.number_of_parameters()) { + accumulator->PrintName(*info.parameter_name(i)); + accumulator->Add("="); + } + accumulator->Add("%o", GetParameter(i)); + } + + accumulator->Add(")"); + if (mode == OVERVIEW) { + accumulator->Add("\n"); + return; + } + accumulator->Add(" {\n"); + + // Compute the number of locals and expression stack elements. + int stack_locals_count = info.number_of_stack_slots(); + int heap_locals_count = info.number_of_context_slots(); + int expressions_count = ComputeExpressionsCount(); + + // Print stack-allocated local variables. + if (stack_locals_count > 0) { + accumulator->Add(" // stack-allocated locals\n"); + } + for (int i = 0; i < stack_locals_count; i++) { + accumulator->Add(" var "); + accumulator->PrintName(*info.stack_slot_name(i)); + accumulator->Add(" = "); + if (i < expressions_count) { + accumulator->Add("%o", GetExpression(i)); + } else { + accumulator->Add("// no expression found - inconsistent frame?"); + } + accumulator->Add("\n"); + } + + // Try to get hold of the context of this frame. + Context* context = NULL; + if (this->context() != NULL && this->context()->IsContext()) { + context = Context::cast(this->context()); + } + + // Print heap-allocated local variables. + if (heap_locals_count > Context::MIN_CONTEXT_SLOTS) { + accumulator->Add(" // heap-allocated locals\n"); + } + for (int i = Context::MIN_CONTEXT_SLOTS; i < heap_locals_count; i++) { + accumulator->Add(" var "); + accumulator->PrintName(*info.context_slot_name(i)); + accumulator->Add(" = "); + if (context != NULL) { + if (i < context->length()) { + accumulator->Add("%o", context->get(i)); + } else { + accumulator->Add( + "// warning: missing context slot - inconsistent frame?"); + } + } else { + accumulator->Add("// warning: no context found - inconsistent frame?"); + } + accumulator->Add("\n"); + } + + // Print the expression stack. + int expressions_start = stack_locals_count; + if (expressions_start < expressions_count) { + accumulator->Add(" // expression stack (top to bottom)\n"); + } + for (int i = expressions_count - 1; i >= expressions_start; i--) { + if (IsExpressionInsideHandler(i)) continue; + accumulator->Add(" [%02d] : %o\n", i, GetExpression(i)); + } + + // Print details about the function. + if (FLAG_max_stack_trace_source_length != 0 && code != NULL) { + SharedFunctionInfo* shared = JSFunction::cast(function)->shared(); + accumulator->Add("--------- s o u r c e c o d e ---------\n"); + shared->SourceCodePrint(accumulator, FLAG_max_stack_trace_source_length); + accumulator->Add("\n-----------------------------------------\n"); + } + + accumulator->Add("}\n\n"); +} + + +void ArgumentsAdaptorFrame::Print(StringStream* accumulator, + PrintMode mode, + int index) const { + int actual = ComputeParametersCount(); + int expected = -1; + Object* function = this->function(); + if (function->IsJSFunction()) { + expected = JSFunction::cast(function)->shared()->formal_parameter_count(); + } + + PrintIndex(accumulator, mode, index); + accumulator->Add("arguments adaptor frame: %d->%d", actual, expected); + if (mode == OVERVIEW) { + accumulator->Add("\n"); + return; + } + accumulator->Add(" {\n"); + + // Print actual arguments. + if (actual > 0) accumulator->Add(" // actual arguments\n"); + for (int i = 0; i < actual; i++) { + accumulator->Add(" [%02d] : %o", i, GetParameter(i)); + if (expected != -1 && i >= expected) { + accumulator->Add(" // not passed to callee"); + } + accumulator->Add("\n"); + } + + accumulator->Add("}\n\n"); +} + + +void EntryFrame::Iterate(ObjectVisitor* v) const { + StackHandlerIterator it(this, top_handler()); + ASSERT(!it.done()); + StackHandler* handler = it.handler(); + ASSERT(handler->is_entry()); + handler->Iterate(v, LookupCode()); +#ifdef DEBUG + // Make sure that the entry frame does not contain more than one + // stack handler. + it.Advance(); + ASSERT(it.done()); +#endif + IteratePc(v, pc_address(), LookupCode()); +} + + +void StandardFrame::IterateExpressions(ObjectVisitor* v) const { + const int offset = StandardFrameConstants::kContextOffset; + Object** base = &Memory::Object_at(sp()); + Object** limit = &Memory::Object_at(fp() + offset) + 1; + for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) { + StackHandler* handler = it.handler(); + // Traverse pointers down to - but not including - the next + // handler in the handler chain. Update the base to skip the + // handler and allow the handler to traverse its own pointers. + const Address address = handler->address(); + v->VisitPointers(base, reinterpret_cast<Object**>(address)); + base = reinterpret_cast<Object**>(address + StackHandlerConstants::kSize); + // Traverse the pointers in the handler itself. + handler->Iterate(v, LookupCode()); + } + v->VisitPointers(base, limit); +} + + +void JavaScriptFrame::Iterate(ObjectVisitor* v) const { + IterateExpressions(v); + IteratePc(v, pc_address(), LookupCode()); + IterateArguments(v); +} + + +void JavaScriptFrame::IterateArguments(ObjectVisitor* v) const { + // Traverse callee-saved registers, receiver, and parameters. + const int kBaseOffset = JavaScriptFrameConstants::kLastParameterOffset; + const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset; + Object** base = &Memory::Object_at(fp() + kBaseOffset); + Object** limit = &Memory::Object_at(caller_sp() + kLimitOffset) + 1; + v->VisitPointers(base, limit); +} + + +void InternalFrame::Iterate(ObjectVisitor* v) const { + // Internal frames only have object pointers on the expression stack + // as they never have any arguments. + IterateExpressions(v); + IteratePc(v, pc_address(), LookupCode()); +} + + +// ------------------------------------------------------------------------- + + +JavaScriptFrame* StackFrameLocator::FindJavaScriptFrame(int n) { + ASSERT(n >= 0); + for (int i = 0; i <= n; i++) { + while (!iterator_.frame()->is_java_script()) iterator_.Advance(); + if (i == n) return JavaScriptFrame::cast(iterator_.frame()); + iterator_.Advance(); + } + UNREACHABLE(); + return NULL; +} + + +// ------------------------------------------------------------------------- + + +Code* PcToCodeCache::GcSafeCastToCode(HeapObject* object, Address pc) { + Code* code = reinterpret_cast<Code*>(object); + ASSERT(code != NULL && code->contains(pc)); + return code; +} + + +Code* PcToCodeCache::GcSafeFindCodeForPc(Address pc) { + Heap* heap = isolate_->heap(); + // Check if the pc points into a large object chunk. + LargeObjectChunk* chunk = heap->lo_space()->FindChunkContainingPc(pc); + if (chunk != NULL) return GcSafeCastToCode(chunk->GetObject(), pc); + + // Iterate through the 8K page until we reach the end or find an + // object starting after the pc. + Page* page = Page::FromAddress(pc); + HeapObjectIterator iterator(page, heap->GcSafeSizeOfOldObjectFunction()); + HeapObject* previous = NULL; + while (true) { + HeapObject* next = iterator.next(); + if (next == NULL || next->address() >= pc) { + return GcSafeCastToCode(previous, pc); + } + previous = next; + } +} + + +PcToCodeCache::PcToCodeCacheEntry* PcToCodeCache::GetCacheEntry(Address pc) { + isolate_->counters()->pc_to_code()->Increment(); + ASSERT(IsPowerOf2(kPcToCodeCacheSize)); + uint32_t hash = ComputeIntegerHash( + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc))); + uint32_t index = hash & (kPcToCodeCacheSize - 1); + PcToCodeCacheEntry* entry = cache(index); + if (entry->pc == pc) { + isolate_->counters()->pc_to_code_cached()->Increment(); + ASSERT(entry->code == GcSafeFindCodeForPc(pc)); + } else { + // Because this code may be interrupted by a profiling signal that + // also queries the cache, we cannot update pc before the code has + // been set. Otherwise, we risk trying to use a cache entry before + // the code has been computed. + entry->code = GcSafeFindCodeForPc(pc); + entry->safepoint_entry.Reset(); + entry->pc = pc; + } + return entry; +} + + +// ------------------------------------------------------------------------- + +int NumRegs(RegList reglist) { + int n = 0; + while (reglist != 0) { + n++; + reglist &= reglist - 1; // clear one bit + } + return n; +} + + +struct JSCallerSavedCodeData { + JSCallerSavedCodeData() { + int i = 0; + for (int r = 0; r < kNumRegs; r++) + if ((kJSCallerSaved & (1 << r)) != 0) + reg_code[i++] = r; + + ASSERT(i == kNumJSCallerSaved); + } + int reg_code[kNumJSCallerSaved]; +}; + + +static const JSCallerSavedCodeData kCallerSavedCodeData; + + +int JSCallerSavedCode(int n) { + ASSERT(0 <= n && n < kNumJSCallerSaved); + return kCallerSavedCodeData.reg_code[n]; +} + + +#define DEFINE_WRAPPER(type, field) \ +class field##_Wrapper : public ZoneObject { \ + public: /* NOLINT */ \ + field##_Wrapper(const field& original) : frame_(original) { \ + } \ + field frame_; \ +}; +STACK_FRAME_TYPE_LIST(DEFINE_WRAPPER) +#undef DEFINE_WRAPPER + +static StackFrame* AllocateFrameCopy(StackFrame* frame) { +#define FRAME_TYPE_CASE(type, field) \ + case StackFrame::type: { \ + field##_Wrapper* wrapper = \ + new field##_Wrapper(*(reinterpret_cast<field*>(frame))); \ + return &wrapper->frame_; \ + } + + switch (frame->type()) { + STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE) + default: UNREACHABLE(); + } +#undef FRAME_TYPE_CASE + return NULL; +} + +Vector<StackFrame*> CreateStackMap() { + ZoneList<StackFrame*> list(10); + for (StackFrameIterator it; !it.done(); it.Advance()) { + StackFrame* frame = AllocateFrameCopy(it.frame()); + list.Add(frame); + } + return list.ToVector(); +} + + +} } // namespace v8::internal |