blob: 91a4b7299b062ca91b1fa89b07887c6fe0da1cfc [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
#ifndef RR_RECORD_TASK_H_
#define RR_RECORD_TASK_H_
#include "Registers.h"
#include "Task.h"
#include "TraceFrame.h"
namespace rr {
struct Sighandlers;
class TaskSyscallStateBase {
public:
virtual ~TaskSyscallStateBase() {}
};
/** Different kinds of waits a task can do.
*/
enum WaitType {
// Not waiting for anything
WAIT_TYPE_NONE,
// Waiting for any child process
WAIT_TYPE_ANY,
// Waiting for any child with the same process group ID
WAIT_TYPE_SAME_PGID,
// Waiting for any child with a specific process group ID
WAIT_TYPE_PGID,
// Waiting for a specific process ID
WAIT_TYPE_PID
};
/** Reasons why we simulate stopping of a task (see ptrace(2) man page).
*/
enum EmulatedStopType {
NOT_STOPPED,
GROUP_STOP, // stopped by a signal. This applies to non-ptracees too.
SIGNAL_DELIVERY_STOP,// Stopped before delivering a signal. ptracees only.
SYSCALL_ENTRY_STOP, // Stopped at syscall entry. ptracees only
SYSCALL_EXIT_STOP, // Stopped at syscall exit. ptracees only
SECCOMP_STOP, // Stopped at seccomp stop. ptracees only
CHILD_STOP // All other kinds of non-ptrace stops
};
/**
* Pass USE_SYSGOOD to emulate_ptrace_stop to add 0x80 to the signal
* if PTRACE_O_TRACESYSGOOD is in effect.
*/
enum AddSysgoodFlag { IGNORE_SYSGOOD, USE_SYSGOOD };
struct SyscallbufCodeLayout {
remote_code_ptr syscallbuf_code_start;
remote_code_ptr syscallbuf_code_end;
remote_code_ptr get_pc_thunks_start;
remote_code_ptr get_pc_thunks_end;
remote_code_ptr syscallbuf_syscall_hook;
remote_code_ptr syscallbuf_final_exit_instruction;
};
enum SignalDisposition { SIGNAL_DEFAULT, SIGNAL_IGNORE, SIGNAL_HANDLER };
/**
* Every Task owned by a RecordSession is a RecordTask. Functionality that
* only applies during recording belongs here.
*/
class RecordTask final : public Task {
public:
RecordTask(RecordSession& session, pid_t _tid, uint32_t serial,
SupportedArch a);
Task* clone(CloneReason reason, int flags, remote_ptr<void> stack,
remote_ptr<void> tls, remote_ptr<int> cleartid_addr,
pid_t new_tid, pid_t new_rec_tid, uint32_t new_serial,
Session* other_session = nullptr,
FdTable::shr_ptr new_fds = nullptr,
ThreadGroup::shr_ptr new_tg = nullptr) override;
virtual void post_wait_clone(Task* cloned_from, int flags) override;
virtual void on_syscall_exit(int syscallno, SupportedArch arch,
const Registers& regs) override;
virtual bool will_resume_execution(ResumeRequest, WaitRequest, TicksRequest,
int /*sig*/) override;
virtual void did_wait() override;
bool enable_chaos_memory_allocations() const;
std::vector<remote_code_ptr> syscallbuf_syscall_entry_breakpoints();
bool is_at_syscallbuf_syscall_entry_breakpoint();
bool is_at_syscallbuf_final_instruction_breakpoint();
bool is_at_syscallstub_exit_breakpoint();
/**
* Initialize tracee buffers in this, i.e., implement
* RRCALL_init_syscall_buffer. This task must be at the point
* of *exit from* the rrcall. Registers will be updated with
* the return value from the rrcall, which is also returned
* from this call.
*/
void init_buffers();
void post_exec();
/**
* Called when SYS_rrcall_init_preload has happened.
*/
virtual void at_preload_init() override;
RecordSession& session() const;
TraceWriter& trace_writer() const;
/**
* Emulate 'tracer' ptracing this task.
*/
void set_emulated_ptracer(RecordTask* tracer);
/**
* Call this when an event occurs that should stop a ptraced task.
* If we're emulating ptrace of the task, stop the task and wake the ptracer
* if it's waiting, and queue "status" to be reported to the
* ptracer. If siginfo is non-null, we'll report that siginfo, otherwise we'll
* make one up based on the status (unless the status is an exit code).
* Returns true if the task is stopped-for-emulated-ptrace, false otherwise.
*/
bool emulate_ptrace_stop(WaitStatus status,
const siginfo_t* siginfo = nullptr, int si_code = 0) {
return emulate_ptrace_stop(status, status.group_stop() ? GROUP_STOP : SIGNAL_DELIVERY_STOP,
siginfo, si_code);
}
bool emulate_ptrace_stop(WaitStatus status, EmulatedStopType stop_type,
const siginfo_t* siginfo = nullptr, int si_code = 0);
/**
* Force the ptrace-stop state no matter what state the task is currently in.
*/
void force_emulate_ptrace_stop(WaitStatus status, EmulatedStopType stop_type);
/**
* If necessary, signal the ptracer that this task has exited.
*/
void do_ptrace_exit_stop(WaitStatus exit_status);
/**
* Return the exit event.
* If write_child_tid is set, zero out child_tid now if applicable.
*/
enum WriteChildTid {
KERNEL_WRITES_CHILD_TID,
WRITE_CHILD_TID,
};
void record_exit_event(WriteChildTid write_child_tid = KERNEL_WRITES_CHILD_TID);
/**
* Called when we're about to deliver a signal to this task. If it's a
* synthetic SIGCHLD and there's a ptraced task that needs to SIGCHLD,
* update the siginfo to reflect the status and note that that
* ptraced task has had its SIGCHLD sent.
* Note that we can't set the correct siginfo when we send the signal, because
* it requires us to set information only the kernel has permission to set.
* Returns false if this signal should be deferred.
*/
bool set_siginfo_for_synthetic_SIGCHLD(siginfo_t* si);
/**
* Sets up |si| as if we're delivering a SIGCHLD/waitid for this waited task.
*/
template <typename Arch>
void set_siginfo_for_waited_task(typename Arch::siginfo_t* si) {
// XXX: The `ptrace` argument is likely incorrect here.
emulated_stop_code.fill_siginfo<Arch>(si,
emulated_stop_type != GROUP_STOP, emulated_ptrace_options);
si->_sifields._sigchld.si_pid_ = tgid();
si->_sifields._sigchld.si_uid_ = getuid();
}
/**
* Return a reference to the saved siginfo record for the stop-signal
* that we're currently in a ptrace-stop for.
*/
siginfo_t& get_saved_ptrace_siginfo();
/**
* When emulating a ptrace-continue with a signal number, extract the siginfo
* that was saved by |save_ptrace_signal_siginfo|. If no such siginfo was
* saved, make one up.
*/
siginfo_t take_ptrace_signal_siginfo(int sig);
/**
* Returns true if this task is in a waitpid or similar that would return
* when t's status changes due to a ptrace event.
*/
bool is_waiting_for_ptrace(RecordTask* t);
/**
* Returns true if this task is in a waitpid or similar that would return
* when t's status changes due to a regular event (exit).
*/
bool is_waiting_for(RecordTask* t);
bool already_exited() const override {
return waiting_for_reap;
}
bool is_detached_proxy() const override {
return detached_proxy;
}
/**
* Call this to force a group stop for this task with signal 'sig',
* notifying ptracer if necessary.
*/
void apply_group_stop(int sig);
/**
* Call this after |sig| is delivered to this task. Emulate
* sighandler updates induced by the signal delivery.
*/
void signal_delivered(int sig);
/**
* Return true if |sig| is pending but hasn't been reported to ptrace yet
*/
bool is_signal_pending(int sig);
/**
* Return true if there are any signals pending that are not blocked.
*/
bool has_any_actionable_signal();
/**
* Get all threads out of an emulated GROUP_STOP
*/
void emulate_SIGCONT();
/**
* Return true if the disposition of |sig| in |table| isn't
* SIG_IGN or SIG_DFL, that is, if a user sighandler will be
* invoked when |sig| is received.
*/
bool signal_has_user_handler(int sig) const;
/**
* If signal_has_user_handler(sig) is true, return the address of the
* user handler, otherwise return null.
*/
remote_code_ptr get_signal_user_handler(int sig) const;
/**
* Return true if the signal handler for |sig| takes a siginfo_t*
* parameter.
*/
bool signal_handler_takes_siginfo(int sig) const;
/**
* Return |sig|'s current sigaction. Returned as raw bytes since the
* data is architecture-dependent.
*/
const std::vector<uint8_t>& signal_action(int sig) const;
/** Return true iff |sig| is blocked for this. */
bool is_sig_blocked(int sig);
/**
* Return true iff |sig| is SIG_IGN, or it's SIG_DFL and the
* default disposition is "ignore".
*/
bool is_sig_ignored(int sig) const;
/**
* Return the applications current disposition of |sig|.
*/
SignalDisposition sig_disposition(int sig) const;
/**
* Return the resolved disposition --- what this signal will actually do,
* taking into account the default behavior.
*/
SignalResolvedDisposition sig_resolved_disposition(
int sig, SignalDeterministic deterministic);
/**
* Set the siginfo for the signal-stop of this.
*/
void set_siginfo(const siginfo_t& si);
/** Note that the task sigmask needs to be refetched. */
void invalidate_sigmask() { blocked_sigs_dirty = true; }
/**
* Reset the signal handler for this signal to the default.
*/
void did_set_sig_handler_default(int sig);
/**
* Check that our status for |sig| matches what's in /proc/<pid>/status.
*/
void verify_signal_states();
/**
* Stashed-signal API: if a signal becomes pending at an
* awkward time, but could be handled "soon", call
* |stash_sig()| to stash the current pending-signal state.
*
* |has_stashed_sig()| obviously returns true if |stash_sig()|
* has been called successfully.
*
* |pop_stash_sig()| restores the (relevant) state of this
* Task to what was saved in |stash_sig()|, and returns the
* saved siginfo. After this call, |has_stashed_sig()| is
* false.
*
* NB: |get_siginfo()| will always return the "real" siginfo,
* regardless of stash popped-ness state. Callers must ensure
* they do the right thing with the popped siginfo.
*
* If the process unexpectedly died (due to SIGKILL), we don't
* stash anything.
*/
void stash_sig();
void stash_synthetic_sig(const siginfo_t& si,
SignalDeterministic deterministic);
bool has_stashed_sig() const { return !stashed_signals.empty(); }
struct StashedSignal {
StashedSignal(const siginfo_t& siginfo, SignalDeterministic deterministic,
remote_code_ptr ip)
: siginfo(siginfo), deterministic(deterministic), ip(ip) {}
siginfo_t siginfo;
SignalDeterministic deterministic;
remote_code_ptr ip;
};
const StashedSignal* stashed_sig_not_synthetic_SIGCHLD() const;
bool has_stashed_sig(int sig) const;
const StashedSignal* peek_stashed_sig_to_deliver() const;
void pop_stash_sig(const StashedSignal* stashed);
void stashed_signal_processed();
/**
* If a group-stop occurs at an inconvenient time, stash it and
* process it later.
*/
void stash_group_stop() { stashed_group_stop = true; }
void clear_stashed_group_stop() { stashed_group_stop = false; }
bool has_stashed_group_stop() const { return stashed_group_stop; }
/**
* Return true if the current state of this looks like the
* interrupted syscall at the top of our event stack, if there
* is one.
*/
bool is_syscall_restart();
/**
* Return true iff this is at an execution state where
* resuming execution may lead to the restart of an
* interrupted syscall.
*
* For example, if a signal without a user handler is about to
* be delivered to this just after a syscall interruption,
* then delivering the signal may restart the first syscall
* and this method will return true.
*/
bool at_may_restart_syscall() const;
/**
* Return true iff this is at an execution state where
* a syscall that modifies signals was interrupted but will not
* be automatically restarted.
**/
bool at_interrupted_non_restartable_signal_modifying_syscall() const;
/**
* Return true if this is at an arm-desched-event syscall.
*/
bool is_arm_desched_event_syscall();
/**
* Return true if this is at a disarm-desched-event syscall.
*/
bool is_disarm_desched_event_syscall();
/**
* Return true if |t| may not be immediately runnable,
* i.e., resuming execution and then |waitpid()|'ing may block
* for an unbounded amount of time. When the task is in this
* state, the tracer must await a |waitpid()| notification
* that the task is no longer possibly-blocked before resuming
* its execution.
*/
bool may_be_blocked() const;
/**
* Returns true if it looks like this task has been spinning on an atomic
* access/lock.
*/
bool maybe_in_spinlock();
/**
* Return true if this is within the syscallbuf library. This
* *does not* imply that $ip is at a buffered syscall.
* This also includes the runtime stub code that runs
* before entering syscallbuf but does not include the "safe area".
* Returning true from this function implies that the code will execute
* `_syscallbuf_final_exit_instruction` before returning to normal code.
*/
bool is_in_syscallbuf();
/**
* Shortcut to the most recent |pending_event->desched.rec| when
* there's a desched event on the stack, and nullptr otherwise.
* Exists just so that clients don't need to dig around in the
* event stack to find this record.
*/
remote_ptr<const struct syscallbuf_record> desched_rec() const;
/**
* Returns true when the task is in a signal handler in an interrupted
* system call being handled by syscall buffering.
*/
bool running_inside_desched() const;
/**
* Returns -1 if we failed (the process unexpectedly exited).
*/
int get_ptrace_eventmsg_seccomp_data();
/**
* Save tracee data to the trace. |addr| is the address in
* the address space of this task. The |record_local*()|
* variants record data that's already been read from this,
* and the |record_remote*()| variants read the data and then
* record it.
* If 'addr' is null then no record is written.
*/
void record_local(remote_ptr<void> addr, ssize_t num_bytes, const void* buf);
template <typename T>
void record_local(remote_ptr<T> addr, const T* buf, size_t count = 1) {
record_local(addr, sizeof(T) * count, buf);
}
void record_remote(remote_ptr<void> addr, ssize_t num_bytes);
template <typename T> void record_remote(remote_ptr<T> addr) {
record_remote(addr, sizeof(T));
}
void record_remote(const MemoryRange& range) {
record_remote(range.start(), range.size());
}
ssize_t record_remote_fallible(const MemoryRange& range) {
return record_remote_fallible(range.start(), range.size());
}
// Record as much as we can of the bytes in this range. Will record only
// contiguous mapped data starting at `addr`.
ssize_t record_remote_fallible(remote_ptr<void> addr, uintptr_t num_bytes,
const std::vector<WriteHole>& holes = std::vector<WriteHole>());
// Record as much as we can of the bytes in this range. Will record only
// contiguous mapped-writable data starting at `addr`. rr mappings (e.g. syscallbuf)
// are treated as non-contiguous with any other mapping.
void record_remote_writable(remote_ptr<void> addr, ssize_t num_bytes);
// Simple helper that attempts to use the local mapping to record if one
// exists
bool record_remote_by_local_map(remote_ptr<void> addr, size_t num_bytes);
template <typename T>
void write_and_record(remote_ptr<T> addr, const T& value, bool* ok = nullptr,
uint32_t flags = 0) {
write_mem(addr, value, ok, flags);
record_local(addr, &value, 1);
}
/**
* Save tracee data to the trace. |addr| is the address in
* the address space of this task.
* If 'addr' is null then a zero-length record is written.
*/
void record_remote_even_if_null(remote_ptr<void> addr, ssize_t num_bytes);
template <typename T> void record_remote_even_if_null(remote_ptr<T> addr) {
record_remote_even_if_null(addr, sizeof(T));
}
/**
* Manage pending events. |push_event()| pushes the given
* event onto the top of the event stack. The |pop_*()|
* helpers pop the event at top of the stack, which must be of
* the specified type.
*/
void push_event(const Event& ev) { pending_events.push_back(ev); }
void push_syscall_event(int syscallno);
void pop_event(EventType expected_type);
void pop_noop() { pop_event(EV_NOOP); }
void pop_desched() { pop_event(EV_DESCHED); }
void pop_seccomp_trap() { pop_event(EV_SECCOMP_TRAP); }
void pop_signal_delivery() { pop_event(EV_SIGNAL_DELIVERY); }
void pop_signal_handler() { pop_event(EV_SIGNAL_HANDLER); }
void pop_syscall() { pop_event(EV_SYSCALL); }
void pop_syscall_interruption() { pop_event(EV_SYSCALL_INTERRUPTION); }
virtual void log_pending_events() const override;
/** Return the event at the top of this's stack. */
Event& ev() { return pending_events.back(); }
const Event& ev() const { return pending_events.back(); }
/**
* Obtain the previous event on the stack (if any) or nullptr (if not)
*/
Event *prev_ev() {
ssize_t depth = pending_events.size();
return depth > 2 ? &pending_events[depth - 2] : nullptr;
}
/**
* Call this before recording events or data. Records
* syscallbuf data and flushes the buffer, if there's buffered
* data.
*
* The timing of calls to this is tricky. We must flush the syscallbuf
* before recording any data associated with events that happened after the
* buffered syscalls. But we don't support flushing a syscallbuf twice with
* no intervening reset, i.e. after flushing we have to be sure we'll get
* a chance to reset the syscallbuf (i.e. record some other kind of event)
* before the tracee runs again in a way that might append another buffered
* syscall --- so we can't flush too early
*/
void maybe_flush_syscallbuf();
/**
* Call this after recording an event when it might be safe to reset the
* syscallbuf. It must be after recording an event to ensure during replay
* we run past any syscallbuf after-syscall code that uses the buffer data.
*/
void maybe_reset_syscallbuf();
/**
* Record an event on behalf of this. Record the registers of
* this (and other relevant execution state) so that it can be
* used or verified during replay, if that state is available
* and meaningful at this's current execution point.
* |record_current_event()| record |this->ev()|, and
* |record_event()| records the specified event.
*/
void record_current_event();
enum FlushSyscallbuf {
FLUSH_SYSCALLBUF,
/* Pass this if it's safe to replay the event before we process the
* syscallbuf records.
*/
DONT_FLUSH_SYSCALLBUF
};
enum AllowSyscallbufReset {
ALLOW_RESET_SYSCALLBUF,
/* Pass this if it's safe to replay the event before we process the
* syscallbuf records.
*/
DONT_RESET_SYSCALLBUF
};
// Take `ev` by value to avoid bugs where we pass in an event in
// `pending_events`, which could lead to dangling references when
// flushing the syscallbuf manipulates `pending_events`.
void record_event(Event ev, FlushSyscallbuf flush = FLUSH_SYSCALLBUF,
AllowSyscallbufReset reset = ALLOW_RESET_SYSCALLBUF,
const Registers* registers = nullptr);
bool is_fatal_signal(int sig, SignalDeterministic deterministic) const;
/**
* Return the pid of the newborn thread created by this task.
* Called when this task has a PTRACE_CLONE_EVENT with CLONE_THREAD.
*/
pid_t find_newborn_thread();
/**
* Return the pid of the newborn process (whose parent has pid `parent_pid`,
* which need not be the same as the current task's pid, due to CLONE_PARENT)
* created by this task. Called when this task has a PTRACE_CLONE_EVENT
* without CLONE_THREAD, or PTRACE_FORK_EVENT.
*/
pid_t find_newborn_process(pid_t child_parent);
/**
* If the process looks alive, kill it.
*/
void kill_if_alive();
remote_ptr<void> robust_list() const { return robust_futex_list; }
size_t robust_list_len() const { return robust_futex_list_len; }
/** Uses /proc so not trivially cheap. */
pid_t get_parent_pid() const;
/**
* Return true if this is a "clone child" per the wait(2) man page.
*/
bool is_clone_child() { return termination_signal != SIGCHLD; }
void set_termination_signal(int sig) { termination_signal = sig; }
/**
* When a signal triggers an emulated a ptrace-stop for this task,
* save the siginfo so a later emulated ptrace-continue with this signal
* number can use it.
*/
void save_ptrace_signal_siginfo(const siginfo_t& si);
enum { SYNTHETIC_TIME_SLICE_SI_CODE = -9999 };
/**
* Tasks normally can't change their tid. There is one very special situation
* where they can: when a non-main-thread does an execve, its tid changes
* to the tid of the thread-group leader.
*/
void set_tid_and_update_serial(pid_t tid, pid_t own_namespace_tid);
/**
* Return our cached copy of the signal mask, updating it if necessary.
*/
sig_set_t get_sigmask();
/**
* Just get the signal mask of the process.
*/
sig_set_t read_sigmask_from_process();
/**
* Unblock the signal for the process.
* Returns `false` if the process died underneath us.
*/
bool unblock_signal(int sig);
/**
* Set the signal handler to default for the process.
*/
void set_sig_handler_default(int sig);
~RecordTask();
void maybe_restore_original_syscall_registers();
/**
* The task reached zombie state. Do whatever processing is necessary (reaping
* it, emulating ptrace stops, etc.)
*/
void did_reach_zombie();
// Is this task a container init? (which has special signal behavior)
bool is_container_init() const { return tg->tgid_own_namespace == 1; }
/**
* Linux requires the invariant that all members of a thread group
* are reaped before the thread group leader. This determines whether or
* not we're allowed to attempt reaping this thread or whether doing so
* risks deadlock.
*/
bool may_reap();
/**
* Reaps a task-exit notification, thus detaching us from the tracee.
* N.B.: If may_reap is false, this risks a deadlock.
*/
void reap();
bool waiting_for_pid_namespace_tasks_to_exit() const;
int process_depth() const;
/**
* Called when this task is able to receive a SIGCHLD (e.g. because
* we completed delivery of a signal). Sends a new synthetic
* SIGCHLD to the task if there are still tasks that need a SIGCHLD
* sent for them.
* May queue signals for specific tasks.
*/
void send_synthetic_SIGCHLD_if_necessary();
bool set_sigmask(sig_set_t mask);
/**
* Update the futex robust list head pointer to |list| (which
* is of size |len|).
*/
void set_robust_list(remote_ptr<void> list, size_t len) {
robust_futex_list = list;
robust_futex_list_len = len;
}
void set_stopped(bool stopped) override;
private:
/* Retrieve the tid of this task from the tracee and store it */
void update_own_namespace_tid();
/**
* Wait for |futex| in this address space to have the value
* |val|.
*
* WARNING: this implementation semi-busy-waits for the value
* change. This must only be used in contexts where the futex
* will change "soon".
*/
void futex_wait(remote_ptr<int> futex, int val, bool* ok);
/**
* Call this when SYS_sigaction is finishing with |regs|.
*/
void update_sigaction(const Registers& regs);
template <typename Arch> void init_buffers_arch();
template <typename Arch>
void on_syscall_exit_arch(int syscallno, const Registers& regs);
/** Helper function for update_sigaction. */
template <typename Arch> void update_sigaction_arch(const Registers& regs);
/** Update the clear-tid futex to |tid_addr|. */
void set_tid_addr(remote_ptr<int> tid_addr);
virtual bool post_vm_clone(CloneReason reason, int flags, Task* origin) override;
public:
uint64_t scheduler_token;
std::unique_ptr<TaskSyscallStateBase> syscall_state;
Ticks ticks_at_last_recorded_syscall_exit;
remote_code_ptr ip_at_last_recorded_syscall_exit;
// Scheduler state
Registers registers_at_start_of_last_timeslice;
FrameTime time_at_start_of_last_timeslice;
/* Task 'nice' value set by setpriority(2).
We use this to drive scheduling decisions. rr's scheduler is
deliberately simple and unfair; a task never runs as long as there's
another runnable task with a lower nice value. */
int priority;
/* Tasks with in_round_robin_queue set are in the session's
* in_round_robin_queue instead of its task_priority_set.
*/
bool in_round_robin_queue;
/* exit(), or exit_group() with one task, has been called, so
* the exit can be treated as stable. */
bool stable_exit;
bool detached_proxy;
// ptrace emulation state
// Task for which we're emulating ptrace of this task, or null
RecordTask* emulated_ptracer;
std::set<RecordTask*> emulated_ptrace_tracees;
uintptr_t emulated_ptrace_event_msg;
// Saved emulated-ptrace signals
std::vector<siginfo_t> saved_ptrace_siginfos;
// Code to deliver to ptracer/waiter when it waits. Note that zero can be a
// valid code! Reset to zero when leaving the stop due to PTRACE_CONT etc.
WaitStatus emulated_stop_code;
// Always zero while no ptracer is attached.
int emulated_ptrace_options;
// One of PTRACE_CONT, PTRACE_SYSCALL --- or 0 if the tracee has not been
// continued by its ptracer yet, or has no ptracer.
int emulated_ptrace_cont_command;
// true when a ptracer/waiter wait() can return |emulated_stop_code|.
bool emulated_stop_pending;
// true if this task needs to send a SIGCHLD to its ptracer for its
// emulated ptrace stop
bool emulated_ptrace_SIGCHLD_pending;
// true if this task needs to send a SIGCHLD to its parent for its
// emulated stop
bool emulated_SIGCHLD_pending;
// tracer attached via PTRACE_SEIZE
bool emulated_ptrace_seized;
WaitType in_wait_type;
pid_t in_wait_pid;
// Signal handler state
// Points to the signal-hander table of this task. If this
// task is a non-fork clone child, then the table will be
// shared with all its "thread" siblings. Any updates made to
// that shared table are immediately visible to all sibling
// threads.
//
// fork children always get their own copies of the table.
// And if this task exec()s, the table is copied and stripped
// of user sighandlers (see below). */
std::shared_ptr<Sighandlers> sighandlers;
// If not NOT_STOPPED, then the task is logically stopped and this is the type
// of stop.
EmulatedStopType emulated_stop_type;
// True if the task sigmask may have changed and we need to refetch it.
bool blocked_sigs_dirty;
// Most accesses to this should use set_sigmask and get_sigmask to ensure
// the mirroring to syscallbuf is correct.
sig_set_t blocked_sigs;
uint32_t syscallbuf_blocked_sigs_generation;
// Syscallbuf state
SyscallbufCodeLayout syscallbuf_code_layout;
ScopedFd desched_fd;
/* Value of hdr->num_rec_bytes when the buffer was flushed */
uint32_t flushed_num_rec_bytes;
/* Nonzero after the trace recorder has flushed the
* syscallbuf. When this happens, the recorder must prepare a
* "reset" of the buffer, to zero the record count, at the
* next available slow (taking |desched| into
* consideration). */
bool flushed_syscallbuf;
/* This bit is set when code wants to prevent the syscall
* record buffer from being reset when it normally would be.
* This bit is set by the desched code. */
bool delay_syscallbuf_reset_for_desched;
/* This is set when code wants to prevent the syscall
* record buffer from being reset when it normally would be.
* This is set by the code for handling seccomp SIGSYS signals. */
bool delay_syscallbuf_reset_for_seccomp_trap;
// Value to return from PR_GET_SECCOMP
uint8_t prctl_seccomp_status;
// Mirrored kernel state
// This state agrees with kernel-internal values
// Futex list passed to |set_robust_list()|. We could keep a
// strong type for this list head and read it if we wanted to,
// but for now we only need to remember its address / size at
// the time of the most recent set_robust_list() call.
remote_ptr<void> robust_futex_list;
size_t robust_futex_list_len;
// The memory cell the kernel will clear and notify on exit,
// if our clone parent requested it.
remote_ptr<int> tid_futex;
// Signal delivered by the kernel when this task terminates, or zero
int termination_signal;
// Our value for PR_GET/SET_TSC (one of PR_TSC_ENABLED, PR_TSC_SIGSEGV).
int tsc_mode;
// Our value for ARCH_GET/SET_CPUID (0 -> generate SIGSEGV, 1 -> do CPUID).
// Only used if session().has_cpuid_faulting().
int cpuid_mode;
// The current stack of events being processed.
std::vector<Event> pending_events;
// Stashed signal-delivery state, ready to be delivered at
// next opportunity.
std::deque<StashedSignal> stashed_signals;
bool stashed_signals_blocking_more_signals;
bool stashed_group_stop;
bool break_at_syscallbuf_traced_syscalls;
bool break_at_syscallbuf_untraced_syscalls;
bool break_at_syscallbuf_final_instruction;
remote_code_ptr syscallstub_exit_breakpoint;
// The pmc is programmed to interrupt at a value requested by the tracee, not
// by rr.
bool next_pmc_interrupt_is_for_user;
bool did_record_robust_futex_changes;
// This task is just waiting to be reaped.
bool waiting_for_reap;
// This task is waiting for a ptrace exit event. It should not
// be manually run.
bool waiting_for_ptrace_exit;
// When exiting a syscall, we should call MonkeyPatcher::try_patch_syscall again.
bool retry_syscall_patching;
// We've sent a SIGKILL during shutdown for this task.
bool sent_shutdown_kill;
// Last exec system call was an execveat
bool did_execveat;
// Set if the tracee requested an override of the ticks request.
// Used for testing.
TicksRequest tick_request_override;
// Set to prevent the scheduler from scheduling this tid, even
// if it is otherwise considered runnable. Used for testing.
bool schedule_frozen;
};
} // namespace rr
#endif /* RR_RECORD_TASK_H_ */