dmikulin updated this revision to Diff 79987.
dmikulin added a comment.

Addressed review comments.
Haven't had a chance to re-test it on an arm board.


Repository:
  rL LLVM

https://reviews.llvm.org/D25756

Files:
  source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
  source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
  source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
  source/Plugins/Process/FreeBSD/ProcessMonitor.cpp

Index: source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
===================================================================
--- source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
+++ source/Plugins/Process/FreeBSD/ProcessMonitor.cpp
@@ -1141,11 +1141,17 @@
 
   case SI_KERNEL:
   case TRAP_BRKPT:
-    if (log)
-      log->Printf(
-          "ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64,
-          __FUNCTION__, tid);
-    message = ProcessMessage::Break(tid);
+    if (monitor->m_process->IsSoftwareStepBreakpoint(tid)) {
+      if (log)
+        log->Printf ("ProcessMonitor::%s() received sw single step breakpoint "
+                     "event, tid = %" PRIu64, __FUNCTION__, tid);
+      message = ProcessMessage::Trace(tid);
+    } else {
+      if (log)
+        log->Printf ("ProcessMonitor::%s() received breakpoint event, tid = %"
+                     PRIu64, __FUNCTION__, tid);
+      message = ProcessMessage::Break(tid);
+    }
     break;
   }
 
Index: source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
===================================================================
--- source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
+++ source/Plugins/Process/FreeBSD/ProcessFreeBSD.h
@@ -171,7 +171,27 @@
   virtual FreeBSDThread *CreateNewFreeBSDThread(lldb_private::Process &process,
                                                 lldb::tid_t tid);
 
-protected:
+  static bool
+  SingleStepBreakpointHit(void *baton,
+			  lldb_private::StoppointCallbackContext *context,
+			  lldb::user_id_t break_id,
+			  lldb::user_id_t break_loc_id);
+
+  lldb_private::Error SetupSoftwareSingleStepping(lldb::tid_t tid);
+
+  lldb_private::Error
+  SetSoftwareSingleStepBreakpoint (lldb::tid_t tid, lldb::addr_t addr);
+
+  bool IsSoftwareStepBreakpoint(lldb::tid_t tid);
+
+  bool SupportHardwareSingleStepping() const;
+
+  typedef std::vector<lldb::tid_t> tid_collection;
+  tid_collection &GetStepTids() { return m_step_tids; }
+
+ protected:
+  static const size_t MAX_TRAP_OPCODE_SIZE = 8;
+
   /// Target byte order.
   lldb::ByteOrder m_byte_order;
 
@@ -207,10 +227,10 @@
 
   friend class FreeBSDThread;
 
-  typedef std::vector<lldb::tid_t> tid_collection;
   tid_collection m_suspend_tids;
   tid_collection m_run_tids;
   tid_collection m_step_tids;
+  std::map<lldb::tid_t, lldb::break_id_t> m_threads_stepping_with_breakpoint;
 
   int m_resume_signo;
 };
Index: source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
===================================================================
--- source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
+++ source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp
@@ -13,6 +13,7 @@
 
 // C++ Includes
 #include <mutex>
+#include <unordered_map>
 
 // Other libraries and framework includes
 #include "lldb/Core/PluginManager.h"
@@ -122,6 +123,7 @@
 
   std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
   bool do_step = false;
+  bool software_single_step = !SupportHardwareSingleStepping();
 
   for (tid_collection::const_iterator t_pos = m_run_tids.begin(),
                                       t_end = m_run_tids.end();
@@ -133,6 +135,11 @@
        t_pos != t_end; ++t_pos) {
     m_monitor->ThreadSuspend(*t_pos, false);
     do_step = true;
+    if (software_single_step) {
+      Error error = SetupSoftwareSingleStepping(*t_pos);
+      if (error.Fail())
+        return error;
+    }
   }
   for (tid_collection::const_iterator t_pos = m_suspend_tids.begin(),
                                       t_end = m_suspend_tids.end();
@@ -145,7 +152,7 @@
   if (log)
     log->Printf("process %" PRIu64 " resuming (%s)", GetID(),
                 do_step ? "step" : "continue");
-  if (do_step)
+  if (do_step && !software_single_step)
     m_monitor->SingleStep(GetID(), m_resume_signo);
   else
     m_monitor->Resume(GetID(), m_resume_signo);
@@ -913,3 +920,194 @@
       "no platform or not the host - how did we get here with ProcessFreeBSD?");
   return DataBufferSP();
 }
