df48d89796
In compiled code, a block of instructions that calls the single step handler in the runtime is generated for any pause points. Enabling a breakpoint at a pause point then patches those instructions to instead call a stub. The stub first calls the breakpoint handler in the runtime. The runtime's breakpoint handler returns the original call target for the breakpoint, which the stub then calls. Since the debugger ignores any pause points with the same source location after hitting a breakpoint, this means that setting a breakpoint at a pause point only generates a breakpoint hit event, ensuring that a request to single step after hitting a breakpoint steps to the next location, not the breakpoint location. Similar to compiled code, enabling a breakpoint in interpreted code replaces the original instruction with a special instruction that first calls the breakpoint handler and then performs the original instruction's implementation. However, there is no specific instruction for pause points in interpreted code. Instead, the dispatch loop in the interpreter has two labels for each instruction: one for the instruction's implementation, and another that is used instead when single stepping is enabled. All the single step labels point at the same code block, which first calls the single step handler and then dispatches to the code for the instruction's implementation. This difference in handling breakpoints and single step checks means that pause events are prioritized, not breakpoint hit events, which means single stepping into a breakpoint generates first a pause event and then a breakpoint hit event, which differs from the behavior for compiled code. To fix this, refactor single step handling in the interpreter so that the single step label for breakpoint instructions point to a different code block which first calls the breakpoint handler and then calls the single step handler before dispatching to the original instruction's implementation. TEST=ci Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-dyn-mac-debug-arm64-try Change-Id: Id33132bbecc5d0cbd9e92bc5a13d8d8a80155344 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/481782 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com> Auto-Submit: Tess Strickland <sstrickl@google.com>
90 lines
3.0 KiB
C++
90 lines
3.0 KiB
C++
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/constants_kbc.h"
|
|
|
|
namespace dart {
|
|
|
|
static const intptr_t kInstructionSize0 = 1;
|
|
static const intptr_t kInstructionSizeA = 2;
|
|
static const intptr_t kInstructionSizeD = 2;
|
|
static const intptr_t kInstructionSizeWideD = 5;
|
|
static const intptr_t kInstructionSizeX = 2;
|
|
static const intptr_t kInstructionSizeWideX = 5;
|
|
static const intptr_t kInstructionSizeT = 2;
|
|
static const intptr_t kInstructionSizeWideT = 4;
|
|
static const intptr_t kInstructionSizeA_E = 3;
|
|
static const intptr_t kInstructionSizeWideA_E = 6;
|
|
static const intptr_t kInstructionSizeA_Y = 3;
|
|
static const intptr_t kInstructionSizeWideA_Y = 6;
|
|
static const intptr_t kInstructionSizeD_F = 3;
|
|
static const intptr_t kInstructionSizeWideD_F = 6;
|
|
static const intptr_t kInstructionSizeA_B_C = 4;
|
|
|
|
const intptr_t KernelBytecode::kInstructionSize[] = {
|
|
#define SIZE_ORDN(encoding) kInstructionSize##encoding
|
|
#define SIZE_WIDE(encoding) kInstructionSizeWide##encoding
|
|
#define SIZE_RESV(encoding) SIZE_ORDN(encoding)
|
|
#define SIZE(name, encoding, kind, op1, op2, op3) SIZE_##kind(encoding),
|
|
KERNEL_BYTECODES_LIST(SIZE)
|
|
#undef SIZE_ORDN
|
|
#undef SIZE_WIDE
|
|
#undef SIZE_RESV
|
|
#undef SIZE
|
|
};
|
|
|
|
static const KBCInstr kVMInternal_ImplicitConstructorClosureInstructions[] = {
|
|
KernelBytecode::kVMInternal_ImplicitConstructorClosure,
|
|
0,
|
|
0,
|
|
KernelBytecode::kPush,
|
|
0,
|
|
KernelBytecode::kReturnTOS,
|
|
};
|
|
|
|
static const KBCInstr
|
|
kVMInternal_ImplicitConstructorClosure_WideInstructions[] = {
|
|
KernelBytecode::kTrap,
|
|
};
|
|
|
|
static const KBCInstr kVMInternal_ImplicitInstanceClosureInstructions[] = {
|
|
KernelBytecode::kVMInternal_ImplicitInstanceClosure,
|
|
0,
|
|
0,
|
|
KernelBytecode::kReturnTOS,
|
|
};
|
|
|
|
static const KBCInstr kVMInternal_ImplicitInstanceClosure_WideInstructions[] = {
|
|
KernelBytecode::kTrap,
|
|
};
|
|
|
|
#define DECLARE_INSTRUCTIONS(name, fmt, kind, fmta, fmtb, fmtc) \
|
|
static const KBCInstr k##name##Instructions[] = { \
|
|
KernelBytecode::k##name, \
|
|
KernelBytecode::kReturnTOS, \
|
|
};
|
|
INTERNAL_KERNEL_BYTECODES_WITH_DEFAULT_CODE(DECLARE_INSTRUCTIONS)
|
|
#undef DECLARE_INSTRUCTIONS
|
|
|
|
void KernelBytecode::GetVMInternalBytecodeInstructions(
|
|
Opcode opcode,
|
|
const KBCInstr** instructions,
|
|
intptr_t* instructions_size) {
|
|
switch (opcode) {
|
|
#define CASE(name, fmt, kind, fmta, fmtb, fmtc) \
|
|
case k##name: \
|
|
*instructions = k##name##Instructions; \
|
|
*instructions_size = sizeof(k##name##Instructions); \
|
|
return;
|
|
|
|
INTERNAL_KERNEL_BYTECODES_LIST_WITH_NO_BREAKPOINTS(CASE)
|
|
#undef CASE
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|