blob: bc007f5e81a547ac7b81459c0481c72823e7a812 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVELOPER_DEBUG_DEBUG_AGENT_SOFTWARE_BREAKPOINT_H_
#define SRC_DEVELOPER_DEBUG_DEBUG_AGENT_SOFTWARE_BREAKPOINT_H_
#include <zircon/status.h>
#include "src/developer/debug/debug_agent/arch.h"
#include "src/developer/debug/debug_agent/process_breakpoint.h"
#include "src/developer/debug/debug_agent/suspend_handle.h"
#include "src/developer/debug/ipc/protocol.h"
namespace debug_agent {
class ProcessMemoryAccessor;
class SoftwareBreakpoint : public ProcessBreakpoint {
public:
SoftwareBreakpoint(Breakpoint* breakpoint, DebuggedProcess* process, uint64_t address);
virtual ~SoftwareBreakpoint();
debug_ipc::BreakpointType Type() const override { return debug_ipc::BreakpointType::kSoftware; }
// Software breakpoint is either installed for all threads or no one.
bool Installed(zx_koid_t thread_koid) const override { return installed_; }
// virtual picture of memory is needed, this function will replace the replacement from this
// breakpoint if it appears in the given block. Otherwise does nothing.
void FixupMemoryBlock(debug_ipc::MemoryBlock* block);
// Public ProcessBreakpoint overrides. See ProcessBreakpoint for more details.
void EndStepOver(DebuggedThread* thread) override;
void ExecuteStepOver(DebuggedThread* thread) override;
void StepOverCleanup(DebuggedThread* thread) override;
const DebuggedThread* currently_stepping_over_thread() const {
return currently_stepping_over_thread_.get();
}
// Returns a sorted list of the koids associated with a currently held suspend token.
// If a thread has more than one suspend token, it wil appear twice.
//
// Exposed mostly for testing purposes (see process_breakpoint_unittest.cc).
std::vector<zx_koid_t> CurrentlySuspendedThreads() const;
private:
// ProcessBreakpoint overrides.
debug::Status Update() override;
// A software breakpoint gets uninstalled for all the threads.
debug::Status Uninstall(DebuggedThread* thread) override { return Uninstall(); }
debug::Status Uninstall() override;
debug::Status Install();
// As stepping over are queued, only one thread should be left running at a time. This makes the
// breakpoint get a suspend token for each other thread within the system.
void SuspendAllOtherThreads(zx_koid_t stepping_over_koid);
// Set to true when the instruction has been replaced.
bool installed_ = false;
// Previous memory contents before being replaced with the break instruction.
arch::BreakInstructionType previous_data_ = 0;
// Tracks the threads currently single-stepping over this breakpoint.
// There can be only one thread stepping over, as they're serialized by the process so that only
// one thread is stepping at a time.
fxl::WeakPtr<DebuggedThread> currently_stepping_over_thread_;
// A step is executed by putting back the original instruction, stepping the thread, and then
// re-inserting the breakpoint instruction. The breakpoint instruction can't be put back until
// there are no more threads in this map.
//
// It is a multimap because if two threads are queued on the same breakpoint (they both hit it at
// the same time), the breakpoint will get suspend tokens for all the threads (except the
// corresponding exception one) multiple times. If there is only one suspend token per koid, the
// breakpoint will uncorrectly resume the thread that just stepped over when the other would
// step over too, which is incorrect. We need the ability to have multiple tokens associated to
// a thread so that the interim between executing the second step over the same breakpoint can
// coincide with waiting for the resources of the first step over to be freed.
//
// See the implementation of |StepOverCleanup| for more details.
std::multimap<zx_koid_t, std::unique_ptr<SuspendHandle>> suspend_tokens_;
};
} // namespace debug_agent
#endif // SRC_DEVELOPER_DEBUG_DEBUG_AGENT_SOFTWARE_BREAKPOINT_H_