Index: packages/Python/lldbsuite/test/functionalities/single_step_atomic_sequence/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/single_step_atomic_sequence/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/functionalities/single_step_atomic_sequence/TestStepInAtomicSequence.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/single_step_atomic_sequence/TestStepInAtomicSequence.py @@ -0,0 +1,108 @@ +""" +Test to single step from atomic sequence +""" + +from __future__ import print_function + +import os, time +import re +import unittest2 +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class StepInAtomicSequenceAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIf(archs=no_match(re.compile('mips*'))) + def test(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out.*" ]) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByName('main', 'a.out') + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) + self.assertEqual(len(threads), 1) + self.thread = threads[0] + + arch = self.getArchitecture() + if re.match("mips" , arch): + atomic_seq_start_opcodes = ["ll", "lld"] + atomic_seq_end_opcodes = ["sc", "scd"] + else: + # TODO please add your arch here + self.fail('unimplemented for arch = "{arch}"'.format(arch=self.getArchitecture())) + + self.atomic_start_insns = atomic_seq_start_opcodes + self.atomic_end_insns = atomic_seq_end_opcodes + + list = target.FindFunctions('main', lldb.eFunctionNameTypeAuto) + self.assertTrue(list.GetSize() == 1) + sc = list.GetContextAtIndex(0) + self.assertTrue(sc.GetSymbol().GetName() == "main") + function = sc.GetFunction() + self.assertTrue(function) + self.function(function, target) + + def function (self, function, target): + """Stop at start of atomic sequence and single step""" + # Get the list of all instructions in the function + insts = function.GetInstructions(target) + print(insts) + i = 0 + for inst in insts: + inst_opcode = inst.GetMnemonic(target) + if inst_opcode in self.atomic_start_insns: + # Get the address of instruction starting atomic sequence + start_of_seq = inst.GetAddress().GetLoadAddress(target) + + # We have found starting instruction of atomic sequence + # now scan more instructions to find end of atomic sequence + # Assume that no atomic sequence is longer than 16 instructions + for j in range(1,16): + next_inst = insts.GetInstructionAtIndex(i+j) + next_inst_opcode = next_inst.GetMnemonic(target) + if next_inst_opcode in self.atomic_end_insns: + end_of_seq = next_inst.GetAddress().GetLoadAddress(target) + break + + # Stop at start of atomic sequence + self.thread.RunToAddress(start_of_seq) + + # Step one instruction + self.thread.StepInstruction(False) + + # Get the PC after single step + frame = self.thread.GetFrameAtIndex(0) + current_pc = frame.GetPC() + + print ("Sequence Starts at 0x%x:"% start_of_seq) + print ("Sequence ends at 0x%x:"% end_of_seq) + print ("After step stopped at 0x%x:"% current_pc) + + # Check if single step have skipped entire atomic sequence + self.assertTrue(current_pc > end_of_seq) + i += 1 + else: + i += 1 + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: packages/Python/lldbsuite/test/functionalities/single_step_atomic_sequence/main.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/single_step_atomic_sequence/main.cpp @@ -0,0 +1,21 @@ +#include +using namespace std; + +std::atomic ai; + +int tst_val= 4; +int new_val= 5; +bool exchanged= false; + +int main() +{ + ai= 3; + + // tst_val != ai ==> tst_val is modified + exchanged= ai.compare_exchange_strong( tst_val, new_val ); + + // tst_val == ai ==> ai is modified + exchanged= ai.compare_exchange_strong( tst_val, new_val ); + + return 0; +} Index: source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h =================================================================== --- source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h +++ source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h @@ -118,6 +118,18 @@ GetOpcodeForInstruction (const char *op_name); bool + IsBranch (llvm::MCInst& insn); + + bool + GetInstructionAtAddress (lldb::addr_t insn_addr, llvm::MCInst& mc_insn); + + void + AddBreakPointAddress (lldb::addr_t bp_addr); + + bool + WriteBreakPointAddresses (); + + bool Emulate_DADDiu (llvm::MCInst& insn); bool @@ -133,6 +145,9 @@ Emulate_LDST_Reg (llvm::MCInst& insn); bool + Emulate_LL (llvm::MCInst& insn); + + bool Emulate_BXX_3ops (llvm::MCInst& insn); bool @@ -238,6 +253,11 @@ std::unique_ptr m_asm_info; std::unique_ptr m_context; std::unique_ptr m_insn_info; + bool m_auto_advance_pc; + lldb::addr_t m_atomic_seq_start; + lldb::addr_t m_atomic_seq_end; + uint32_t m_bytes_from_atomic_seq_start; + std::vector m_breakpoint_addresses; }; #endif // EmulateInstructionMIPS64_h_ Index: source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp =================================================================== --- source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp +++ source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp @@ -152,6 +152,11 @@ m_disasm.reset (target->createMCDisassembler (*m_subtype_info, *m_context)); assert (m_disasm.get()); + + m_auto_advance_pc = false; + m_atomic_seq_start = 0; + m_atomic_seq_end = 0; + m_bytes_from_atomic_seq_start = 0; } void @@ -510,7 +515,8 @@ { "LHE", &EmulateInstructionMIPS64::Emulate_LDST_Imm, "LHE rt, offset(base)" }, { "LHU", &EmulateInstructionMIPS64::Emulate_LDST_Imm, "LHU rt, offset(base)" }, { "LHUE", &EmulateInstructionMIPS64::Emulate_LDST_Imm, "LHUE rt, offset(base)" }, - { "LL", &EmulateInstructionMIPS64::Emulate_LDST_Imm, "LL rt, offset(base)" }, + { "LL", &EmulateInstructionMIPS64::Emulate_LL, "LL rt, offset(base)" }, + { "LLD", &EmulateInstructionMIPS64::Emulate_LL, "LLD rt, offset(base)" }, { "LLE", &EmulateInstructionMIPS64::Emulate_LDST_Imm, "LLE rt, offset(base)" }, { "LUXC1", &EmulateInstructionMIPS64::Emulate_LDST_Reg, "LUXC1 fd, index (base)" }, { "LW", &EmulateInstructionMIPS64::Emulate_LDST_Imm, "LW rt, offset(rs)" }, @@ -648,6 +654,74 @@ } bool +EmulateInstructionMIPS64::IsBranch (llvm::MCInst& insn) +{ + return m_insn_info->get(insn.getOpcode()).isBranch(); +} + +bool +EmulateInstructionMIPS64::GetInstructionAtAddress (lldb::addr_t insn_addr, llvm::MCInst& mc_insn) +{ + DataExtractor data; + Opcode opcode; + uint64_t insn_size; + bool success = false; + + Context read_inst_context; + read_inst_context.type = eContextReadOpcode; + read_inst_context.SetNoArgs (); + + opcode.SetOpcode32 (ReadMemoryUnsigned (read_inst_context, insn_addr, 4, 0, &success), GetByteOrder()); + + if (!success) + return false; + + // Get mc_insn + if (opcode.GetData (data)) + { + llvm::MCDisassembler::DecodeStatus decode_status; + llvm::ArrayRef raw_insn (data.GetDataStart(), data.GetByteSize()); + + decode_status = m_disasm->getInstruction (mc_insn, insn_size, raw_insn, insn_addr, llvm::nulls(), llvm::nulls()); + + if (decode_status == llvm::MCDisassembler::Success) + return true; + } + return false; +} + +void +EmulateInstructionMIPS64::AddBreakPointAddress (lldb::addr_t bp_addr) +{ + // Do not add duplicate addresses + if (std::find(m_breakpoint_addresses.begin(), m_breakpoint_addresses.end(), bp_addr) == m_breakpoint_addresses.end()) + { + m_breakpoint_addresses.push_back(bp_addr); + } +} + +bool +EmulateInstructionMIPS64::WriteBreakPointAddresses () +{ + Context bad_vaddr_context; + bad_vaddr_context.type = eContextInvalid; + + if (!m_breakpoint_addresses.empty()) + { + for (const auto addr: m_breakpoint_addresses) + { + // skip breakpoint placed in atomic sequence + if (addr > m_atomic_seq_start && addr <= m_atomic_seq_end) + continue; + if (!WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, dwarf_pc_mips, addr)) + return false; + } + } + + return true; +} + +bool EmulateInstructionMIPS64::EvaluateInstruction (uint32_t evaluate_options) { bool success = false; @@ -681,12 +755,12 @@ MipsOpcode *opcode_data = GetOpcodeForInstruction (op_name); if (opcode_data == NULL) - return false; + return false; uint64_t old_pc = 0, new_pc = 0; - const bool auto_advance_pc = evaluate_options & eEmulateInstructionOptionAutoAdvancePC; + m_auto_advance_pc = evaluate_options & eEmulateInstructionOptionAutoAdvancePC; - if (auto_advance_pc) + if (m_auto_advance_pc) { old_pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips64, 0, &success); if (!success) @@ -698,7 +772,7 @@ if (!success) return false; - if (auto_advance_pc) + if (m_auto_advance_pc) { new_pc = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc_mips64, 0, &success); if (!success) @@ -929,6 +1003,14 @@ if (!success) return false; + // Consider branch instructions in atomic sequence. + if (m_atomic_seq_start) + { + target = pc + offset + m_bytes_from_atomic_seq_start; + AddBreakPointAddress ((lldb::addr_t)target); + return true; + } + rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; @@ -1176,6 +1258,14 @@ if (!success) return false; + // Consider branch instructions in atomic sequence. + if (m_atomic_seq_start) + { + target = pc + offset; + AddBreakPointAddress ((lldb::addr_t)target); + return true; + } + rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; @@ -1278,6 +1368,14 @@ if (!success) return false; + // Consider branch instructions in atomic sequence. + if (m_atomic_seq_start) + { + target = pc + offset + 4; + AddBreakPointAddress ((lldb::addr_t)target); + return true; + } + rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; @@ -1374,6 +1472,14 @@ if (!success) return false; + // Consider branch instructions in atomic sequence. + if (m_atomic_seq_start) + { + target = pc + offset + 4; + AddBreakPointAddress ((lldb::addr_t)target); + return true; + } + rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; @@ -2045,3 +2151,69 @@ return true; } + +bool +EmulateInstructionMIPS64::Emulate_LL (llvm::MCInst& insn) +{ + // Emulate this instruction only for single step + if (!m_auto_advance_pc) + return true; + + bool success = false; + uint32_t atomic_sequence_length = 16; // Assume that no atomic sequence is longer than 16 instructions. + lldb::addr_t insn_addr = m_addr; + + // Mark start of atomic sequence + m_atomic_seq_start = insn_addr; + + for (uint32_t insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) + { + llvm::MCInst mc_insn; + insn_addr = insn_addr + 4; + + // Get the instruction + success = GetInstructionAtAddress (insn_addr, mc_insn); + if (!success) + return false; + + // Keep the track of how much bytes we have come from LL instruction. + m_bytes_from_atomic_seq_start += 4; + + const char *op_name = m_insn_info->getName (mc_insn.getOpcode ()); + + // Check if it is branch instruction. + if (IsBranch (mc_insn)) + { + MipsOpcode *opcode_data = GetOpcodeForInstruction (op_name); + if (opcode_data != NULL) + { + /* Let the existing emulation function do the PC write */ + success = (this->*opcode_data->callback) (mc_insn); + } + } + else + { + if (!strcasecmp (op_name, "SC") || + !strcasecmp (op_name, "SCD")) + { + // SC/SCD is the end of atomic sequence, add breakpoint at next instruction. + AddBreakPointAddress (insn_addr + 4); + // Address of sc/scd instruction + m_atomic_seq_end = insn_addr; + break; + } + } + } + + // Place the breakpoints If and only if we found end of atomic sequence. + if (m_atomic_seq_end) + WriteBreakPointAddresses (); + + // Reset the counters + m_atomic_seq_start = 0; + m_atomic_seq_end = 0; + m_bytes_from_atomic_seq_start = 0; + m_breakpoint_addresses.clear(); + + return true; +} Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -147,7 +147,7 @@ // List of thread ids stepping with a breakpoint with the address of // the relevan breakpoint - std::map m_threads_stepping_with_breakpoint; + std::multimap m_threads_stepping_with_breakpoint; /// @class LauchArgs /// Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -1522,6 +1522,7 @@ // eRegisterKindDWARF -> RegsiterValue std::unordered_map m_register_values; + std::vector next_pc_addresses; EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : m_process(process), m_reg_context(reg_context) {} @@ -1581,6 +1582,10 @@ { EmulatorBaton* emulator_baton = static_cast(baton); emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value; + if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC) + { + emulator_baton->next_pc_addresses.push_back(reg_value.GetAsUInt64()); + } return true; } @@ -1632,6 +1637,7 @@ auto pc_it = baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); auto flags_it = baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]); + const auto arch_machine = m_arch.GetMachine(); lldb::addr_t next_pc; lldb::addr_t next_flags; @@ -1662,7 +1668,12 @@ return Error ("Instruction emulation failed unexpectedly."); } - if (m_arch.GetMachine() == llvm::Triple::arm) + if (baton.next_pc_addresses.empty()) + { + baton.next_pc_addresses.push_back(next_pc); + } + + if (arch_machine == llvm::Triple::arm) { if (next_flags & 0x20) { @@ -1675,11 +1686,18 @@ error = SetSoftwareBreakpoint(next_pc, 4); } } - else if (m_arch.GetMachine() == llvm::Triple::mips64 - || m_arch.GetMachine() == llvm::Triple::mips64el - || m_arch.GetMachine() == llvm::Triple::mips - || m_arch.GetMachine() == llvm::Triple::mipsel) - error = SetSoftwareBreakpoint(next_pc, 4); + else if (arch_machine == llvm::Triple::mips64 || + arch_machine == llvm::Triple::mips64el || + arch_machine == llvm::Triple::mips || + arch_machine == llvm::Triple::mipsel) + { + for (const auto addr: baton.next_pc_addresses) + { + error = SetSoftwareBreakpoint(addr, 4); + if (error.Fail()) + return error; + } + } else { // No size hint is given for the next breakpoint @@ -1689,7 +1707,12 @@ if (error.Fail()) return error; - m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); + for (const auto addr: baton.next_pc_addresses) + { + m_threads_stepping_with_breakpoint.insert({thread.GetID(), addr}); + } + + baton.next_pc_addresses.clear(); return Error(); }