Commit e920b2e3 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[arm32] Fix breakpoints in simulator/debugger

- Debugger stepping assumes that the pc points to the instruction
  that should get executed next, so we need to increment it when
  we hit a stop or a bkpt instruction or else we'll end up in an
  infinite loop.
- The "break" and the "stop unstop" command write into code space, so
  they need to temporarily make code space writable or else they
  just crash. (Note that this doesn't work for embedded builtins.)

Bug: v8:10164
Change-Id: Id77f5e97892076a9fdf8de0230632e0ce979da43
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2026732
Auto-Submit: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarDan Elphick <delphick@chromium.org>
Commit-Queue: Dan Elphick <delphick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66039}
parent f9257802
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "src/codegen/macro-assembler.h" #include "src/codegen/macro-assembler.h"
#include "src/diagnostics/disasm.h" #include "src/diagnostics/disasm.h"
#include "src/heap/combined-heap.h" #include "src/heap/combined-heap.h"
#include "src/heap/heap-inl.h" // For CodeSpaceMemoryModificationScope.
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/runtime/runtime-utils.h" #include "src/runtime/runtime-utils.h"
#include "src/utils/ostreams.h" #include "src/utils/ostreams.h"
...@@ -43,8 +44,6 @@ DEFINE_LAZY_LEAKY_OBJECT_GETTER(Simulator::GlobalMonitor, ...@@ -43,8 +44,6 @@ DEFINE_LAZY_LEAKY_OBJECT_GETTER(Simulator::GlobalMonitor,
class ArmDebugger { class ArmDebugger {
public: public:
explicit ArmDebugger(Simulator* sim) : sim_(sim) {} explicit ArmDebugger(Simulator* sim) : sim_(sim) {}
void Stop(Instruction* instr);
void Debug(); void Debug();
private: private:
...@@ -61,26 +60,20 @@ class ArmDebugger { ...@@ -61,26 +60,20 @@ class ArmDebugger {
bool GetVFPSingleValue(const char* desc, float* value); bool GetVFPSingleValue(const char* desc, float* value);
bool GetVFPDoubleValue(const char* desc, double* value); bool GetVFPDoubleValue(const char* desc, double* value);
// Set or delete a breakpoint. Returns true if successful. // Set or delete breakpoint (there can be only one).
bool SetBreakpoint(Instruction* breakpc); bool SetBreakpoint(Instruction* breakpc);
bool DeleteBreakpoint(Instruction* breakpc); void DeleteBreakpoint();
// Undo and redo all breakpoints. This is needed to bracket disassembly and // Undo and redo the breakpoint. This is needed to bracket disassembly and
// execution to skip past breakpoints when run from the debugger. // execution to skip past the breakpoint when run from the debugger.
void UndoBreakpoints(); void UndoBreakpoint();
void RedoBreakpoints(); void RedoBreakpoint();
}; };
void ArmDebugger::Stop(Instruction* instr) { void Simulator::DebugAtNextPC() {
// Get the stop code. PrintF("Starting debugger on the next instruction:\n");
uint32_t code = instr->SvcValue() & kStopCodeMask; set_pc(get_pc() + kInstrSize);
// Print the stop message and code if it is not the default code. ArmDebugger(this).Debug();
if (code != kMaxStopCode) {
PrintF("Simulator hit stop %u\n", code);
} else {
PrintF("Simulator hit\n");
}
Debug();
} }
int32_t ArmDebugger::GetRegisterValue(int regnum) { int32_t ArmDebugger::GetRegisterValue(int regnum) {
...@@ -148,25 +141,33 @@ bool ArmDebugger::SetBreakpoint(Instruction* breakpc) { ...@@ -148,25 +141,33 @@ bool ArmDebugger::SetBreakpoint(Instruction* breakpc) {
return true; return true;
} }
bool ArmDebugger::DeleteBreakpoint(Instruction* breakpc) { namespace {
if (sim_->break_pc_ != nullptr) { // This function is dangerous, but it's only available in non-production
sim_->break_pc_->SetInstructionBits(sim_->break_instr_); // (simulator) builds.
} void SetInstructionBitsInCodeSpace(Instruction* instr, Instr value,
Heap* heap) {
CodeSpaceMemoryModificationScope scope(heap);
instr->SetInstructionBits(value);
}
} // namespace
void ArmDebugger::DeleteBreakpoint() {
UndoBreakpoint();
sim_->break_pc_ = nullptr; sim_->break_pc_ = nullptr;
sim_->break_instr_ = 0; sim_->break_instr_ = 0;
return true;
} }
void ArmDebugger::UndoBreakpoints() { void ArmDebugger::UndoBreakpoint() {
if (sim_->break_pc_ != nullptr) { if (sim_->break_pc_ != nullptr) {
sim_->break_pc_->SetInstructionBits(sim_->break_instr_); SetInstructionBitsInCodeSpace(sim_->break_pc_, sim_->break_instr_,
sim_->isolate_->heap());
} }
} }
void ArmDebugger::RedoBreakpoints() { void ArmDebugger::RedoBreakpoint() {
if (sim_->break_pc_ != nullptr) { if (sim_->break_pc_ != nullptr) {
sim_->break_pc_->SetInstructionBits(kBreakpointInstr); SetInstructionBitsInCodeSpace(sim_->break_pc_, kBreakpointInstr,
sim_->isolate_->heap());
} }
} }
...@@ -190,9 +191,9 @@ void ArmDebugger::Debug() { ...@@ -190,9 +191,9 @@ void ArmDebugger::Debug() {
arg1[ARG_SIZE] = 0; arg1[ARG_SIZE] = 0;
arg2[ARG_SIZE] = 0; arg2[ARG_SIZE] = 0;
// Undo all set breakpoints while running in the debugger shell. This will // Unset breakpoint while running in the debugger shell, making it invisible
// make them invisible to all commands. // to all commands.
UndoBreakpoints(); UndoBreakpoint();
while (!done && !sim_->has_bad_pc()) { while (!done && !sim_->has_bad_pc()) {
if (last_pc != sim_->get_pc()) { if (last_pc != sim_->get_pc()) {
...@@ -402,9 +403,7 @@ void ArmDebugger::Debug() { ...@@ -402,9 +403,7 @@ void ArmDebugger::Debug() {
PrintF("break <address>\n"); PrintF("break <address>\n");
} }
} else if (strcmp(cmd, "del") == 0) { } else if (strcmp(cmd, "del") == 0) {
if (!DeleteBreakpoint(nullptr)) { DeleteBreakpoint();
PrintF("deleting breakpoint failed\n");
}
} else if (strcmp(cmd, "flags") == 0) { } else if (strcmp(cmd, "flags") == 0) {
PrintF("N flag: %d; ", sim_->n_flag_); PrintF("N flag: %d; ", sim_->n_flag_);
PrintF("Z flag: %d; ", sim_->z_flag_); PrintF("Z flag: %d; ", sim_->z_flag_);
...@@ -421,8 +420,9 @@ void ArmDebugger::Debug() { ...@@ -421,8 +420,9 @@ void ArmDebugger::Debug() {
Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc); Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc);
if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
// Remove the current stop. // Remove the current stop.
if (sim_->isStopInstruction(stop_instr)) { if (stop_instr->IsStop()) {
stop_instr->SetInstructionBits(kNopInstr); SetInstructionBitsInCodeSpace(stop_instr, kNopInstr,
sim_->isolate_->heap());
} else { } else {
PrintF("Not at debugger stop.\n"); PrintF("Not at debugger stop.\n");
} }
...@@ -526,9 +526,9 @@ void ArmDebugger::Debug() { ...@@ -526,9 +526,9 @@ void ArmDebugger::Debug() {
} }
} }
// Add all the breakpoints back to stop execution and enter the debugger // Reinstall breakpoint to stop execution and enter the debugger shell when
// shell when hit. // hit.
RedoBreakpoints(); RedoBreakpoint();
#undef COMMAND_SIZE #undef COMMAND_SIZE
#undef ARG_SIZE #undef ARG_SIZE
...@@ -1785,13 +1785,11 @@ void Simulator::SoftwareInterrupt(Instruction* instr) { ...@@ -1785,13 +1785,11 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
set_pc(get_register(lr)); set_pc(get_register(lr));
break; break;
} }
case kBreakpoint: { case kBreakpoint:
ArmDebugger dbg(this); ArmDebugger(this).Debug();
dbg.Debug();
break; break;
}
// stop uses all codes greater than 1 << 23. // stop uses all codes greater than 1 << 23.
default: { default:
if (svc >= (1 << 23)) { if (svc >= (1 << 23)) {
uint32_t code = svc & kStopCodeMask; uint32_t code = svc & kStopCodeMask;
if (isWatchedStop(code)) { if (isWatchedStop(code)) {
...@@ -1800,15 +1798,17 @@ void Simulator::SoftwareInterrupt(Instruction* instr) { ...@@ -1800,15 +1798,17 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
// Stop if it is enabled, otherwise go on jumping over the stop // Stop if it is enabled, otherwise go on jumping over the stop
// and the message address. // and the message address.
if (isEnabledStop(code)) { if (isEnabledStop(code)) {
ArmDebugger dbg(this); if (code != kMaxStopCode) {
dbg.Stop(instr); PrintF("Simulator hit stop %u. ", code);
} else {
PrintF("Simulator hit stop. ");
}
DebugAtNextPC();
} }
} else { } else {
// This is not a valid svc code. // This is not a valid svc code.
UNREACHABLE(); UNREACHABLE();
break;
} }
}
} }
} }
...@@ -1848,10 +1848,6 @@ Float64 Simulator::canonicalizeNaN(Float64 value) { ...@@ -1848,10 +1848,6 @@ Float64 Simulator::canonicalizeNaN(Float64 value) {
} }
// Stop helper functions. // Stop helper functions.
bool Simulator::isStopInstruction(Instruction* instr) {
return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >= kStopCode);
}
bool Simulator::isWatchedStop(uint32_t code) { bool Simulator::isWatchedStop(uint32_t code) {
DCHECK_LE(code, kMaxStopCode); DCHECK_LE(code, kMaxStopCode);
return code < kNumOfWatchedStops; return code < kNumOfWatchedStops;
...@@ -2243,12 +2239,10 @@ void Simulator::DecodeType01(Instruction* instr) { ...@@ -2243,12 +2239,10 @@ void Simulator::DecodeType01(Instruction* instr) {
set_register(lr, old_pc + kInstrSize); set_register(lr, old_pc + kInstrSize);
break; break;
} }
case BKPT: { case BKPT:
ArmDebugger dbg(this); PrintF("Simulator hit BKPT. ");
PrintF("Simulator hit BKPT.\n"); DebugAtNextPC();
dbg.Debug();
break; break;
}
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
......
...@@ -330,9 +330,9 @@ class Simulator : public SimulatorBase { ...@@ -330,9 +330,9 @@ class Simulator : public SimulatorBase {
void HandleRList(Instruction* instr, bool load); void HandleRList(Instruction* instr, bool load);
void HandleVList(Instruction* inst); void HandleVList(Instruction* inst);
void SoftwareInterrupt(Instruction* instr); void SoftwareInterrupt(Instruction* instr);
void DebugAtNextPC();
// Stop helper functions. // Stop helper functions.
inline bool isStopInstruction(Instruction* instr);
inline bool isWatchedStop(uint32_t bkpt_code); inline bool isWatchedStop(uint32_t bkpt_code);
inline bool isEnabledStop(uint32_t bkpt_code); inline bool isEnabledStop(uint32_t bkpt_code);
inline void EnableStop(uint32_t bkpt_code); inline void EnableStop(uint32_t bkpt_code);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment