Support debugger inspection of locals in optimized frames
Optimized frames are now handled by the debugger. When discovering optimized frames during stack inspection in the debugger they are "deoptimized" using the normal deoptimization code and the deoptimizer output information is used to provide frame information to the debugger. Before this change the debugger reported each optimized frame as one frame no matter the number of inlined functuions that might have been called inside of it. Also all locals where reported as undefined. Locals can still be reposted as undefined when their value is not "known" by the optimized frame. As the structures used to calculate the output frames when deoptimizing are not GC safe the information for the debugger is copied to another structure (DeoptimizedFrameInfo) which is registered with the global deoptimizer data and processed during GC. R=fschneider@chromium.org BUG=v8:1140 TEST=test/mjsunit/debug-evaluate-locals-optimized* Review URL: http://codereview.chromium.org//7230045 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8464 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
cd9356baa6
commit
7d2be7c0e0
@ -267,6 +267,9 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
output_ = new FrameDescription*[1];
|
||||
output_[0] = new(output_frame_size) FrameDescription(
|
||||
output_frame_size, function_);
|
||||
#ifdef DEBUG
|
||||
output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
|
||||
// Clear the incoming parameters in the optimized frame to avoid
|
||||
// confusing the garbage collector.
|
||||
@ -382,6 +385,9 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
#ifdef DEBUG
|
||||
output_frame->SetKind(Code::FUNCTION);
|
||||
#endif
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
@ -516,7 +522,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
|
||||
|
||||
// Set the continuation for the topmost frame.
|
||||
if (is_topmost) {
|
||||
if (is_topmost && bailout_type_ != DEBUGGER) {
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* continuation = (bailout_type_ == EAGER)
|
||||
? builtins->builtin(Builtins::kNotifyDeoptimized)
|
||||
@ -529,8 +535,28 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
#define __ masm()->
|
||||
void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
|
||||
// Set the register values. The values are not important as there are no
|
||||
// callee saved registers in JavaScript frames, so all registers are
|
||||
// spilled. Registers fp and sp are set to the correct values though.
|
||||
|
||||
for (int i = 0; i < Register::kNumRegisters; i++) {
|
||||
input_->SetRegister(i, i * 4);
|
||||
}
|
||||
input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
|
||||
input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
|
||||
for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) {
|
||||
input_->SetDoubleRegister(i, 0.0);
|
||||
}
|
||||
|
||||
// Fill the frame content from the actual data on the frame.
|
||||
for (intptr_t i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
|
||||
input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define __ masm()->
|
||||
|
||||
// This code tries to be close to ia32 code so that any changes can be
|
||||
// easily ported.
|
||||
|
@ -618,6 +618,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
|
||||
RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared);
|
||||
|
||||
if (info->IsOptimizing()) {
|
||||
ASSERT(shared->scope_info() != SerializedScopeInfo::Empty());
|
||||
function->ReplaceCode(*code);
|
||||
} else {
|
||||
// Update the shared function info with the compiled code and the
|
||||
|
@ -44,6 +44,9 @@ DeoptimizerData::DeoptimizerData() {
|
||||
lazy_deoptimization_entry_code_ = NULL;
|
||||
current_ = NULL;
|
||||
deoptimizing_code_list_ = NULL;
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
deoptimized_frame_info_ = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -58,6 +61,16 @@ DeoptimizerData::~DeoptimizerData() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
void DeoptimizerData::Iterate(ObjectVisitor* v) {
|
||||
if (deoptimized_frame_info_ != NULL) {
|
||||
deoptimized_frame_info_->Iterate(v);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Deoptimizer* Deoptimizer::New(JSFunction* function,
|
||||
BailoutType type,
|
||||
unsigned bailout_id,
|
||||
@ -70,7 +83,8 @@ Deoptimizer* Deoptimizer::New(JSFunction* function,
|
||||
type,
|
||||
bailout_id,
|
||||
from,
|
||||
fp_to_sp_delta);
|
||||
fp_to_sp_delta,
|
||||
NULL);
|
||||
ASSERT(isolate->deoptimizer_data()->current_ == NULL);
|
||||
isolate->deoptimizer_data()->current_ = deoptimizer;
|
||||
return deoptimizer;
|
||||
@ -86,6 +100,92 @@ Deoptimizer* Deoptimizer::Grab(Isolate* isolate) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
|
||||
JavaScriptFrame* frame,
|
||||
int frame_index,
|
||||
Isolate* isolate) {
|
||||
ASSERT(isolate == Isolate::Current());
|
||||
ASSERT(frame->is_optimized());
|
||||
ASSERT(isolate->deoptimizer_data()->deoptimized_frame_info_ == NULL);
|
||||
|
||||
// Get the function and code from the frame.
|
||||
JSFunction* function = JSFunction::cast(frame->function());
|
||||
Code* code = frame->LookupCode();
|
||||
Address code_start_address = code->instruction_start();
|
||||
|
||||
// Locate the deoptimization point in the code. As we are at a call the
|
||||
// return address must be at a place in the code with deoptimization support.
|
||||
int deoptimization_index = Safepoint::kNoDeoptimizationIndex;
|
||||
// Scope this as the safe point constructor will disallow allocation.
|
||||
{
|
||||
SafepointTable table(code);
|
||||
for (unsigned i = 0; i < table.length(); ++i) {
|
||||
Address address = code_start_address + table.GetPcOffset(i);
|
||||
if (address == frame->pc()) {
|
||||
SafepointEntry safepoint_entry = table.GetEntry(i);
|
||||
ASSERT(safepoint_entry.deoptimization_index() !=
|
||||
Safepoint::kNoDeoptimizationIndex);
|
||||
deoptimization_index = safepoint_entry.deoptimization_index();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT(deoptimization_index != Safepoint::kNoDeoptimizationIndex);
|
||||
|
||||
// Always use the actual stack slots when calculating the fp to sp
|
||||
// delta adding two for the function and context.
|
||||
unsigned stack_slots = code->stack_slots();
|
||||
unsigned fp_to_sp_delta = ((stack_slots + 2) * kPointerSize);
|
||||
|
||||
Deoptimizer* deoptimizer = new Deoptimizer(isolate,
|
||||
function,
|
||||
Deoptimizer::DEBUGGER,
|
||||
deoptimization_index,
|
||||
frame->pc(),
|
||||
fp_to_sp_delta,
|
||||
code);
|
||||
Address tos = frame->fp() - fp_to_sp_delta;
|
||||
deoptimizer->FillInputFrame(tos, frame);
|
||||
|
||||
// Calculate the output frames.
|
||||
Deoptimizer::ComputeOutputFrames(deoptimizer);
|
||||
|
||||
// Create the GC safe output frame information and register it for GC
|
||||
// handling.
|
||||
ASSERT_LT(frame_index, deoptimizer->output_count());
|
||||
DeoptimizedFrameInfo* info =
|
||||
new DeoptimizedFrameInfo(deoptimizer, frame_index);
|
||||
isolate->deoptimizer_data()->deoptimized_frame_info_ = info;
|
||||
|
||||
// Get the "simulated" top and size for the requested frame.
|
||||
Address top =
|
||||
reinterpret_cast<Address>(deoptimizer->output_[frame_index]->GetTop());
|
||||
intptr_t size =
|
||||
deoptimizer->output_[frame_index]->GetFrameSize() / kPointerSize;
|
||||
|
||||
// Done with the GC-unsafe frame descriptions. This re-enables allocation.
|
||||
deoptimizer->DeleteFrameDescriptions();
|
||||
|
||||
// Allocate a heap number for the doubles belonging to this frame.
|
||||
deoptimizer->MaterializeHeapNumbersForDebuggerInspectableFrame(
|
||||
top, size, info);
|
||||
|
||||
// Finished using the deoptimizer instance.
|
||||
delete deoptimizer;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info,
|
||||
Isolate* isolate) {
|
||||
ASSERT(isolate == Isolate::Current());
|
||||
ASSERT(isolate->deoptimizer_data()->deoptimized_frame_info_ == info);
|
||||
delete info;
|
||||
isolate->deoptimizer_data()->deoptimized_frame_info_ = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
|
||||
int count,
|
||||
@ -209,18 +309,24 @@ Deoptimizer::Deoptimizer(Isolate* isolate,
|
||||
BailoutType type,
|
||||
unsigned bailout_id,
|
||||
Address from,
|
||||
int fp_to_sp_delta)
|
||||
int fp_to_sp_delta,
|
||||
Code* optimized_code)
|
||||
: isolate_(isolate),
|
||||
function_(function),
|
||||
bailout_id_(bailout_id),
|
||||
bailout_type_(type),
|
||||
from_(from),
|
||||
fp_to_sp_delta_(fp_to_sp_delta),
|
||||
input_(NULL),
|
||||
output_count_(0),
|
||||
output_(NULL),
|
||||
deferred_heap_numbers_(0) {
|
||||
if (FLAG_trace_deopt && type != OSR) {
|
||||
PrintF("**** DEOPT: ");
|
||||
if (type == DEBUGGER) {
|
||||
PrintF("**** DEOPT FOR DEBUGGER: ");
|
||||
} else {
|
||||
PrintF("**** DEOPT: ");
|
||||
}
|
||||
function->PrintName();
|
||||
PrintF(" at bailout #%u, address 0x%" V8PRIxPTR ", frame size %d\n",
|
||||
bailout_id,
|
||||
@ -248,10 +354,16 @@ Deoptimizer::Deoptimizer(Isolate* isolate,
|
||||
optimized_code_ = function_->code();
|
||||
ASSERT(optimized_code_->kind() == Code::OPTIMIZED_FUNCTION);
|
||||
ASSERT(!optimized_code_->contains(from));
|
||||
} else if (type == DEBUGGER) {
|
||||
optimized_code_ = optimized_code;
|
||||
ASSERT(optimized_code_->contains(from));
|
||||
}
|
||||
ASSERT(HEAP->allow_allocation(false));
|
||||
unsigned size = ComputeInputFrameSize();
|
||||
input_ = new(size) FrameDescription(size, function);
|
||||
#ifdef DEBUG
|
||||
input_->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -417,6 +529,7 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
|
||||
|
||||
void Deoptimizer::MaterializeHeapNumbers() {
|
||||
ASSERT_NE(DEBUGGER, bailout_type_);
|
||||
for (int i = 0; i < deferred_heap_numbers_.length(); i++) {
|
||||
HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i];
|
||||
Handle<Object> num = isolate_->factory()->NewNumber(d.value());
|
||||
@ -432,6 +545,35 @@ void Deoptimizer::MaterializeHeapNumbers() {
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
void Deoptimizer::MaterializeHeapNumbersForDebuggerInspectableFrame(
|
||||
Address top, intptr_t size, DeoptimizedFrameInfo* info) {
|
||||
ASSERT_EQ(DEBUGGER, bailout_type_);
|
||||
for (int i = 0; i < deferred_heap_numbers_.length(); i++) {
|
||||
HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i];
|
||||
|
||||
// Check of the heap number to materialize actually belong to the frame
|
||||
// being extracted.
|
||||
Address slot = d.slot_address();
|
||||
if (top <= slot && slot < top + size) {
|
||||
Handle<Object> num = isolate_->factory()->NewNumber(d.value());
|
||||
int expression_index =
|
||||
info->expression_count_ - (slot - top) / kPointerSize - 1;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF("Materializing a new heap number %p [%e] in slot %p"
|
||||
"for expression stack index %d\n",
|
||||
reinterpret_cast<void*>(*num),
|
||||
d.value(),
|
||||
d.slot_address(),
|
||||
expression_index);
|
||||
}
|
||||
info->SetExpression(expression_index, *num);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
|
||||
int frame_index,
|
||||
unsigned output_offset) {
|
||||
@ -984,6 +1126,20 @@ unsigned FrameDescription::GetOffsetFromSlotIndex(Deoptimizer* deoptimizer,
|
||||
}
|
||||
|
||||
|
||||
unsigned FrameDescription::GetExpressionCount(Deoptimizer* deoptimizer) {
|
||||
ASSERT_EQ(Code::FUNCTION, kind_);
|
||||
return (GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction()))
|
||||
/ kPointerSize;
|
||||
}
|
||||
|
||||
|
||||
Object* FrameDescription::GetExpression(Deoptimizer* deoptimizer, int index) {
|
||||
ASSERT_EQ(Code::FUNCTION, kind_);
|
||||
unsigned offset = GetOffsetFromSlotIndex(deoptimizer, index);
|
||||
return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
|
||||
}
|
||||
|
||||
|
||||
void TranslationBuffer::Add(int32_t value) {
|
||||
// Encode the sign bit in the least significant bit.
|
||||
bool is_negative = (value < 0);
|
||||
@ -1256,4 +1412,24 @@ void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
|
||||
}
|
||||
|
||||
|
||||
DeoptimizedFrameInfo::DeoptimizedFrameInfo(
|
||||
Deoptimizer* deoptimizer, int frame_index) {
|
||||
FrameDescription* output_frame = deoptimizer->output_[frame_index];
|
||||
expression_count_ = output_frame->GetExpressionCount(deoptimizer);
|
||||
expression_stack_ = new Object*[expression_count_];
|
||||
for (int i = 0; i < expression_count_; i++) {
|
||||
SetExpression(i, output_frame->GetExpression(deoptimizer, i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DeoptimizedFrameInfo::~DeoptimizedFrameInfo() {
|
||||
delete expression_stack_;
|
||||
}
|
||||
|
||||
void DeoptimizedFrameInfo::Iterate(ObjectVisitor* v) {
|
||||
v->VisitPointers(expression_stack_, expression_stack_ + expression_count_);
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -41,7 +41,7 @@ namespace internal {
|
||||
class FrameDescription;
|
||||
class TranslationIterator;
|
||||
class DeoptimizingCodeListNode;
|
||||
|
||||
class DeoptimizedFrameInfo;
|
||||
|
||||
class HeapNumberMaterializationDescriptor BASE_EMBEDDED {
|
||||
public:
|
||||
@ -81,11 +81,19 @@ class DeoptimizerData {
|
||||
DeoptimizerData();
|
||||
~DeoptimizerData();
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
void Iterate(ObjectVisitor* v);
|
||||
#endif
|
||||
|
||||
private:
|
||||
LargeObjectChunk* eager_deoptimization_entry_code_;
|
||||
LargeObjectChunk* lazy_deoptimization_entry_code_;
|
||||
Deoptimizer* current_;
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
DeoptimizedFrameInfo* deoptimized_frame_info_;
|
||||
#endif
|
||||
|
||||
// List of deoptimized code which still have references from active stack
|
||||
// frames. These code objects are needed by the deoptimizer when deoptimizing
|
||||
// a frame for which the code object for the function function has been
|
||||
@ -103,7 +111,10 @@ class Deoptimizer : public Malloced {
|
||||
enum BailoutType {
|
||||
EAGER,
|
||||
LAZY,
|
||||
OSR
|
||||
OSR,
|
||||
// This last bailout type is not really a bailout, but used by the
|
||||
// debugger to deoptimize stack frames to allow inspection.
|
||||
DEBUGGER
|
||||
};
|
||||
|
||||
int output_count() const { return output_count_; }
|
||||
@ -116,6 +127,16 @@ class Deoptimizer : public Malloced {
|
||||
Isolate* isolate);
|
||||
static Deoptimizer* Grab(Isolate* isolate);
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// The returned object with information on the optimized frame needs to be
|
||||
// freed before another one can be generated.
|
||||
static DeoptimizedFrameInfo* DebuggerInspectableFrame(JavaScriptFrame* frame,
|
||||
int frame_index,
|
||||
Isolate* isolate);
|
||||
static void DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info,
|
||||
Isolate* isolate);
|
||||
#endif
|
||||
|
||||
// Makes sure that there is enough room in the relocation
|
||||
// information of a code object to perform lazy deoptimization
|
||||
// patching. If there is not enough room a new relocation
|
||||
@ -171,6 +192,10 @@ class Deoptimizer : public Malloced {
|
||||
~Deoptimizer();
|
||||
|
||||
void MaterializeHeapNumbers();
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
void MaterializeHeapNumbersForDebuggerInspectableFrame(
|
||||
Address top, intptr_t size, DeoptimizedFrameInfo* info);
|
||||
#endif
|
||||
|
||||
static void ComputeOutputFrames(Deoptimizer* deoptimizer);
|
||||
|
||||
@ -233,7 +258,8 @@ class Deoptimizer : public Malloced {
|
||||
BailoutType type,
|
||||
unsigned bailout_id,
|
||||
Address from,
|
||||
int fp_to_sp_delta);
|
||||
int fp_to_sp_delta,
|
||||
Code* optimized_code);
|
||||
void DeleteFrameDescriptions();
|
||||
|
||||
void DoComputeOutputFrames();
|
||||
@ -269,6 +295,11 @@ class Deoptimizer : public Malloced {
|
||||
static Code* FindDeoptimizingCodeFromAddress(Address addr);
|
||||
static void RemoveDeoptimizingCode(Code* code);
|
||||
|
||||
// Fill the input from from a JavaScript frame. This is used when
|
||||
// the debugger needs to inspect an optimized frame. For normal
|
||||
// deoptimizations the input frame is filled in generated code.
|
||||
void FillInputFrame(Address tos, JavaScriptFrame* frame);
|
||||
|
||||
Isolate* isolate_;
|
||||
JSFunction* function_;
|
||||
Code* optimized_code_;
|
||||
@ -290,6 +321,7 @@ class Deoptimizer : public Malloced {
|
||||
|
||||
friend class FrameDescription;
|
||||
friend class DeoptimizingCodeListNode;
|
||||
friend class DeoptimizedFrameInfo;
|
||||
};
|
||||
|
||||
|
||||
@ -360,6 +392,17 @@ class FrameDescription {
|
||||
|
||||
void SetContinuation(intptr_t pc) { continuation_ = pc; }
|
||||
|
||||
#ifdef DEBUG
|
||||
Code::Kind GetKind() const { return kind_; }
|
||||
void SetKind(Code::Kind kind) { kind_ = kind; }
|
||||
#endif
|
||||
|
||||
// Get the expression stack height for a unoptimized frame.
|
||||
unsigned GetExpressionCount(Deoptimizer* deoptimizer);
|
||||
|
||||
// Get the expression stack value for an unoptimized frame.
|
||||
Object* GetExpression(Deoptimizer* deoptimizer, int index);
|
||||
|
||||
static int registers_offset() {
|
||||
return OFFSET_OF(FrameDescription, registers_);
|
||||
}
|
||||
@ -399,6 +442,9 @@ class FrameDescription {
|
||||
intptr_t pc_;
|
||||
intptr_t fp_;
|
||||
Smi* state_;
|
||||
#ifdef DEBUG
|
||||
Code::Kind kind_;
|
||||
#endif
|
||||
|
||||
// Continuation is the PC where the execution continues after
|
||||
// deoptimizing.
|
||||
@ -597,6 +643,42 @@ class SlotRef BASE_EMBEDDED {
|
||||
};
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
// Class used to represent an unoptimized frame when the debugger
|
||||
// needs to inspect a frame that is part of an optimized frame. The
|
||||
// internally used FrameDescription objects are not GC safe so for use
|
||||
// by the debugger frame information is copied to an object of this type.
|
||||
class DeoptimizedFrameInfo : public Malloced {
|
||||
public:
|
||||
DeoptimizedFrameInfo(Deoptimizer* deoptimizer, int frame_index);
|
||||
virtual ~DeoptimizedFrameInfo();
|
||||
|
||||
// GC support.
|
||||
void Iterate(ObjectVisitor* v);
|
||||
|
||||
// Return the height of the expression stack.
|
||||
int expression_count() { return expression_count_; }
|
||||
|
||||
// Get an expression from the expression stack.
|
||||
Object* GetExpression(int index) {
|
||||
ASSERT(0 <= index && index < expression_count());
|
||||
return expression_stack_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
// Set an expression on the expression stack.
|
||||
void SetExpression(int index, Object* obj) {
|
||||
ASSERT(0 <= index && index < expression_count());
|
||||
expression_stack_[index] = obj;
|
||||
}
|
||||
|
||||
int expression_count_;
|
||||
Object** expression_stack_;
|
||||
|
||||
friend class Deoptimizer;
|
||||
};
|
||||
#endif
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_DEOPTIMIZER_H_
|
||||
|
@ -528,6 +528,17 @@ Address StandardFrame::GetExpressionAddress(int n) const {
|
||||
}
|
||||
|
||||
|
||||
Object* StandardFrame::GetExpression(Address fp, int index) {
|
||||
return Memory::Object_at(GetExpressionAddress(fp, index));
|
||||
}
|
||||
|
||||
|
||||
Address StandardFrame::GetExpressionAddress(Address fp, int n) {
|
||||
const int offset = StandardFrameConstants::kExpressionsOffset;
|
||||
return fp + offset - n * kPointerSize;
|
||||
}
|
||||
|
||||
|
||||
int StandardFrame::ComputeExpressionsCount() const {
|
||||
const int offset =
|
||||
StandardFrameConstants::kExpressionsOffset + kPointerSize;
|
||||
@ -646,6 +657,16 @@ bool JavaScriptFrame::IsConstructor() const {
|
||||
}
|
||||
|
||||
|
||||
int JavaScriptFrame::GetArgumentsLength() const {
|
||||
// If there is an arguments adaptor frame get the arguments length from it.
|
||||
if (has_adapted_arguments()) {
|
||||
return Smi::cast(GetExpression(caller_fp(), 0))->value();
|
||||
} else {
|
||||
return GetNumberOfIncomingArguments();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Code* JavaScriptFrame::unchecked_code() const {
|
||||
JSFunction* function = JSFunction::cast(this->function());
|
||||
return function->unchecked_code();
|
||||
@ -812,6 +833,22 @@ DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData(
|
||||
}
|
||||
|
||||
|
||||
int OptimizedFrame::GetInlineCount() {
|
||||
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);
|
||||
USE(opcode);
|
||||
int frame_count = it.Next();
|
||||
return frame_count;
|
||||
}
|
||||
|
||||
|
||||
void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) {
|
||||
ASSERT(functions->length() == 0);
|
||||
ASSERT(is_optimized());
|
||||
|
@ -383,6 +383,7 @@ class StandardFrame: public StackFrame {
|
||||
inline Object* GetExpression(int index) const;
|
||||
inline void SetExpression(int index, Object* value);
|
||||
int ComputeExpressionsCount() const;
|
||||
static Object* GetExpression(Address fp, int index);
|
||||
|
||||
virtual void SetCallerFp(Address caller_fp);
|
||||
|
||||
@ -411,6 +412,7 @@ class StandardFrame: public StackFrame {
|
||||
|
||||
// Returns the address of the n'th expression stack element.
|
||||
Address GetExpressionAddress(int n) const;
|
||||
static Address GetExpressionAddress(Address fp, int n);
|
||||
|
||||
// Determines if the n'th expression stack element is in a stack
|
||||
// handler or not. Requires traversing all handlers in this frame.
|
||||
@ -483,6 +485,7 @@ class JavaScriptFrame: public StandardFrame {
|
||||
// actual passed arguments are available in an arguments adaptor
|
||||
// frame below it on the stack.
|
||||
inline bool has_adapted_arguments() const;
|
||||
int GetArgumentsLength() const;
|
||||
|
||||
// Garbage collection support.
|
||||
virtual void Iterate(ObjectVisitor* v) const;
|
||||
@ -495,6 +498,9 @@ class JavaScriptFrame: public StandardFrame {
|
||||
// Determine the code for the frame.
|
||||
virtual Code* unchecked_code() const;
|
||||
|
||||
// Returns the levels of inlining for this frame.
|
||||
virtual int GetInlineCount() { return 1; }
|
||||
|
||||
// Return a list with JSFunctions of this frame.
|
||||
virtual void GetFunctions(List<JSFunction*>* functions);
|
||||
|
||||
@ -533,6 +539,8 @@ class OptimizedFrame : public JavaScriptFrame {
|
||||
// GC support.
|
||||
virtual void Iterate(ObjectVisitor* v) const;
|
||||
|
||||
virtual int GetInlineCount();
|
||||
|
||||
// Return a list with JSFunctions of this frame.
|
||||
// The functions are ordered bottom-to-top (i.e. functions.last()
|
||||
// is the top-most activation)
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "codegen.h"
|
||||
#include "compilation-cache.h"
|
||||
#include "debug.h"
|
||||
#include "deoptimizer.h"
|
||||
#include "global-handles.h"
|
||||
#include "heap-profiler.h"
|
||||
#include "liveobjectlist-inl.h"
|
||||
@ -4664,6 +4665,9 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
isolate_->debug()->Iterate(v);
|
||||
if (isolate_->deoptimizer_data() != NULL) {
|
||||
isolate_->deoptimizer_data()->Iterate(v);
|
||||
}
|
||||
#endif
|
||||
v->Synchronize("debug");
|
||||
isolate_->compilation_cache()->Iterate(v);
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "lithium-allocator.h"
|
||||
#include "parser.h"
|
||||
#include "scopeinfo.h"
|
||||
#include "scopes.h"
|
||||
#include "stub-cache.h"
|
||||
|
||||
@ -4430,6 +4431,13 @@ bool HGraphBuilder::TryInline(Call* expr) {
|
||||
TraceInline(target, caller, "could not generate deoptimization info");
|
||||
return false;
|
||||
}
|
||||
if (target_shared->scope_info() == SerializedScopeInfo::Empty()) {
|
||||
// The scope info might not have been set if a lazily compiled
|
||||
// function is inlined before being called for the first time.
|
||||
Handle<SerializedScopeInfo> target_scope_info =
|
||||
SerializedScopeInfo::Create(target_info.scope());
|
||||
target_shared->set_scope_info(*target_scope_info);
|
||||
}
|
||||
target_shared->EnableDeoptimizationSupport(*target_info.code());
|
||||
Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
|
||||
&target_info,
|
||||
|
@ -348,6 +348,9 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
output_ = new FrameDescription*[1];
|
||||
output_[0] = new(output_frame_size) FrameDescription(
|
||||
output_frame_size, function_);
|
||||
#ifdef DEBUG
|
||||
output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
|
||||
// Clear the incoming parameters in the optimized frame to avoid
|
||||
// confusing the garbage collector.
|
||||
@ -461,6 +464,9 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
#ifdef DEBUG
|
||||
output_frame->SetKind(Code::FUNCTION);
|
||||
#endif
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
@ -587,7 +593,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
output_frame->SetState(Smi::FromInt(state));
|
||||
|
||||
// Set the continuation for the topmost frame.
|
||||
if (is_topmost) {
|
||||
if (is_topmost && bailout_type_ != DEBUGGER) {
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* continuation = (bailout_type_ == EAGER)
|
||||
? builtins->builtin(Builtins::kNotifyDeoptimized)
|
||||
@ -600,6 +606,27 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
|
||||
// Set the register values. The values are not important as there are no
|
||||
// callee saved registers in JavaScript frames, so all registers are
|
||||
// spilled. Registers ebp and esp are set to the correct values though.
|
||||
|
||||
for (int i = 0; i < Register::kNumRegisters; i++) {
|
||||
input_->SetRegister(i, i * 4);
|
||||
}
|
||||
input_->SetRegister(esp.code(), reinterpret_cast<intptr_t>(frame->sp()));
|
||||
input_->SetRegister(ebp.code(), reinterpret_cast<intptr_t>(frame->fp()));
|
||||
for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) {
|
||||
input_->SetDoubleRegister(i, 0.0);
|
||||
}
|
||||
|
||||
// Fill the frame content from the actual data on the frame.
|
||||
for (intptr_t i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
|
||||
input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define __ masm()->
|
||||
|
||||
void Deoptimizer::EntryGenerator::Generate() {
|
||||
|
@ -1243,13 +1243,17 @@ const kFrameDetailsLocalCountIndex = 4;
|
||||
const kFrameDetailsSourcePositionIndex = 5;
|
||||
const kFrameDetailsConstructCallIndex = 6;
|
||||
const kFrameDetailsAtReturnIndex = 7;
|
||||
const kFrameDetailsDebuggerFrameIndex = 8;
|
||||
const kFrameDetailsFlagsIndex = 8;
|
||||
const kFrameDetailsFirstDynamicIndex = 9;
|
||||
|
||||
const kFrameDetailsNameIndex = 0;
|
||||
const kFrameDetailsValueIndex = 1;
|
||||
const kFrameDetailsNameValueSize = 2;
|
||||
|
||||
const kFrameDetailsFlagDebuggerFrame = 1;
|
||||
const kFrameDetailsFlagOptimizedFrame = 2;
|
||||
const kFrameDetailsFlagInlinedFrame = 4;
|
||||
|
||||
/**
|
||||
* Wrapper for the frame details information retreived from the VM. The frame
|
||||
* details from the VM is an array with the following content. See runtime.cc
|
||||
@ -1262,7 +1266,7 @@ const kFrameDetailsNameValueSize = 2;
|
||||
* 5: Source position
|
||||
* 6: Construct call
|
||||
* 7: Is at return
|
||||
* 8: Debugger frame
|
||||
* 8: Flags (debugger frame, optimized frame, inlined frame)
|
||||
* Arguments name, value
|
||||
* Locals name, value
|
||||
* Return value if any
|
||||
@ -1308,7 +1312,22 @@ FrameDetails.prototype.isAtReturn = function() {
|
||||
|
||||
FrameDetails.prototype.isDebuggerFrame = function() {
|
||||
%CheckExecutionState(this.break_id_);
|
||||
return this.details_[kFrameDetailsDebuggerFrameIndex];
|
||||
var f = kFrameDetailsFlagDebuggerFrame;
|
||||
return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
|
||||
}
|
||||
|
||||
|
||||
FrameDetails.prototype.isOptimizedFrame = function() {
|
||||
%CheckExecutionState(this.break_id_);
|
||||
var f = kFrameDetailsFlagOptimizedFrame;
|
||||
return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
|
||||
}
|
||||
|
||||
|
||||
FrameDetails.prototype.isInlinedFrame = function() {
|
||||
%CheckExecutionState(this.break_id_);
|
||||
var f = kFrameDetailsFlagInlinedFrame;
|
||||
return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
|
||||
}
|
||||
|
||||
|
||||
@ -1447,6 +1466,16 @@ FrameMirror.prototype.isDebuggerFrame = function() {
|
||||
};
|
||||
|
||||
|
||||
FrameMirror.prototype.isOptimizedFrame = function() {
|
||||
return this.details_.isOptimizedFrame();
|
||||
};
|
||||
|
||||
|
||||
FrameMirror.prototype.isInlinedFrame = function() {
|
||||
return this.details_.isInlinedFrame();
|
||||
};
|
||||
|
||||
|
||||
FrameMirror.prototype.argumentCount = function() {
|
||||
return this.details_.argumentCount();
|
||||
};
|
||||
|
@ -7192,6 +7192,9 @@ void Code::Disassemble(const char* name, FILE* out) {
|
||||
if (ic_state() == MONOMORPHIC) {
|
||||
PrintF(out, "type = %s\n", PropertyType2String(type()));
|
||||
}
|
||||
if (is_call_stub() || is_keyed_call_stub()) {
|
||||
PrintF(out, "argc = %d\n", arguments_count());
|
||||
}
|
||||
}
|
||||
if ((name != NULL) && (name[0] != '\0')) {
|
||||
PrintF(out, "name = %s\n", name);
|
||||
|
100
src/runtime.cc
100
src/runtime.cc
@ -9938,7 +9938,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameCount) {
|
||||
// If there is no JavaScript stack frame count is 0.
|
||||
return Smi::FromInt(0);
|
||||
}
|
||||
for (JavaScriptFrameIterator it(isolate, id); !it.done(); it.Advance()) n++;
|
||||
|
||||
for (JavaScriptFrameIterator it(isolate, id); !it.done(); it.Advance()) {
|
||||
n += it.frame()->GetInlineCount();
|
||||
}
|
||||
return Smi::FromInt(n);
|
||||
}
|
||||
|
||||
@ -9951,7 +9954,7 @@ static const int kFrameDetailsLocalCountIndex = 4;
|
||||
static const int kFrameDetailsSourcePositionIndex = 5;
|
||||
static const int kFrameDetailsConstructCallIndex = 6;
|
||||
static const int kFrameDetailsAtReturnIndex = 7;
|
||||
static const int kFrameDetailsDebuggerFrameIndex = 8;
|
||||
static const int kFrameDetailsFlagsIndex = 8;
|
||||
static const int kFrameDetailsFirstDynamicIndex = 9;
|
||||
|
||||
// Return an array with frame details
|
||||
@ -9967,7 +9970,7 @@ static const int kFrameDetailsFirstDynamicIndex = 9;
|
||||
// 5: Source position
|
||||
// 6: Constructor call
|
||||
// 7: Is at return
|
||||
// 8: Debugger frame
|
||||
// 8: Flags
|
||||
// Arguments name, value
|
||||
// Locals name, value
|
||||
// Return value if any
|
||||
@ -9990,16 +9993,26 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
// If there are no JavaScript stack frames return undefined.
|
||||
return heap->undefined_value();
|
||||
}
|
||||
|
||||
int deoptimized_frame_index = -1; // Frame index in optimized frame.
|
||||
DeoptimizedFrameInfo* deoptimized_frame = NULL;
|
||||
|
||||
int count = 0;
|
||||
JavaScriptFrameIterator it(isolate, id);
|
||||
for (; !it.done(); it.Advance()) {
|
||||
if (count == index) break;
|
||||
count++;
|
||||
if (index < count + it.frame()->GetInlineCount()) break;
|
||||
count += it.frame()->GetInlineCount();
|
||||
}
|
||||
if (it.done()) return heap->undefined_value();
|
||||
|
||||
bool is_optimized_frame =
|
||||
it.frame()->LookupCode()->kind() == Code::OPTIMIZED_FUNCTION;
|
||||
if (it.frame()->is_optimized()) {
|
||||
deoptimized_frame_index =
|
||||
it.frame()->GetInlineCount() - (index - count) - 1;
|
||||
deoptimized_frame = Deoptimizer::DebuggerInspectableFrame(
|
||||
it.frame(),
|
||||
deoptimized_frame_index,
|
||||
isolate);
|
||||
}
|
||||
|
||||
// Traverse the saved contexts chain to find the active context for the
|
||||
// selected frame.
|
||||
@ -10022,6 +10035,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
// Get scope info and read from it for local variable information.
|
||||
Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
|
||||
Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
|
||||
ASSERT(*scope_info != SerializedScopeInfo::Empty());
|
||||
ScopeInfo<> info(*scope_info);
|
||||
|
||||
// Get the locals names and values into a temporary array.
|
||||
@ -10033,38 +10047,33 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
isolate->factory()->NewFixedArray(info.NumberOfLocals() * 2);
|
||||
|
||||
// Fill in the values of the locals.
|
||||
if (is_optimized_frame) {
|
||||
// If we are inspecting an optimized frame use undefined as the
|
||||
// value for all locals.
|
||||
//
|
||||
// TODO(1140): We should be able to get the correct values
|
||||
// for locals in optimized frames.
|
||||
for (int i = 0; i < info.NumberOfLocals(); i++) {
|
||||
locals->set(i * 2, *info.LocalName(i));
|
||||
locals->set(i * 2 + 1, isolate->heap()->undefined_value());
|
||||
}
|
||||
} else {
|
||||
int i = 0;
|
||||
for (; i < info.number_of_stack_slots(); ++i) {
|
||||
// Use the value from the stack.
|
||||
locals->set(i * 2, *info.LocalName(i));
|
||||
int i = 0;
|
||||
for (; i < info.number_of_stack_slots(); ++i) {
|
||||
// Use the value from the stack.
|
||||
locals->set(i * 2, *info.LocalName(i));
|
||||
if (it.frame()->is_optimized()) {
|
||||
// Get the value from the deoptimized frame.
|
||||
locals->set(i * 2 + 1,
|
||||
deoptimized_frame->GetExpression(i));
|
||||
} else {
|
||||
// Get the value from the stack.
|
||||
locals->set(i * 2 + 1, it.frame()->GetExpression(i));
|
||||
}
|
||||
// Get the context containing declarations.
|
||||
Handle<Context> context(
|
||||
Context::cast(it.frame()->context())->declaration_context());
|
||||
for (; i < info.NumberOfLocals(); ++i) {
|
||||
Handle<String> name = info.LocalName(i);
|
||||
locals->set(i * 2, *name);
|
||||
locals->set(i * 2 + 1,
|
||||
context->get(scope_info->ContextSlotIndex(*name, NULL)));
|
||||
}
|
||||
}
|
||||
// Get the context containing declarations.
|
||||
Handle<Context> context(
|
||||
Context::cast(it.frame()->context())->declaration_context());
|
||||
for (; i < info.NumberOfLocals(); ++i) {
|
||||
Handle<String> name = info.LocalName(i);
|
||||
locals->set(i * 2, *name);
|
||||
locals->set(i * 2 + 1,
|
||||
context->get(scope_info->ContextSlotIndex(*name, NULL)));
|
||||
}
|
||||
|
||||
// Check whether this frame is positioned at return. If not top
|
||||
// frame or if the frame is optimized it cannot be at a return.
|
||||
bool at_return = false;
|
||||
if (!is_optimized_frame && index == 0) {
|
||||
if (!it.frame()->is_optimized() && index == 0) {
|
||||
at_return = isolate->debug()->IsBreakAtReturn(it.frame());
|
||||
}
|
||||
|
||||
@ -10145,10 +10154,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
// Add the at return information.
|
||||
details->set(kFrameDetailsAtReturnIndex, heap->ToBoolean(at_return));
|
||||
|
||||
// Add information on whether this frame is invoked in the debugger context.
|
||||
details->set(kFrameDetailsDebuggerFrameIndex,
|
||||
heap->ToBoolean(*save->context() ==
|
||||
*isolate->debug()->debug_context()));
|
||||
// Add flags to indicate information on whether this frame is
|
||||
// bit 0: invoked in the debugger context.
|
||||
// bit 1: optimized frame.
|
||||
// bit 2: inlined in optimized frame
|
||||
int flags = 0;
|
||||
if (*save->context() == *isolate->debug()->debug_context()) {
|
||||
flags |= 1 << 0;
|
||||
}
|
||||
if (it.frame()->is_optimized()) {
|
||||
flags |= 1 << 1;
|
||||
if (deoptimized_frame_index > 0) {
|
||||
flags |= 1 << 2;
|
||||
}
|
||||
}
|
||||
details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags));
|
||||
|
||||
// Fill the dynamic part.
|
||||
int details_index = kFrameDetailsFirstDynamicIndex;
|
||||
@ -10167,7 +10187,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
//
|
||||
// TODO(3141533): We should be able to get the actual parameter
|
||||
// value for optimized frames.
|
||||
if (!is_optimized_frame &&
|
||||
if (!it.frame()->is_optimized() &&
|
||||
(i < it.frame()->ComputeParametersCount())) {
|
||||
details->set(details_index++, it.frame()->GetParameter(i));
|
||||
} else {
|
||||
@ -10203,6 +10223,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
}
|
||||
details->set(kFrameDetailsReceiverIndex, *receiver);
|
||||
|
||||
// Get rid of the calculated deoptimized frame if any.
|
||||
if (deoptimized_frame != NULL) {
|
||||
Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame,
|
||||
isolate);
|
||||
}
|
||||
|
||||
ASSERT_EQ(details_size, details_index);
|
||||
return *isolate->factory()->NewJSArrayWithElements(details);
|
||||
}
|
||||
|
@ -340,6 +340,9 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
output_ = new FrameDescription*[1];
|
||||
output_[0] = new(output_frame_size) FrameDescription(
|
||||
output_frame_size, function_);
|
||||
#ifdef DEBUG
|
||||
output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
|
||||
// Clear the incoming parameters in the optimized frame to avoid
|
||||
// confusing the garbage collector.
|
||||
@ -454,6 +457,9 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
#ifdef DEBUG
|
||||
output_frame->SetKind(Code::FUNCTION);
|
||||
#endif
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
@ -584,7 +590,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
output_frame->SetState(Smi::FromInt(state));
|
||||
|
||||
// Set the continuation for the topmost frame.
|
||||
if (is_topmost) {
|
||||
if (is_topmost && bailout_type_ != DEBUGGER) {
|
||||
Code* continuation = (bailout_type_ == EAGER)
|
||||
? isolate_->builtins()->builtin(Builtins::kNotifyDeoptimized)
|
||||
: isolate_->builtins()->builtin(Builtins::kNotifyLazyDeoptimized);
|
||||
@ -596,6 +602,26 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
|
||||
// Set the register values. The values are not important as there are no
|
||||
// callee saved registers in JavaScript frames, so all registers are
|
||||
// spilled. Registers rbp and rsp are set to the correct values though.
|
||||
for (int i = 0; i < Register::kNumRegisters; i++) {
|
||||
input_->SetRegister(i, i * 4);
|
||||
}
|
||||
input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
|
||||
input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
|
||||
for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) {
|
||||
input_->SetDoubleRegister(i, 0.0);
|
||||
}
|
||||
|
||||
// Fill the frame content from the actual data on the frame.
|
||||
for (intptr_t i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
|
||||
input_->SetFrameSlot(i, Memory::uint64_at(tos + i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define __ masm()->
|
||||
|
||||
void Deoptimizer::EntryGenerator::Generate() {
|
||||
|
132
test/mjsunit/debug-evaluate-locals-optimized-double.js
Normal file
132
test/mjsunit/debug-evaluate-locals-optimized-double.js
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright 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.
|
||||
|
||||
// Flags: --expose-debug-as debug --allow-natives-syntax
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
listenerComplete = false;
|
||||
exception = false;
|
||||
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
try {
|
||||
if (event == Debug.DebugEvent.Break)
|
||||
{
|
||||
assertEquals(6, exec_state.frameCount());
|
||||
|
||||
for (var i = 0; i < exec_state.frameCount(); i++) {
|
||||
var frame = exec_state.frame(i);
|
||||
// All frames except the bottom one has normal variables a and b.
|
||||
if (i < exec_state.frameCount() - 1) {
|
||||
assertEquals('a', frame.localName(0));
|
||||
assertEquals('b', frame.localName(1));
|
||||
assertEquals(i * 2 + 1 + (i * 2 + 1) / 100,
|
||||
frame.localValue(0).value());
|
||||
assertEquals(i * 2 + 2 + (i * 2 + 2) / 100,
|
||||
frame.localValue(1).value());
|
||||
}
|
||||
|
||||
// When function f is optimized (2 means YES, see runtime.cc) we
|
||||
// expect an optimized frame for f with g1, g2 and g3 inlined.
|
||||
if (%GetOptimizationStatus(f) == 2) {
|
||||
if (i == 1 || i == 2 || i == 3) {
|
||||
assertTrue(frame.isOptimizedFrame());
|
||||
assertTrue(frame.isInlinedFrame());
|
||||
} else if (i == 4) {
|
||||
assertTrue(frame.isOptimizedFrame());
|
||||
assertFalse(frame.isInlinedFrame());
|
||||
} else {
|
||||
assertFalse(frame.isOptimizedFrame());
|
||||
assertFalse(frame.isInlinedFrame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate that all was processed.
|
||||
listenerComplete = true;
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e
|
||||
};
|
||||
};
|
||||
|
||||
f();f();f();
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f();
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
function h(x, y) {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
debugger; // Breakpoint.
|
||||
};
|
||||
|
||||
function g3(x, y) {
|
||||
var a = 3;
|
||||
var b = 4;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
h(a, b);
|
||||
return a+b;
|
||||
};
|
||||
|
||||
function g2(x, y) {
|
||||
var a = 5;
|
||||
var b = 6;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
g3(a, b);
|
||||
};
|
||||
|
||||
function g1(x, y) {
|
||||
var a = 7;
|
||||
var b = 8;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
g2(a, b);
|
||||
};
|
||||
|
||||
function f(x, y) {
|
||||
var a = 9;
|
||||
var b = 10;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
g1(a, b);
|
||||
};
|
||||
|
||||
f(11.11, 12.12);
|
||||
|
||||
// Make sure that the debug event listener vas invoked.
|
||||
assertFalse(exception, "exception in listener " + exception)
|
||||
assertTrue(listenerComplete);
|
||||
|
||||
Debug.setListener(null);
|
119
test/mjsunit/debug-evaluate-locals-optimized.js
Normal file
119
test/mjsunit/debug-evaluate-locals-optimized.js
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 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.
|
||||
|
||||
// Flags: --expose-debug-as debug --allow-natives-syntax
|
||||
// Get the Debug object exposed from the debug context global object.
|
||||
Debug = debug.Debug
|
||||
|
||||
listenerComplete = false;
|
||||
exception = false;
|
||||
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
try {
|
||||
if (event == Debug.DebugEvent.Break)
|
||||
{
|
||||
assertEquals(6, exec_state.frameCount());
|
||||
|
||||
for (var i = 0; i < exec_state.frameCount(); i++) {
|
||||
var frame = exec_state.frame(i);
|
||||
// All frames except the bottom one has normal variables a and b.
|
||||
if (i < exec_state.frameCount() - 1) {
|
||||
assertEquals('a', frame.localName(0));
|
||||
assertEquals('b', frame.localName(1));
|
||||
assertEquals(i * 2 + 1, frame.localValue(0).value());
|
||||
assertEquals(i * 2 + 2, frame.localValue(1).value());
|
||||
}
|
||||
|
||||
// When function f is optimized (2 means YES, see runtime.cc) we
|
||||
// expect an optimized frame for f with g1, g2 and g3 inlined.
|
||||
if (%GetOptimizationStatus(f) == 2) {
|
||||
if (i == 1 || i == 2 || i == 3) {
|
||||
assertTrue(frame.isOptimizedFrame());
|
||||
assertTrue(frame.isInlinedFrame());
|
||||
} else if (i == 4) {
|
||||
assertTrue(frame.isOptimizedFrame());
|
||||
assertFalse(frame.isInlinedFrame());
|
||||
} else {
|
||||
assertFalse(frame.isOptimizedFrame());
|
||||
assertFalse(frame.isInlinedFrame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate that all was processed.
|
||||
listenerComplete = true;
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e
|
||||
};
|
||||
};
|
||||
|
||||
f();f();f();
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f();
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
function h(x, y) {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
debugger; // Breakpoint.
|
||||
};
|
||||
|
||||
function g3(x, y) {
|
||||
var a = 3;
|
||||
var b = 4;
|
||||
h(a, b);
|
||||
};
|
||||
|
||||
function g2(x, y) {
|
||||
var a = 5;
|
||||
var b = 6;
|
||||
g3(a, b);
|
||||
};
|
||||
|
||||
function g1(x, y) {
|
||||
var a = 7;
|
||||
var b = 8;
|
||||
g2(a, b);
|
||||
};
|
||||
|
||||
function f(x, y) {
|
||||
var a = 9;
|
||||
var b = 10;
|
||||
g1(a, b);
|
||||
};
|
||||
|
||||
f(11, 12);
|
||||
|
||||
// Make sure that the debug event listener vas invoked.
|
||||
assertFalse(exception, "exception in listener " + exception)
|
||||
assertTrue(listenerComplete);
|
||||
|
||||
Debug.setListener(null);
|
Loading…
Reference in New Issue
Block a user