Changes:
- report signals. A bit more code changes than I expected.
- implement QPassSignals, trivial.
Note: $CSIG is not supported yet, and I am not sure I understand
how it should work. Next time...
#include linux/module.h
#include linux/proc_fs.h
#include linux/utrace.h
#include linux/poll.h
#include linux/mm.h
#include linux/regset.h
#include asm/uaccess.h
static int o_remote_debug;
module_param_named(echo, o_remote_debug, bool, 0);
#define BUFFER_SIZE 1024
#define PACKET_SIZE 1024
struct pbuf {
char*cur, *pkt;
charbuf[BUFFER_SIZE];
};
static inline void pb_init(struct pbuf *pb)
{
pb-cur = pb-buf;
pb-pkt = NULL;
}
enum {
U_STOP_IDLE = 0,
U_STOP_PENDING,
U_STOP_SENT,
};
struct ugdb {
struct list_headu_processes;
struct list_headu_stopped;
int u_stop_state;
struct mutexu_mutex;
spinlock_t u_slock;
struct ugdb_thread
*u_cur_tinfo,
*u_cur_hg,
*u_cur_hc;
wait_queue_head_t u_wait;
int u_err;
struct pbuf u_pbuf;
charu_cbuf[PACKET_SIZE];
int u_clen;
sigset_tu_sig_ign;
unsigned int
u_no_ack:1,
u_allstop:1;
};
static inline void ugdb_ck_stopped(struct ugdb *ugdb)
{
spin_lock(ugdb-u_slock);
WARN_ON(!list_empty(ugdb-u_stopped)
ugdb-u_stop_state == U_STOP_IDLE);
WARN_ON(list_empty(ugdb-u_stopped)
ugdb-u_stop_state == U_STOP_PENDING);
spin_unlock(ugdb-u_slock);
}
static struct ugdb *ugdb_create(void)
{
struct ugdb *ugdb;
int err;
err = -ENODEV;
// XXX: ugly. proc_reg_open() should take care.
if (!try_module_get(THIS_MODULE))
goto out;
err = -ENOMEM;
ugdb = kzalloc(sizeof(*ugdb), GFP_KERNEL);
if (!ugdb)
goto put_module;
INIT_LIST_HEAD(ugdb-u_processes);
INIT_LIST_HEAD(ugdb-u_stopped);
mutex_init(ugdb-u_mutex);
spin_lock_init(ugdb-u_slock);
init_waitqueue_head(ugdb-u_wait);
pb_init(ugdb-u_pbuf);
return ugdb;
put_module:
module_put(THIS_MODULE);
out:
return ERR_PTR(err);
}
#define P_DETACHING (1 1)
#define P_ZOMBIE(1 2)
struct ugdb_process {
int p_pid;
int p_state;
struct list_headp_threads;
struct ugdb *p_ugdb;
struct list_headp_processes;
};
static inline bool process_alive(struct ugdb_process *process)
{
return !(process-p_state P_ZOMBIE);
}
static inline void mark_process_dead(struct ugdb_process *process)
{
process-p_state |= P_ZOMBIE;
}
static struct ugdb_process *ugdb_create_process(struct ugdb *ugdb, int pid_nr)
{
struct ugdb_process *process;
process = kzalloc(sizeof(*process), GFP_KERNEL);
if (!process)
return NULL;
process-p_pid = pid_nr;
process-p_ugdb = ugdb;
INIT_LIST_HEAD(process-p_threads);
list_add_tail(process-p_processes, ugdb-u_processes);
return process;
}
#define T_STOP_RUN 0
#define T_STOP_REQ (1 0)/* requested by gdb */
#define T_STOP_ALL (1 1)/* vCont;c:pX.-1, for report_clone */
#define T_STOP_ACK (1 2)/* visible to vStopped */
#define T_STOP_STOPPED (1 3)/* reported as stopped to gdb */
/* TASK_TRACED + deactivated ? */
#define T_EV_NONE 0
#define T_EV_EXIT (1 24)
#define T_EV_SIGN (2 24)
#define T_EV_TYPE(event)((0xff 24) (event))
#define T_EV_DATA(event)(~(0xff 24) (event))
struct ugdb_thread {
int t_tid;
int t_stop_state;
int t_stop_event;
struct ugdb *t_ugdb;
struct ugdb_process *t_process;
struct list_headt_threads;
struct list_headt_stopped;
struct pid *t_spid;
struct utrace_engine*t_engine;
};
static inline bool thread_alive(struct ugdb_thread *thread)
{
WARN_ON((thread-t_tid != 0) != process_alive(thread-t_process));
return thread-t_tid != 0;
}
static inline void mark_thread_dead(struct ugdb_thread *thread)
{
mark_process_dead(thread-t_process);
thread-t_tid = 0;
}
/*
* The thread should be alive, and it can't pass ugdb_report_death()
* if the caller holds ugdb-u_mutex. However