From e7708688baff20eb8ed64c9bb7142ac0bd67081e Mon Sep 17 00:00:00 2001 From: Tamas Berghammer Date: Wed, 22 Apr 2015 10:00:23 +0000 Subject: [PATCH] Fix signle stepping on arm when multiple thread is involved On linux-arm we use software single stepping where setting the new breakpoint is only possible while the process is in stopped state. This CL moves the setup code for single stepping form the SigneStep operation into the Resum method to avoid an error when the process already started when we want to step one of the thread. Differential revision: http://reviews.llvm.org/D9108 llvm-svn: 235494 --- .../Process/Linux/NativeProcessLinux.cpp | 367 ++++++++++-------- .../Process/Linux/NativeProcessLinux.h | 6 + 2 files changed, 205 insertions(+), 168 deletions(-) diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index d338ffdf36d6..e9014e08f32e 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2782,6 +2782,178 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e }); } +namespace { + +struct EmulatorBaton +{ + NativeProcessLinux* m_process; + NativeRegisterContext* m_reg_context; + RegisterValue m_pc; + RegisterValue m_flags; + + EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : + m_process(process), m_reg_context(reg_context) {} +}; + +} // anonymous namespace + +static size_t +ReadMemoryCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t length) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + lldb::addr_t bytes_read; + emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static bool +ReadRegisterCallback (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + // The emulator only fill in the dwarf regsiter numbers (and in some case + // the generic register numbers). Get the full register info from the + // register context based on the dwarf register numbers. + const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( + eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); + + Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + return error.Success(); +} + +static bool +WriteRegisterCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + switch (reg_info->kinds[eRegisterKindGeneric]) + { + case LLDB_REGNUM_GENERIC_PC: + emulator_baton->m_pc = reg_value; + break; + case LLDB_REGNUM_GENERIC_FLAGS: + emulator_baton->m_flags = reg_value; + break; + } + + return true; +} + +static size_t +WriteMemoryCallback (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length) +{ + return length; +} + +static lldb::addr_t +ReadFlags (NativeRegisterContext* regsiter_context) +{ + const RegisterInfo* flags_info = regsiter_context->GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS); +} + +Error +NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadProtocolSP thread_sp) +{ + Error error; + NativeRegisterContextSP register_context_sp = thread_sp->GetRegisterContext(); + + std::unique_ptr emulator_ap( + EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr)); + + if (emulator_ap == nullptr) + return Error("Instruction emulator not found!"); + + EmulatorBaton baton(this, register_context_sp.get()); + emulator_ap->SetBaton(&baton); + emulator_ap->SetReadMemCallback(&ReadMemoryCallback); + emulator_ap->SetReadRegCallback(&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return Error("Read instruction failed!"); + + lldb::addr_t next_pc; + lldb::addr_t next_flags; + if (emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC)) + { + next_pc = baton.m_pc.GetAsUInt64(); + if (baton.m_flags.GetType() != RegisterValue::eTypeInvalid) + next_flags = baton.m_flags.GetAsUInt32(); + else + next_flags = ReadFlags (register_context_sp.get()); + } + else if (baton.m_pc.GetType() == RegisterValue::eTypeInvalid) + { + // 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_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); + next_flags = ReadFlags (register_context_sp.get()); + } + 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 Error ("Instruction emulation failed unexpectedly."); + } + + if (m_arch.GetMachine() == llvm::Triple::arm) + { + if (next_flags & 0x20) + { + // Thumb mode + error = SetSoftwareBreakpoint(next_pc, 2); + } + else + { + // Arm mode + error = SetSoftwareBreakpoint(next_pc, 4); + } + } + else + { + // No size hint is given for the next breakpoint + error = SetSoftwareBreakpoint(next_pc, 0); + } + + + if (error.Fail()) + return error; + + m_threads_stepping_with_breakpoint.insert({thread_sp->GetID(), next_pc}); + + return Error(); +} + +bool +NativeProcessLinux::SupportHardwareSingleStepping() const +{ + return m_arch.GetMachine() != llvm::Triple::arm; +} + Error NativeProcessLinux::Resume (const ResumeActionList &resume_actions) { @@ -2794,9 +2966,29 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions) int deferred_signo = 0; NativeThreadProtocolSP deferred_signal_thread_sp; bool stepping = false; + bool software_single_step = !SupportHardwareSingleStepping(); Mutex::Locker locker (m_threads_mutex); + if (software_single_step) + { + for (auto thread_sp : m_threads) + { + assert (thread_sp && "thread list should not contain NULL threads"); + + const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true); + if (action == nullptr) + continue; + + if (action->state == eStateStepping) + { + Error error = SetupSoftwareSingleStepping(thread_sp); + if (error.Fail()) + return error; + } + } + } + for (auto thread_sp : m_threads) { assert (thread_sp && "thread list should not contain NULL threads"); @@ -2845,7 +3037,13 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions) [=](lldb::tid_t tid_to_step, bool supress_signal) { std::static_pointer_cast (thread_sp)->SetStepping (); - const auto step_result = SingleStep (tid_to_step,(signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER); + + Error step_result; + if (software_single_step) + step_result = Resume (tid_to_step, (signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER); + else + step_result = SingleStep (tid_to_step,(signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER); + assert (step_result.Success() && "SingleStep() failed"); if (step_result.Success()) SetState(eStateStepping, true); @@ -3700,171 +3898,6 @@ NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) return op.GetError(); } -#if defined(__arm__) - -namespace { - -struct EmulatorBaton -{ - NativeProcessLinux* m_process; - NativeRegisterContext* m_reg_context; - RegisterValue m_pc; - RegisterValue m_cpsr; - - EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : - m_process(process), m_reg_context(reg_context) {} -}; - -} // anonymous namespace - -static size_t -ReadMemoryCallback (EmulateInstruction *instruction, - void *baton, - const EmulateInstruction::Context &context, - lldb::addr_t addr, - void *dst, - size_t length) -{ - EmulatorBaton* emulator_baton = static_cast(baton); - - lldb::addr_t bytes_read; - emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); - return bytes_read; -} - -static bool -ReadRegisterCallback (EmulateInstruction *instruction, - void *baton, - const RegisterInfo *reg_info, - RegisterValue ®_value) -{ - EmulatorBaton* emulator_baton = static_cast(baton); - - // The emulator only fill in the dwarf regsiter numbers (and in some case - // the generic register numbers). Get the full register info from the - // register context based on the dwarf register numbers. - const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( - eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); - - Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); - return error.Success(); -} - -static bool -WriteRegisterCallback (EmulateInstruction *instruction, - void *baton, - const EmulateInstruction::Context &context, - const RegisterInfo *reg_info, - const RegisterValue ®_value) -{ - EmulatorBaton* emulator_baton = static_cast(baton); - - switch (reg_info->kinds[eRegisterKindGeneric]) - { - case LLDB_REGNUM_GENERIC_PC: - emulator_baton->m_pc = reg_value; - break; - case LLDB_REGNUM_GENERIC_FLAGS: - emulator_baton->m_cpsr = reg_value; - break; - } - - return true; -} - -static size_t -WriteMemoryCallback (EmulateInstruction *instruction, - void *baton, - const EmulateInstruction::Context &context, - lldb::addr_t addr, - const void *dst, - size_t length) -{ - return length; -} - -static lldb::addr_t -ReadCpsr (NativeRegisterContext* regsiter_context) -{ - const RegisterInfo* cpsr_info = regsiter_context->GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); - return regsiter_context->ReadRegisterAsUnsigned(cpsr_info, LLDB_INVALID_ADDRESS); -} - -Error -NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) -{ - Error error; - NativeRegisterContextSP register_context_sp = GetThreadByID(tid)->GetRegisterContext(); - - std::unique_ptr emulator_ap( - EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr)); - - if (emulator_ap == nullptr) - return Error("Instruction emulator not found!"); - - EmulatorBaton baton(this, register_context_sp.get()); - emulator_ap->SetBaton(&baton); - emulator_ap->SetReadMemCallback(&ReadMemoryCallback); - emulator_ap->SetReadRegCallback(&ReadRegisterCallback); - emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); - emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); - - if (!emulator_ap->ReadInstruction()) - return Error("Read instruction failed!"); - - lldb::addr_t next_pc; - lldb::addr_t next_cpsr; - if (emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC)) - { - next_pc = baton.m_pc.GetAsUInt32(); - if (baton.m_cpsr.GetType() != RegisterValue::eTypeInvalid) - next_cpsr = baton.m_cpsr.GetAsUInt32(); - else - next_cpsr = ReadCpsr (register_context_sp.get()); - } - else if (baton.m_pc.GetType() == RegisterValue::eTypeInvalid) - { - // 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_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); - next_cpsr = ReadCpsr (register_context_sp.get()); - } - 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 Error ("Instruction emulation failed unexpectedly."); - } - - if (next_cpsr & 0x20) - { - // Thumb mode - error = SetBreakpoint(next_pc, 2, false); - } - else - { - // Arm mode - error = SetBreakpoint(next_pc, 4, false); - } - - if (error.Fail()) - return error; - - m_threads_stepping_with_breakpoint.insert({tid, next_pc}); - - error = Resume(tid, signo); - if (error.Fail()) - return error; - - return Error(); -} - -#else // defined(__arm__) - Error NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) { @@ -3873,8 +3906,6 @@ NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) return op.GetError(); } -#endif // defined(__arm__) - Error NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h index 303a1907fb42..c0f68bede0f9 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -277,6 +277,12 @@ namespace process_linux { void MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool exited); + bool + SupportHardwareSingleStepping() const; + + Error + SetupSoftwareSingleStepping(NativeThreadProtocolSP thread_sp); + #if 0 static ::ProcessMessage::CrashReason GetCrashReasonForSIGSEGV(const siginfo_t *info);