+
+struct EmulatorBaton {
+  ProcessFreeBSD *m_process;
+  RegisterContext *m_reg_context;
+
+  // eRegisterKindDWARF -> RegisterValue
+  std::unordered_map<uint32_t, RegisterValue> m_register_values;
+
+  EmulatorBaton(ProcessFreeBSD *process, RegisterContext *reg_context)
+      : m_process(process), m_reg_context(reg_context) {}
+};
+
+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<EmulatorBaton *>(baton);
+
+  Error error;
+  size_t bytes_read =
+      emulator_baton->m_process->DoReadMemory(addr, dst, length, error);
+  if (!error.Success())
+    bytes_read = 0;
+  return bytes_read;
+}
+
+static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
+                                 const RegisterInfo *reg_info,
+                                 RegisterValue &reg_value) {
+  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+
+  auto it = emulator_baton->m_register_values.find(
+      reg_info->kinds[eRegisterKindDWARF]);
+  if (it != emulator_baton->m_register_values.end()) {
+    reg_value = it->second;
+    return true;
+  }
+
+  // The emulator only fills in the dwarf register numbers (and in some cases
+  // 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]);
+
+  bool error =
+      emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+  return error;
+}
+
+static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
+                                  const EmulateInstruction::Context &context,
+                                  const RegisterInfo *reg_info,
+                                  const RegisterValue &reg_value) {
+  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
+  emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
+      reg_value;
+  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;
+}
+
+bool ProcessFreeBSD::SingleStepBreakpointHit(
+    void *baton, lldb_private::StoppointCallbackContext *context,
+    lldb::user_id_t break_id, lldb::user_id_t break_loc_id) {
+  return false;
+}
+
+Error ProcessFreeBSD::SetSoftwareSingleStepBreakpoint(lldb::tid_t tid,
+                                                      lldb::addr_t addr) {
+  Error error;
+
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  if (log) {
+    log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+    log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+  }
+
+  // Validate the address.
+  if (addr == LLDB_INVALID_ADDRESS)
+    return Error("ProcessFreeBSD::%s invalid load address specified.",
+                 __FUNCTION__);
+
+  Breakpoint *const sw_step_break =
+      m_process->GetTarget().CreateBreakpoint(addr, true, false).get();
+  sw_step_break->SetCallback(SingleStepBreakpointHit, this, true);
+  sw_step_break->SetBreakpointKind("software-signle-step");
+
+  if (log)
+    log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64 " -- SUCCESS",
+                __FUNCTION__, addr);
+
+  m_threads_stepping_with_breakpoint.insert({tid, sw_step_break->GetID()});
+  return Error();
+}
+
+bool ProcessFreeBSD::IsSoftwareStepBreakpoint(lldb::tid_t tid) {
+  ThreadSP thread = GetThreadList().FindThreadByID(tid);
+  if (!thread)
+    return false;
+
+  assert(thread->GetRegisterContext());
+  lldb::addr_t stop_pc = thread->GetRegisterContext()->GetPC();
+
+  const auto &iter = m_threads_stepping_with_breakpoint.find(tid);
+  if (iter == m_threads_stepping_with_breakpoint.end())
+    return false;
+
+  lldb::break_id_t bp_id = iter->second;
+  BreakpointSP bp = GetTarget().GetBreakpointByID(bp_id);
+  if (!bp)
+    return false;
+
+  BreakpointLocationSP bp_loc = bp->FindLocationByAddress(stop_pc);
+  if (!bp_loc)
+    return false;
+
+  GetTarget().RemoveBreakpointByID(bp_id);
+  m_threads_stepping_with_breakpoint.erase(tid);
+  return true;
+}
+
+bool ProcessFreeBSD::SupportHardwareSingleStepping() const {
+  lldb_private::ArchSpec arch = GetTarget().GetArchitecture();
+  if (arch.GetMachine() == llvm::Triple::arm ||
+      arch.GetMachine() == llvm::Triple::mips64 ||
+      arch.GetMachine() == llvm::Triple::mips64el ||
+      arch.GetMachine() == llvm::Triple::mips ||
+      arch.GetMachine() == llvm::Triple::mipsel)
+    return false;
+  return true;
+}
+
+Error ProcessFreeBSD::SetupSoftwareSingleStepping(lldb::tid_t tid) {
+  std::unique_ptr<EmulateInstruction> emulator_ap(
+      EmulateInstruction::FindPlugin(GetTarget().GetArchitecture(),
+                                     eInstructionTypePCModifying, nullptr));
+
+  if (emulator_ap == nullptr)
+    return Error("Instruction emulator not found!");
+
+  FreeBSDThread *thread = static_cast<FreeBSDThread *>(
+      m_thread_list.FindThreadByID(tid, false).get());
+  if (thread == NULL)
+    return Error("Thread not found not found!");
+
+  lldb::RegisterContextSP register_context_sp = thread->GetRegisterContext();
+
+  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!");
+
+  bool emulation_result =
+      emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+  const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo(
+      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+  auto pc_it =
+      baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
+
+  lldb::addr_t next_pc;
+  if (emulation_result) {
+    assert(pc_it != baton.m_register_values.end() &&
+           "Emulation was successful but PC wasn't updated");
+    next_pc = pc_it->second.GetAsUInt64();
+  } 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_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize();
+  } 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");
+  }
+
+  SetSoftwareSingleStepBreakpoint(tid, next_pc);
+  return Error();
+}
Index: source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
===================================================================
--- source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
+++ source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp
@@ -549,6 +549,12 @@
       // FreeBSD kernel as of 10.x, does not support thumb breakpoints
       return 0;
     }
+
+    static const uint8_t g_arm_breakpoint_opcode[] = {0xFE,0xDE,0xFF,0xE7};
+    size_t trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+    assert(bp_site);
+    if (bp_site->SetTrapOpcode(g_arm_breakpoint_opcode, trap_opcode_size))
+      return trap_opcode_size;
   }
     LLVM_FALLTHROUGH;
   default:
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to