https://github.com/dlav-sc updated https://github.com/llvm/llvm-project/pull/127505
>From 2520b8d7884d320ed7923685b1a3e8652a7e8f5f Mon Sep 17 00:00:00 2001 From: Daniil Avdeev <daniil.avd...@syntacore.com> Date: Fri, 23 May 2025 13:27:46 +0000 Subject: [PATCH 1/2] [lldb][RISCV] handle lr/sc single step lldb-server had limited support for single-stepping through the lr/sc atomic sequence. This patch enhances that support for all possible atomic sequences. --- lldb/include/lldb/Core/EmulateInstruction.h | 45 +++++++ lldb/source/Core/EmulateInstruction.cpp | 93 +++++++++++++ .../Instruction/ARM/EmulateInstructionARM.cpp | 11 ++ .../Instruction/ARM/EmulateInstructionARM.h | 18 +++ .../LoongArch/EmulateInstructionLoongArch.cpp | 122 +++++++++++------- .../LoongArch/EmulateInstructionLoongArch.h | 2 - .../RISCV/EmulateInstructionRISCV.cpp | 117 ++++++++++++++--- .../RISCV/EmulateInstructionRISCV.h | 37 +++++- .../Process/FreeBSD/NativeProcessFreeBSD.cpp | 14 +- .../NativeProcessSoftwareSingleStep.cpp | 108 +++------------- .../Utility/NativeProcessSoftwareSingleStep.h | 2 +- .../LoongArch/TestLoongArchEmulator.cpp | 36 +++--- 12 files changed, 422 insertions(+), 183 deletions(-) diff --git a/lldb/include/lldb/Core/EmulateInstruction.h b/lldb/include/lldb/Core/EmulateInstruction.h index b459476883fc5..0e3564206ccb8 100644 --- a/lldb/include/lldb/Core/EmulateInstruction.h +++ b/lldb/include/lldb/Core/EmulateInstruction.h @@ -32,6 +32,31 @@ class RegisterValue; class Stream; class Target; class UnwindPlan; +class EmulateInstruction; + +using BreakpointLocations = std::vector<lldb::addr_t>; + +class SingleStepBreakpointLocationsPredictor { +public: + SingleStepBreakpointLocationsPredictor( + std::unique_ptr<EmulateInstruction> emulator_up) + : m_emulator_up{std::move(emulator_up)} {} + + virtual BreakpointLocations GetBreakpointLocations(Status &status); + + virtual unsigned GetBreakpointSize(lldb::addr_t, Status &) { return 4; } + + virtual ~SingleStepBreakpointLocationsPredictor() = default; + +protected: + lldb::addr_t GetSequentiallyNextInstructionPC(Status &error); + + lldb::addr_t GetBreakpointLocationAddress(lldb::addr_t entry_pc, + Status &error); + + std::unique_ptr<EmulateInstruction> m_emulator_up; + bool m_emulation_result = false; +}; /// \class EmulateInstruction EmulateInstruction.h /// "lldb/Core/EmulateInstruction.h" @@ -497,7 +522,19 @@ class EmulateInstruction : public PluginInterface { static uint32_t GetInternalRegisterNumber(RegisterContext *reg_ctx, const RegisterInfo ®_info); + static std::unique_ptr<SingleStepBreakpointLocationsPredictor> + CreateBreakpointLocationPredictor( + std::unique_ptr<EmulateInstruction> emulator_up); + + // Helper functions + std::optional<lldb::addr_t> ReadPC(); + bool WritePC(lldb::addr_t addr); + protected: + using BreakpointLocationsPredictorCreator = + std::function<std::unique_ptr<SingleStepBreakpointLocationsPredictor>( + std::unique_ptr<EmulateInstruction>)>; + ArchSpec m_arch; void *m_baton = nullptr; ReadMemoryCallback m_read_mem_callback = &ReadMemoryDefault; @@ -508,6 +545,14 @@ class EmulateInstruction : public PluginInterface { Opcode m_opcode; private: + virtual BreakpointLocationsPredictorCreator + GetSingleStepBreakpointLocationsPredictorCreator() { + return [](std::unique_ptr<EmulateInstruction> emulator_up) { + return std::make_unique<SingleStepBreakpointLocationsPredictor>( + std::move(emulator_up)); + }; + } + // For EmulateInstruction only EmulateInstruction(const EmulateInstruction &) = delete; const EmulateInstruction &operator=(const EmulateInstruction &) = delete; diff --git a/lldb/source/Core/EmulateInstruction.cpp b/lldb/source/Core/EmulateInstruction.cpp index d240b4d3b3310..3bc7beff8ac7f 100644 --- a/lldb/source/Core/EmulateInstruction.cpp +++ b/lldb/source/Core/EmulateInstruction.cpp @@ -588,7 +588,100 @@ EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx, return LLDB_INVALID_REGNUM; } +std::unique_ptr<SingleStepBreakpointLocationsPredictor> +EmulateInstruction::CreateBreakpointLocationPredictor( + std::unique_ptr<EmulateInstruction> emulator_up) { + auto creator = + emulator_up->GetSingleStepBreakpointLocationsPredictorCreator(); + return creator(std::move(emulator_up)); +} + +std::optional<lldb::addr_t> EmulateInstruction::ReadPC() { + bool success = false; + auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, + LLDB_INVALID_ADDRESS, &success); + return success ? std::optional<addr_t>(addr) : std::nullopt; +} + +bool EmulateInstruction::WritePC(lldb::addr_t addr) { + EmulateInstruction::Context ctx; + ctx.type = eContextAdvancePC; + ctx.SetNoArgs(); + return WriteRegisterUnsigned(ctx, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC, addr); +} + bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) { unwind_plan.Clear(); return false; } + +BreakpointLocations +SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(Status &status) { + if (!m_emulator_up->ReadInstruction()) { + // try to get at least the size of next instruction to set breakpoint. + lldb::addr_t next_pc = GetSequentiallyNextInstructionPC(status); + return BreakpointLocations{next_pc}; + } + + auto entry_pc = m_emulator_up->ReadPC(); + if (!entry_pc) { + status = Status("Can't read PC"); + return {}; + } + + m_emulation_result = m_emulator_up->EvaluateInstruction( + eEmulateInstructionOptionAutoAdvancePC); + + lldb::addr_t next_pc = GetBreakpointLocationAddress(*entry_pc, status); + return BreakpointLocations{next_pc}; +} + +lldb::addr_t +SingleStepBreakpointLocationsPredictor::GetSequentiallyNextInstructionPC( + Status &error) { + auto instr_size = m_emulator_up->GetLastInstrSize(); + if (!instr_size) { + error = Status("Read instruction failed!"); + return LLDB_INVALID_ADDRESS; + } + + auto pc = m_emulator_up->ReadPC(); + if (!pc) { + error = Status("Can't read PC"); + return LLDB_INVALID_ADDRESS; + } + + lldb::addr_t next_pc = *pc + *instr_size; + return next_pc; +} + +lldb::addr_t +SingleStepBreakpointLocationsPredictor::GetBreakpointLocationAddress( + lldb::addr_t entry_pc, Status &error) { + auto addr = m_emulator_up->ReadPC(); + if (!addr) { + error = Status("Can't read PC"); + return LLDB_INVALID_ADDRESS; + } + lldb::addr_t pc = *addr; + + if (m_emulation_result) { + assert(entry_pc != pc && "Emulation was successfull but PC wasn't updated"); + return pc; + } + + if (entry_pc == pc) { + // Emulate instruction failed and it haven't changed PC. Advance PC with + // the size of the current opcode because the emulation of all + // PC modifying instruction should be successful. The failure most + // likely caused by a not supported instruction which don't modify PC. + return pc + m_emulator_up->GetOpcode().GetByteSize(); + } + + // The instruction emulation failed after it modified the PC. It is an + // unknown error where we can't continue because the next instruction is + // modifying the PC but we don't know how. + error = Status("Instruction emulation failed unexpectedly."); + return LLDB_INVALID_ADDRESS; +} diff --git a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp index 32975d88af46e..ff06a4a8dd0a4 100644 --- a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp +++ b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp @@ -14471,3 +14471,14 @@ bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) { unwind_plan.SetReturnAddressRegister(dwarf_lr); return true; } + +unsigned ARMSingleStepBreakpointLocationsPredictor::GetBreakpointSize( + lldb::addr_t bp_addr, Status &error) { + auto flags = m_emulator_up->ReadRegisterUnsigned( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_ADDRESS, + nullptr); + if (flags == LLDB_INVALID_ADDRESS) + error = Status("Reading flags failed!"); + + return (flags & 0x20) ? /* Thumb mode */ 2 : /* Arm mode */ 4; +} diff --git a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h index 9eca0288607c1..c1ed9def6e27d 100644 --- a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h +++ b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h @@ -16,6 +16,16 @@ namespace lldb_private { +class ARMSingleStepBreakpointLocationsPredictor + : public SingleStepBreakpointLocationsPredictor { +public: + ARMSingleStepBreakpointLocationsPredictor( + std::unique_ptr<EmulateInstruction> emulator_up) + : SingleStepBreakpointLocationsPredictor{std::move(emulator_up)} {} + + unsigned GetBreakpointSize(lldb::addr_t bp_addr, Status &error) override; +}; + // ITSession - Keep track of the IT Block progression. class ITSession { public: @@ -770,6 +780,14 @@ class EmulateInstructionARM : public EmulateInstruction { // B6.2.13 SUBS PC, LR and related instructions bool EmulateSUBSPcLrEtc(const uint32_t opcode, const ARMEncoding encoding); + BreakpointLocationsPredictorCreator + GetSingleStepBreakpointLocationsPredictorCreator() override { + return [](std::unique_ptr<EmulateInstruction> emulator_up) { + return std::make_unique<ARMSingleStepBreakpointLocationsPredictor>( + std::move(emulator_up)); + }; + } + uint32_t m_arm_isa; Mode m_opcode_mode; uint32_t m_opcode_cpsr; diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp index 37f08592d62b9..db32bf8a4dd51 100644 --- a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp +++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp @@ -86,7 +86,6 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) { uint32_t inst_size = m_opcode.GetByteSize(); uint32_t inst = m_opcode.GetOpcode32(); bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC; - bool success = false; Opcode *opcode_data = GetOpcodeForInstruction(inst); if (!opcode_data) @@ -94,9 +93,10 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) { lldb::addr_t old_pc = 0; if (increase_pc) { - old_pc = ReadPC(&success); - if (!success) + auto addr = ReadPC(); + if (!addr) return false; + old_pc = *addr; } // Call the Emulate... function. @@ -104,9 +104,10 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) { return false; if (increase_pc) { - lldb::addr_t new_pc = ReadPC(&success); - if (!success) + auto addr = ReadPC(); + if (!addr) return false; + lldb::addr_t new_pc = *addr; if (new_pc == old_pc && !WritePC(old_pc + inst_size)) return false; @@ -115,13 +116,14 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) { } bool EmulateInstructionLoongArch::ReadInstruction() { - bool success = false; - m_addr = ReadPC(&success); - if (!success) { + auto addr = ReadPC(); + if (!addr) { m_addr = LLDB_INVALID_ADDRESS; return false; } + m_addr = *addr; + bool success = false; Context ctx; ctx.type = eContextReadOpcode; ctx.SetNoArgs(); @@ -131,19 +133,6 @@ bool EmulateInstructionLoongArch::ReadInstruction() { return true; } -lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) { - return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, - LLDB_INVALID_ADDRESS, success); -} - -bool EmulateInstructionLoongArch::WritePC(lldb::addr_t pc) { - EmulateInstruction::Context ctx; - ctx.type = eContextAdvancePC; - ctx.SetNoArgs(); - return WriteRegisterUnsigned(ctx, eRegisterKindGeneric, - LLDB_REGNUM_GENERIC_PC, pc); -} - std::optional<RegisterInfo> EmulateInstructionLoongArch::GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_index) { @@ -273,9 +262,12 @@ bool EmulateInstructionLoongArch::EmulateNonJMP(uint32_t inst) { return false; } bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) @@ -293,9 +285,12 @@ bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) { bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) @@ -313,9 +308,12 @@ bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) { bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) { bool success = false; uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch; - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); uint8_t cj_val = (uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success); @@ -335,9 +333,12 @@ bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) { bool EmulateInstructionLoongArch::EmulateBCNEZ64(uint32_t inst) { bool success = false; uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch; - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); uint8_t cj_val = (uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success); @@ -358,9 +359,12 @@ bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) { uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); bool success = false; - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + EmulateInstruction::Context ctx; if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, rd, pc + 4)) return false; @@ -374,10 +378,11 @@ bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) { // b offs26 // PC = PC + SignExtend({offs26, 2' b0}, GRLEN) bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) { - bool success = false; - uint64_t pc = ReadPC(&success); - if (!success) + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16); uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2); return WritePC(next_pc); @@ -387,10 +392,11 @@ bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) { // GR[1] = PC + 4 // PC = PC + SignExtend({offs26, 2'b0}, GRLEN) bool EmulateInstructionLoongArch::EmulateBL64(uint32_t inst) { - bool success = false; - uint64_t pc = ReadPC(&success); - if (!success) + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + EmulateInstruction::Context ctx; if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_loongarch, pc + 4)) return false; @@ -406,9 +412,12 @@ bool EmulateInstructionLoongArch::EmulateBEQ64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) return false; @@ -429,9 +438,12 @@ bool EmulateInstructionLoongArch::EmulateBNE64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) return false; @@ -452,9 +464,12 @@ bool EmulateInstructionLoongArch::EmulateBLT64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + int64_t rj_val = (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) @@ -477,9 +492,12 @@ bool EmulateInstructionLoongArch::EmulateBGE64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + int64_t rj_val = (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) @@ -502,9 +520,12 @@ bool EmulateInstructionLoongArch::EmulateBLTU64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) return false; @@ -525,9 +546,12 @@ bool EmulateInstructionLoongArch::EmulateBGEU64(uint32_t inst) { bool success = false; uint32_t rj = Bits32(inst, 9, 5); uint32_t rd = Bits32(inst, 4, 0); - uint64_t pc = ReadPC(&success); - if (!success) + + auto addr = ReadPC(); + if (!addr) return false; + uint64_t pc = *addr; + uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); if (!success) return false; diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h index 47fe454fcd88c..2a8acba42f49e 100644 --- a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h +++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h @@ -57,8 +57,6 @@ class EmulateInstructionLoongArch : public EmulateInstruction { std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num) override; - lldb::addr_t ReadPC(bool *success); - bool WritePC(lldb::addr_t pc); bool IsLoongArch64() { return m_arch_subtype == llvm::Triple::loongarch64; } bool TestExecute(uint32_t inst); diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp index badc7ba36f011..c0fcca4e0049f 100644 --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -650,9 +650,10 @@ std::optional<DecodeResult> EmulateInstructionRISCV::Decode(uint32_t inst) { for (const InstrPattern &pat : PATTERNS) { if ((inst & pat.type_mask) == pat.eigen && (inst_type & pat.inst_type) != 0) { - LLDB_LOGF( - log, "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64 ") was decoded to %s", - __FUNCTION__, inst, m_addr, pat.name); + LLDB_LOGF(log, + "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64 + ") was decoded to %s", + __FUNCTION__, inst, m_addr, pat.name); auto decoded = is_16b ? pat.decode(try_rvc) : pat.decode(inst); return DecodeResult{decoded, inst, is_16b, pat}; } @@ -1649,21 +1650,6 @@ bool EmulateInstructionRISCV::ReadInstruction() { return true; } -std::optional<addr_t> EmulateInstructionRISCV::ReadPC() { - bool success = false; - auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, - LLDB_INVALID_ADDRESS, &success); - return success ? std::optional<addr_t>(addr) : std::nullopt; -} - -bool EmulateInstructionRISCV::WritePC(addr_t pc) { - EmulateInstruction::Context ctx; - ctx.type = eContextAdvancePC; - ctx.SetNoArgs(); - return WriteRegisterUnsigned(ctx, eRegisterKindGeneric, - LLDB_REGNUM_GENERIC_PC, pc); -} - RoundingMode EmulateInstructionRISCV::GetRoundingMode() { bool success = false; auto fcsr = ReadRegisterUnsigned(eRegisterKindLLDB, fpr_fcsr_riscv, @@ -1792,4 +1778,99 @@ bool EmulateInstructionRISCV::SupportsThisArch(const ArchSpec &arch) { return arch.GetTriple().isRISCV(); } +BreakpointLocations +RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations( + Status &status) { + EmulateInstructionRISCV *riscv_emulator = + static_cast<EmulateInstructionRISCV *>(m_emulator_up.get()); + + auto pc = riscv_emulator->ReadPC(); + if (!pc) { + status = Status("Can't read PC"); + return {}; + } + + auto inst = riscv_emulator->ReadInstructionAt(*pc); + if (!inst) { + // Can't read instruction. Try default handler. + return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations( + status); + } + + if (FoundLoadReserve(inst->decoded)) + return HandleAtomicSequence(*pc, status); + + if (FoundStoreConditional(inst->decoded)) { + // Ill-formed program, just set a breakpoint to the current position + return {*pc}; + } + + return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(status); +} + +unsigned RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointSize( + lldb::addr_t bp_addr, Status &error) { + EmulateInstructionRISCV *riscv_emulator = + static_cast<EmulateInstructionRISCV *>(m_emulator_up.get()); + + if (auto inst = riscv_emulator->ReadInstructionAt(bp_addr); inst) + return inst->is_rvc ? 2 : 4; + + // Try last instruction size. + if (auto last_instr_size = riscv_emulator->GetLastInstrSize(); + last_instr_size) + return *last_instr_size; + + // Just place non-compressed software trap (EBREAK). + return 4; +} + +BreakpointLocations +RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( + lldb::addr_t pc, Status &error) { + EmulateInstructionRISCV *riscv_emulator = + static_cast<EmulateInstructionRISCV *>(m_emulator_up.get()); + // Handle instuctions between LR and SC. According to unprivilleged + // RISC-V ISA there can be at most 16 instructions in the sequence + + lldb::addr_t entry_pc = pc; + pc += 4; // add LR_W, LR_D instruction size + + size_t atomic_length = 0; + std::optional<DecodeResult> inst; + std::vector<lldb::addr_t> bp_addrs; + do { + inst = riscv_emulator->ReadInstructionAt(pc); + if (!inst) { + error = Status("Can't read instruction"); + return {}; + } + + if (B *branch = std::get_if<B>(&inst->decoded)) + bp_addrs.push_back(pc + SignExt(branch->imm)); + + unsigned addent = inst->is_rvc ? 2 : 4; + pc += addent; + atomic_length += addent; + } while ((atomic_length < s_max_atomic_sequence_length) && + !FoundStoreConditional(inst->decoded)); + + if (atomic_length >= s_max_atomic_sequence_length) { + // Ill-formed program, just set a breakpoint to the current position + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOGF(log, + "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " + "corresponding store conditional insturuction", + __FUNCTION__); + return {entry_pc}; + } + + bp_addrs.erase( + llvm::remove_if(bp_addrs, + [pc](lldb::addr_t bp_addr) { return pc >= bp_addr; }), + bp_addrs.end()); + bp_addrs.push_back(pc); + return bp_addrs; +} + } // namespace lldb_private diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h index 53ac11c2e1102..662ed6c812571 100644 --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h @@ -20,6 +20,33 @@ namespace lldb_private { +class RISCVSingleStepBreakpointLocationsPredictor + : public SingleStepBreakpointLocationsPredictor { +public: + RISCVSingleStepBreakpointLocationsPredictor( + std::unique_ptr<EmulateInstruction> emulator) + : SingleStepBreakpointLocationsPredictor{std::move(emulator)} {} + + BreakpointLocations GetBreakpointLocations(Status &status) override; + + unsigned GetBreakpointSize(lldb::addr_t bp_addr, Status &error) override; + +private: + static bool FoundLoadReserve(const RISCVInst &inst) { + return std::holds_alternative<LR_W>(inst) || + std::holds_alternative<LR_D>(inst); + } + + static bool FoundStoreConditional(const RISCVInst &inst) { + return std::holds_alternative<SC_W>(inst) || + std::holds_alternative<SC_D>(inst); + } + + BreakpointLocations HandleAtomicSequence(lldb::addr_t pc, Status &error); + + static constexpr size_t s_max_atomic_sequence_length = 64; +}; + class EmulateInstructionRISCV : public EmulateInstruction { public: static llvm::StringRef GetPluginNameStatic() { return "riscv"; } @@ -67,9 +94,6 @@ class EmulateInstructionRISCV : public EmulateInstruction { std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num) override; - std::optional<lldb::addr_t> ReadPC(); - bool WritePC(lldb::addr_t pc); - std::optional<DecodeResult> ReadInstructionAt(lldb::addr_t addr); std::optional<DecodeResult> Decode(uint32_t inst); bool Execute(DecodeResult inst, bool ignore_cond); @@ -98,6 +122,13 @@ class EmulateInstructionRISCV : public EmulateInstruction { bool SetAccruedExceptions(llvm::APFloatBase::opStatus); private: + BreakpointLocationsPredictorCreator + GetSingleStepBreakpointLocationsPredictorCreator() override { + return [](std::unique_ptr<EmulateInstruction> emulator_up) { + return std::make_unique<RISCVSingleStepBreakpointLocationsPredictor>( + std::move(emulator_up)); + }; + } /// Last decoded instruction from m_opcode DecodeResult m_decoded; /// Last decoded instruction size estimate. diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp index bf552e19742c4..2c2af272040a3 100644 --- a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp @@ -326,11 +326,15 @@ void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) { if (thread_info != m_threads_stepping_with_breakpoint.end() && thread_info->second == regctx.GetPC()) { thread->SetStoppedByTrace(); - Status brkpt_error = RemoveBreakpoint(thread_info->second); - if (brkpt_error.Fail()) - LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", - thread_info->first, brkpt_error); - m_threads_stepping_with_breakpoint.erase(thread_info); + while (thread_info != m_threads_stepping_with_breakpoint.end() { + Status brkpt_error = RemoveBreakpoint(thread_info->second); + if (brkpt_error.Fail()) + LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", + thread_info->first, brkpt_error); + m_threads_stepping_with_breakpoint.erase(thread_info); + thread_info = + m_threads_stepping_with_breakpoint.find(thread->GetID()); + } } else thread->SetStoppedByBreakpoint(); FixupBreakpointPCAsNeeded(*thread); diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp index 31d59e387d25b..d38a2c7edde5a 100644 --- a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp @@ -87,34 +87,10 @@ static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, return length; } -static lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) { - const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); - return regsiter_context.ReadRegisterAsUnsigned(flags_info, - LLDB_INVALID_ADDRESS); -} - -static int GetSoftwareBreakpointSize(const ArchSpec &arch, - lldb::addr_t next_flags) { - if (arch.GetMachine() == llvm::Triple::arm) { - if (next_flags & 0x20) - // Thumb mode - return 2; - // Arm mode - return 4; - } - if (arch.IsMIPS() || arch.GetTriple().isPPC64() || - arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch()) - return 4; - return 0; -} - -static Status SetSoftwareBreakpointOnPC(const ArchSpec &arch, lldb::addr_t pc, - lldb::addr_t next_flags, - NativeProcessProtocol &process) { - int size_hint = GetSoftwareBreakpointSize(arch, next_flags); +static Status SetSoftwareBreakpoint(lldb::addr_t bp_addr, unsigned bp_size, + NativeProcessProtocol &process) { Status error; - error = process.SetBreakpoint(pc, size_hint, /*hardware=*/false); + error = process.SetBreakpoint(bp_addr, bp_size, /*hardware=*/false); // If setting the breakpoint fails because pc is out of the address // space, ignore it and let the debugee segfault. @@ -136,7 +112,6 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping( std::unique_ptr<EmulateInstruction> emulator_up( EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying, nullptr)); - if (emulator_up == nullptr) return Status::FromErrorString("Instruction emulator not found!"); @@ -147,65 +122,24 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping( emulator_up->SetWriteMemCallback(&WriteMemoryCallback); emulator_up->SetWriteRegCallback(&WriteRegisterCallback); - if (!emulator_up->ReadInstruction()) { - // try to get at least the size of next instruction to set breakpoint. - auto instr_size = emulator_up->GetLastInstrSize(); - if (!instr_size) - return Status::FromErrorString("Read instruction failed!"); - bool success = false; - auto pc = emulator_up->ReadRegisterUnsigned(eRegisterKindGeneric, - LLDB_REGNUM_GENERIC_PC, - LLDB_INVALID_ADDRESS, &success); - if (!success) - return Status::FromErrorString("Reading pc failed!"); - lldb::addr_t next_pc = pc + *instr_size; - auto result = - SetSoftwareBreakpointOnPC(arch, next_pc, /* next_flags */ 0x0, process); - m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); - return result; - } + auto bp_locaions_predictor = + EmulateInstruction::CreateBreakpointLocationPredictor( + std::move(emulator_up)); + + auto bp_locations = bp_locaions_predictor->GetBreakpointLocations(error); + if (error.Fail()) + return error; + + for (auto &&bp_addr : bp_locations) { + unsigned bp_size = bp_locaions_predictor->GetBreakpointSize(bp_addr, error); + if (error.Fail()) + return error; + + error = SetSoftwareBreakpoint(bp_addr, bp_size, process); + if (error.Fail()) + return error; - bool emulation_result = - emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); - - const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); - const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); - - auto pc_it = - baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); - auto flags_it = reg_info_flags == nullptr - ? baton.m_register_values.end() - : baton.m_register_values.find( - reg_info_flags->kinds[eRegisterKindDWARF]); - - lldb::addr_t next_pc; - lldb::addr_t next_flags; - if (emulation_result) { - assert(pc_it != baton.m_register_values.end() && - "Emulation was successfull but PC wasn't updated"); - next_pc = pc_it->second.GetAsUInt64(); - - if (flags_it != baton.m_register_values.end()) - next_flags = flags_it->second.GetAsUInt64(); - else - next_flags = ReadFlags(register_context); - } else if (pc_it == baton.m_register_values.end()) { - // Emulate instruction failed and it haven't changed PC. Advance PC with - // the size of the current opcode because the emulation of all - // PC modifying instruction should be successful. The failure most - // likely caused by a not supported instruction which don't modify PC. - next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize(); - next_flags = ReadFlags(register_context); - } else { - // The instruction emulation failed after it modified the PC. It is an - // unknown error where we can't continue because the next instruction is - // modifying the PC but we don't know how. - return Status::FromErrorString( - "Instruction emulation failed unexpectedly."); + m_threads_stepping_with_breakpoint.insert({thread.GetID(), bp_addr}); } - auto result = SetSoftwareBreakpointOnPC(arch, next_pc, next_flags, process); - m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); - return result; + return error; } diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h index f9435b7a84ba4..35e834454e916 100644 --- a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h +++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h @@ -23,7 +23,7 @@ class NativeProcessSoftwareSingleStep { protected: // List of thread ids stepping with a breakpoint with the address of // the relevan breakpoint - std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint; + std::multimap<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint; }; } // namespace lldb_private diff --git a/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp b/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp index f9372ded0133b..1be0ef2af3c2e 100644 --- a/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp +++ b/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp @@ -177,10 +177,10 @@ TEST_F(LoongArch64EmulatorTester, testJIRL) { gpr.gpr[12] = 0x12000400; ASSERT_TRUE(TestExecute(inst)); auto r1 = gpr.gpr[1]; - auto pc = ReadPC(&success); - ASSERT_TRUE(success); + auto pc = ReadPC(); + ASSERT_TRUE(pc); ASSERT_EQ(r1, old_pc + 4); - ASSERT_EQ(pc, gpr.gpr[12] + (offs16 * 4)); + ASSERT_EQ(*pc, gpr.gpr[12] + (offs16 * 4)); } TEST_F(LoongArch64EmulatorTester, testB) { @@ -193,9 +193,9 @@ TEST_F(LoongArch64EmulatorTester, testB) { uint32_t inst = 0b01010000000000000100000000000001; uint32_t offs26 = 0x10010; ASSERT_TRUE(TestExecute(inst)); - auto pc = ReadPC(&success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc + (offs26 * 4)); + auto pc = ReadPC(); + ASSERT_TRUE(pc); + ASSERT_EQ(*pc, old_pc + (offs26 * 4)); } TEST_F(LoongArch64EmulatorTester, testBL) { @@ -209,10 +209,10 @@ TEST_F(LoongArch64EmulatorTester, testBL) { uint32_t offs26 = 0x10010; ASSERT_TRUE(TestExecute(inst)); auto r1 = gpr.gpr[1]; - auto pc = ReadPC(&success); - ASSERT_TRUE(success); + auto pc = ReadPC(); + ASSERT_TRUE(pc); ASSERT_EQ(r1, old_pc + 4); - ASSERT_EQ(pc, old_pc + (offs26 * 4)); + ASSERT_EQ(*pc, old_pc + (offs26 * 4)); } static void testBcondBranch(LoongArch64EmulatorTester *tester, @@ -226,9 +226,9 @@ static void testBcondBranch(LoongArch64EmulatorTester *tester, // b<cmp> r12, r13, (-256) uint32_t inst = encoder(12, 13, -256); ASSERT_TRUE(tester->TestExecute(inst)); - auto pc = tester->ReadPC(&success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc + (branched ? (-256 * 4) : 4)); + auto pc = tester->ReadPC(); + ASSERT_TRUE(pc); + ASSERT_EQ(*pc, old_pc + (branched ? (-256 * 4) : 4)); } static void testBZcondBranch(LoongArch64EmulatorTester *tester, @@ -241,9 +241,9 @@ static void testBZcondBranch(LoongArch64EmulatorTester *tester, // b<cmp>z r4, (-256) uint32_t inst = encoder(4, -256); ASSERT_TRUE(tester->TestExecute(inst)); - auto pc = tester->ReadPC(&success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc + (branched ? (-256 * 4) : 4)); + auto pc = tester->ReadPC(); + ASSERT_TRUE(pc); + ASSERT_EQ(*pc, old_pc + (branched ? (-256 * 4) : 4)); } static void testBCZcondBranch(LoongArch64EmulatorTester *tester, @@ -256,9 +256,9 @@ static void testBCZcondBranch(LoongArch64EmulatorTester *tester, // bc<cmp>z fcc0, 256 uint32_t inst = encoder(0, 256); ASSERT_TRUE(tester->TestExecute(inst)); - auto pc = tester->ReadPC(&success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc + (branched ? (256 * 4) : 4)); + auto pc = tester->ReadPC(); + ASSERT_TRUE(pc); + ASSERT_EQ(*pc, old_pc + (branched ? (256 * 4) : 4)); } GEN_BCOND_TEST(64, BEQ, 1, 1, 0) >From 626f439dfd1b5b0f5e445123604e5ee42f40c3fb Mon Sep 17 00:00:00 2001 From: Daniil Avdeev <daniil.avd...@syntacore.com> Date: Fri, 23 May 2025 13:26:17 +0000 Subject: [PATCH 2/2] [lldb][RISCV] add lr/sc step tests --- .../RISCV/EmulateInstructionRISCV.cpp | 55 +++++++++---- lldb/test/API/riscv/step/Makefile | 3 + lldb/test/API/riscv/step/TestSoftwareStep.py | 81 +++++++++++++++++++ lldb/test/API/riscv/step/branch.c | 19 +++++ .../step/incomplete_sequence_without_lr.c | 19 +++++ .../step/incomplete_sequence_without_sc.c | 18 +++++ lldb/test/API/riscv/step/main.c | 19 +++++ 7 files changed, 200 insertions(+), 14 deletions(-) create mode 100644 lldb/test/API/riscv/step/Makefile create mode 100644 lldb/test/API/riscv/step/TestSoftwareStep.py create mode 100644 lldb/test/API/riscv/step/branch.c create mode 100644 lldb/test/API/riscv/step/incomplete_sequence_without_lr.c create mode 100644 lldb/test/API/riscv/step/incomplete_sequence_without_sc.c create mode 100644 lldb/test/API/riscv/step/main.c diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp index c0fcca4e0049f..c9722b5dacb08 100644 --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -1801,8 +1801,15 @@ RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations( return HandleAtomicSequence(*pc, status); if (FoundStoreConditional(inst->decoded)) { - // Ill-formed program, just set a breakpoint to the current position - return {*pc}; + // Ill-formed atomic sequence (SC doesn't have corresponding LR + // instruction). Consider SC instruction like a non-atomic store and set a + // breakpoint at the next instruction. + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOGF(log, + "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " + "corresponding load reserve insturuction", + __FUNCTION__); + return {*pc + 4}; } return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(status); @@ -1821,7 +1828,7 @@ unsigned RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointSize( last_instr_size) return *last_instr_size; - // Just place non-compressed software trap (EBREAK). + // Just place non-compressed software trap. return 4; } @@ -1830,11 +1837,12 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( lldb::addr_t pc, Status &error) { EmulateInstructionRISCV *riscv_emulator = static_cast<EmulateInstructionRISCV *>(m_emulator_up.get()); - // Handle instuctions between LR and SC. According to unprivilleged - // RISC-V ISA there can be at most 16 instructions in the sequence - lldb::addr_t entry_pc = pc; - pc += 4; // add LR_W, LR_D instruction size + // Handle instructions between LR and SC. According to unprivilleged + // RISC-V ISA there can be at most 16 instructions in the sequence. + + lldb::addr_t entry_pc = pc; // LR instruction address + pc += 4; // add LR_W, LR_D instruction size size_t atomic_length = 0; std::optional<DecodeResult> inst; @@ -1856,20 +1864,39 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( !FoundStoreConditional(inst->decoded)); if (atomic_length >= s_max_atomic_sequence_length) { - // Ill-formed program, just set a breakpoint to the current position + // Ill-formed atomic sequence (LR doesn't have corresponding SC + // instruction). In this case consider LR like a non-atomic load instruction + // and set a breakpoint at the next after LR instruction. Log *log = GetLog(LLDBLog::Unwind); LLDB_LOGF(log, "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " "corresponding store conditional insturuction", __FUNCTION__); - return {entry_pc}; + return {entry_pc + 4}; } - bp_addrs.erase( - llvm::remove_if(bp_addrs, - [pc](lldb::addr_t bp_addr) { return pc >= bp_addr; }), - bp_addrs.end()); - bp_addrs.push_back(pc); + lldb::addr_t exit_pc = pc; + + // Check if we have a branch to the start of the atomic sequence after SC + // instruction. If we have such branch, consider it as a part of the atomic + // sequence. + inst = riscv_emulator->ReadInstructionAt(exit_pc); + if (inst) { + B *branch = std::get_if<B>(&inst->decoded); + if (branch && (exit_pc + SignExt(branch->imm)) == entry_pc) + exit_pc += inst->is_rvc ? 2 : 4; + } + + // Set breakpoints at the jump addresses of the forward branches that points + // after the end of the atomic sequence. + bp_addrs.erase(llvm::remove_if(bp_addrs, + [exit_pc](lldb::addr_t bp_addr) { + return exit_pc >= bp_addr; + }), + bp_addrs.end()); + + // Set breakpoint at the end of atomic sequence. + bp_addrs.push_back(exit_pc); return bp_addrs; } diff --git a/lldb/test/API/riscv/step/Makefile b/lldb/test/API/riscv/step/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/riscv/step/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/riscv/step/TestSoftwareStep.py b/lldb/test/API/riscv/step/TestSoftwareStep.py new file mode 100644 index 0000000000000..32bd08716c29f --- /dev/null +++ b/lldb/test/API/riscv/step/TestSoftwareStep.py @@ -0,0 +1,81 @@ +""" +Test software step-inst +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestSoftwareStep(TestBase): + @skipIf(archs=no_match(re.compile("rv*"))) + def test_cas(self): + self.build() + (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint( + self, "cas" + ) + entry_pc = cur_thread.GetFrameAtIndex(0).GetPC() + + self.runCmd("thread step-inst") + self.expect( + "thread list", + substrs=["stopped", "stop reason = instruction step into"], + ) + + pc = cur_thread.GetFrameAtIndex(0).GetPC() + self.assertTrue((pc - entry_pc) > 0x10) + + @skipIf(archs=no_match(re.compile("rv*"))) + def test_branch_cas(self): + self.build(dictionary={"C_SOURCES": "branch.c", "EXE": "branch.x"}) + (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint( + self, "branch_cas", exe_name="branch.x" + ) + entry_pc = cur_thread.GetFrameAtIndex(0).GetPC() + + self.runCmd("thread step-inst") + self.expect( + "thread list", + substrs=["stopped", "stop reason = instruction step into"], + ) + + pc = cur_thread.GetFrameAtIndex(0).GetPC() + self.assertTrue((pc - entry_pc) > 0x10) + + @skipIf(archs=no_match(re.compile("rv*"))) + def test_incomplete_sequence_without_lr(self): + self.build(dictionary={"C_SOURCES": "incomplete_sequence_without_lr.c", "EXE": "incomplete_lr.x"}) + (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint( + self, "incomplete_cas", exe_name="incomplete_lr.x" + ) + entry_pc = cur_thread.GetFrameAtIndex(0).GetPC() + + self.runCmd("thread step-inst") + + self.expect( + "thread list", + substrs=["stopped", "stop reason = instruction step into"], + ) + + pc = cur_thread.GetFrameAtIndex(0).GetPC() + self.assertTrue((pc - entry_pc) == 0x4) + + @skipIf(archs=no_match(re.compile("rv*"))) + def test_incomplete_sequence_without_sc(self): + self.build(dictionary={"C_SOURCES": "incomplete_sequence_without_sc.c", "EXE": "incomplete_sc.x"}) + (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint( + self, "incomplete_cas", exe_name="incomplete_sc.x" + ) + entry_pc = cur_thread.GetFrameAtIndex(0).GetPC() + + self.runCmd("thread step-inst") + + self.expect( + "thread list", + substrs=["stopped", "stop reason = instruction step into"], + ) + + pc = cur_thread.GetFrameAtIndex(0).GetPC() + self.assertTrue((pc - entry_pc) == 0x4) + diff --git a/lldb/test/API/riscv/step/branch.c b/lldb/test/API/riscv/step/branch.c new file mode 100644 index 0000000000000..232ede52dd462 --- /dev/null +++ b/lldb/test/API/riscv/step/branch.c @@ -0,0 +1,19 @@ +void __attribute__((naked)) branch_cas(int *a, int *b) { + asm volatile("1:\n\t" + "lr.w a2, (a0)\n\t" + "and a5, a2, a4\n\t" + "bne a5, a1, 2f\n\t" + "xor a5, a2, a0\n\t" + "and a5, a5, a4\n\t" + "xor a5, a2, a5\n\t" + "sc.w a5, a1, (a3)\n\t" + "beqz a5, 1b\n\t" + "2:\n\t" + "ret\n\t"); +} + +int main() { + int a = 4; + int b = 2; + branch_cas(&a, &b); +} diff --git a/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c b/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c new file mode 100644 index 0000000000000..86872df5d8ede --- /dev/null +++ b/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c @@ -0,0 +1,19 @@ +void __attribute__((naked)) incomplete_cas(int *a, int *b) { + asm volatile("1:\n\t" + "sc.w a5, a1, (a3)\n\t" + "and a5, a2, a4\n\t" + "beq a5, a1, 2f\n\t" + "xor a5, a2, a0\n\t" + "and a5, a5, a4\n\t" + "xor a5, a2, a5\n\t" + "sc.w a5, a1, (a3)\n\t" + "bnez a5, 1b\n\t" + "2:\n\t" + "ret\n\t"); +} + +int main() { + int a = 4; + int b = 2; + incomplete_cas(&a, &b); +} diff --git a/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c b/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c new file mode 100644 index 0000000000000..06a41b17ed266 --- /dev/null +++ b/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c @@ -0,0 +1,18 @@ +void __attribute__((naked)) incomplete_cas(int *a, int *b) { + asm volatile("1:\n\t" + "lr.w a2, (a0)\n\t" + "and a5, a2, a4\n\t" + "beq a5, a1, 2f\n\t" + "xor a5, a2, a0\n\t" + "and a5, a5, a4\n\t" + "xor a5, a2, a5\n\t" + "bnez a5, 1b\n\t" + "2:\n\t" + "ret\n\t"); +} + +int main() { + int a = 4; + int b = 2; + incomplete_cas(&a, &b); +} diff --git a/lldb/test/API/riscv/step/main.c b/lldb/test/API/riscv/step/main.c new file mode 100644 index 0000000000000..a58b6ab6a0fbf --- /dev/null +++ b/lldb/test/API/riscv/step/main.c @@ -0,0 +1,19 @@ +void __attribute__((naked)) cas(int *a, int *b) { + asm volatile("1:\n\t" + "lr.w a2, (a0)\n\t" + "and a5, a2, a4\n\t" + "beq a5, a1, 2f\n\t" + "xor a5, a2, a0\n\t" + "and a5, a5, a4\n\t" + "xor a5, a2, a5\n\t" + "sc.w a5, a1, (a3)\n\t" + "beqz a5, 1b\n\t" + "2:\n\t" + "ret\n\t"); +} + +int main() { + int a = 4; + int b = 2; + cas(&a, &b); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits