Пример #1
0
void ProtocolInterpreter::onCommand(std::string const &command,
                                    std::string const &arguments) {
  size_t commandLength;
  Handler const *handler = findHandler(command, commandLength);
  if (handler == nullptr) {
    DS2LOG(Packet, "handler for command '%s' unknown", command.c_str());

    //
    // The handler couldn't be found, we don't support this packet.
    //
    _session->sendError(kErrorUnsupported);
    return;
  }

  std::string extra;
  if (commandLength != command.length()) {
    //
    // Command has part of the argument, LLDB doesn't use separators :(
    //
    extra = command.substr(commandLength);
  }

  extra += arguments;

  if (extra.find_first_of("*}") != std::string::npos) {
    extra = Unescape(extra);
    DS2LOG(Packet, "args='%.*s'", static_cast<int>(extra.length()), &extra[0]);
  }

  (handler->handler->*handler->callback)(*handler, extra);
}
Пример #2
0
void SoftwareBreakpointManager::enableLocation(Site const &site) {
  std::string opcode;
  std::string old;
  ErrorCode error;

  getOpcode(site.size, opcode);
  old.resize(opcode.size());
  error = _process->readMemory(site.address, &old[0], old.size());
  if (error != kSuccess) {
    DS2LOG(Error, "cannot enable breakpoint at %#lx",
           (unsigned long)site.address.value());
    return;
  }

  error = _process->writeMemory(site.address, &opcode[0], opcode.size());
  if (error != kSuccess) {
    DS2LOG(Error, "cannot enable breakpoint at %#lx",
           (unsigned long)site.address.value());
    return;
  }

  DS2LOG(Info, "set breakpoint instruction %#lx at %#lx (saved insn %#lx)",
         (unsigned long)(site.size == 2 ? *(uint16_t *)&opcode[0]
                                        : *(uint32_t *)&opcode[0]),
         (unsigned long)site.address.value(),
         (unsigned long)(site.size == 2 ? *(uint16_t *)&old[0]
                                        : *(uint32_t *)&old[0]));

  _insns[site.address] = old;
}
Пример #3
0
void SoftwareBreakpointManager::enableLocation(Site const &site) {
  uint8_t const opcode = 0xcc; // int 3
  uint8_t old;
  ErrorCode error;

  error = _process->readMemory(site.address, &old, sizeof(old));
  if (error != kSuccess) {
    DS2LOG(BPManager, Error, "cannot enable breakpoint at %#lx",
           (unsigned long)site.address.value());
    return;
  }

  error = _process->writeMemory(site.address, &opcode, sizeof(opcode));
  if (error != kSuccess) {
    DS2LOG(BPManager, Error, "cannot enable breakpoint at %#lx",
           (unsigned long)site.address.value());
    return;
  }

  DS2LOG(BPManager, Info,
         "set breakpoint instruction %#x at %#lx (saved insn %#x)", opcode,
         (unsigned long)site.address.value(), old);

  _insns[site.address] = old;
}
Пример #4
0
ErrorCode ProcessBase::resume(int signal, std::set<Thread *> const &excluded) {
  enumerateThreads([&](Thread *thread) {
    if (excluded.find(thread) != excluded.end())
      return;

    switch (thread->state()) {
    case Thread::kInvalid:
    case Thread::kTerminated:
      DS2BUG("trying to resume tid %" PRI_PID " in state %s", thread->tid(),
             Stringify::ThreadState(thread->state()));
      break;

    case Thread::kRunning:
      DS2LOG(Debug, "not resuming tid %" PRI_PID ", already in state %s",
             thread->tid(), Stringify::ThreadState(thread->state()));
      break;

    case Thread::kStopped:
    case Thread::kStepped: {
      Architecture::CPUState state;
      thread->readCPUState(state);
      DS2LOG(Debug,
             "resuming tid %" PRI_PID " from pc %" PRI_PTR " with signal %d",
             thread->tid(), PRI_PTR_CAST(state.pc()), signal);
      ErrorCode error = thread->resume(signal);
      if (error != kSuccess) {
        DS2LOG(Warning, "failed resuming tid %" PRI_PID ", error=%s",
               thread->tid(), Stringify::Error(error));
      }
    } break;
    }
  });

  return kSuccess;
}
Пример #5
0
ErrorCode Thread::readCPUState(Architecture::CPUState &state) {
  CONTEXT context;

  memset(&context, 0, sizeof(context));
  context.ContextFlags = CONTEXT_INTEGER |        // GP registers.
                         CONTEXT_CONTROL |        // Some more GP + CPSR.
                         CONTEXT_FLOATING_POINT | // FP registers.
                         CONTEXT_DEBUG_REGISTERS; // Debug registers.

  BOOL result = GetThreadContext(_handle, &context);
  if (!result) {
    return Platform::TranslateError();
  }

  // GP registers + CPSR.
  state.gp.r0 = context.R0;
  state.gp.r1 = context.R1;
  state.gp.r2 = context.R2;
  state.gp.r3 = context.R3;
  state.gp.r4 = context.R4;
  state.gp.r5 = context.R5;
  state.gp.r6 = context.R6;
  state.gp.r7 = context.R7;
  state.gp.r8 = context.R8;
  state.gp.r9 = context.R9;
  state.gp.r10 = context.R10;
  state.gp.r11 = context.R11;
  state.gp.ip = context.R12;
  state.gp.sp = context.Sp;
  state.gp.lr = context.Lr;
  state.gp.pc = context.Pc;
  state.gp.cpsr = context.Cpsr;

  // Floating point registers.
  static_assert(sizeof(context.D) == sizeof(state.vfp.dbl),
                "floating point register count mismatch");
  for (size_t i = 0; i < array_sizeof(context.D); ++i) {
    state.vfp.dbl[i].value = context.D[i];
  }
  state.vfp.fpscr = context.Fpscr;

  if (state.isThumb()) {
    if (state.gp.pc & 1ULL) {
      DS2LOG(Debug, "removing thumb bit from pc and lr");
      state.gp.pc &= ~1ULL;
    } else {
      DS2LOG(Warning,
             "CPU is in thumb mode but doesn't have thumb bit set in pc");
    }
  }

  // TODO(sas): Handle debug registers.

  return kSuccess;
}
Пример #6
0
ErrorCode PTrace::attach(ProcessId pid) {
  if (pid <= kAnyProcessId)
    return kErrorProcessNotFound;

  DS2LOG(Debug, "attaching to pid %" PRIu64, (uint64_t)pid);

  if (wrapPtrace(PT_ATTACH, pid, nullptr, nullptr) < 0) {
    ErrorCode error = Platform::TranslateError();
    DS2LOG(Error, "Unable to attach: %d", error);
    return error;
  }

  return kSuccess;
}
Пример #7
0
void SoftwareBreakpointManager::disableLocation(Site const &site) {
  ErrorCode error;
  uint8_t old = _insns[site.address];

  error = _process->writeMemory(site.address, &old, sizeof(old));
  if (error != kSuccess) {
    DS2LOG(BPManager, Error, "cannot restore instruction at %#lx",
           (unsigned long)site.address.value());
    return;
  }

  DS2LOG(BPManager, Info, "reset instruction %#x at %#lx", old,
         (unsigned long)site.address.value());

  _insns.erase(site.address);
}
Пример #8
0
void SoftwareBreakpointManager::getOpcode(uint32_t type,
                                          std::string &opcode) const {
  switch (type) {
#if defined(ARCH_ARM)
  case 2: // udf #1
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    opcode += '\xde';
    opcode += '\x01';
#else
    opcode += '\x01';
    opcode += '\xde';
#endif
    break;
  case 3: // udf.w #0
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    opcode += '\xa0';
    opcode += '\x00';
    opcode += '\xf7';
    opcode += '\xf0';
#else
    opcode += '\xf0';
    opcode += '\xf7';
    opcode += '\x00';
    opcode += '\xa0';
#endif
    break;
  case 4: // udf #16
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    opcode += '\xe7';
    opcode += '\xf0';
    opcode += '\x01';
    opcode += '\xf0';
#else
    opcode += '\xf0';
    opcode += '\x01';
    opcode += '\xf0';
    opcode += '\xe7';
#endif
    break;
#elif defined(ARCH_ARM64)
  case 4:
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    opcode += '\xd4';
    opcode += '\x20';
    opcode += '\x20';
    opcode += '\x00';
#else
    opcode += '\x00';
    opcode += '\x20';
    opcode += '\x20';
    opcode += '\xd4';
#endif
    break;
#endif
  default:
    DS2LOG(Error, "invalid breakpoint type %d", type);
    DS2ASSERT(0 && "invalid breakpoint type");
    break;
  }
}
Пример #9
0
ds2::Target::Process *Process::Create(ProcessSpawner &spawner) {
  ErrorCode error;

  //
  // Create the process.
  //
  auto process = new Target::Process;

  error = spawner.run();
  if (error != kSuccess)
    goto fail;

  DS2LOG(Debug, "created process %I64u", (uint64_t)spawner.pid());

  //
  // Wait the process.
  //
  error = process->initialize(spawner.pid(), spawner.handle(), spawner.tid(),
                              spawner.threadHandle(), 0);
  if (error != kSuccess)
    goto fail;

  return process;

fail:
  delete process;
  return nullptr;
}
Пример #10
0
ds2::Target::Process *Process::Attach(ProcessId pid) {
  if (pid <= 0)
    return nullptr;

  //
  // Create the process.
  //
  Target::Process *process = new Target::Process;

  //
  // And try to attach.
  //
  ErrorCode error = process->ptrace().attach(pid);
  if (error != kSuccess) {
    DS2LOG(Error, "ptrace attach failed: %s", strerror(errno));
    goto fail;
  }

  error = process->initialize(pid, kFlagAttachedProcess);
  if (error != kSuccess) {
    process->ptrace().detach(pid);
    goto fail;
  }

  return process;

fail:
  delete process;
  return nullptr;
}
Пример #11
0
FILE *ProcFS::OpenFILE(char const *what, char const *mode) {
  char path[PATH_MAX + 1];
  ds2_snprintf(path, PATH_MAX, "/proc/%s", what);
  FILE *res = fopen(path, mode);
  if (res == nullptr)
    DS2LOG(Target, Error, "can't open %s: %s", path, strerror(errno));
  return res;
}
Пример #12
0
ErrorCode ProcessBase::suspend() {
  std::set<Thread *> threads;
  enumerateThreads([&](Thread *thread) { threads.insert(thread); });

  for (auto thread : threads) {
    switch (thread->state()) {
    case Thread::kInvalid:
      DS2BUG("trying to suspend tid %" PRI_PID " in state %s", thread->tid(),
             Stringify::ThreadState(thread->state()));
      break;

    case Thread::kStepped:
    case Thread::kStopped:
    case Thread::kTerminated:
      DS2LOG(Debug, "not suspending tid %" PRI_PID ", already in state %s",
             thread->tid(), Stringify::ThreadState(thread->state()));
      if (thread->state() == Thread::kTerminated) {
        removeThread(thread->tid());
      }
      break;

    case Thread::kRunning: {
      DS2LOG(Debug, "suspending tid %" PRI_PID, thread->tid());
      ErrorCode error = thread->suspend();
      switch (error) {
      case kSuccess:
        break;

      case kErrorProcessNotFound:
        DS2LOG(Debug,
               "tried to suspended tid %" PRI_PID " which is already dead",
               thread->tid());
        removeThread(thread->tid());
        return error;

      default:
        DS2LOG(Warning, "failed suspending tid %" PRI_PID ", error=%s",
               thread->tid(), Stringify::Error(error));
        return error;
      }
    } break;
    }
  }

  return kSuccess;
}
Пример #13
0
ErrorCode Process::wait(int *status, bool hang) {
  DEBUG_EVENT de;

  BOOL result = WaitForDebugEvent(&de, hang ? INFINITE : 0);
  if (!result)
    return Platform::TranslateError();

  switch (de.dwDebugEventCode) {
  case CREATE_PROCESS_DEBUG_EVENT:
#define CHECK_AND_CLOSE(HAN)                                                   \
  do {                                                                         \
    if ((de.u.CreateProcessInfo.HAN) != NULL)                                  \
      CloseHandle(de.u.CreateProcessInfo.HAN);                                 \
  } while (0)
    CHECK_AND_CLOSE(hFile);
    CHECK_AND_CLOSE(hProcess);
    CHECK_AND_CLOSE(hThread);
#undef CHECK_AND_CLOSE
    return kSuccess;

  case EXIT_PROCESS_DEBUG_EVENT:
    _terminated = true;
    return kSuccess;

  case CREATE_THREAD_DEBUG_EVENT:
    DS2LOG(Fatal, "debug event CREATE_THREAD");
  case EXIT_THREAD_DEBUG_EVENT:
    DS2LOG(Fatal, "debug event EXIT_THREAD");
  case RIP_EVENT:
    DS2LOG(Fatal, "debug event RIP");

  case EXCEPTION_DEBUG_EVENT:
  case LOAD_DLL_DEBUG_EVENT:
  case UNLOAD_DLL_DEBUG_EVENT:
  case OUTPUT_DEBUG_STRING_EVENT: {
    auto threadIt = _threads.find(de.dwThreadId);
    DS2ASSERT(threadIt != _threads.end());
    threadIt->second->updateState(de);
  } break;

  default:
    DS2BUG("unknown debug event code: %d", de.dwDebugEventCode);
  }

  return kSuccess;
}
Пример #14
0
void ProtocolInterpreter::onInvalidData(std::string const &data) {
  DS2LOG(Warning, "received invalid data: '%s'", data.c_str());

  if (_session == nullptr)
    return;

  _session->onInvalidData(data);
}
Пример #15
0
void SoftwareBreakpointManager::disableLocation(Site const &site) {
  ErrorCode error;
  std::string old = _insns[site.address];

  error = _process->writeMemory(site.address, &old[0], old.size());
  if (error != kSuccess) {
    DS2LOG(Error, "cannot restore instruction at %#lx",
           (unsigned long)site.address.value());
    return;
  }

  DS2LOG(Info, "reset instruction %#lx at %#lx",
         (unsigned long)(site.size == 2 ? *(uint16_t *)&old[0]
                                        : *(uint32_t *)&old[0]),
         (unsigned long)site.address.value());

  _insns.erase(site.address);
}
Пример #16
0
void ProcessBase::insert(ThreadBase *thread) {
  if (!_threads
           .insert(std::make_pair(thread->tid(), static_cast<Thread *>(thread)))
           .second)
    return;

  DS2LOG(Debug, "[new Thread %" PRI_PTR " (LWP %" PRIu64 ")]",
         PRI_PTR_CAST(thread), (uint64_t)thread->tid());
}
Пример #17
0
FILE *ProcFS::OpenFILE(pid_t pid, pid_t tid, char const *what,
                       char const *mode) {
  char path[PATH_MAX + 1];
  MakePath(path, PATH_MAX, pid, tid, what);
  FILE *res = fopen(path, mode);
  if (res == nullptr)
    DS2LOG(Target, Error, "can't open %s: %s", path, strerror(errno));
  return res;
}
Пример #18
0
ErrorCode SoftwareBreakpointManager::isValid(Address const &address,
                                             size_t size, Mode mode) const {
  DS2ASSERT(mode == kModeExec);
  if (size < 2 || size > 4) {
    DS2LOG(Debug, "Received unsupported breakpoint size %zu", size);
    return kErrorInvalidArgument;
  }

  return super::isValid(address, size, mode);
}
Пример #19
0
Файл: PTrace.cpp Проект: sas/ds2
ErrorCode PTrace::traceMe(bool disableASLR) {
  if (disableASLR) {
    int persona = ::personality(std::numeric_limits<uint32_t>::max());
    if (::personality(persona | ADDR_NO_RANDOMIZE) == -1) {
      DS2LOG(Warning, "unable to disable ASLR, error=%s", strerror(errno));
    }
  }

  return super::traceMe(false);
}
Пример #20
0
ErrorCode Process::suspend() {
  std::set<Thread *> threads;
  enumerateThreads([&](Thread *thread) { threads.insert(thread); });

  for (auto thread : threads) {
    Architecture::CPUState state;
    if (thread->state() != Thread::kRunning) {
      thread->readCPUState(state);
    }
    DS2LOG(Debug, "tid %d state %d at pc %#" PRIx64, thread->tid(),
           thread->state(),
           thread->state() == Thread::kStopped ? (uint64_t)state.pc() : 0);
    if (thread->state() == Thread::kRunning) {
      ErrorCode error;

      DS2LOG(Debug, "suspending tid %d", thread->tid());
      error = thread->suspend();

      if (error == kSuccess) {
        DS2LOG(Debug, "suspended tid %d at pc %#" PRIx64, thread->tid(),
               (uint64_t)state.pc());
        thread->readCPUState(state);
      } else if (error == kErrorProcessNotFound) {
        //
        // Thread is dead.
        //
        removeThread(thread->tid());
        DS2LOG(Debug, "tried to suspended tid %d which is already dead",
               thread->tid());
      } else {
        return error;
      }
    } else if (thread->state() == Thread::kTerminated) {
      //
      // Thread is dead.
      //
      removeThread(thread->tid());
    }
  }

  return kSuccess;
}
Пример #21
0
void BreakpointManager::enable(Target::Thread *thread) {
  //
  // Both callbacks should be installed by child class before
  // enabling the breakpoint manager.
  //
  if (enabled(thread)) {
    DS2LOG(Warning, "double-enabling breakpoints");
  }

  enumerate([this, thread](Site const &site) { enableLocation(site, thread); });
}
Пример #22
0
ErrorCode PTrace::detach(ProcessId pid) {
  if (pid <= kAnyProcessId)
    return kErrorProcessNotFound;

  DS2LOG(Debug, "detaching from pid %" PRIu64, (uint64_t)pid);

  if (wrapPtrace(PT_DETACH, pid, nullptr, nullptr) < 0)
    return Platform::TranslateError();

  return kSuccess;
}
Пример #23
0
ErrorCode Process::resume(int signal, std::set<Thread *> const &excluded) {
  enumerateThreads([&](Thread *thread) {
    if (excluded.find(thread) != excluded.end())
      return;

    if (thread->state() == Thread::kStopped ||
        thread->state() == Thread::kStepped) {
      Architecture::CPUState state;
      thread->readCPUState(state);
      DS2LOG(Debug, "resuming tid %I64u from pc %#I64x",
             (uint64_t)thread->tid(), (uint64_t)state.pc());
      ErrorCode error = thread->resume(signal);
      if (error != kSuccess) {
        DS2LOG(Warning, "failed resuming tid %I64u, error=%d",
               (uint64_t)thread->tid(), error);
      }
    }
  });

  return kSuccess;
}
Пример #24
0
ErrorCode Process::checkMemoryErrorCode(uint64_t address) {
  // mmap() returns MAP_FAILED thanks to the libc wrapper. Here we don't have
  // any, so we get the raw kernel return value, which is the address of the
  // newly allocated pages, if the call succeeds, or -errno if the call fails.
  if (address & (Platform::GetPageSize() - 1)) {
    int error = -address;
    DS2LOG(Debug, "mmap failed with errno=%s", Stringify::Errno(error));
    return Platform::TranslateError(error);
  }

  return kSuccess;
}
Пример #25
0
void ProcessBase::removeThread(ThreadId tid) {
  auto it = _threads.find(tid);
  if (it == _threads.end())
    return;

  Thread *thread = it->second;
  _threads.erase(it);

  DS2LOG(Debug, "[delete Thread %" PRI_PTR " (LWP %" PRIu64 ") exited]",
         PRI_PTR_CAST(thread), (uint64_t)thread->tid());

  delete thread;
}
Пример #26
0
ErrorCode PrepareSoftwareSingleStep(Process *process,
                                    BreakpointManager *manager,
                                    CPUState const &state,
                                    Address const &address) {
  ErrorCode error;
  bool link = false;
  bool isThumb = !!(state.gp.cpsr & (1 << 5));
  uint32_t pc = address.valid() ? address.value() : state.pc();
  uint32_t nextPC = static_cast<uint32_t>(-1);
  uint32_t nextPCSize = 0;
  uint32_t branchPC = static_cast<uint32_t>(-1);
  uint32_t branchPCSize = 0;

  if (isThumb) {
    error = PrepareThumbSoftwareSingleStep(process, pc, state, link, nextPC,
                                           nextPCSize, branchPC, branchPCSize);
  } else {
    error = PrepareARMSoftwareSingleStep(process, pc, state, link, nextPC,
                                         nextPCSize, branchPC, branchPCSize);
  }

  if (error != kSuccess) {
    return error;
  }

  DS2LOG(Debug, "PC=%#x, branchPC=%#x[size=%d, link=%s] nextPC=%#x[size=%d]",
         pc, branchPC, branchPCSize, link ? "true" : "false", nextPC,
         nextPCSize);

  if (branchPC != static_cast<uint32_t>(-1)) {
    DS2ASSERT(branchPCSize != 0);
    error = manager->add(branchPC, BreakpointManager::kTypeTemporaryOneShot,
                         branchPCSize, BreakpointManager::kModeExec);
    if (error != kSuccess) {
      return error;
    }
  }

  if (nextPC != static_cast<uint32_t>(-1)) {
    DS2ASSERT(nextPCSize != 0);
    error = manager->add(nextPC, BreakpointManager::kTypeTemporaryOneShot,
                         nextPCSize, BreakpointManager::kModeExec);
    if (error != kSuccess) {
      return error;
    }
  }

  return kSuccess;
}
Пример #27
0
ErrorCode Thread::writeCPUState(Architecture::CPUState const &state) {
  CONTEXT context;

  memset(&context, 0, sizeof(context));
  // TODO(sas): Handle debug registers.
  context.ContextFlags = CONTEXT_INTEGER |       // GP registers.
                         CONTEXT_CONTROL |       // Some more GP + CPSR.
                         CONTEXT_FLOATING_POINT; // FP registers.

  // GP registers + CPSR.
  context.R0 = state.gp.r0;
  context.R1 = state.gp.r1;
  context.R2 = state.gp.r2;
  context.R3 = state.gp.r3;
  context.R4 = state.gp.r4;
  context.R5 = state.gp.r5;
  context.R6 = state.gp.r6;
  context.R7 = state.gp.r7;
  context.R8 = state.gp.r8;
  context.R9 = state.gp.r9;
  context.R10 = state.gp.r10;
  context.R11 = state.gp.r11;
  context.R12 = state.gp.ip;
  context.Sp = state.gp.sp;
  context.Lr = state.gp.lr;
  context.Pc = state.gp.pc;
  context.Cpsr = state.gp.cpsr;

  // Floating point registers.
  for (size_t i = 0; i < array_sizeof(context.D); ++i) {
    context.D[i] = state.vfp.dbl[i].value;
  }
  context.Fpscr = state.vfp.fpscr;

  if (state.isThumb()) {
    DS2ASSERT(!(state.gp.pc & 1ULL));
    DS2LOG(Debug, "setting back thumb bit on pc and lr");
    context.Pc |= 1ULL;
  }

  BOOL result = SetThreadContext(_handle, &context);
  if (!result) {
    return Platform::TranslateError();
  }

  return kSuccess;
}
Пример #28
0
Файл: PTrace.cpp Проект: sas/ds2
ErrorCode PTrace::traceThat(ProcessId pid) {
  if (pid <= 0)
    return kErrorInvalidArgument;

  unsigned long traceFlags = PTRACE_O_TRACECLONE;

  //
  // Trace clone and exit events to track threads.
  //
  if (wrapPtrace(PTRACE_SETOPTIONS, pid, nullptr, traceFlags) < 0) {
    DS2LOG(Warning, "unable to set PTRACE_O_TRACECLONE on pid %d, error=%s",
           pid, strerror(errno));
    return Platform::TranslateError();
  }

  return kSuccess;
}
Пример #29
0
ds2::Target::Process *Process::Attach(ProcessId pid) {
  if (pid <= 0)
    return nullptr;

  auto process = ds2::make_unique<Target::Process>();

  if (process->ptrace().attach(pid) != kSuccess) {
    DS2LOG(Error, "ptrace attach failed: %s", strerror(errno));
    return nullptr;
  }

  if (process->initialize(pid, kFlagAttachedProcess) != kSuccess) {
    process->ptrace().detach(pid);
    return nullptr;
  }

  return process.release();
}
Пример #30
0
ErrorCode Thread::step(int signal, Address const &address) {
  if (_state == kInvalid || _state == kRunning) {
    return kErrorInvalidArgument;
  } else if (_state == kTerminated) {
    return kErrorProcessNotFound;
  }

  DS2LOG(Debug, "stepping tid %d", tid());

  // Prepare a software (arch-dependent) single step and resume execution.
  Architecture::CPUState state;
  CHK(readCPUState(state));
  CHK(PrepareSoftwareSingleStep(
      process(), process()->softwareBreakpointManager(), state, address));
  CHK(resume(signal, address));

  return kSuccess;
}