Changes:
- partly fix the multitracing problems.
ugdb still can't work with ptrace, ptrace-utrace.c needs
changes. But at least multiple udgb tracers can coexist,
more or less.
But of course they can confuse each other anyway, no matter
what ugdb does.
- implement memory writes ($M).
- refactor memory reads to avoid the Too late to report the
error case.
But, Jan. Implementing the memory writes does not mean breakpoints
automatically start to work!
Yes, gdb writes cc, and yes the tracee reports SIGTRAP. But after
that continue does nothing except $c, and the tracee naturally
gets SIGILL. I expected that, since ugdb doesn't even know the code
was changed, gdb should write the original byte back before continue,
but this doesn't happen.
Tried to understand how this works with gdbserver, but failed so far.
Will continue, but any hint is very much appreciated ;)
Oleg.
#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