blob: 043df2cc7947f2dd88fa509e6770279bea765f0a [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 GARNET_BIN_DEBUG_AGENT_DEBUGGED_THREAD_H_
#define GARNET_BIN_DEBUG_AGENT_DEBUGGED_THREAD_H_
#include <zx/thread.h>
#include "garnet/lib/debug_ipc/protocol.h"
#include "lib/fxl/macros.h"
struct zx_thread_state_general_regs;
namespace debug_agent {
class DebugAgent;
class DebuggedProcess;
class ProcessBreakpoint;
enum class ThreadCreationOption {
// Already running, don't do anything
kRunningKeepRunning,
// Already suspended, keep it suspended
kSuspendedKeepSuspended,
// Already suspended, run it
kSuspendedShouldRun
};
class DebuggedThread {
public:
// The SuspendReason indicates why the thread was suspended from our
// perspective. This doesn't take into account other things on the system
// that may have suspended a thread. If somebody does this, the thread will
// be suspended but our state will be kNone (meaning resuming it is not
// something we can do).
enum class SuspendReason {
// Not suspended.
kNone,
// Exception from the program.
kException,
// Anything else.
kOther
};
const char* SuspendReasonToString(SuspendReason);
// When a thread is first created and we get a notification about it, it
// will be suspended, but when we attach to a process with existing threads
// it won't in in this state. The |starting| flag indicates that this is
// a thread discovered via a debug notification.
DebuggedThread(DebuggedProcess* process, zx::thread thread,
zx_koid_t thread_koid, ThreadCreationOption option);
virtual ~DebuggedThread();
zx::thread& thread() { return thread_; }
const zx::thread& thread() const { return thread_; }
zx_koid_t koid() const { return koid_; }
void OnException(uint32_t type);
// Pauses execution of the thread. Returns true if the pause was successful.
// Returns false on error or of the thread was already stopped. Pausing
// happens asynchronously so the thread will not necessarily have stopped
// when this returns.
bool Pause();
// Resumes execution of the thread. The thread should currently be in a
// stopped state. If it's not stopped, this will be ignored.
void Resume(const debug_ipc::ResumeRequest& request);
// Fills the thread status record. If full_stack is set, a full backtrace
// will be generated, otherwise a minimal one will be generated.
//
// If optional_regs is non-null, it should point to the current registers of
// the thread. If null, these will be fetched automatically. See the global
// FillThreadRecord() for more.
void FillThreadRecord(debug_ipc::ThreadRecord::StackAmount stack_amount,
const zx_thread_state_general_regs* optional_regs,
debug_ipc::ThreadRecord* record) const;
// Register reading and writing.
void ReadRegisters(
const std::vector<debug_ipc::RegisterCategory::Type>& cats_to_get,
std::vector<debug_ipc::RegisterCategory>* out) const;
zx_status_t WriteRegisters(const std::vector<debug_ipc::Register>& regs);
// Sends a notification to the client about the state of this thread.
void SendThreadNotification() const;
// Notification that a ProcessBreakpoint is about to be deleted.
void WillDeleteProcessBreakpoint(ProcessBreakpoint* bp);
private:
enum class OnStop {
kIgnore, // Don't do anything, keep the thread stopped and don't notify.
kSendNotification // Send client notification like normal.
};
// Handles a software breakpoint exception, updating the state as necessary.
// If the address corresponds to a breakpoint we have set, it will call
// UpdateForHitProcessBreakpoint (see below).
OnStop UpdateForSoftwareBreakpoint(
zx_thread_state_general_regs* regs,
std::vector<debug_ipc::BreakpointStats>* hit_breakpoints);
OnStop UpdateForHardwareBreakpoint(
zx_thread_state_general_regs* regs,
std::vector<debug_ipc::BreakpointStats>* hit_breakpoints);
// Handles an exception corresponding to a ProcessBreakpoint. All
// Breakpoints affected will have their updated stats added to
// *hit_breakpoints.
//
// WARNING: The ProcessBreakpoint argument could be deleted in this call
// if it was a one-shot breakpoint.
void UpdateForHitProcessBreakpoint(
debug_ipc::BreakpointType exception_type,
ProcessBreakpoint* process_breakpoint, zx_thread_state_general_regs* regs,
std::vector<debug_ipc::BreakpointStats>* hit_breakpoints);
// Resumes the thread according to the current run mode.
void ResumeForRunMode();
// Sets or clears the single step bit on the thread.
void SetSingleStep(bool single_step);
DebugAgent* debug_agent_; // Non-owning.
DebuggedProcess* process_; // Non-owning.
zx::thread thread_;
zx_koid_t koid_;
// The main thing we're doing. When automatically resuming, this will be
// what happens.
debug_ipc::ResumeRequest::How run_mode_ =
debug_ipc::ResumeRequest::How::kContinue;
// When run_mode_ == kStepInRange, this defines the range (end non-inclusive).
uint64_t step_in_range_begin_ = 0;
uint64_t step_in_range_end_ = 0;
// This is the reason for the thread suspend. This controls how the thread
// will be resumed. SuspendReason::kOther implies the suspend_token_ is
// valid.
SuspendReason suspend_reason_ = SuspendReason::kNone;
zx::suspend_token suspend_token_;
// This can be set in two cases:
// - When suspended after hitting a breakpoint, this will be the breakpoint
// that was hit.
// - When single-stepping over a breakpoint, this will be the breakpoint
// being stepped over.
ProcessBreakpoint* current_breakpoint_ = nullptr;
FXL_DISALLOW_COPY_AND_ASSIGN(DebuggedThread);
};
} // namespace debug_agent
#endif // GARNET_BIN_DEBUG_AGENT_DEBUGGED_THREAD_H_