Refactor BreakLocationIterator.
We now have BreakLocation::Iterator to iterate via RelocIterator, and create a BreakLocation when we are done iterating. The reloc info is stored in BreakLocation in a GC-safe way and instantiated on demand. R=ulan@chromium.org BUG=v8:3924 LOG=N Review URL: https://codereview.chromium.org/967323002 Cr-Commit-Position: refs/heads/master@{#26983}
This commit is contained in:
parent
efe828e699
commit
1a608493e5
@ -246,27 +246,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
Instr* pc = reinterpret_cast<Instr*>(pc_);
|
||||
Instr* instr = reinterpret_cast<Instr*>(instructions);
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc + i) = *(instr + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
// Patch the code at the current address with a call to the target.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of Operand and MemOperand
|
||||
// See assembler-arm-inl.h for inlined constructors
|
||||
|
@ -824,6 +824,8 @@ class Assembler : public AssemblerBase {
|
||||
static const int kPcLoadDelta = 8;
|
||||
|
||||
static const int kJSReturnSequenceInstructions = 4;
|
||||
static const int kJSReturnSequenceLength =
|
||||
kJSReturnSequenceInstructions * kInstrSize;
|
||||
static const int kDebugBreakSlotInstructions = 3;
|
||||
static const int kDebugBreakSlotLength =
|
||||
kDebugBreakSlotInstructions * kInstrSize;
|
||||
|
@ -12,12 +12,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
// Patch the code changing the return from JS function sequence from
|
||||
// mov sp, fp
|
||||
// ldmia sp!, {fp, lr}
|
||||
@ -28,7 +23,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
// blx ip
|
||||
// <debug break return code entry point address>
|
||||
// bkpt 0
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
|
||||
patcher.masm()->blx(v8::internal::ip);
|
||||
patcher.Emit(
|
||||
@ -37,29 +32,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceInstructions);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the frame exit code is identified by the JS frame exit code
|
||||
// having been patched with a call instruction.
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Patch the code changing the debug break slot code from
|
||||
// mov r2, r2
|
||||
@ -69,7 +42,7 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
// ldr ip, [pc, #0]
|
||||
// blx ip
|
||||
// <debug break slot code entry point address>
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
|
||||
patcher.masm()->blx(v8::internal::ip);
|
||||
patcher.Emit(
|
||||
@ -77,13 +50,6 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kDebugBreakSlotInstructions);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
@ -838,7 +838,7 @@ bool RelocInfo::IsPatchedReturnSequence() {
|
||||
// The sequence must be:
|
||||
// ldr ip0, [pc, #offset]
|
||||
// blr ip0
|
||||
// See arm64/debug-arm64.cc BreakLocationIterator::SetDebugBreakAtReturn().
|
||||
// See arm64/debug-arm64.cc BreakLocation::SetDebugBreakAtReturn().
|
||||
Instruction* i1 = reinterpret_cast<Instruction*>(pc_);
|
||||
Instruction* i2 = i1->following();
|
||||
return i1->IsLdrLiteralX() && (i1->Rt() == ip0.code()) &&
|
||||
|
@ -188,26 +188,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
Instr* pc = reinterpret_cast<Instr*>(pc_);
|
||||
Instr* instr = reinterpret_cast<Instr*>(instructions);
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc + i) = *(instr + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count * kInstructionSize);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
Register GetAllocatableRegisterThatIsNotOneOf(Register reg1, Register reg2,
|
||||
Register reg3, Register reg4) {
|
||||
CPURegList regs(reg1, reg2, reg3, reg4);
|
||||
|
@ -952,7 +952,9 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Number of instructions generated for the return sequence in
|
||||
// FullCodeGenerator::EmitReturnSequence.
|
||||
static const int kJSRetSequenceInstructions = 7;
|
||||
static const int kJSReturnSequenceInstructions = 7;
|
||||
static const int kJSReturnSequenceLength =
|
||||
kJSReturnSequenceInstructions * kInstructionSize;
|
||||
// Distance between start of patched return sequence and the emitted address
|
||||
// to jump to.
|
||||
static const int kPatchReturnSequenceAddressOffset = 0;
|
||||
@ -960,7 +962,7 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Number of instructions necessary to be able to later patch it to a call.
|
||||
// See DebugCodegen::GenerateSlot() and
|
||||
// BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
// BreakLocation::SetDebugBreakAtSlot().
|
||||
static const int kDebugBreakSlotInstructions = 4;
|
||||
static const int kDebugBreakSlotLength =
|
||||
kDebugBreakSlotInstructions * kInstructionSize;
|
||||
|
@ -15,12 +15,8 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
// Patch the code emitted by FullCodeGenerator::EmitReturnSequence, changing
|
||||
// the return from JS function sequence from
|
||||
// mov sp, fp
|
||||
@ -39,8 +35,8 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
|
||||
// The patching code must not overflow the space occupied by the return
|
||||
// sequence.
|
||||
STATIC_ASSERT(Assembler::kJSRetSequenceInstructions >= 5);
|
||||
PatchingAssembler patcher(reinterpret_cast<Instruction*>(rinfo()->pc()), 5);
|
||||
STATIC_ASSERT(Assembler::kJSReturnSequenceInstructions >= 5);
|
||||
PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc()), 5);
|
||||
byte* entry =
|
||||
debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry();
|
||||
|
||||
@ -59,27 +55,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
// Reset the code emitted by EmitReturnSequence to its original state.
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSRetSequenceInstructions);
|
||||
}
|
||||
|
||||
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
// Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug
|
||||
// break slot code from
|
||||
// mov x0, x0 @ nop DEBUG_BREAK_NOP
|
||||
@ -99,7 +75,7 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
// The patching code must not overflow the space occupied by the return
|
||||
// sequence.
|
||||
STATIC_ASSERT(Assembler::kDebugBreakSlotInstructions >= 4);
|
||||
PatchingAssembler patcher(reinterpret_cast<Instruction*>(rinfo()->pc()), 4);
|
||||
PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc()), 4);
|
||||
byte* entry =
|
||||
debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry();
|
||||
|
||||
@ -117,13 +93,6 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kDebugBreakSlotInstructions);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs,
|
||||
|
@ -456,10 +456,10 @@ void FullCodeGenerator::EmitReturnSequence() {
|
||||
|
||||
// Make sure that the constant pool is not emitted inside of the return
|
||||
// sequence. This sequence can get patched when the debugger is used. See
|
||||
// debug-arm64.cc:BreakLocationIterator::SetDebugBreakAtReturn().
|
||||
// debug-arm64.cc:BreakLocation::SetDebugBreakAtReturn().
|
||||
{
|
||||
InstructionAccurateScope scope(masm_,
|
||||
Assembler::kJSRetSequenceInstructions);
|
||||
Assembler::kJSReturnSequenceInstructions);
|
||||
CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
|
||||
__ RecordJSReturn();
|
||||
// This code is generated using Assembler methods rather than Macro
|
||||
|
@ -476,6 +476,9 @@ class RelocInfo {
|
||||
static inline bool IsDebugBreakSlot(Mode mode) {
|
||||
return mode == DEBUG_BREAK_SLOT;
|
||||
}
|
||||
static inline bool IsDebuggerStatement(Mode mode) {
|
||||
return mode == DEBUG_BREAK;
|
||||
}
|
||||
static inline bool IsNone(Mode mode) {
|
||||
return mode == NONE32 || mode == NONE64;
|
||||
}
|
||||
@ -595,9 +598,6 @@ class RelocInfo {
|
||||
template<typename StaticVisitor> inline void Visit(Heap* heap);
|
||||
inline void Visit(Isolate* isolate, ObjectVisitor* v);
|
||||
|
||||
// Patch the code with some other code.
|
||||
void PatchCode(byte* instructions, int instruction_count);
|
||||
|
||||
// Patch the code with a call.
|
||||
void PatchCodeWithCall(Address target, int guard_bytes);
|
||||
|
||||
|
497
src/debug.cc
497
src/debug.cc
@ -60,47 +60,29 @@ static v8::Handle<v8::Context> GetDebugEventContext(Isolate* isolate) {
|
||||
}
|
||||
|
||||
|
||||
BreakLocationIterator::BreakLocationIterator(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type) {
|
||||
debug_info_ = debug_info;
|
||||
type_ = type;
|
||||
reloc_iterator_ = NULL;
|
||||
reloc_iterator_original_ = NULL;
|
||||
Reset(); // Initialize the rest of the member variables.
|
||||
BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type)
|
||||
: debug_info_(debug_info),
|
||||
type_(type),
|
||||
reloc_iterator_(debug_info->code(),
|
||||
~RelocInfo::ModeMask(RelocInfo::CODE_AGE_SEQUENCE)),
|
||||
reloc_iterator_original_(
|
||||
debug_info->original_code(),
|
||||
~RelocInfo::ModeMask(RelocInfo::CODE_AGE_SEQUENCE)),
|
||||
break_index_(-1),
|
||||
position_(1),
|
||||
statement_position_(1) {
|
||||
Next();
|
||||
}
|
||||
|
||||
|
||||
BreakLocationIterator::~BreakLocationIterator() {
|
||||
DCHECK(reloc_iterator_ != NULL);
|
||||
DCHECK(reloc_iterator_original_ != NULL);
|
||||
delete reloc_iterator_;
|
||||
delete reloc_iterator_original_;
|
||||
}
|
||||
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// point location when looking for source break locations.
|
||||
static bool IsSourceBreakStub(Code* code) {
|
||||
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
||||
return major_key == CodeStub::CallFunction;
|
||||
}
|
||||
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// location.
|
||||
static bool IsBreakStub(Code* code) {
|
||||
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
||||
return major_key == CodeStub::CallFunction;
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::Next() {
|
||||
void BreakLocation::Iterator::Next() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
DCHECK(!RinfoDone());
|
||||
|
||||
// Iterate through reloc info for code and original code stopping at each
|
||||
// breakable code target.
|
||||
bool first = break_point_ == -1;
|
||||
bool first = break_index_ == -1;
|
||||
while (!RinfoDone()) {
|
||||
if (!first) RinfoNext();
|
||||
first = false;
|
||||
@ -115,8 +97,8 @@ void BreakLocationIterator::Next() {
|
||||
}
|
||||
// Always update the position as we don't want that to be before the
|
||||
// statement position.
|
||||
position_ = static_cast<int>(
|
||||
rinfo()->data() - debug_info_->shared()->start_position());
|
||||
position_ = static_cast<int>(rinfo()->data() -
|
||||
debug_info_->shared()->start_position());
|
||||
DCHECK(position_ >= 0);
|
||||
DCHECK(statement_position_ >= 0);
|
||||
}
|
||||
@ -131,7 +113,7 @@ void BreakLocationIterator::Next() {
|
||||
position_ = 0;
|
||||
}
|
||||
statement_position_ = position_;
|
||||
break_point_++;
|
||||
break_index_++;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -143,7 +125,7 @@ void BreakLocationIterator::Next() {
|
||||
Code* code = Code::GetCodeFromTargetAddress(target);
|
||||
|
||||
if (RelocInfo::IsConstructCall(rmode()) || code->is_call_stub()) {
|
||||
break_point_++;
|
||||
break_index_++;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -152,144 +134,117 @@ void BreakLocationIterator::Next() {
|
||||
|
||||
if ((code->is_inline_cache_stub() && !code->is_binary_op_stub() &&
|
||||
!code->is_compare_ic_stub() && !code->is_to_boolean_ic_stub())) {
|
||||
break_point_++;
|
||||
break_index_++;
|
||||
return;
|
||||
}
|
||||
if (code->kind() == Code::STUB) {
|
||||
if (IsDebuggerStatement()) {
|
||||
break_point_++;
|
||||
if (RelocInfo::IsDebuggerStatement(rmode())) {
|
||||
break_index_++;
|
||||
return;
|
||||
} else if (type_ == ALL_BREAK_LOCATIONS) {
|
||||
if (IsBreakStub(code)) {
|
||||
break_point_++;
|
||||
} else if (CodeStub::GetMajorKey(code) == CodeStub::CallFunction) {
|
||||
break_index_++;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
DCHECK(type_ == SOURCE_BREAK_LOCATIONS);
|
||||
if (IsSourceBreakStub(code)) {
|
||||
break_point_++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDebugBreakSlot() && type_ != CALLS_AND_RETURNS) {
|
||||
if (RelocInfo::IsDebugBreakSlot(rmode()) && type_ != CALLS_AND_RETURNS) {
|
||||
// There is always a possible break point at a debug break slot.
|
||||
break_point_++;
|
||||
break_index_++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::Next(int count) {
|
||||
while (count > 0) {
|
||||
Next();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find the break point at the supplied address, or the closest one before
|
||||
// the address.
|
||||
void BreakLocationIterator::FindBreakLocationFromAddress(Address pc) {
|
||||
// Run through all break points to locate the one closest to the address.
|
||||
int closest_break_point = 0;
|
||||
int distance = kMaxInt;
|
||||
while (!Done()) {
|
||||
// Check if this break point is closer that what was previously found.
|
||||
if (this->pc() <= pc && pc - this->pc() < distance) {
|
||||
closest_break_point = break_point();
|
||||
distance = static_cast<int>(pc - this->pc());
|
||||
// Check whether we can't get any closer.
|
||||
if (distance == 0) break;
|
||||
}
|
||||
Next();
|
||||
}
|
||||
|
||||
// Move to the break point found.
|
||||
Reset();
|
||||
Next(closest_break_point);
|
||||
BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, Address pc) {
|
||||
Iterator it(debug_info, type);
|
||||
it.SkipTo(BreakIndexFromAddress(debug_info, type, pc));
|
||||
return it.GetBreakLocation();
|
||||
}
|
||||
|
||||
|
||||
// Find the break point closest to the supplied source position.
|
||||
void BreakLocationIterator::FindBreakLocationFromPosition(int position,
|
||||
// Find the break point at the supplied address, or the closest one before
|
||||
// the address.
|
||||
void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, Address pc,
|
||||
List<BreakLocation>* result_out) {
|
||||
int break_index = BreakIndexFromAddress(debug_info, type, pc);
|
||||
Iterator it(debug_info, type);
|
||||
it.SkipTo(break_index);
|
||||
int statement_position = it.statement_position();
|
||||
while (!it.Done() && it.statement_position() == statement_position) {
|
||||
result_out->Add(it.GetBreakLocation());
|
||||
it.Next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, Address pc) {
|
||||
// Run through all break points to locate the one closest to the address.
|
||||
int closest_break = 0;
|
||||
int distance = kMaxInt;
|
||||
for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
|
||||
// Check if this break point is closer that what was previously found.
|
||||
if (it.pc() <= pc && pc - it.pc() < distance) {
|
||||
closest_break = it.break_index();
|
||||
distance = static_cast<int>(pc - it.pc());
|
||||
// Check whether we can't get any closer.
|
||||
if (distance == 0) break;
|
||||
}
|
||||
}
|
||||
return closest_break;
|
||||
}
|
||||
|
||||
|
||||
BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, int position,
|
||||
BreakPositionAlignment alignment) {
|
||||
// Run through all break points to locate the one closest to the source
|
||||
// position.
|
||||
int closest_break_point = 0;
|
||||
int closest_break = 0;
|
||||
int distance = kMaxInt;
|
||||
|
||||
while (!Done()) {
|
||||
for (Iterator it(debug_info, type); !it.Done(); it.Next()) {
|
||||
int next_position;
|
||||
switch (alignment) {
|
||||
case STATEMENT_ALIGNED:
|
||||
next_position = this->statement_position();
|
||||
break;
|
||||
case BREAK_POSITION_ALIGNED:
|
||||
next_position = this->position();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
next_position = this->statement_position();
|
||||
if (alignment == STATEMENT_ALIGNED) {
|
||||
next_position = it.statement_position();
|
||||
} else {
|
||||
DCHECK(alignment == BREAK_POSITION_ALIGNED);
|
||||
next_position = it.position();
|
||||
}
|
||||
// Check if this break point is closer that what was previously found.
|
||||
if (position <= next_position && next_position - position < distance) {
|
||||
closest_break_point = break_point();
|
||||
closest_break = it.break_index();
|
||||
distance = next_position - position;
|
||||
// Check whether we can't get any closer.
|
||||
if (distance == 0) break;
|
||||
}
|
||||
Next();
|
||||
}
|
||||
|
||||
// Move to the break point found.
|
||||
Reset();
|
||||
Next(closest_break_point);
|
||||
Iterator it(debug_info, type);
|
||||
it.SkipTo(closest_break);
|
||||
return it.GetBreakLocation();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::Reset() {
|
||||
// Create relocation iterators for the two code objects.
|
||||
if (reloc_iterator_ != NULL) delete reloc_iterator_;
|
||||
if (reloc_iterator_original_ != NULL) delete reloc_iterator_original_;
|
||||
reloc_iterator_ = new RelocIterator(
|
||||
debug_info_->code(),
|
||||
~RelocInfo::ModeMask(RelocInfo::CODE_AGE_SEQUENCE));
|
||||
reloc_iterator_original_ = new RelocIterator(
|
||||
debug_info_->original_code(),
|
||||
~RelocInfo::ModeMask(RelocInfo::CODE_AGE_SEQUENCE));
|
||||
|
||||
// Position at the first break point.
|
||||
break_point_ = -1;
|
||||
position_ = 1;
|
||||
statement_position_ = 1;
|
||||
Next();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::Done() const {
|
||||
return RinfoDone();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetBreakPoint(Handle<Object> break_point_object) {
|
||||
void BreakLocation::SetBreakPoint(Handle<Object> break_point_object) {
|
||||
// If there is not already a real break point here patch code with debug
|
||||
// break.
|
||||
if (!HasBreakPoint()) SetDebugBreak();
|
||||
DCHECK(IsDebugBreak() || IsDebuggerStatement());
|
||||
// Set the break point information.
|
||||
DebugInfo::SetBreakPoint(debug_info_, code_position(),
|
||||
position(), statement_position(),
|
||||
break_point_object);
|
||||
DebugInfo::SetBreakPoint(debug_info_, pc_offset_, position_,
|
||||
statement_position_, break_point_object);
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearBreakPoint(Handle<Object> break_point_object) {
|
||||
void BreakLocation::ClearBreakPoint(Handle<Object> break_point_object) {
|
||||
// Clear the break point information.
|
||||
DebugInfo::ClearBreakPoint(debug_info_, code_position(), break_point_object);
|
||||
DebugInfo::ClearBreakPoint(debug_info_, pc_offset_, break_point_object);
|
||||
// If there are no more break points here remove the debug break.
|
||||
if (!HasBreakPoint()) {
|
||||
ClearDebugBreak();
|
||||
@ -298,7 +253,7 @@ void BreakLocationIterator::ClearBreakPoint(Handle<Object> break_point_object) {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetOneShot() {
|
||||
void BreakLocation::SetOneShot() {
|
||||
// Debugger statement always calls debugger. No need to modify it.
|
||||
if (IsDebuggerStatement()) return;
|
||||
|
||||
@ -313,7 +268,7 @@ void BreakLocationIterator::SetOneShot() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearOneShot() {
|
||||
void BreakLocation::ClearOneShot() {
|
||||
// Debugger statement always calls debugger. No need to modify it.
|
||||
if (IsDebuggerStatement()) return;
|
||||
|
||||
@ -329,7 +284,7 @@ void BreakLocationIterator::ClearOneShot() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreak() {
|
||||
void BreakLocation::SetDebugBreak() {
|
||||
// Debugger statement always calls debugger. No need to modify it.
|
||||
if (IsDebuggerStatement()) return;
|
||||
|
||||
@ -339,7 +294,7 @@ void BreakLocationIterator::SetDebugBreak() {
|
||||
// handler as the handler and the function is the same.
|
||||
if (IsDebugBreak()) return;
|
||||
|
||||
if (RelocInfo::IsJSReturn(rmode())) {
|
||||
if (IsExit()) {
|
||||
// Patch the frame exit code with a break point.
|
||||
SetDebugBreakAtReturn();
|
||||
} else if (IsDebugBreakSlot()) {
|
||||
@ -353,59 +308,48 @@ void BreakLocationIterator::SetDebugBreak() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreak() {
|
||||
void BreakLocation::ClearDebugBreak() {
|
||||
// Debugger statement always calls debugger. No need to modify it.
|
||||
if (IsDebuggerStatement()) return;
|
||||
|
||||
if (RelocInfo::IsJSReturn(rmode())) {
|
||||
// Restore the frame exit code.
|
||||
ClearDebugBreakAtReturn();
|
||||
if (IsExit()) {
|
||||
// Restore the frame exit code with a break point.
|
||||
RestoreFromOriginal(Assembler::kJSReturnSequenceLength);
|
||||
} else if (IsDebugBreakSlot()) {
|
||||
// Restore the code in the break slot.
|
||||
ClearDebugBreakAtSlot();
|
||||
RestoreFromOriginal(Assembler::kDebugBreakSlotLength);
|
||||
} else {
|
||||
// Patch the IC call.
|
||||
ClearDebugBreakAtIC();
|
||||
// Restore the IC call.
|
||||
rinfo().set_target_address(original_rinfo().target_address());
|
||||
}
|
||||
DCHECK(!IsDebugBreak());
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsStepInLocation(Isolate* isolate) {
|
||||
if (RelocInfo::IsConstructCall(original_rmode())) {
|
||||
return true;
|
||||
} else if (RelocInfo::IsCodeTarget(rmode())) {
|
||||
void BreakLocation::RestoreFromOriginal(int length_in_bytes) {
|
||||
memcpy(pc(), original_pc(), length_in_bytes);
|
||||
CpuFeatures::FlushICache(pc(), length_in_bytes);
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocation::IsStepInLocation() const {
|
||||
if (IsConstructCall()) return true;
|
||||
if (RelocInfo::IsCodeTarget(rmode())) {
|
||||
HandleScope scope(debug_info_->GetIsolate());
|
||||
Address target = original_rinfo()->target_address();
|
||||
Handle<Code> target_code(Code::GetCodeFromTargetAddress(target));
|
||||
if (target_code->kind() == Code::STUB) {
|
||||
return CodeStub::GetMajorKey(*target_code) == CodeStub::CallFunction;
|
||||
}
|
||||
Handle<Code> target_code = CodeTarget();
|
||||
return target_code->is_call_stub();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check whether the break point is at a position which will exit the function.
|
||||
bool BreakLocationIterator::IsExit() const {
|
||||
return (RelocInfo::IsJSReturn(rmode()));
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::HasBreakPoint() {
|
||||
return debug_info_->HasBreakPoint(code_position());
|
||||
}
|
||||
|
||||
|
||||
// Check whether there is a debug break at the current position.
|
||||
bool BreakLocationIterator::IsDebugBreak() {
|
||||
if (RelocInfo::IsJSReturn(rmode())) {
|
||||
return IsDebugBreakAtReturn();
|
||||
bool BreakLocation::IsDebugBreak() const {
|
||||
if (IsExit()) {
|
||||
return rinfo().IsPatchedReturnSequence();
|
||||
} else if (IsDebugBreakSlot()) {
|
||||
return IsDebugBreakAtSlot();
|
||||
return rinfo().IsPatchedDebugBreakSlotSequence();
|
||||
} else {
|
||||
return Debug::IsDebugBreak(rinfo()->target_address());
|
||||
return Debug::IsDebugBreak(rinfo().target_address());
|
||||
}
|
||||
}
|
||||
|
||||
@ -457,70 +401,53 @@ static Handle<Code> DebugBreakForIC(Handle<Code> code, RelocInfo::Mode mode) {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtIC() {
|
||||
void BreakLocation::SetDebugBreakAtIC() {
|
||||
// Patch the original code with the current address as the current address
|
||||
// might have changed by the inline caching since the code was copied.
|
||||
original_rinfo()->set_target_address(rinfo()->target_address());
|
||||
original_rinfo().set_target_address(rinfo().target_address());
|
||||
|
||||
RelocInfo::Mode mode = rmode();
|
||||
if (RelocInfo::IsCodeTarget(mode)) {
|
||||
Address target = rinfo()->target_address();
|
||||
Handle<Code> target_code(Code::GetCodeFromTargetAddress(target));
|
||||
if (RelocInfo::IsCodeTarget(rmode_)) {
|
||||
Handle<Code> target_code = CodeTarget();
|
||||
|
||||
// Patch the code to invoke the builtin debug break function matching the
|
||||
// calling convention used by the call site.
|
||||
Handle<Code> dbgbrk_code = DebugBreakForIC(target_code, mode);
|
||||
rinfo()->set_target_address(dbgbrk_code->entry());
|
||||
Handle<Code> debug_break_code = DebugBreakForIC(target_code, rmode_);
|
||||
rinfo().set_target_address(debug_break_code->entry());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtIC() {
|
||||
// Patch the code to the original invoke.
|
||||
rinfo()->set_target_address(original_rinfo()->target_address());
|
||||
Handle<Object> BreakLocation::BreakPointObjects() const {
|
||||
return debug_info_->GetBreakPointObjects(pc_offset_);
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebuggerStatement() {
|
||||
return RelocInfo::DEBUG_BREAK == rmode();
|
||||
Handle<Code> BreakLocation::CodeTarget() const {
|
||||
DCHECK(IsCodeTarget());
|
||||
Address target = rinfo().target_address();
|
||||
return Handle<Code>(Code::GetCodeFromTargetAddress(target));
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakSlot() {
|
||||
return RelocInfo::DEBUG_BREAK_SLOT == rmode();
|
||||
Handle<Code> BreakLocation::OriginalCodeTarget() const {
|
||||
DCHECK(IsCodeTarget());
|
||||
Address target = original_rinfo().target_address();
|
||||
return Handle<Code>(Code::GetCodeFromTargetAddress(target));
|
||||
}
|
||||
|
||||
|
||||
Object* BreakLocationIterator::BreakPointObjects() {
|
||||
return debug_info_->GetBreakPointObjects(code_position());
|
||||
bool BreakLocation::Iterator::RinfoDone() const {
|
||||
DCHECK(reloc_iterator_.done() == reloc_iterator_original_.done());
|
||||
return reloc_iterator_.done();
|
||||
}
|
||||
|
||||
|
||||
// Clear out all the debug break code. This is ONLY supposed to be used when
|
||||
// shutting down the debugger as it will leave the break point information in
|
||||
// DebugInfo even though the code is patched back to the non break point state.
|
||||
void BreakLocationIterator::ClearAllDebugBreak() {
|
||||
while (!Done()) {
|
||||
ClearDebugBreak();
|
||||
Next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::RinfoDone() const {
|
||||
DCHECK(reloc_iterator_->done() == reloc_iterator_original_->done());
|
||||
return reloc_iterator_->done();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::RinfoNext() {
|
||||
reloc_iterator_->next();
|
||||
reloc_iterator_original_->next();
|
||||
void BreakLocation::Iterator::RinfoNext() {
|
||||
reloc_iterator_.next();
|
||||
reloc_iterator_original_.next();
|
||||
#ifdef DEBUG
|
||||
DCHECK(reloc_iterator_->done() == reloc_iterator_original_->done());
|
||||
if (!reloc_iterator_->done()) {
|
||||
DCHECK(rmode() == original_rmode());
|
||||
}
|
||||
DCHECK(reloc_iterator_.done() == reloc_iterator_original_.done());
|
||||
DCHECK(reloc_iterator_.done() || rmode() == original_rmode());
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -854,14 +781,14 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) {
|
||||
Handle<DebugInfo> debug_info = GetDebugInfo(shared);
|
||||
|
||||
// Find the break point where execution has stopped.
|
||||
BreakLocationIterator break_location_iterator(debug_info,
|
||||
ALL_BREAK_LOCATIONS);
|
||||
// pc points to the instruction after the current one, possibly a break
|
||||
// PC points to the instruction after the current one, possibly a break
|
||||
// location as well. So the "- 1" to exclude it from the search.
|
||||
break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
|
||||
Address call_pc = frame->pc() - 1;
|
||||
BreakLocation break_location =
|
||||
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
|
||||
|
||||
// Check whether step next reached a new statement.
|
||||
if (!StepNextContinue(&break_location_iterator, frame)) {
|
||||
if (!StepNextContinue(&break_location, frame)) {
|
||||
// Decrease steps left if performing multiple steps.
|
||||
if (thread_local_.step_count_ > 0) {
|
||||
thread_local_.step_count_--;
|
||||
@ -871,9 +798,8 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) {
|
||||
// If there is one or more real break points check whether any of these are
|
||||
// triggered.
|
||||
Handle<Object> break_points_hit(heap->undefined_value(), isolate_);
|
||||
if (break_location_iterator.HasBreakPoint()) {
|
||||
Handle<Object> break_point_objects =
|
||||
Handle<Object>(break_location_iterator.BreakPointObjects(), isolate_);
|
||||
if (break_location.HasBreakPoint()) {
|
||||
Handle<Object> break_point_objects = break_location.BreakPointObjects();
|
||||
break_points_hit = CheckBreakPoints(break_point_objects);
|
||||
}
|
||||
|
||||
@ -1058,11 +984,10 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function,
|
||||
DCHECK(*source_position >= 0);
|
||||
|
||||
// Find the break point and change it.
|
||||
BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
|
||||
it.FindBreakLocationFromPosition(*source_position, STATEMENT_ALIGNED);
|
||||
it.SetBreakPoint(break_point_object);
|
||||
|
||||
*source_position = it.statement_position();
|
||||
BreakLocation location = BreakLocation::FromPosition(
|
||||
debug_info, SOURCE_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED);
|
||||
*source_position = location.statement_position();
|
||||
location.SetBreakPoint(break_point_object);
|
||||
|
||||
// At least one active break point now.
|
||||
return debug_info->GetBreakPointCount() > 0;
|
||||
@ -1078,11 +1003,12 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
||||
PrepareForBreakPoints();
|
||||
|
||||
// Obtain shared function info for the function.
|
||||
Object* result = FindSharedFunctionInfoInScript(script, *source_position);
|
||||
Handle<Object> result =
|
||||
FindSharedFunctionInfoInScript(script, *source_position);
|
||||
if (result->IsUndefined()) return false;
|
||||
|
||||
// Make sure the function has set up the debug info.
|
||||
Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
|
||||
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
|
||||
if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
|
||||
// Return if retrieving debug info failed.
|
||||
return false;
|
||||
@ -1102,12 +1028,12 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
||||
DCHECK(position >= 0);
|
||||
|
||||
// Find the break point and change it.
|
||||
BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
|
||||
it.FindBreakLocationFromPosition(position, alignment);
|
||||
it.SetBreakPoint(break_point_object);
|
||||
BreakLocation location = BreakLocation::FromPosition(
|
||||
debug_info, SOURCE_BREAK_LOCATIONS, position, alignment);
|
||||
location.SetBreakPoint(break_point_object);
|
||||
|
||||
position = (alignment == STATEMENT_ALIGNED) ? it.statement_position()
|
||||
: it.position();
|
||||
position = (alignment == STATEMENT_ALIGNED) ? location.statement_position()
|
||||
: location.position();
|
||||
|
||||
*source_position = position + shared->start_position();
|
||||
|
||||
@ -1122,18 +1048,21 @@ void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
|
||||
|
||||
DebugInfoListNode* node = debug_info_list_;
|
||||
while (node != NULL) {
|
||||
Object* result = DebugInfo::FindBreakPointInfo(node->debug_info(),
|
||||
break_point_object);
|
||||
Handle<Object> result =
|
||||
DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object);
|
||||
if (!result->IsUndefined()) {
|
||||
// Get information in the break point.
|
||||
BreakPointInfo* break_point_info = BreakPointInfo::cast(result);
|
||||
Handle<BreakPointInfo> break_point_info =
|
||||
Handle<BreakPointInfo>::cast(result);
|
||||
Handle<DebugInfo> debug_info = node->debug_info();
|
||||
|
||||
// Find the break point and clear it.
|
||||
BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
|
||||
it.FindBreakLocationFromAddress(debug_info->code()->entry() +
|
||||
break_point_info->code_position()->value());
|
||||
it.ClearBreakPoint(break_point_object);
|
||||
Address pc = debug_info->code()->entry() +
|
||||
break_point_info->code_position()->value();
|
||||
|
||||
BreakLocation location =
|
||||
BreakLocation::FromAddress(debug_info, SOURCE_BREAK_LOCATIONS, pc);
|
||||
location.ClearBreakPoint(break_point_object);
|
||||
|
||||
// If there are no more break points left remove the debug info for this
|
||||
// function.
|
||||
@ -1148,15 +1077,17 @@ void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
|
||||
}
|
||||
|
||||
|
||||
// Clear out all the debug break code. This is ONLY supposed to be used when
|
||||
// shutting down the debugger as it will leave the break point information in
|
||||
// DebugInfo even though the code is patched back to the non break point state.
|
||||
void Debug::ClearAllBreakPoints() {
|
||||
DebugInfoListNode* node = debug_info_list_;
|
||||
while (node != NULL) {
|
||||
// Remove all debug break code.
|
||||
BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
|
||||
it.ClearAllDebugBreak();
|
||||
node = node->next();
|
||||
for (DebugInfoListNode* node = debug_info_list_; node != NULL;
|
||||
node = node->next()) {
|
||||
for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
|
||||
!it.Done(); it.Next()) {
|
||||
it.GetBreakLocation().ClearDebugBreak();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all debug info.
|
||||
while (debug_info_list_ != NULL) {
|
||||
RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info());
|
||||
@ -1179,10 +1110,9 @@ void Debug::FloodWithOneShot(Handle<JSFunction> function,
|
||||
}
|
||||
|
||||
// Flood the function with break points.
|
||||
BreakLocationIterator it(GetDebugInfo(shared), type);
|
||||
while (!it.Done()) {
|
||||
it.SetOneShot();
|
||||
it.Next();
|
||||
for (BreakLocation::Iterator it(GetDebugInfo(shared), type); !it.Done();
|
||||
it.Next()) {
|
||||
it.GetBreakLocation().SetOneShot();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1341,64 +1271,49 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
bool is_load_or_store = false;
|
||||
bool is_inline_cache_stub = false;
|
||||
bool is_at_restarted_function = false;
|
||||
bool is_exit = false;
|
||||
bool is_construct_call = false;
|
||||
Handle<Code> call_function_stub;
|
||||
|
||||
{
|
||||
// Find the break location where execution has stopped.
|
||||
DisallowHeapAllocation no_gc;
|
||||
BreakLocationIterator it(debug_info, ALL_BREAK_LOCATIONS);
|
||||
|
||||
// pc points to the instruction after the current one, possibly a break
|
||||
// PC points to the instruction after the current one, possibly a break
|
||||
// location as well. So the "- 1" to exclude it from the search.
|
||||
it.FindBreakLocationFromAddress(frame->pc() - 1);
|
||||
|
||||
is_exit = it.IsExit();
|
||||
is_construct_call = RelocInfo::IsConstructCall(it.rmode());
|
||||
Address call_pc = frame->pc() - 1;
|
||||
BreakLocation location =
|
||||
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
|
||||
|
||||
if (thread_local_.restarter_frame_function_pointer_ == NULL) {
|
||||
if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
|
||||
bool is_call_target = false;
|
||||
Address target = it.rinfo()->target_address();
|
||||
Code* code = Code::GetCodeFromTargetAddress(target);
|
||||
|
||||
is_call_target = code->is_call_stub();
|
||||
is_inline_cache_stub = code->is_inline_cache_stub();
|
||||
is_load_or_store = is_inline_cache_stub && !is_call_target;
|
||||
if (location.IsCodeTarget()) {
|
||||
Handle<Code> target_code = location.CodeTarget();
|
||||
is_inline_cache_stub = target_code->is_inline_cache_stub();
|
||||
is_load_or_store = is_inline_cache_stub && !target_code->is_call_stub();
|
||||
|
||||
// Check if target code is CallFunction stub.
|
||||
Code* maybe_call_function_stub = code;
|
||||
Handle<Code> maybe_call_function_stub = target_code;
|
||||
// If there is a breakpoint at this line look at the original code to
|
||||
// check if it is a CallFunction stub.
|
||||
if (it.IsDebugBreak()) {
|
||||
Address original_target = it.original_rinfo()->target_address();
|
||||
maybe_call_function_stub =
|
||||
Code::GetCodeFromTargetAddress(original_target);
|
||||
if (location.IsDebugBreak()) {
|
||||
maybe_call_function_stub = location.OriginalCodeTarget();
|
||||
}
|
||||
if ((maybe_call_function_stub->kind() == Code::STUB &&
|
||||
CodeStub::GetMajorKey(maybe_call_function_stub) ==
|
||||
CodeStub::GetMajorKey(*maybe_call_function_stub) ==
|
||||
CodeStub::CallFunction) ||
|
||||
maybe_call_function_stub->is_call_stub()) {
|
||||
// Save reference to the code as we may need it to find out arguments
|
||||
// count for 'step in' later.
|
||||
call_function_stub = Handle<Code>(maybe_call_function_stub);
|
||||
call_function_stub = maybe_call_function_stub;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
is_at_restarted_function = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the last break code target step out is the only possibility.
|
||||
if (is_exit || step_action == StepOut) {
|
||||
if (location.IsExit() || step_action == StepOut) {
|
||||
if (step_action == StepOut) {
|
||||
// Skip step_count frames starting with the current one.
|
||||
while (step_count-- > 0 && !frames_it.done()) {
|
||||
frames_it.Advance();
|
||||
}
|
||||
} else {
|
||||
DCHECK(is_exit);
|
||||
DCHECK(location.IsExit());
|
||||
frames_it.Advance();
|
||||
}
|
||||
// Skip builtin functions on the stack.
|
||||
@ -1415,7 +1330,7 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
// Set target frame pointer.
|
||||
ActivateStepOut(frames_it.frame());
|
||||
}
|
||||
} else if (!(is_inline_cache_stub || is_construct_call ||
|
||||
} else if (!(is_inline_cache_stub || location.IsConstructCall() ||
|
||||
!call_function_stub.is_null() || is_at_restarted_function) ||
|
||||
step_action == StepNext || step_action == StepMin) {
|
||||
// Step next or step min.
|
||||
@ -1511,7 +1426,7 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
// Step in through CallFunction stub should also be prepared by caller of
|
||||
// this function (Debug::PrepareStep) which should flood target function
|
||||
// with breakpoints.
|
||||
DCHECK(is_construct_call || is_inline_cache_stub ||
|
||||
DCHECK(location.IsConstructCall() || is_inline_cache_stub ||
|
||||
!call_function_stub.is_null() || is_at_restarted_function);
|
||||
ActivateStepIn(frame);
|
||||
}
|
||||
@ -1524,7 +1439,7 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
// there will be several break points in the same statement when the code is
|
||||
// flooded with one-shot break points. This function helps to perform several
|
||||
// steps before reporting break back to the debugger.
|
||||
bool Debug::StepNextContinue(BreakLocationIterator* break_location_iterator,
|
||||
bool Debug::StepNextContinue(BreakLocation* break_location,
|
||||
JavaScriptFrame* frame) {
|
||||
// StepNext and StepOut shouldn't bring us deeper in code, so last frame
|
||||
// shouldn't be a parent of current frame.
|
||||
@ -1543,11 +1458,11 @@ bool Debug::StepNextContinue(BreakLocationIterator* break_location_iterator,
|
||||
// statement is hit.
|
||||
if (step_action == StepNext || step_action == StepIn) {
|
||||
// Never continue if returning from function.
|
||||
if (break_location_iterator->IsExit()) return false;
|
||||
if (break_location->IsExit()) return false;
|
||||
|
||||
// Continue if we are still on the same frame and in the same statement.
|
||||
int current_statement_position =
|
||||
break_location_iterator->code()->SourceStatementPosition(frame->pc());
|
||||
break_location->code()->SourceStatementPosition(frame->pc());
|
||||
return thread_local_.last_fp_ == frame->UnpaddedFP() &&
|
||||
thread_local_.last_statement_position_ == current_statement_position;
|
||||
}
|
||||
@ -1565,9 +1480,6 @@ bool Debug::IsDebugBreak(Address addr) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Simple function for returning the source positions for active break points.
|
||||
Handle<Object> Debug::GetSourceBreakLocations(
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
@ -1655,15 +1567,12 @@ void Debug::ClearOneShot() {
|
||||
// The current implementation just runs through all the breakpoints. When the
|
||||
// last break point for a function is removed that function is automatically
|
||||
// removed from the list.
|
||||
|
||||
DebugInfoListNode* node = debug_info_list_;
|
||||
while (node != NULL) {
|
||||
BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
|
||||
while (!it.Done()) {
|
||||
it.ClearOneShot();
|
||||
it.Next();
|
||||
for (DebugInfoListNode* node = debug_info_list_; node != NULL;
|
||||
node = node->next()) {
|
||||
for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
|
||||
!it.Done(); it.Next()) {
|
||||
it.GetBreakLocation().ClearOneShot();
|
||||
}
|
||||
node = node->next();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2080,7 +1989,7 @@ void Debug::PrepareForBreakPoints() {
|
||||
}
|
||||
|
||||
|
||||
Object* Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
|
||||
Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
|
||||
int position) {
|
||||
// Iterate the heap looking for SharedFunctionInfo generated from the
|
||||
// script. The inner most SharedFunctionInfo containing the source position
|
||||
@ -2164,7 +2073,7 @@ Object* Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
|
||||
} // End for loop.
|
||||
} // End no-allocation scope.
|
||||
|
||||
if (target.is_null()) return heap->undefined_value();
|
||||
if (target.is_null()) return isolate_->factory()->undefined_value();
|
||||
|
||||
// There will be at least one break point when we are done.
|
||||
has_break_points_ = true;
|
||||
@ -2180,11 +2089,11 @@ Object* Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
|
||||
MaybeHandle<Code> maybe_result = target_function.is_null()
|
||||
? Compiler::GetUnoptimizedCode(target)
|
||||
: Compiler::GetUnoptimizedCode(target_function);
|
||||
if (maybe_result.is_null()) return isolate_->heap()->undefined_value();
|
||||
if (maybe_result.is_null()) return isolate_->factory()->undefined_value();
|
||||
}
|
||||
} // End while loop.
|
||||
|
||||
return *target;
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
|
187
src/debug.h
187
src/debug.h
@ -66,83 +66,161 @@ enum BreakPositionAlignment {
|
||||
};
|
||||
|
||||
|
||||
// Class for iterating through the break points in a function and changing
|
||||
// them.
|
||||
class BreakLocationIterator {
|
||||
class BreakLocation {
|
||||
public:
|
||||
explicit BreakLocationIterator(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type);
|
||||
virtual ~BreakLocationIterator();
|
||||
// Find the break point at the supplied address, or the closest one before
|
||||
// the address.
|
||||
static BreakLocation FromAddress(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, Address pc);
|
||||
|
||||
void Next();
|
||||
void Next(int count);
|
||||
void FindBreakLocationFromAddress(Address pc);
|
||||
void FindBreakLocationFromPosition(int position,
|
||||
static void FromAddressSameStatement(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, Address pc,
|
||||
List<BreakLocation>* result_out);
|
||||
|
||||
static BreakLocation FromPosition(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, int position,
|
||||
BreakPositionAlignment alignment);
|
||||
void Reset();
|
||||
bool Done() const;
|
||||
|
||||
bool IsDebugBreak() const;
|
||||
inline bool IsExit() const { return RelocInfo::IsJSReturn(rmode_); }
|
||||
inline bool IsConstructCall() const {
|
||||
return RelocInfo::IsConstructCall(rmode_);
|
||||
}
|
||||
inline bool IsCodeTarget() const { return RelocInfo::IsCodeTarget(rmode_); }
|
||||
|
||||
Handle<Code> CodeTarget() const;
|
||||
Handle<Code> OriginalCodeTarget() const;
|
||||
|
||||
bool IsStepInLocation() const;
|
||||
inline bool HasBreakPoint() const {
|
||||
return debug_info_->HasBreakPoint(pc_offset_);
|
||||
}
|
||||
|
||||
Handle<Object> BreakPointObjects() const;
|
||||
|
||||
void SetBreakPoint(Handle<Object> break_point_object);
|
||||
void ClearBreakPoint(Handle<Object> break_point_object);
|
||||
|
||||
void SetOneShot();
|
||||
void ClearOneShot();
|
||||
bool IsStepInLocation(Isolate* isolate);
|
||||
bool IsExit() const;
|
||||
bool HasBreakPoint();
|
||||
bool IsDebugBreak();
|
||||
Object* BreakPointObjects();
|
||||
void ClearAllDebugBreak();
|
||||
|
||||
inline RelocInfo rinfo() const {
|
||||
return RelocInfo(pc(), rmode(), data_, code());
|
||||
}
|
||||
|
||||
inline int code_position() {
|
||||
return static_cast<int>(pc() - debug_info_->code()->entry());
|
||||
inline RelocInfo original_rinfo() const {
|
||||
return RelocInfo(original_pc(), original_rmode(), original_data_,
|
||||
original_code());
|
||||
}
|
||||
inline int break_point() { return break_point_; }
|
||||
inline int position() { return position_; }
|
||||
inline int statement_position() { return statement_position_; }
|
||||
inline Address pc() { return reloc_iterator_->rinfo()->pc(); }
|
||||
inline Code* code() { return debug_info_->code(); }
|
||||
inline RelocInfo* rinfo() { return reloc_iterator_->rinfo(); }
|
||||
inline RelocInfo::Mode rmode() const {
|
||||
return reloc_iterator_->rinfo()->rmode();
|
||||
|
||||
inline int position() const { return position_; }
|
||||
inline int statement_position() const { return statement_position_; }
|
||||
|
||||
inline Address pc() const { return code()->entry() + pc_offset_; }
|
||||
inline Address original_pc() const {
|
||||
return original_code()->entry() + original_pc_offset_;
|
||||
}
|
||||
|
||||
inline RelocInfo::Mode rmode() const { return rmode_; }
|
||||
inline RelocInfo::Mode original_rmode() const { return original_rmode_; }
|
||||
|
||||
inline Code* code() const { return debug_info_->code(); }
|
||||
inline Code* original_code() const { return debug_info_->original_code(); }
|
||||
|
||||
private:
|
||||
BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo,
|
||||
RelocInfo* original_rinfo, int position, int statement_position)
|
||||
: debug_info_(debug_info),
|
||||
pc_offset_(static_cast<int>(rinfo->pc() - debug_info->code()->entry())),
|
||||
original_pc_offset_(static_cast<int>(
|
||||
original_rinfo->pc() - debug_info->original_code()->entry())),
|
||||
rmode_(rinfo->rmode()),
|
||||
original_rmode_(original_rinfo->rmode()),
|
||||
data_(rinfo->data()),
|
||||
original_data_(original_rinfo->data()),
|
||||
position_(position),
|
||||
statement_position_(statement_position) {}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(Handle<DebugInfo> debug_info, BreakLocatorType type);
|
||||
|
||||
BreakLocation GetBreakLocation() {
|
||||
return BreakLocation(debug_info_, rinfo(), original_rinfo(), position(),
|
||||
statement_position());
|
||||
}
|
||||
|
||||
inline bool Done() const { return RinfoDone(); }
|
||||
void Next();
|
||||
|
||||
void SkipTo(int count) {
|
||||
while (count-- > 0) Next();
|
||||
}
|
||||
|
||||
inline RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
|
||||
inline RelocInfo::Mode original_rmode() {
|
||||
return reloc_iterator_.rinfo()->rmode();
|
||||
}
|
||||
|
||||
inline RelocInfo* rinfo() { return reloc_iterator_.rinfo(); }
|
||||
inline RelocInfo* original_rinfo() {
|
||||
return reloc_iterator_original_->rinfo();
|
||||
}
|
||||
inline RelocInfo::Mode original_rmode() const {
|
||||
return reloc_iterator_original_->rinfo()->rmode();
|
||||
return reloc_iterator_original_.rinfo();
|
||||
}
|
||||
|
||||
bool IsDebuggerStatement();
|
||||
inline Address pc() { return rinfo()->pc(); }
|
||||
inline Address original_pc() { return original_rinfo()->pc(); }
|
||||
|
||||
protected:
|
||||
int break_index() const { return break_index_; }
|
||||
|
||||
inline int position() const { return position_; }
|
||||
inline int statement_position() const { return statement_position_; }
|
||||
|
||||
private:
|
||||
bool RinfoDone() const;
|
||||
void RinfoNext();
|
||||
|
||||
Handle<DebugInfo> debug_info_;
|
||||
BreakLocatorType type_;
|
||||
int break_point_;
|
||||
RelocIterator reloc_iterator_;
|
||||
RelocIterator reloc_iterator_original_;
|
||||
int break_index_;
|
||||
int position_;
|
||||
int statement_position_;
|
||||
Handle<DebugInfo> debug_info_;
|
||||
RelocIterator* reloc_iterator_;
|
||||
RelocIterator* reloc_iterator_original_;
|
||||
|
||||
private:
|
||||
void SetDebugBreak();
|
||||
DisallowHeapAllocation no_gc_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Iterator);
|
||||
};
|
||||
|
||||
friend class Debug;
|
||||
|
||||
static int BreakIndexFromAddress(Handle<DebugInfo> debug_info,
|
||||
BreakLocatorType type, Address pc);
|
||||
|
||||
void ClearDebugBreak();
|
||||
void RestoreFromOriginal(int length_in_bytes);
|
||||
|
||||
void SetDebugBreakAtIC();
|
||||
void ClearDebugBreakAtIC();
|
||||
|
||||
bool IsDebugBreakAtReturn();
|
||||
void SetDebugBreak();
|
||||
void SetDebugBreakAtReturn();
|
||||
void ClearDebugBreakAtReturn();
|
||||
|
||||
bool IsDebugBreakSlot();
|
||||
bool IsDebugBreakAtSlot();
|
||||
void SetDebugBreakAtSlot();
|
||||
void ClearDebugBreakAtSlot();
|
||||
void SetDebugBreakAtIC();
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BreakLocationIterator);
|
||||
inline bool IsDebuggerStatement() const {
|
||||
return RelocInfo::IsDebuggerStatement(rmode_);
|
||||
}
|
||||
inline bool IsDebugBreakSlot() const {
|
||||
return RelocInfo::IsDebugBreakSlot(rmode_);
|
||||
}
|
||||
|
||||
Handle<DebugInfo> debug_info_;
|
||||
int pc_offset_;
|
||||
int original_pc_offset_;
|
||||
RelocInfo::Mode rmode_;
|
||||
RelocInfo::Mode original_rmode_;
|
||||
intptr_t data_;
|
||||
intptr_t original_data_;
|
||||
int position_;
|
||||
int statement_position_;
|
||||
};
|
||||
|
||||
|
||||
@ -403,8 +481,7 @@ class Debug {
|
||||
void ClearStepping();
|
||||
void ClearStepOut();
|
||||
bool IsStepping() { return thread_local_.step_count_ > 0; }
|
||||
bool StepNextContinue(BreakLocationIterator* break_location_iterator,
|
||||
JavaScriptFrame* frame);
|
||||
bool StepNextContinue(BreakLocation* location, JavaScriptFrame* frame);
|
||||
bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
|
||||
void HandleStepIn(Handle<Object> function_obj, Handle<Object> holder,
|
||||
Address fp, bool is_constructor);
|
||||
@ -422,13 +499,11 @@ class Debug {
|
||||
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
// This function is used in FunctionNameUsing* tests.
|
||||
Object* FindSharedFunctionInfoInScript(Handle<Script> script, int position);
|
||||
Handle<Object> FindSharedFunctionInfoInScript(Handle<Script> script,
|
||||
int position);
|
||||
|
||||
// Returns true if the current stub call is patched to call the debugger.
|
||||
static bool IsDebugBreak(Address addr);
|
||||
// Returns true if the current return statement has been patched to be
|
||||
// a debugger breakpoint.
|
||||
static bool IsDebugBreakAtReturn(RelocInfo* rinfo);
|
||||
|
||||
static Handle<Object> GetSourceBreakLocations(
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
|
@ -179,17 +179,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc_ + i) = *(instructions + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard int3 instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
|
@ -13,60 +13,61 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard int3 instructions can be added if required.
|
||||
void PatchCodeWithCall(Address pc, Address target, int guard_bytes) {
|
||||
// Call instruction takes up 5 bytes and int3 takes up one byte.
|
||||
static const int kCallCodeSize = 5;
|
||||
int code_size = kCallCodeSize + guard_bytes;
|
||||
|
||||
// Create a code patcher.
|
||||
CodePatcher patcher(pc, code_size);
|
||||
|
||||
// Add a label for checking the size of the code used for returning.
|
||||
#ifdef DEBUG
|
||||
Label check_codesize;
|
||||
patcher.masm()->bind(&check_codesize);
|
||||
#endif
|
||||
|
||||
// Patch the code.
|
||||
patcher.masm()->call(target, RelocInfo::NONE32);
|
||||
|
||||
// Check that the size of the code generated is as expected.
|
||||
DCHECK_EQ(kCallCodeSize,
|
||||
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
|
||||
// Add the requested number of int3 instructions after the call.
|
||||
DCHECK_GE(guard_bytes, 0);
|
||||
for (int i = 0; i < guard_bytes; i++) {
|
||||
patcher.masm()->int3();
|
||||
}
|
||||
|
||||
CpuFeatures::FlushICache(pc, code_size);
|
||||
}
|
||||
|
||||
|
||||
// Patch the JS frame exit code with a debug break call. See
|
||||
// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-ia32.cc
|
||||
// for the precise return instructions sequence.
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
DCHECK(Assembler::kJSReturnSequenceLength >=
|
||||
Assembler::kCallInstructionLength);
|
||||
rinfo()->PatchCodeWithCall(
|
||||
debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
|
||||
PatchCodeWithCall(
|
||||
pc(), debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
|
||||
Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceLength);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the frame exit code is identified by the JS frame exit code
|
||||
// having been patched with a call instruction.
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
Isolate* isolate = debug_info_->GetIsolate();
|
||||
rinfo()->PatchCodeWithCall(
|
||||
isolate->builtins()->Slot_DebugBreak()->entry(),
|
||||
PatchCodeWithCall(
|
||||
pc(), isolate->builtins()->Slot_DebugBreak()->entry(),
|
||||
Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
|
@ -307,8 +307,8 @@ Address RelocInfo::call_address() {
|
||||
DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
|
||||
(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
|
||||
// The pc_ offset of 0 assumes mips patched return sequence per
|
||||
// debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
// debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocation::SetDebugBreakAtSlot().
|
||||
return Assembler::target_address_at(pc_, host_);
|
||||
}
|
||||
|
||||
@ -317,8 +317,8 @@ void RelocInfo::set_call_address(Address target) {
|
||||
DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
|
||||
(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
|
||||
// The pc_ offset of 0 assumes mips patched return sequence per
|
||||
// debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
// debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocation::SetDebugBreakAtSlot().
|
||||
Assembler::set_target_address_at(pc_, host_, target);
|
||||
if (host() != NULL) {
|
||||
Object* target_code = Code::GetCodeFromTargetAddress(target);
|
||||
|
@ -214,27 +214,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
Instr* pc = reinterpret_cast<Instr*>(pc_);
|
||||
Instr* instr = reinterpret_cast<Instr*>(instructions);
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc + i) = *(instr + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
// Patch the code at the current address with a call to the target.
|
||||
UNIMPLEMENTED_MIPS();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of Operand and MemOperand.
|
||||
// See assembler-mips-inl.h for inlined constructors.
|
||||
|
@ -591,6 +591,8 @@ class Assembler : public AssemblerBase {
|
||||
// Number of instructions used for the JS return sequence. The constant is
|
||||
// used by the debugger to patch the JS return sequence.
|
||||
static const int kJSReturnSequenceInstructions = 7;
|
||||
static const int kJSReturnSequenceLength =
|
||||
kJSReturnSequenceInstructions * kInstrSize;
|
||||
static const int kDebugBreakSlotInstructions = 4;
|
||||
static const int kDebugBreakSlotLength =
|
||||
kDebugBreakSlotInstructions * kInstrSize;
|
||||
|
@ -14,12 +14,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
// Mips return sequence:
|
||||
// mov sp, fp
|
||||
// lw fp, sp(0)
|
||||
@ -31,7 +26,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
|
||||
// Make sure this constant matches the number if instrucntions we emit.
|
||||
DCHECK(Assembler::kJSReturnSequenceInstructions == 7);
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
// li and Call pseudo-instructions emit two instructions each.
|
||||
patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
|
||||
debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry())));
|
||||
@ -45,29 +40,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceInstructions);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the exit code is identified by the JS frame exit code
|
||||
// having been patched with li/call psuedo-instrunction (liu/ori/jalr).
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Patch the code changing the debug break slot code from:
|
||||
// nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
|
||||
@ -77,20 +50,13 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
// to a call to the debug break slot code.
|
||||
// li t9, address (lui t9 / ori t9 instruction pair)
|
||||
// call t9 (jalr t9 / nop instruction pair)
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
|
||||
debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry())));
|
||||
patcher.masm()->Call(v8::internal::t9);
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kDebugBreakSlotInstructions);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
@ -301,8 +301,8 @@ Address RelocInfo::call_address() {
|
||||
DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
|
||||
(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
|
||||
// The pc_ offset of 0 assumes mips patched return sequence per
|
||||
// debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
// debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocation::SetDebugBreakAtSlot().
|
||||
return Assembler::target_address_at(pc_, host_);
|
||||
}
|
||||
|
||||
@ -311,8 +311,8 @@ void RelocInfo::set_call_address(Address target) {
|
||||
DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
|
||||
(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
|
||||
// The pc_ offset of 0 assumes mips patched return sequence per
|
||||
// debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
// debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocation::SetDebugBreakAtSlot().
|
||||
Assembler::set_target_address_at(pc_, host_, target);
|
||||
if (host() != NULL) {
|
||||
Object* target_code = Code::GetCodeFromTargetAddress(target);
|
||||
|
@ -191,27 +191,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
Instr* pc = reinterpret_cast<Instr*>(pc_);
|
||||
Instr* instr = reinterpret_cast<Instr*>(instructions);
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc + i) = *(instr + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
// Patch the code at the current address with a call to the target.
|
||||
UNIMPLEMENTED_MIPS();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of Operand and MemOperand.
|
||||
// See assembler-mips-inl.h for inlined constructors.
|
||||
|
@ -585,6 +585,8 @@ class Assembler : public AssemblerBase {
|
||||
// Number of instructions used for the JS return sequence. The constant is
|
||||
// used by the debugger to patch the JS return sequence.
|
||||
static const int kJSReturnSequenceInstructions = 7;
|
||||
static const int kJSReturnSequenceLength =
|
||||
kJSReturnSequenceInstructions * kInstrSize;
|
||||
static const int kDebugBreakSlotInstructions = 6;
|
||||
static const int kDebugBreakSlotLength =
|
||||
kDebugBreakSlotInstructions * kInstrSize;
|
||||
|
@ -14,12 +14,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
// Mips return sequence:
|
||||
// mov sp, fp
|
||||
// lw fp, sp(0)
|
||||
@ -31,7 +26,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
|
||||
// Make sure this constant matches the number if instructions we emit.
|
||||
DCHECK(Assembler::kJSReturnSequenceInstructions == 7);
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
// li and Call pseudo-instructions emit 6 + 2 instructions.
|
||||
patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int64_t>(
|
||||
debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry())),
|
||||
@ -44,29 +39,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceInstructions);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the exit code is identified by the JS frame exit code
|
||||
// having been patched with li/call psuedo-instrunction (liu/ori/jalr).
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Patch the code changing the debug break slot code from:
|
||||
// nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
|
||||
@ -78,7 +51,7 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
// to a call to the debug break slot code.
|
||||
// li t9, address (4-instruction sequence on mips64)
|
||||
// call t9 (jalr t9 / nop instruction pair)
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
patcher.masm()->li(v8::internal::t9,
|
||||
Operand(reinterpret_cast<int64_t>(
|
||||
debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry())),
|
||||
@ -87,13 +60,6 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kDebugBreakSlotInstructions);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
@ -16726,12 +16726,14 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
|
||||
|
||||
|
||||
// Get the break point objects for a code position.
|
||||
Object* DebugInfo::GetBreakPointObjects(int code_position) {
|
||||
Handle<Object> DebugInfo::GetBreakPointObjects(int code_position) {
|
||||
Object* break_point_info = GetBreakPointInfo(code_position);
|
||||
if (break_point_info->IsUndefined()) {
|
||||
return GetHeap()->undefined_value();
|
||||
return GetIsolate()->factory()->undefined_value();
|
||||
}
|
||||
return BreakPointInfo::cast(break_point_info)->break_point_objects();
|
||||
return Handle<Object>(
|
||||
BreakPointInfo::cast(break_point_info)->break_point_objects(),
|
||||
GetIsolate());
|
||||
}
|
||||
|
||||
|
||||
@ -16750,22 +16752,22 @@ int DebugInfo::GetBreakPointCount() {
|
||||
}
|
||||
|
||||
|
||||
Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
|
||||
Handle<Object> break_point_object) {
|
||||
Heap* heap = debug_info->GetHeap();
|
||||
if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
|
||||
Handle<Object> DebugInfo::FindBreakPointInfo(
|
||||
Handle<DebugInfo> debug_info, Handle<Object> break_point_object) {
|
||||
Isolate* isolate = debug_info->GetIsolate();
|
||||
if (!debug_info->break_points()->IsUndefined()) {
|
||||
for (int i = 0; i < debug_info->break_points()->length(); i++) {
|
||||
if (!debug_info->break_points()->get(i)->IsUndefined()) {
|
||||
Handle<BreakPointInfo> break_point_info =
|
||||
Handle<BreakPointInfo>(BreakPointInfo::cast(
|
||||
debug_info->break_points()->get(i)));
|
||||
Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
|
||||
BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate);
|
||||
if (BreakPointInfo::HasBreakPointObject(break_point_info,
|
||||
break_point_object)) {
|
||||
return *break_point_info;
|
||||
return break_point_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
return heap->undefined_value();
|
||||
}
|
||||
return isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
|
@ -10773,9 +10773,9 @@ class DebugInfo: public Struct {
|
||||
int source_position, int statement_position,
|
||||
Handle<Object> break_point_object);
|
||||
// Get the break point objects for a code position.
|
||||
Object* GetBreakPointObjects(int code_position);
|
||||
Handle<Object> GetBreakPointObjects(int code_position);
|
||||
// Find the break point info holding this break point object.
|
||||
static Object* FindBreakPointInfo(Handle<DebugInfo> debug_info,
|
||||
static Handle<Object> FindBreakPointInfo(Handle<DebugInfo> debug_info,
|
||||
Handle<Object> break_point_object);
|
||||
// Get the number of break points for this function.
|
||||
int GetBreakPointCount();
|
||||
|
@ -242,8 +242,8 @@ Address RelocInfo::call_address() {
|
||||
DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
|
||||
(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
|
||||
// The pc_ offset of 0 assumes patched return sequence per
|
||||
// BreakLocationIterator::SetDebugBreakAtReturn(), or debug break
|
||||
// slot per BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
// BreakLocation::SetDebugBreakAtReturn(), or debug break
|
||||
// slot per BreakLocation::SetDebugBreakAtSlot().
|
||||
return Assembler::target_address_at(pc_, host_);
|
||||
}
|
||||
|
||||
@ -285,7 +285,7 @@ void RelocInfo::WipeOut() {
|
||||
bool RelocInfo::IsPatchedReturnSequence() {
|
||||
//
|
||||
// The patched return sequence is defined by
|
||||
// BreakLocationIterator::SetDebugBreakAtReturn()
|
||||
// BreakLocation::SetDebugBreakAtReturn()
|
||||
// FIXED_SEQUENCE
|
||||
|
||||
Instr instr0 = Assembler::instr_at(pc_);
|
||||
|
@ -160,27 +160,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
Instr* pc = reinterpret_cast<Instr*>(pc_);
|
||||
Instr* instr = reinterpret_cast<Instr*>(instructions);
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc + i) = *(instr + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
// Patch the code at the current address with a call to the target.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of Operand and MemOperand
|
||||
// See assembler-ppc-inl.h for inlined constructors
|
||||
|
@ -659,9 +659,11 @@ class Assembler : public AssemblerBase {
|
||||
// blrl
|
||||
static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
|
||||
|
||||
// This is the length of the BreakLocationIterator::SetDebugBreakAtReturn()
|
||||
// This is the length of the BreakLocation::SetDebugBreakAtReturn()
|
||||
// code patch FIXED_SEQUENCE
|
||||
static const int kJSReturnSequenceInstructions = kMovInstructions + 3;
|
||||
static const int kJSReturnSequenceLength =
|
||||
kJSReturnSequenceInstructions * kInstrSize;
|
||||
|
||||
// This is the length of the code sequence from SetDebugBreakAtSlot()
|
||||
// FIXED_SEQUENCE
|
||||
|
@ -12,12 +12,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
// Patch the code changing the return from JS function sequence from
|
||||
//
|
||||
// LeaveFrame
|
||||
@ -31,7 +26,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
// blrl
|
||||
// bkpt
|
||||
//
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
|
||||
Assembler::BlockTrampolinePoolScope block_trampoline_pool(patcher.masm());
|
||||
patcher.masm()->mov(
|
||||
v8::internal::r0,
|
||||
@ -45,29 +40,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceInstructions);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the frame exit code is identified by the JS frame exit code
|
||||
// having been patched with a call instruction.
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Patch the code changing the debug break slot code from
|
||||
//
|
||||
@ -83,7 +56,7 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
// mtlr r0
|
||||
// blrl
|
||||
//
|
||||
CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
|
||||
Assembler::BlockTrampolinePoolScope block_trampoline_pool(patcher.masm());
|
||||
patcher.masm()->mov(
|
||||
v8::internal::r0,
|
||||
@ -94,13 +67,6 @@ void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kDebugBreakSlotInstructions);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
@ -1164,18 +1164,19 @@ class ScopeIterator {
|
||||
if (!ignore_nested_scopes) {
|
||||
Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
|
||||
|
||||
// Find the break point where execution has stopped.
|
||||
BreakLocationIterator break_location_iterator(debug_info,
|
||||
ALL_BREAK_LOCATIONS);
|
||||
// pc points to the instruction after the current one, possibly a break
|
||||
// PC points to the instruction after the current one, possibly a break
|
||||
// location as well. So the "- 1" to exclude it from the search.
|
||||
break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
|
||||
Address call_pc = frame->pc() - 1;
|
||||
|
||||
// Find the break point where execution has stopped.
|
||||
BreakLocation location =
|
||||
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
|
||||
|
||||
// Within the return sequence at the moment it is not possible to
|
||||
// get a source position which is consistent with the current scope chain.
|
||||
// Thus all nested with, catch and block contexts are skipped and we only
|
||||
// provide the function scope.
|
||||
ignore_nested_scopes = break_location_iterator.IsExit();
|
||||
ignore_nested_scopes = location.IsExit();
|
||||
}
|
||||
|
||||
if (ignore_nested_scopes) {
|
||||
@ -1559,18 +1560,19 @@ RUNTIME_FUNCTION(Runtime_GetStepInPositions) {
|
||||
|
||||
Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared);
|
||||
|
||||
int len = 0;
|
||||
Handle<JSArray> array(isolate->factory()->NewJSArray(10));
|
||||
// Find the break point where execution has stopped.
|
||||
BreakLocationIterator break_location_iterator(debug_info,
|
||||
ALL_BREAK_LOCATIONS);
|
||||
// Find range of break points starting from the break point where execution
|
||||
// has stopped.
|
||||
Address call_pc = frame->pc() - 1;
|
||||
List<BreakLocation> locations;
|
||||
BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS,
|
||||
call_pc, &locations);
|
||||
|
||||
break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
|
||||
int current_statement_pos = break_location_iterator.statement_position();
|
||||
Handle<JSArray> array = isolate->factory()->NewJSArray(locations.length());
|
||||
|
||||
while (!break_location_iterator.Done()) {
|
||||
int index = 0;
|
||||
for (BreakLocation location : locations) {
|
||||
bool accept;
|
||||
if (break_location_iterator.pc() > frame->pc()) {
|
||||
if (location.pc() > frame->pc()) {
|
||||
accept = true;
|
||||
} else {
|
||||
StackFrame::Id break_frame_id = isolate->debug()->break_frame_id();
|
||||
@ -1587,20 +1589,15 @@ RUNTIME_FUNCTION(Runtime_GetStepInPositions) {
|
||||
}
|
||||
}
|
||||
if (accept) {
|
||||
if (break_location_iterator.IsStepInLocation(isolate)) {
|
||||
Smi* position_value = Smi::FromInt(break_location_iterator.position());
|
||||
if (location.IsStepInLocation()) {
|
||||
Smi* position_value = Smi::FromInt(location.position());
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetElement(
|
||||
array, len, Handle<Object>(position_value, isolate),
|
||||
array, index, Handle<Object>(position_value, isolate),
|
||||
NONE, SLOPPY));
|
||||
len++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
// Advance iterator.
|
||||
break_location_iterator.Next();
|
||||
if (current_statement_pos != break_location_iterator.statement_position()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *array;
|
||||
}
|
||||
|
@ -107,50 +107,6 @@ void CpuFeatures::PrintFeatures() {
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of RelocInfo
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard int3 instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
int code_size = Assembler::kCallSequenceLength + guard_bytes;
|
||||
|
||||
// Create a code patcher.
|
||||
CodePatcher patcher(pc_, code_size);
|
||||
|
||||
// Add a label for checking the size of the code used for returning.
|
||||
#ifdef DEBUG
|
||||
Label check_codesize;
|
||||
patcher.masm()->bind(&check_codesize);
|
||||
#endif
|
||||
|
||||
// Patch the code.
|
||||
patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(target),
|
||||
Assembler::RelocInfoNone());
|
||||
patcher.masm()->call(kScratchRegister);
|
||||
|
||||
// Check that the size of the code generated is as expected.
|
||||
DCHECK_EQ(Assembler::kCallSequenceLength,
|
||||
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
|
||||
// Add the requested number of int3 instructions after the call.
|
||||
for (int i = 0; i < guard_bytes; i++) {
|
||||
patcher.masm()->int3();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc_ + i) = *(instructions + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Register constants.
|
||||
|
||||
|
@ -14,58 +14,57 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard int3 instructions can be added if required.
|
||||
void PatchCodeWithCall(Address pc, Address target, int guard_bytes) {
|
||||
int code_size = Assembler::kCallSequenceLength + guard_bytes;
|
||||
|
||||
// Create a code patcher.
|
||||
CodePatcher patcher(pc, code_size);
|
||||
|
||||
// Add a label for checking the size of the code used for returning.
|
||||
#ifdef DEBUG
|
||||
Label check_codesize;
|
||||
patcher.masm()->bind(&check_codesize);
|
||||
#endif
|
||||
|
||||
// Patch the code.
|
||||
patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(target),
|
||||
Assembler::RelocInfoNone());
|
||||
patcher.masm()->call(kScratchRegister);
|
||||
|
||||
// Check that the size of the code generated is as expected.
|
||||
DCHECK_EQ(Assembler::kCallSequenceLength,
|
||||
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
|
||||
// Add the requested number of int3 instructions after the call.
|
||||
for (int i = 0; i < guard_bytes; i++) {
|
||||
patcher.masm()->int3();
|
||||
}
|
||||
|
||||
CpuFeatures::FlushICache(pc, code_size);
|
||||
}
|
||||
|
||||
|
||||
// Patch the JS frame exit code with a debug break call. See
|
||||
// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x64.cc
|
||||
// for the precise return instructions sequence.
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
DCHECK(Assembler::kJSReturnSequenceLength >= Assembler::kCallSequenceLength);
|
||||
rinfo()->PatchCodeWithCall(
|
||||
debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
|
||||
PatchCodeWithCall(
|
||||
pc(), debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
|
||||
Assembler::kJSReturnSequenceLength - Assembler::kCallSequenceLength);
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceLength);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the frame exit code is identified by the JS frame exit code
|
||||
// having been patched with a call instruction.
|
||||
bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
void BreakLocation::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCodeWithCall(
|
||||
debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry(),
|
||||
PatchCodeWithCall(
|
||||
pc(), debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry(),
|
||||
Assembler::kDebugBreakSlotLength - Assembler::kCallSequenceLength);
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
@ -102,48 +102,6 @@ bool RelocInfo::IsInConstantPool() {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
|
||||
// Patch the code at the current address with the supplied instructions.
|
||||
for (int i = 0; i < instruction_count; i++) {
|
||||
*(pc_ + i) = *(instructions + i);
|
||||
}
|
||||
|
||||
// Indicate that code has changed.
|
||||
CpuFeatures::FlushICache(pc_, instruction_count);
|
||||
}
|
||||
|
||||
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard int3 instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
|
||||
// Call instruction takes up 5 bytes and int3 takes up one byte.
|
||||
static const int kCallCodeSize = 5;
|
||||
int code_size = kCallCodeSize + guard_bytes;
|
||||
|
||||
// Create a code patcher.
|
||||
CodePatcher patcher(pc_, code_size);
|
||||
|
||||
// Add a label for checking the size of the code used for returning.
|
||||
#ifdef DEBUG
|
||||
Label check_codesize;
|
||||
patcher.masm()->bind(&check_codesize);
|
||||
#endif
|
||||
|
||||
// Patch the code.
|
||||
patcher.masm()->call(target, RelocInfo::NONE32);
|
||||
|
||||
// Check that the size of the code generated is as expected.
|
||||
DCHECK_EQ(kCallCodeSize,
|
||||
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
|
||||
// Add the requested number of int3 instructions after the call.
|
||||
DCHECK_GE(guard_bytes, 0);
|
||||
for (int i = 0; i < guard_bytes; i++) {
|
||||
patcher.masm()->int3();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of Operand
|
||||
|
||||
|
@ -13,60 +13,60 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtReturn() {
|
||||
return Debug::IsDebugBreakAtReturn(rinfo());
|
||||
// Patch the code at the current PC with a call to the target address.
|
||||
// Additional guard int3 instructions can be added if required.
|
||||
void RelocInfo::PatchCodeWithCall(Address pc, Address target, int guard_bytes) {
|
||||
// Call instruction takes up 5 bytes and int3 takes up one byte.
|
||||
static const int kCallCodeSize = 5;
|
||||
int code_size = kCallCodeSize + guard_bytes;
|
||||
|
||||
// Create a code patcher.
|
||||
CodePatcher patcher(pc, code_size);
|
||||
|
||||
// Add a label for checking the size of the code used for returning.
|
||||
#ifdef DEBUG
|
||||
Label check_codesize;
|
||||
patcher.masm()->bind(&check_codesize);
|
||||
#endif
|
||||
|
||||
// Patch the code.
|
||||
patcher.masm()->call(target, RelocInfo::NONE32);
|
||||
|
||||
// Check that the size of the code generated is as expected.
|
||||
DCHECK_EQ(kCallCodeSize,
|
||||
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
|
||||
// Add the requested number of int3 instructions after the call.
|
||||
DCHECK_GE(guard_bytes, 0);
|
||||
for (int i = 0; i < guard_bytes; i++) {
|
||||
patcher.masm()->int3();
|
||||
}
|
||||
|
||||
CpuFeatures::FlushICache(pc, code_size);
|
||||
}
|
||||
|
||||
|
||||
// Patch the JS frame exit code with a debug break call. See
|
||||
// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x87.cc
|
||||
// for the precise return instructions sequence.
|
||||
void BreakLocationIterator::SetDebugBreakAtReturn() {
|
||||
void BreakLocation::SetDebugBreakAtReturn() {
|
||||
DCHECK(Assembler::kJSReturnSequenceLength >=
|
||||
Assembler::kCallInstructionLength);
|
||||
rinfo()->PatchCodeWithCall(
|
||||
debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
|
||||
PatchCodeWithCall(
|
||||
pc(), debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
|
||||
Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
|
||||
}
|
||||
|
||||
|
||||
// Restore the JS frame exit code.
|
||||
void BreakLocationIterator::ClearDebugBreakAtReturn() {
|
||||
rinfo()->PatchCode(original_rinfo()->pc(),
|
||||
Assembler::kJSReturnSequenceLength);
|
||||
}
|
||||
|
||||
|
||||
// A debug break in the frame exit code is identified by the JS frame exit code
|
||||
// having been patched with a call instruction.
|
||||
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
|
||||
DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
|
||||
return rinfo->IsPatchedReturnSequence();
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocationIterator::IsDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
// Check whether the debug break slot instructions have been patched.
|
||||
return rinfo()->IsPatchedDebugBreakSlotSequence();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
Isolate* isolate = debug_info_->GetIsolate();
|
||||
rinfo()->PatchCodeWithCall(
|
||||
isolate->builtins()->Slot_DebugBreak()->entry(),
|
||||
rinfo().PatchCodeWithCall(
|
||||
pc(), isolate->builtins()->Slot_DebugBreak()->entry(),
|
||||
Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::ClearDebugBreakAtSlot() {
|
||||
DCHECK(IsDebugBreakSlot());
|
||||
rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
|
||||
}
|
||||
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
|
@ -416,7 +416,7 @@ void CheckDebuggerUnloaded(bool check_functions) {
|
||||
if (RelocInfo::IsCodeTarget(rmode)) {
|
||||
CHECK(!Debug::IsDebugBreak(it.rinfo()->target_address()));
|
||||
} else if (RelocInfo::IsJSReturn(rmode)) {
|
||||
CHECK(!Debug::IsDebugBreakAtReturn(it.rinfo()));
|
||||
CHECK(!it.rinfo()->IsPatchedReturnSequence());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,47 +437,36 @@ static void CheckDebuggerUnloaded(bool check_functions = false) {
|
||||
}
|
||||
|
||||
|
||||
// Inherit from BreakLocationIterator to get access to protected parts for
|
||||
// testing.
|
||||
class TestBreakLocationIterator: public v8::internal::BreakLocationIterator {
|
||||
public:
|
||||
explicit TestBreakLocationIterator(Handle<v8::internal::DebugInfo> debug_info)
|
||||
: BreakLocationIterator(debug_info, v8::internal::SOURCE_BREAK_LOCATIONS) {}
|
||||
v8::internal::RelocIterator* it() { return reloc_iterator_; }
|
||||
v8::internal::RelocIterator* it_original() {
|
||||
return reloc_iterator_original_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Compile a function, set a break point and check that the call at the break
|
||||
// location in the code is the expected debug_break function.
|
||||
void CheckDebugBreakFunction(DebugLocalContext* env,
|
||||
const char* source, const char* name,
|
||||
int position, v8::internal::RelocInfo::Mode mode,
|
||||
Code* debug_break) {
|
||||
v8::internal::Debug* debug = CcTest::i_isolate()->debug();
|
||||
i::Debug* debug = CcTest::i_isolate()->debug();
|
||||
|
||||
// Create function and set the break point.
|
||||
Handle<v8::internal::JSFunction> fun = v8::Utils::OpenHandle(
|
||||
*CompileFunction(env, source, name));
|
||||
Handle<i::JSFunction> fun =
|
||||
v8::Utils::OpenHandle(*CompileFunction(env, source, name));
|
||||
int bp = SetBreakPoint(fun, position);
|
||||
|
||||
// Check that the debug break function is as expected.
|
||||
Handle<v8::internal::SharedFunctionInfo> shared(fun->shared());
|
||||
Handle<i::SharedFunctionInfo> shared(fun->shared());
|
||||
CHECK(Debug::HasDebugInfo(shared));
|
||||
TestBreakLocationIterator it1(Debug::GetDebugInfo(shared));
|
||||
it1.FindBreakLocationFromPosition(position, v8::internal::STATEMENT_ALIGNED);
|
||||
v8::internal::RelocInfo::Mode actual_mode = it1.it()->rinfo()->rmode();
|
||||
if (actual_mode == v8::internal::RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
actual_mode = v8::internal::RelocInfo::CODE_TARGET;
|
||||
i::BreakLocation location = i::BreakLocation::FromPosition(
|
||||
Debug::GetDebugInfo(shared), i::SOURCE_BREAK_LOCATIONS, position,
|
||||
i::STATEMENT_ALIGNED);
|
||||
i::RelocInfo::Mode actual_mode = location.rmode();
|
||||
if (actual_mode == i::RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
actual_mode = i::RelocInfo::CODE_TARGET;
|
||||
}
|
||||
CHECK_EQ(mode, actual_mode);
|
||||
if (mode != v8::internal::RelocInfo::JS_RETURN) {
|
||||
CHECK_EQ(debug_break,
|
||||
Code::GetCodeFromTargetAddress(it1.it()->rinfo()->target_address()));
|
||||
if (mode != i::RelocInfo::JS_RETURN) {
|
||||
CHECK_EQ(debug_break, *location.CodeTarget());
|
||||
} else {
|
||||
CHECK(Debug::IsDebugBreakAtReturn(it1.it()->rinfo()));
|
||||
i::RelocInfo rinfo = location.rinfo();
|
||||
CHECK(i::RelocInfo::IsJSReturn(rinfo.rmode()));
|
||||
CHECK(rinfo.IsPatchedReturnSequence());
|
||||
}
|
||||
|
||||
// Clear the break point and check that the debug break function is no longer
|
||||
@ -485,15 +474,17 @@ void CheckDebugBreakFunction(DebugLocalContext* env,
|
||||
ClearBreakPoint(bp);
|
||||
CHECK(!debug->HasDebugInfo(shared));
|
||||
CHECK(debug->EnsureDebugInfo(shared, fun));
|
||||
TestBreakLocationIterator it2(Debug::GetDebugInfo(shared));
|
||||
it2.FindBreakLocationFromPosition(position, v8::internal::STATEMENT_ALIGNED);
|
||||
actual_mode = it2.it()->rinfo()->rmode();
|
||||
if (actual_mode == v8::internal::RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
actual_mode = v8::internal::RelocInfo::CODE_TARGET;
|
||||
location = i::BreakLocation::FromPosition(Debug::GetDebugInfo(shared),
|
||||
i::SOURCE_BREAK_LOCATIONS, position,
|
||||
i::STATEMENT_ALIGNED);
|
||||
actual_mode = location.rmode();
|
||||
if (actual_mode == i::RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
actual_mode = i::RelocInfo::CODE_TARGET;
|
||||
}
|
||||
CHECK_EQ(mode, actual_mode);
|
||||
if (mode == v8::internal::RelocInfo::JS_RETURN) {
|
||||
CHECK(!Debug::IsDebugBreakAtReturn(it2.it()->rinfo()));
|
||||
if (mode == i::RelocInfo::JS_RETURN) {
|
||||
i::RelocInfo rinfo = location.rinfo();
|
||||
CHECK(!rinfo.IsPatchedReturnSequence());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,11 +81,9 @@ static void CheckFunctionName(v8::Handle<v8::Script> script,
|
||||
|
||||
// Obtain SharedFunctionInfo for the function.
|
||||
isolate->debug()->PrepareForBreakPoints();
|
||||
Object* shared_func_info_ptr =
|
||||
isolate->debug()->FindSharedFunctionInfoInScript(i_script, func_pos);
|
||||
CHECK(shared_func_info_ptr != CcTest::heap()->undefined_value());
|
||||
Handle<SharedFunctionInfo> shared_func_info(
|
||||
SharedFunctionInfo::cast(shared_func_info_ptr));
|
||||
Handle<SharedFunctionInfo> shared_func_info =
|
||||
Handle<SharedFunctionInfo>::cast(
|
||||
isolate->debug()->FindSharedFunctionInfoInScript(i_script, func_pos));
|
||||
|
||||
// Verify inferred function name.
|
||||
SmartArrayPointer<char> inferred_name =
|
||||
|
Loading…
Reference in New Issue
Block a user