mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[vm/debugger] Implement debug breaks in interpreter without bytecode patching.
The list of bytecodes that are checked for debug breaks and single stepping can be tuned later (as well as performance if needed). Fix identification of Dart top activation frame in debugger. Change-Id: Ieab804ba25f84efe173531431c6d311005163433 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104922 Commit-Queue: Régis Crelier <regis@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
487326eef4
commit
ffea2b3245
|
@ -995,6 +995,22 @@ class KernelBytecode {
|
|||
}
|
||||
}
|
||||
|
||||
// The interpreter and this function must agree on the opcodes.
|
||||
DART_FORCE_INLINE static bool IsDebugBreakCheckedOpcode(
|
||||
const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kCheckStack:
|
||||
case KernelBytecode::kDirectCall:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
case KernelBytecode::kDynamicCall:
|
||||
case KernelBytecode::kReturnTOS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t kNativeCallToGrowableListArgc = 2;
|
||||
|
||||
DART_FORCE_INLINE static uint8_t DecodeArgc_Old(const KBCInstr* ret_addr) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "vm/deopt_instructions.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/globals.h"
|
||||
#include "vm/interpreter.h"
|
||||
#include "vm/json_stream.h"
|
||||
#include "vm/kernel.h"
|
||||
#include "vm/longjump.h"
|
||||
|
@ -1484,7 +1485,7 @@ void ActivationFrame::PrintToJSONObjectRegular(JSONObject* jsobj) {
|
|||
jsobj->AddLocation(script, pos);
|
||||
jsobj->AddProperty("function", function());
|
||||
if (IsInterpreted()) {
|
||||
jsobj->AddProperty("bytecode", bytecode());
|
||||
jsobj->AddProperty("code", bytecode());
|
||||
} else {
|
||||
jsobj->AddProperty("code", code());
|
||||
}
|
||||
|
@ -1528,7 +1529,7 @@ void ActivationFrame::PrintToJSONObjectAsyncCausal(JSONObject* jsobj) {
|
|||
jsobj->AddLocation(script, pos);
|
||||
jsobj->AddProperty("function", function());
|
||||
if (IsInterpreted()) {
|
||||
jsobj->AddProperty("bytecode", bytecode());
|
||||
jsobj->AddProperty("code", bytecode());
|
||||
} else {
|
||||
jsobj->AddProperty("code", code());
|
||||
}
|
||||
|
@ -1549,7 +1550,7 @@ void ActivationFrame::PrintToJSONObjectAsyncActivation(JSONObject* jsobj) {
|
|||
jsobj->AddLocation(script, pos);
|
||||
jsobj->AddProperty("function", function());
|
||||
if (IsInterpreted()) {
|
||||
jsobj->AddProperty("bytecode", bytecode());
|
||||
jsobj->AddProperty("code", bytecode());
|
||||
} else {
|
||||
jsobj->AddProperty("code", code());
|
||||
}
|
||||
|
@ -1683,7 +1684,7 @@ void CodeBreakpoint::Enable() {
|
|||
if (!is_enabled_) {
|
||||
if (IsInterpreted()) {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
SetBytecodeBreak();
|
||||
SetBytecodeBreakpoint();
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
@ -1698,7 +1699,7 @@ void CodeBreakpoint::Disable() {
|
|||
if (is_enabled_) {
|
||||
if (IsInterpreted()) {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
UnsetBytecodeBreak();
|
||||
UnsetBytecodeBreakpoint();
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
@ -1910,6 +1911,18 @@ void Debugger::DeoptimizeWorld() {
|
|||
}
|
||||
}
|
||||
|
||||
void Debugger::NotifySingleStepping(bool value) const {
|
||||
isolate_->set_single_step(value);
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Do not call Interpreter::Current(), which may allocate an interpreter.
|
||||
Interpreter* interpreter = Thread::Current()->interpreter();
|
||||
if (interpreter != nullptr) {
|
||||
// Do not reset is_debugging to false if bytecode debug breaks are enabled.
|
||||
interpreter->set_is_debugging(value || HasEnabledBytecodeBreakpoints());
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
}
|
||||
|
||||
ActivationFrame* Debugger::CollectDartFrame(Isolate* isolate,
|
||||
uword pc,
|
||||
StackFrame* frame,
|
||||
|
@ -2406,23 +2419,29 @@ ActivationFrame* Debugger::TopDartFrame() const {
|
|||
StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames,
|
||||
Thread::Current(),
|
||||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* frame = iterator.NextFrame();
|
||||
while ((frame != NULL) && !frame->IsDartFrame()) {
|
||||
StackFrame* frame;
|
||||
while (true) {
|
||||
frame = iterator.NextFrame();
|
||||
}
|
||||
ASSERT(frame != NULL);
|
||||
RELEASE_ASSERT(frame != nullptr);
|
||||
if (!frame->IsDartFrame()) {
|
||||
continue;
|
||||
}
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (frame->is_interpreted()) {
|
||||
Bytecode& bytecode = Bytecode::Handle(frame->LookupDartBytecode());
|
||||
ActivationFrame* activation =
|
||||
new ActivationFrame(frame->pc(), frame->fp(), frame->sp(), bytecode);
|
||||
if (frame->is_interpreted()) {
|
||||
Bytecode& bytecode = Bytecode::Handle(frame->LookupDartBytecode());
|
||||
if (bytecode.function() == Function::null()) {
|
||||
continue; // Skip bytecode stub frame.
|
||||
}
|
||||
ActivationFrame* activation =
|
||||
new ActivationFrame(frame->pc(), frame->fp(), frame->sp(), bytecode);
|
||||
return activation;
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
Code& code = Code::Handle(frame->LookupDartCode());
|
||||
ActivationFrame* activation = new ActivationFrame(
|
||||
frame->pc(), frame->fp(), frame->sp(), code, Object::null_array(), 0);
|
||||
return activation;
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
Code& code = Code::Handle(frame->LookupDartCode());
|
||||
ActivationFrame* activation = new ActivationFrame(
|
||||
frame->pc(), frame->fp(), frame->sp(), code, Object::null_array(), 0);
|
||||
return activation;
|
||||
}
|
||||
|
||||
DebuggerStackTrace* Debugger::StackTrace() {
|
||||
|
@ -2885,6 +2904,24 @@ TokenPosition Debugger::ResolveBreakpointPos(bool in_bytecode,
|
|||
return TokenPosition::kNoSource;
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Find a 'debug break checked' bytecode in the range [pc..end_pc[ and return
|
||||
// the pc after it or nullptr.
|
||||
static const KBCInstr* FindBreakpointCheckedInstr(const KBCInstr* pc,
|
||||
const KBCInstr* end_pc) {
|
||||
while ((pc < end_pc) && !KernelBytecode::IsDebugBreakCheckedOpcode(pc)) {
|
||||
pc = KernelBytecode::Next(pc);
|
||||
}
|
||||
if (pc < end_pc) {
|
||||
ASSERT(KernelBytecode::IsDebugBreakCheckedOpcode(pc));
|
||||
// The checked debug break pc must point to the next bytecode.
|
||||
return KernelBytecode::Next(pc);
|
||||
}
|
||||
// No 'debug break checked' bytecode in the range.
|
||||
return nullptr;
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
void Debugger::MakeCodeBreakpointAt(const Function& func,
|
||||
BreakpointLocation* loc) {
|
||||
ASSERT(loc->token_pos_.IsReal());
|
||||
|
@ -2895,24 +2932,45 @@ void Debugger::MakeCodeBreakpointAt(const Function& func,
|
|||
if (func.HasBytecode()) {
|
||||
Bytecode& bytecode = Bytecode::Handle(func.bytecode());
|
||||
ASSERT(!bytecode.IsNull());
|
||||
uword lowest_pc_offset = kUwordMax;
|
||||
const KBCInstr* pc = nullptr;
|
||||
if (bytecode.HasSourcePositions()) {
|
||||
kernel::BytecodeSourcePositionsIterator iter(Thread::Current()->zone(),
|
||||
bytecode);
|
||||
bool check_range = false;
|
||||
while (iter.MoveNext()) {
|
||||
if (iter.TokenPos() == loc->token_pos_) {
|
||||
if (iter.PcOffset() < lowest_pc_offset) {
|
||||
lowest_pc_offset = iter.PcOffset();
|
||||
if (check_range) {
|
||||
const KBCInstr* end_pc =
|
||||
reinterpret_cast<const KBCInstr*>(bytecode.PayloadStart()) +
|
||||
iter.PcOffset();
|
||||
check_range = false;
|
||||
// Find a 'debug break checked' bytecode in the range [pc..end_pc[.
|
||||
pc = FindBreakpointCheckedInstr(pc, end_pc);
|
||||
if (pc != nullptr) {
|
||||
// TODO(regis): We may want to find all PCs for a token position,
|
||||
// e.g. in the case of duplicated bytecode in finally clauses.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iter.TokenPos() == loc->token_pos_) {
|
||||
pc = reinterpret_cast<const KBCInstr*>(bytecode.PayloadStart()) +
|
||||
iter.PcOffset();
|
||||
check_range = true;
|
||||
}
|
||||
}
|
||||
if (check_range) {
|
||||
ASSERT(pc != nullptr);
|
||||
// Use the end of the bytecode as the end of the range to check.
|
||||
pc = FindBreakpointCheckedInstr(
|
||||
pc, reinterpret_cast<const KBCInstr*>(bytecode.PayloadStart()) +
|
||||
bytecode.Size());
|
||||
}
|
||||
}
|
||||
if (lowest_pc_offset != kUwordMax) {
|
||||
uword lowest_pc = bytecode.PayloadStart() + lowest_pc_offset;
|
||||
CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
|
||||
if (pc != nullptr) {
|
||||
CodeBreakpoint* code_bpt = GetCodeBreakpoint(reinterpret_cast<uword>(pc));
|
||||
if (code_bpt == NULL) {
|
||||
// No code breakpoint for this code exists; create one.
|
||||
code_bpt = new CodeBreakpoint(bytecode, loc->token_pos_, lowest_pc);
|
||||
code_bpt = new CodeBreakpoint(bytecode, loc->token_pos_,
|
||||
reinterpret_cast<uword>(pc));
|
||||
RegisterCodeBreakpoint(code_bpt);
|
||||
}
|
||||
code_bpt->set_bpt_location(loc);
|
||||
|
@ -3575,7 +3633,7 @@ void Debugger::Pause(ServiceEvent* event) {
|
|||
void Debugger::EnterSingleStepMode() {
|
||||
ResetSteppingFramePointers();
|
||||
DeoptimizeWorld();
|
||||
isolate_->set_single_step(true);
|
||||
NotifySingleStepping(true);
|
||||
}
|
||||
|
||||
void Debugger::ResetSteppingFramePointers() {
|
||||
|
@ -3637,7 +3695,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
|||
// as well. We need to deoptimize the world in case we are about
|
||||
// to call an optimized function.
|
||||
DeoptimizeWorld();
|
||||
isolate_->set_single_step(true);
|
||||
NotifySingleStepping(true);
|
||||
skip_next_step_ = skip_next_step;
|
||||
SetAsyncSteppingFramePointer(stack_trace);
|
||||
if (FLAG_verbose_debug) {
|
||||
|
@ -3645,7 +3703,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
|||
}
|
||||
} else if (resume_action_ == kStepOver) {
|
||||
DeoptimizeWorld();
|
||||
isolate_->set_single_step(true);
|
||||
NotifySingleStepping(true);
|
||||
skip_next_step_ = skip_next_step;
|
||||
SetSyncSteppingFramePointer(stack_trace);
|
||||
SetAsyncSteppingFramePointer(stack_trace);
|
||||
|
@ -3669,7 +3727,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
|||
}
|
||||
// Fall through to synchronous stepping.
|
||||
DeoptimizeWorld();
|
||||
isolate_->set_single_step(true);
|
||||
NotifySingleStepping(true);
|
||||
// Find topmost caller that is debuggable.
|
||||
for (intptr_t i = 1; i < stack_trace->Length(); i++) {
|
||||
ActivationFrame* frame = stack_trace->FrameAt(i);
|
||||
|
@ -3959,7 +4017,7 @@ bool Debugger::IsDebugging(Thread* thread, const Function& func) {
|
|||
void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) {
|
||||
resume_action_ = kContinue;
|
||||
ResetSteppingFramePointers();
|
||||
isolate_->set_single_step(false);
|
||||
NotifySingleStepping(false);
|
||||
ASSERT(!IsPaused());
|
||||
if ((bpt != NULL) && bpt->IsSingleShot()) {
|
||||
RemoveBreakpoint(bpt->id());
|
||||
|
@ -4695,7 +4753,7 @@ void Debugger::Continue() {
|
|||
SetResumeAction(kContinue);
|
||||
stepping_fp_ = 0;
|
||||
async_stepping_fp_ = 0;
|
||||
isolate_->set_single_step(false);
|
||||
NotifySingleStepping(false);
|
||||
}
|
||||
|
||||
BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url,
|
||||
|
|
|
@ -230,8 +230,8 @@ class CodeBreakpoint {
|
|||
|
||||
void PatchCode();
|
||||
void RestoreCode();
|
||||
void SetBytecodeBreak();
|
||||
void UnsetBytecodeBreak();
|
||||
void SetBytecodeBreakpoint();
|
||||
void UnsetBytecodeBreakpoint();
|
||||
|
||||
RawCode* code_;
|
||||
RawBytecode* bytecode_;
|
||||
|
@ -552,6 +552,10 @@ class Debugger {
|
|||
ignore_breakpoints_ = ignore_breakpoints;
|
||||
}
|
||||
|
||||
bool HasEnabledBytecodeBreakpoints() const;
|
||||
// Called from the interpreter. Note that pc already points to next bytecode.
|
||||
bool HasBytecodeBreakpointAt(const KBCInstr* next_pc) const;
|
||||
|
||||
// Put the isolate into single stepping mode when Dart code next runs.
|
||||
//
|
||||
// This is used by the vm service to allow the user to step while
|
||||
|
@ -671,6 +675,7 @@ class Debugger {
|
|||
intptr_t requested_column,
|
||||
TokenPosition exact_token_pos);
|
||||
void DeoptimizeWorld();
|
||||
void NotifySingleStepping(bool value) const;
|
||||
BreakpointLocation* SetCodeBreakpoints(bool in_bytecode,
|
||||
BreakpointLocation* loc,
|
||||
const Script& script,
|
||||
|
|
|
@ -7,24 +7,47 @@
|
|||
|
||||
#include "vm/debugger.h"
|
||||
#include "vm/instructions_kbc.h"
|
||||
#include "vm/interpreter.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
#ifndef PRODUCT
|
||||
|
||||
void CodeBreakpoint::SetBytecodeBreak() {
|
||||
void CodeBreakpoint::SetBytecodeBreakpoint() {
|
||||
ASSERT(!is_enabled_);
|
||||
ASSERT(!Isolate::Current()->is_using_old_bytecode_instructions());
|
||||
// TODO(regis): Register pc_ (or the token pos range including pc_) with the
|
||||
// interpreter as a debug break address.
|
||||
is_enabled_ = true;
|
||||
Interpreter::Current()->set_is_debugging(true);
|
||||
}
|
||||
|
||||
void CodeBreakpoint::UnsetBytecodeBreak() {
|
||||
void CodeBreakpoint::UnsetBytecodeBreakpoint() {
|
||||
ASSERT(is_enabled_);
|
||||
// TODO(regis): Unregister pc_ (or the token pos range including pc_) with the
|
||||
// interpreter as a debug break address.
|
||||
is_enabled_ = false;
|
||||
if (!Isolate::Current()->single_step() &&
|
||||
!Isolate::Current()->debugger()->HasEnabledBytecodeBreakpoints()) {
|
||||
Interpreter::Current()->set_is_debugging(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Debugger::HasEnabledBytecodeBreakpoints() const {
|
||||
CodeBreakpoint* cbpt = code_breakpoints_;
|
||||
while (cbpt != nullptr) {
|
||||
if (cbpt->IsEnabled() && cbpt->IsInterpreted()) {
|
||||
return true;
|
||||
}
|
||||
cbpt = cbpt->next();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Debugger::HasBytecodeBreakpointAt(const KBCInstr* next_pc) const {
|
||||
CodeBreakpoint* cbpt = code_breakpoints_;
|
||||
while (cbpt != nullptr) {
|
||||
if ((reinterpret_cast<uword>(next_pc)) == cbpt->pc_ && cbpt->IsEnabled()) {
|
||||
ASSERT(cbpt->IsInterpreted());
|
||||
return true;
|
||||
}
|
||||
cbpt = cbpt->next();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ Interpreter::~Interpreter() {
|
|||
Interpreter* Interpreter::Current() {
|
||||
Thread* thread = Thread::Current();
|
||||
Interpreter* interpreter = thread->interpreter();
|
||||
if (interpreter == NULL) {
|
||||
if (interpreter == nullptr) {
|
||||
TransitionGeneratedToVM transition(thread);
|
||||
interpreter = new Interpreter();
|
||||
Thread::Current()->set_interpreter(interpreter);
|
||||
|
@ -1167,6 +1167,25 @@ static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 7,
|
|||
} \
|
||||
}
|
||||
|
||||
#ifdef PRODUCT
|
||||
#define DEBUG_CHECK
|
||||
#else
|
||||
#define DEBUG_CHECK \
|
||||
if (is_debugging()) { \
|
||||
/* Check for debug breakpoint or if single stepping. */ \
|
||||
if (thread->isolate()->debugger()->HasBytecodeBreakpointAt(pc)) { \
|
||||
SP[1] = null_value; \
|
||||
Exit(thread, FP, SP + 2, pc); \
|
||||
NativeArguments args(thread, 0, NULL, SP + 1); \
|
||||
INVOKE_RUNTIME(DRT_BreakpointRuntimeHandler, args) \
|
||||
} else if (thread->isolate()->single_step()) { \
|
||||
Exit(thread, FP, SP + 1, pc); \
|
||||
NativeArguments args(thread, 0, NULL, NULL); \
|
||||
INVOKE_RUNTIME(DRT_SingleStepHandler, args); \
|
||||
} \
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
||||
bool Interpreter::AssertAssignable(Thread* thread,
|
||||
const KBCInstr* pc,
|
||||
RawObject** FP,
|
||||
|
@ -1762,6 +1781,7 @@ SwitchDispatch:
|
|||
|
||||
{
|
||||
BYTECODE(CheckStack, A);
|
||||
DEBUG_CHECK;
|
||||
{
|
||||
// Check the interpreter's own stack limit for actual interpreter's stack
|
||||
// overflows, and also the thread's stack limit for scheduled interrupts.
|
||||
|
@ -1956,16 +1976,7 @@ SwitchDispatch:
|
|||
|
||||
{
|
||||
BYTECODE(DirectCall, D_F);
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Check if single stepping.
|
||||
if (thread->isolate()->single_step()) {
|
||||
Exit(thread, FP, SP + 1, pc);
|
||||
NativeArguments args(thread, 0, NULL, NULL);
|
||||
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
DEBUG_CHECK;
|
||||
// Invoke target function.
|
||||
{
|
||||
const uint32_t argc = rF;
|
||||
|
@ -1986,16 +1997,7 @@ SwitchDispatch:
|
|||
|
||||
{
|
||||
BYTECODE(InterfaceCall, D_F);
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Check if single stepping.
|
||||
if (thread->isolate()->single_step()) {
|
||||
Exit(thread, FP, SP + 1, pc);
|
||||
NativeArguments args(thread, 0, NULL, NULL);
|
||||
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
DEBUG_CHECK;
|
||||
{
|
||||
const uint32_t argc = rF;
|
||||
const uint32_t kidx = rD;
|
||||
|
@ -2018,16 +2020,7 @@ SwitchDispatch:
|
|||
|
||||
{
|
||||
BYTECODE(UncheckedInterfaceCall, D_F);
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Check if single stepping.
|
||||
if (thread->isolate()->single_step()) {
|
||||
Exit(thread, FP, SP + 1, pc);
|
||||
NativeArguments args(thread, 0, NULL, NULL);
|
||||
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
DEBUG_CHECK;
|
||||
{
|
||||
const uint32_t argc = rF;
|
||||
const uint32_t kidx = rD;
|
||||
|
@ -2050,16 +2043,7 @@ SwitchDispatch:
|
|||
|
||||
{
|
||||
BYTECODE(DynamicCall, D_F);
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Check if single stepping.
|
||||
if (thread->isolate()->single_step()) {
|
||||
Exit(thread, FP, SP + 1, pc);
|
||||
NativeArguments args(thread, 0, NULL, NULL);
|
||||
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
DEBUG_CHECK;
|
||||
{
|
||||
const uint32_t argc = rF;
|
||||
const uint32_t kidx = rD;
|
||||
|
@ -2275,6 +2259,7 @@ SwitchDispatch:
|
|||
RawObject* result; // result to return to the caller.
|
||||
|
||||
BYTECODE(ReturnTOS, 0);
|
||||
DEBUG_CHECK;
|
||||
result = *SP;
|
||||
// Restore caller PC.
|
||||
pc = SavedCallerPC(FP);
|
||||
|
|
|
@ -122,6 +122,11 @@ class Interpreter {
|
|||
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
||||
void MajorGC() { lookup_cache_.Clear(); }
|
||||
|
||||
#ifndef PRODUCT
|
||||
void set_is_debugging(bool value) { is_debugging_ = value; }
|
||||
bool is_debugging() const { return is_debugging_; }
|
||||
#endif // !PRODUCT
|
||||
|
||||
private:
|
||||
uintptr_t* stack_;
|
||||
uword stack_base_;
|
||||
|
@ -274,6 +279,10 @@ class Interpreter {
|
|||
last_setjmp_buffer_ = buffer;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
bool is_debugging_;
|
||||
#endif // !PRODUCT
|
||||
|
||||
friend class InterpreterSetjmpBuffer;
|
||||
DISALLOW_COPY_AND_ASSIGN(Interpreter);
|
||||
};
|
||||
|
|
|
@ -976,9 +976,10 @@ DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) {
|
|||
StackFrameIterator::kNoCrossThreadIteration);
|
||||
StackFrame* caller_frame = iterator.NextFrame();
|
||||
ASSERT(caller_frame != NULL);
|
||||
ASSERT(!caller_frame->is_interpreted());
|
||||
const Code& orig_stub = Code::Handle(
|
||||
zone, isolate->debugger()->GetPatchedStubAddress(caller_frame->pc()));
|
||||
Code& orig_stub = Code::Handle(zone);
|
||||
if (!caller_frame->is_interpreted()) {
|
||||
orig_stub = isolate->debugger()->GetPatchedStubAddress(caller_frame->pc());
|
||||
}
|
||||
const Error& error =
|
||||
Error::Handle(zone, isolate->debugger()->PauseBreakpoint());
|
||||
ThrowIfError(error);
|
||||
|
|
Loading…
Reference in a new issue