Skip to content

Commit af074fb

Browse files
sstricklCommit Queue
authored andcommitted
[vm,dyn_modules] Initial handling of breakpoints in the interpreter.
Adds a set of new VMInternal_Breakpoint instructions, one for each possible instruction size, and adds a bytecode_ and saved_opcode_ field to code breakpoints. When enabling a breakpoint, the original opcode of the instruction is replaced with the same-sized VmInternal_Breakpoint opcode and stored in the saved_opcode_ field of the CodeBreakpoint. When disabling it, the original opcode is replaced. New labels are added to the dispatch loop for single stepping purposes. Both the computed goto and switch dispatch versions of the dispatch loop are appropriately altered to dispatch to the single stepping labels instead of the original ones if single stepping is currently enabled. Fix up more parts of the debugger that assumed functions had Code objects to handle functions with Bytecode objects as well. In particular, instead of using the PcDescriptors to find safepoint source locations in Bytecode objects, the source positions information is used instead (since the PcDescriptors for Bytecode objects only stores information about the start and end of try blocks at the moment). Todo (from looking at the remaining failing tests): * Handle async jumps. * Handle coverage information. TEST=now-passing tests from pkg/vm_service like pkg/vm_service/test/break_on_function_test Change-Id: Icbd4b818e00508d9a4e74c81520aad2363b26d41 Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-mac-debug-arm64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/444880 Reviewed-by: Alexander Markov <[email protected]> Commit-Queue: Tess Strickland <[email protected]>
1 parent 12b9e31 commit af074fb

File tree

8 files changed

+628
-104
lines changed

8 files changed

+628
-104
lines changed

runtime/vm/code_patcher.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
#include "vm/code_patcher.h"
6+
#if defined(DART_DYNAMIC_MODULES)
7+
#include "vm/constants_kbc.h"
8+
#endif
69
#include "vm/cpu.h"
710
#include "vm/instructions.h"
811
#include "vm/object.h"
@@ -61,4 +64,63 @@ bool MatchesPattern(uword end, const int16_t* pattern, intptr_t size) {
6164
return true;
6265
}
6366

67+
#if !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
68+
69+
uint32_t BytecodePatcher::AddBreakpointAt(uword return_address,
70+
const Bytecode& bytecode) {
71+
auto thread = Thread::Current();
72+
uint32_t old_opcode;
73+
thread->isolate_group()->RunWithStoppedMutators([&]() {
74+
old_opcode =
75+
AddBreakpointAtWithMutatorsStopped(thread, return_address, bytecode);
76+
});
77+
return old_opcode;
78+
}
79+
80+
void BytecodePatcher::RemoveBreakpointAt(uword return_address,
81+
const Bytecode& bytecode,
82+
uint32_t opcode) {
83+
auto thread = Thread::Current();
84+
thread->isolate_group()->RunWithStoppedMutators([&]() {
85+
RemoveBreakpointAtWithMutatorsStopped(thread, return_address, bytecode,
86+
opcode);
87+
});
88+
}
89+
90+
KBCInstr* GetInstructionBefore(const Bytecode& bytecode, uword return_address) {
91+
ASSERT(bytecode.ContainsInstructionAt(return_address));
92+
ASSERT(return_address != bytecode.PayloadStart());
93+
uword prev = bytecode.PayloadStart();
94+
uword current = KernelBytecode::Next(prev);
95+
while (current < return_address) {
96+
prev = current;
97+
current = KernelBytecode::Next(prev);
98+
}
99+
ASSERT_EQUAL(current, return_address);
100+
return reinterpret_cast<KBCInstr*>(prev);
101+
}
102+
103+
uint32_t BytecodePatcher::AddBreakpointAtWithMutatorsStopped(
104+
Thread* thread,
105+
uword return_address,
106+
const Bytecode& bytecode) {
107+
auto* const instr = GetInstructionBefore(bytecode, return_address);
108+
uint32_t old_opcode = *instr;
109+
*instr = KernelBytecode::BreakpointOpcode(instr);
110+
return old_opcode;
111+
}
112+
113+
void BytecodePatcher::RemoveBreakpointAtWithMutatorsStopped(
114+
Thread* thread,
115+
uword return_address,
116+
const Bytecode& bytecode,
117+
uint32_t opcode) {
118+
auto* const instr = GetInstructionBefore(bytecode, return_address);
119+
// Must be previously enabled and not yet removed.
120+
ASSERT(*instr == KernelBytecode::BreakpointOpcode(
121+
static_cast<KernelBytecode::Opcode>(opcode)));
122+
*instr = opcode;
123+
}
124+
#endif // !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
125+
64126
} // namespace dart

runtime/vm/code_patcher.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,32 @@ class CodePatcher : public AllStatic {
101101
static intptr_t GetSubtypeTestCachePoolIndex(uword return_address);
102102
};
103103

