af074fb62d
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 <alexmarkov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
139 lines
6.0 KiB
C++
139 lines
6.0 KiB
C++
// Copyright (c) 2012, 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.
|
|
// Class for patching compiled code.
|
|
|
|
#ifndef RUNTIME_VM_CODE_PATCHER_H_
|
|
#define RUNTIME_VM_CODE_PATCHER_H_
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/native_entry.h"
|
|
|
|
namespace dart {
|
|
|
|
// Forward declaration.
|
|
class Code;
|
|
class ICData;
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
// Stack-allocated class to create a scope where the specified region
|
|
// [address, address + size] has write access enabled. This is used
|
|
// when patching generated code. Access is reset to read-execute in
|
|
// the destructor of this scope.
|
|
// Dual mapping of instructions pages is not supported on these target arch.
|
|
class WritableInstructionsScope : public ValueObject {
|
|
public:
|
|
WritableInstructionsScope(uword address, intptr_t size);
|
|
~WritableInstructionsScope();
|
|
|
|
private:
|
|
const uword address_;
|
|
const intptr_t size_;
|
|
};
|
|
#endif // defined(TARGET_ARCH_IA32)
|
|
|
|
class CodePatcher : public AllStatic {
|
|
public:
|
|
// Dart static calls have a distinct, machine-dependent code pattern.
|
|
|
|
// Patch static call before return_address in given code to the new target.
|
|
static void PatchStaticCallAt(uword return_address,
|
|
const Code& code,
|
|
const Code& new_target);
|
|
|
|
// Return the target address of the static call before return_address
|
|
// in given code.
|
|
static CodePtr GetStaticCallTargetAt(uword return_address, const Code& code);
|
|
|
|
// Get instance call information. Returns the call target and sets the output
|
|
// parameter data if non-null.
|
|
static CodePtr GetInstanceCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
Object* data);
|
|
|
|
// Change the state of an instance call by patching the corresponding object
|
|
// pool entries (non-IA32) or instructions (IA32).
|
|
static void PatchInstanceCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
const Object& data,
|
|
const Code& target);
|
|
static void PatchInstanceCallAtWithMutatorsStopped(Thread* thread,
|
|
uword return_address,
|
|
const Code& caller_code,
|
|
const Object& data,
|
|
const Code& target);
|
|
|
|
// Return target of an unoptimized static call and its ICData object
|
|
// (calls target via a stub).
|
|
static FunctionPtr GetUnoptimizedStaticCallAt(uword return_address,
|
|
const Code& code,
|
|
ICData* ic_data);
|
|
|
|
static void PatchPoolPointerCallAt(uword return_address,
|
|
const Code& code,
|
|
const Code& new_target);
|
|
|
|
static void PatchSwitchableCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
const Object& data,
|
|
const Code& target);
|
|
static void PatchSwitchableCallAtWithMutatorsStopped(Thread* thread,
|
|
uword return_address,
|
|
const Code& caller_code,
|
|
const Object& data,
|
|
const Code& target);
|
|
static ObjectPtr GetSwitchableCallDataAt(uword return_address,
|
|
const Code& caller_code);
|
|
static ObjectPtr GetSwitchableCallTargetAt(uword return_address,
|
|
const Code& caller_code);
|
|
static uword GetSwitchableCallTargetEntryAt(uword return_address,
|
|
const Code& caller_code);
|
|
|
|
static CodePtr GetNativeCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
NativeFunction* target);
|
|
|
|
static void PatchNativeCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
NativeFunction target,
|
|
const Code& trampoline);
|
|
|
|
static intptr_t GetSubtypeTestCachePoolIndex(uword return_address);
|
|
};
|
|
|
|
#if !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
|
|
class BytecodePatcher : public AllStatic {
|
|
public:
|
|
// Patch call instruction prior to return_address to add a breakpoint.
|
|
// Returns the original opcode of the patched instruction.
|
|
static uint32_t AddBreakpointAt(uword return_address,
|
|
const Bytecode& bytecode);
|
|
|
|
// Patch call instruction prior to return_address to remove a breakpoint.
|
|
// Replaces the breakpoint instruction opcode with the provided opcode.
|
|
static void RemoveBreakpointAt(uword return_address,
|
|
const Bytecode& code,
|
|
uint32_t opcode);
|
|
|
|
private:
|
|
static uint32_t AddBreakpointAtWithMutatorsStopped(Thread* thread,
|
|
uword return_address,
|
|
const Bytecode& bytecode);
|
|
|
|
static void RemoveBreakpointAtWithMutatorsStopped(Thread* thread,
|
|
uword return_address,
|
|
const Bytecode& bytecode,
|
|
uint32_t opcode);
|
|
};
|
|
#endif // !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
|
|
|
|
// Beginning from [end - size] we compare [size] bytes with [pattern]. All
|
|
// [0..255] values in [pattern] have to match, negative values are skipped.
|
|
//
|
|
// Example pattern: `[0x3d, 0x8b, -1, -1]`.
|
|
bool MatchesPattern(uword end, const int16_t* pattern, intptr_t size);
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_CODE_PATCHER_H_
|