104+
#if !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
105+
class BytecodePatcher : public AllStatic {
106+
public:
107+
// Patch call instruction prior to return_address to add a breakpoint.
108+
// Returns the original opcode of the patched instruction.
109+
static uint32_t AddBreakpointAt(uword return_address,
110+
const Bytecode& bytecode);
111+
112+
// Patch call instruction prior to return_address to remove a breakpoint.
113+
// Replaces the breakpoint instruction opcode with the provided opcode.
114+
static void RemoveBreakpointAt(uword return_address,
115+
const Bytecode& code,
116+
uint32_t opcode);
117+
118+
private:
119+
static uint32_t AddBreakpointAtWithMutatorsStopped(Thread* thread,
120+
uword return_address,
121+
const Bytecode& bytecode);
122+
123+
static void RemoveBreakpointAtWithMutatorsStopped(Thread* thread,
124+
uword return_address,
125+
const Bytecode& bytecode,
126+
uint32_t opcode);
127+
};
128+
#endif // !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
129+
104130
// Beginning from [end - size] we compare [size] bytes with [pattern]. All
105131
// [0..255] values in [pattern] have to match, negative values are skipped.
106132
//

runtime/vm/constants_kbc.h

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ namespace dart {
225225
V(VMInternal_ForwardDynamicInvocation, 0, ORDN, ___, ___, ___) \
226226
V(VMInternal_ImplicitStaticClosure, 0, ORDN, ___, ___, ___) \
227227
V(VMInternal_NoSuchMethodDispatcher, 0, ORDN, ___, ___, ___) \
228+
/* One breakpoint opcode for each instruction size. */ \
229+
V(VMInternal_Breakpoint_0, 0, ORDN, ___, ___, ___) \
230+
V(VMInternal_Breakpoint_A_B_C, A_B_C, ORDN, num, num, num) \
231+
V(VMInternal_Breakpoint_D, D, ORDN, num, ___, ___) \
232+
V(VMInternal_Breakpoint_D_Wide, D, WIDE, num, ___, ___) \
233+
V(VMInternal_Breakpoint_A_E, A_E, ORDN, num, num, ___) \
234+
V(VMInternal_Breakpoint_A_E_Wide, A_E, WIDE, num, num, ___) \
228235

229236
#define INTERNAL_KERNEL_BYTECODES_LIST(V) \
230237
INTERNAL_KERNEL_BYTECODES_WITH_CUSTOM_CODE(V) \
@@ -250,6 +257,7 @@ class KernelBytecode {
250257
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
251258
KERNEL_BYTECODES_LIST(DECLARE_BYTECODE)
252259
#undef DECLARE_BYTECODE
260+
kNumOpcodes,
253261
};
254262

255263
static const char* NameOf(Opcode op) {
@@ -274,7 +282,12 @@ class KernelBytecode {
274282

275283
// Should be used only on instructions with wide variants.
276284
DART_FORCE_INLINE static bool IsWide(const KBCInstr* instr) {
277-
return ((DecodeOpcode(instr) & kWideModifier) != 0);
285+
return IsWide(DecodeOpcode(instr));
286+
}
287+
288+
// Should be used only on instructions with wide variants.
289+
DART_FORCE_INLINE static constexpr bool IsWide(Opcode opcode) {
290+
return ((opcode & kWideModifier) != 0);
278291
}
279292

280293
public:
@@ -423,7 +436,11 @@ class KernelBytecode {
423436
// - The bytecode generator emits a source position.
424437
// - The bytecode compiler may emit a DebugStepCheck call.
425438
DART_FORCE_INLINE static bool IsDebugCheckedOpcode(const KBCInstr* instr) {
426-
switch (DecodeOpcode(instr)) {
439+
return IsDebugCheckedOpcode(DecodeOpcode(instr));
440+
}
441+
442+
DART_FORCE_INLINE static bool IsDebugCheckedOpcode(Opcode op) {
443+
switch (op) {
427444
case KernelBytecode::kDebugCheck:
428445
case KernelBytecode::kDirectCall:
429446
case KernelBytecode::kDirectCall_Wide:
@@ -494,7 +511,42 @@ class KernelBytecode {
494511
const KBCInstr** instructions,
495512
intptr_t* instructions_size);
496513

514+
static Opcode BreakpointOpcode(Opcode opcode) {
515+
Opcode replacement;
516+
switch (kInstructionSize[opcode]) {
517+
case 1:
518+
replacement = kVMInternal_Breakpoint_0;
519+
break;
520+
case 2:
521+
replacement = kVMInternal_Breakpoint_D;
522+
break;
523+
case 3:
524+
replacement = kVMInternal_Breakpoint_A_E;
525+
break;
526+
case 4:
527+
replacement = kVMInternal_Breakpoint_A_B_C;
528+
break;
529+
case 5:
530+
replacement = kVMInternal_Breakpoint_D_Wide;
531+
break;
532+
case 6:
533+
replacement = kVMInternal_Breakpoint_A_E_Wide;
534+
break;
535+
default:
536+
UNREACHABLE();
537+
return kTrap;
538+
}
539+
ASSERT_EQUAL(kInstructionSize[replacement], kInstructionSize[opcode]);
540+
return replacement;
541+
}
542+
543+
static Opcode BreakpointOpcode(const KBCInstr* instr) {
544+
return BreakpointOpcode(DecodeOpcode(instr));
545+
}
546+
497547
private:
548+
friend class Interpreter; // for IsWide(Opcode) in static_asserts.
549+
498550
DISALLOW_ALLOCATION();
499551
DISALLOW_IMPLICIT_CONSTRUCTORS(KernelBytecode);
500552
};

0 commit comments

Comments
 (0)