This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: linux kernel gdb stub for userspace processes, prototype version 3


Hi Frank,

Current gdb stub seems to cause a SIGSTOP when a signal handler is
called. And this behaviour is not consistent.
This behaviour is different from when gdb was invoked on the program
without the stap.

I believe the STOP at signal handler would only happen when 
UTRACE_SIGNAL_HANDLER  or UTRACE_SIGNAL_REPORT before the utrace-gdb
quiesce handler gets called.

The patch is as below.

commit eb53493b0208bc036dc570560ac3449aed450fbf
Author: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Date:   Thu Jul 9 21:03:45 2009 +0530

    make sure quiesce doesnt make the process to stop at the signal handler

diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c
index 3835761..cbed911 100644
--- a/kernel/utrace-gdb.c
+++ b/kernel/utrace-gdb.c
@@ -10,7 +10,7 @@
  * Red Hat Author: Frank Ch. Eigler
  */
 
-/* #define DEBUG 1 */
+#define DEBUG 1
 
 #include <asm/syscall.h>
 #include <asm/signal.h>
@@ -249,6 +249,7 @@ u32 gdb_utrace_report_signal(u32 action,
         if (utrace_signal_action(action) == UTRACE_SIGNAL_REPORT ||
             utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER) { /* case 5 */
                 /* NB: disregard p->at_quiesce_do */
+                p->at_quiesce_do = UTRACE_RESUME;
                 ret = UTRACE_RESUME | utrace_signal_action(action);
         } else if (p->skip_signals > 0 /*&& kern_p*/) { /* case 4 */
                 p->skip_signals --;


> Hi -
> 
> Further to http://sourceware.org/ml/systemtap/2009-q2/msg00969.html, I
> attach another snapshot of my gdb-stub in linux-kernel prototype.  It's
> working a lot better.  Usage is as before:
> 
> % PROCESS &
> [1] 21175
> % gdb PROCESS
> (gdb) target remote /proc/21175/gdb
> (gdb) # whatever strikes your fancy
> 
> Known limitations:
> - http://sourceware.org/ml/gdb/2009-07/msg00036.html
>   (occasional "Remote failure reply: E....." error)
> - only for single-threaded programs
> - x86-64 and x86 only
> - floating poing registers not yet done
> - checkpatch.pl not yet satisfied
> 
> This patch should apply to recent utrace-patched kernels.
> 
> - FChE
> 
> 
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 3326bbf..0afb05a 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -77,6 +77,7 @@
>  #include <linux/audit.h>
>  #include <linux/poll.h>
>  #include <linux/nsproxy.h>
> +#include <linux/utrace.h>
>  #include <linux/oom.h>
>  #include <linux/elf.h>
>  #include <linux/pid_namespace.h>
> @@ -2542,6 +2543,9 @@ static const struct pid_entry tgid_base_stuff[] = {
>  #ifdef CONFIG_TASK_IO_ACCOUNTING
>  	INF("io",	S_IRUGO, proc_tgid_io_accounting),
>  #endif
> +#ifdef CONFIG_UTRACE_GDB
> +	REG("gdb",	S_IRUSR|S_IWUSR, proc_gdb_operations),
> +#endif
>  };
> 
>  static int proc_tgid_base_readdir(struct file * filp,
> diff --git a/include/linux/utrace.h b/include/linux/utrace.h
> index f877ec6..f33a5da 100644
> --- a/include/linux/utrace.h
> +++ b/include/linux/utrace.h
> @@ -689,4 +689,8 @@ static inline __must_check int utrace_barrier_pid(struct pid *pid,
> 
>  #endif	/* CONFIG_UTRACE */
> 
> +#ifdef CONFIG_UTRACE_GDB
> +extern const struct file_operations proc_gdb_operations;
> +#endif
> +
>  #endif	/* linux/utrace.h */
> diff --git a/init/Kconfig b/init/Kconfig
> index a6987df..7ffa60d 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1158,6 +1158,14 @@ menuconfig UTRACE
>  	  kernel interface exported to kernel modules, to track events in
>  	  user threads, extract and change user thread state.
> 
> +config UTRACE_GDB
> +	bool "/proc/<pid>/gdb file for gdb remote connection"
> +	select UTRACE
> +        default y
> +	help
> +	  Enable the utrace-based /proc/<pid>/gdb process debugging
> +	  interface, for connection using the gdb remote protocol.
> +
>  source "block/Kconfig"
> 
>  config PREEMPT_NOTIFIERS
> diff --git a/kernel/Makefile b/kernel/Makefile
> index a79634e..21457e6 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -69,6 +69,7 @@ obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
>  obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
>  obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
>  obj-$(CONFIG_UTRACE) += utrace.o
> +obj-$(CONFIG_UTRACE_GDB) += utrace-gdb.o
>  obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
>  obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
>  obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
> diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c
> new file mode 100644
> index 0000000..3835761
> --- /dev/null
> +++ b/kernel/utrace-gdb.c
> @@ -0,0 +1,1148 @@
> +/*
> + * utrace-based gdb remote protocol server for user processes
> + *
> + * Copyright (C) 2009 Red Hat, Inc.  All rights reserved.
> + *
> + * This copyrighted material is made available to anyone wishing to use,
> + * modify, copy, or redistribute it subject to the terms and conditions
> + * of the GNU General Public License v.2.
> + *
> + * Red Hat Author: Frank Ch. Eigler
> + */
> +
> +/* #define DEBUG 1 */
> +
> +#include <asm/syscall.h>
> +#include <asm/signal.h>
> +#include <linux/ptrace.h>
> +#include <linux/err.h>
> +#include <linux/pid.h>
> +#include <linux/sched.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/mm.h>
> +#include <linux/proc_fs.h>
> +#include <linux/ctype.h>
> +#include <linux/regset.h>
> +#include <linux/utrace.h>
> +#include <linux/tracehook.h>
> +
> +
> +
> +/** struct gdb_connection - Tracks one active gdb-process session.
> + */
> +
> +#define GDB_BUFMAX 4096
> +
> +
> +struct gdb_connection {
> +        pid_t target;
> +        struct utrace_engine *engine;
> +
> +        /* changed under output_mutex */
> +        int at_quiesce_do;
> +        unsigned char stopcode[GDB_BUFMAX]; // set <=> at_quiesce_do = UTRACE_STOP
> +        int skip_signals;
> +        int stop_signals;
> +        /* XXX: per-thread later */ 
> +
> +        char output_buf[GDB_BUFMAX];
> +        size_t output_buf_size;
> +        loff_t output_buf_read;
> +        struct mutex output_mutex;
> +        wait_queue_head_t output_wait;
> +
> +        char input_buf[GDB_BUFMAX];
> +        size_t input_buf_size;
> +        struct mutex input_mutex;
> +        wait_queue_head_t input_wait;
> +
> +        struct list_head link;
> +};
> +
> +
> +static LIST_HEAD(gdb_connections);
> +static DEFINE_MUTEX(gdb_connections_mutex);
> +static const struct utrace_engine_ops gdb_utrace_ops;
> +
> +
> +
> +/* ------------------------------------------------------------------------ */
> +
> +static unsigned byteme (unsigned char hex1, unsigned char hex2)
> +{
> +        return (isdigit(hex1) ? hex1-'0' : tolower(hex1)-'a'+10) * 16 +
> +               (isdigit(hex2) ? hex2-'0' : tolower(hex2)-'a'+10);
> +}
> +
> +
> +
> +/* Begin a new packet.  Add the $, and remember where we put it.
> + * Return the offset for later checksum addition via
> + * push_output_packet_end. */
> +static size_t push_output_packet_start (struct gdb_connection *p)
> +{
> +        size_t start = p->output_buf_size;
> +
> +        BUG_ON (p->output_buf_size + 1 >= GDB_BUFMAX);
> +        p->output_buf[p->output_buf_size++] = '$';
> +        return start;
> +}
> +
> +
> +/* Add a character to the output queue.  Assumes output_mutex held. */
> +static void push_output (struct gdb_connection *p, unsigned char c)
> +{
> +        /* We know some space must exist; we check for this in
> +           proc_gdb_write() for example. */
> +        BUG_ON (p->output_buf_size >= GDB_BUFMAX);
> +        p->output_buf[p->output_buf_size++] = c;
> +}
> +
> +
> +static char hex[] = "0123456789ABCDEF";
> +
> +/* Add a byte (hexified) to the output queue.  Assumes output_mutex held. */
> +static void push_output_hex (struct gdb_connection *p, unsigned char c)
> +{
> +        /* We know some space must exist; we check for this in
> +           proc_gdb_write() for example. */
> +        BUG_ON (p->output_buf_size >= GDB_BUFMAX);
> +        p->output_buf[p->output_buf_size++] = hex[(c & 0xf0) >> 4];
> +        p->output_buf[p->output_buf_size++] = hex[(c & 0x0f) >> 0];
> +}
> +
> +
> +/* Finish the last packet.  Starting after the given '$' offset, compute
> + * the checksum and append it.  */
> +static void push_output_packet_end (struct gdb_connection *p, size_t start)
> +{
> +        unsigned char checksum = 0;
> +        int i;
> +
> +        BUG_ON (p->output_buf_size + 3 >= GDB_BUFMAX);
> +        BUG_ON (p->output_buf[start] != '$');
> +
> +        for (i=start+1; i<p->output_buf_size; i++)
> +                checksum += p->output_buf[i];
> +
> +        p->output_buf[p->output_buf_size++] = '#';
> +        p->output_buf[p->output_buf_size++] = hex[(checksum & 0xf0) >> 4];
> +        p->output_buf[p->output_buf_size++] = hex[(checksum & 0x0f) >> 0];
> +}
> +
> +
> +/* Add a complete packet payload to the output queue.  */
> +static void push_output_packet (struct gdb_connection *p, const char *s)
> +{
> +        size_t ss = strlen(s);
> +        size_t start;
> +        int i;
> +
> +        start = push_output_packet_start(p);
> +        for (i=0; i<ss; i++)
> +                push_output(p, s[i]);
> +        push_output_packet_end(p, start);
> +}
> +
> +
> +
> +/* ------------------------------------------------------------------------ */
> +
> +/* utrace callbacks */
> +
> +
> +u32 gdb_utrace_report_quiesce(enum utrace_resume_action action,
> +                              struct utrace_engine *engine,
> +                              struct task_struct *task,
> +                              unsigned long event)
> +{
> +        struct gdb_connection *p = engine->data;
> +        pr_debug ("report_quiesce %d event 0x%lx 0x%x->0x%x\n", task->pid, 
> +                  event, action, p->at_quiesce_do);
> +
> +        return p->at_quiesce_do;
> +}
> +
> +
> +u32 gdb_utrace_report_clone(enum utrace_resume_action action,
> +			       struct utrace_engine *engine,
> +			       struct task_struct *parent,
> +			       unsigned long clone_flags,
> +			       struct task_struct *child)
> +{
> +        pr_debug ("report_clone %d->%d\n", parent->pid, child->pid);
> +
> +        if (clone_flags & CLONE_THREAD) {
> +                printk (KERN_WARNING "unsupported multithreading on /proc/%d/gdb.\n",
> +                        task_pid_nr (parent));
> +        }
> +        /* XXX: is there anything else to do here? */
> +	return UTRACE_RESUME;
> +}
> +
> +
> +u32 gdb_utrace_report_exec(enum utrace_resume_action action,
> +			      struct utrace_engine *engine,
> +			      struct task_struct *task,
> +			      const struct linux_binfmt *fmt,
> +			      const struct linux_binprm *bprm,
> +			      struct pt_regs *regs)
> +{
> +        /* XXX: Model an exec as if it were an exit. */
> +        struct gdb_connection *p = engine->data;
> +
> +        pr_debug ("report_exec %d->%s\n", task->pid, task->comm);
> +
> +        mutex_lock(&p->output_mutex);
> +
> +        p->at_quiesce_do = UTRACE_STOP;
> +        snprintf (p->stopcode, GDB_BUFMAX, "W%02x", 0);
> +        push_output_packet (p, p->stopcode);
> +
> +        mutex_unlock(&p->output_mutex);
> +        wake_up(&p->output_wait);
> +
> +        /* Suspend the exec operation, to ensure that the connected gdb
> +           receives the notification packet, and lets us go. */
> +	return UTRACE_STOP;
> +}
> +
> +
> +u32 gdb_utrace_report_signal(u32 action,
> +				struct utrace_engine *engine,
> +				struct task_struct *task,
> +				struct pt_regs *regs,
> +				siginfo_t *info,
> +				const struct k_sigaction *orig_ka,
> +				struct k_sigaction *return_ka)
> +{
> +        struct gdb_connection *p = engine->data;
> +        u32 ret = action;
> +        int kern_p;
> +
> +        mutex_lock(&p->output_mutex);
> +
> +        kern_p = (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)));
> +
> +        pr_debug ("report_signal %d (0x%x) kern %d skip %d stop %d\n",
> +                  task->pid, action, kern_p, p->skip_signals, p->stop_signals);
> +
> +        /* The target is about to receive a signal.  There are several
> +         * cases: 
> +         * 
> +         * 1) This is an ordinary signal.  We UTRACE_STOP to notify gdb.
> +         *
> +         * 2) This is a SIGTRAP arising from a breakpoint.  We UTRACE_STOP.
> +         *
> +         * 3) This is a signal our code injected to stop the process, in lieu
> +         * of UTRACE_INTERRUPT.  We UTRACE_STOP | UTRACE_SIGNAL_IGN.
> +         *
> +         * 4) This is a signal our code injected on behalf of gdb (C/S/I packets).
> +         * We UTRACE_RESUME.
> +         *
> +         * 5) This is a UTRACE_SIGNAL_REPORT or UTRACE_SIGNAL_HANDLER event.
> +         * Just let utrace continue, as these signal events of minor internal
> +         * interest.
> +         */
> +
> +        if (utrace_signal_action(action) == UTRACE_SIGNAL_REPORT ||
> +            utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER) { /* case 5 */
> +                /* NB: disregard p->at_quiesce_do */
> +                ret = UTRACE_RESUME | utrace_signal_action(action);
> +        } else if (p->skip_signals > 0 /*&& kern_p*/) { /* case 4 */
> +                p->skip_signals --;
> +                p->at_quiesce_do = UTRACE_RESUME;
> +                ret = UTRACE_RESUME; /* deliver */
> +        } else if (p->stop_signals > 0 /*&& kern_p*/) { /* Case 3 */
> +                p->stop_signals --;
> +                snprintf (p->stopcode, GDB_BUFMAX, "S%02x", info->si_signo);
> +                push_output_packet (p, p->stopcode);
> +                p->at_quiesce_do = UTRACE_STOP;
> +                ret = UTRACE_STOP | UTRACE_SIGNAL_IGN;
> +        } else { /* Cases 1, 2 */
> +                snprintf (p->stopcode, GDB_BUFMAX, "S%02x", info->si_signo);
> +                push_output_packet (p, p->stopcode);
> +                p->at_quiesce_do = UTRACE_STOP;
> +                ret = UTRACE_STOP;
> +        }
> +
> +        pr_debug ("action 0x%x\n", ret);
> +
> +        mutex_unlock(&p->output_mutex);
> +        wake_up(&p->output_wait);
> +
> +        return ret;
> +}
> +
> +
> +u32 gdb_utrace_report_exit(enum utrace_resume_action action,
> +                            struct utrace_engine *engine,
> +                            struct task_struct *task,
> +                            long orig_code, long *code)
> +{
> +        struct gdb_connection *p = engine->data;
> +
> +        pr_debug ("report_exit %d (%lx)\n", task->pid, (unsigned long) orig_code);
> +
> +        mutex_lock(&p->output_mutex);
> +
> +        p->at_quiesce_do = UTRACE_STOP;
> +        snprintf (p->stopcode, GDB_BUFMAX,
> +                  "W%02x", (unsigned)(orig_code & 0xFF));
> +        push_output_packet (p, p->stopcode);
> +
> +        mutex_unlock(&p->output_mutex);
> +        wake_up(&p->output_wait);
> +
> +        /* Suspend the exit operation, to ensure that the connected gdb
> +           receives the notification packet, and lets us go. */
> +	return UTRACE_STOP;
> +}
> +
> +
> +u32 gdb_utrace_report_death(struct utrace_engine *engine,
> +                            struct task_struct *task,
> +                            bool group_dead, int signal)
> +{
> +        struct gdb_connection *p = engine->data;
> +
> +        pr_debug ("report_death %d (%d)\n", task->pid, signal);
> +
> +        mutex_lock(&p->output_mutex);
> +
> +        p->at_quiesce_do = UTRACE_DETACH;
> +        snprintf (p->stopcode, GDB_BUFMAX, "X%2x", (unsigned)(signal & 0xFF));
> +        push_output_packet (p, p->stopcode);
> +
> +        p->engine = NULL;
> +
> +        mutex_unlock(&p->output_mutex);
> +        wake_up(&p->output_wait);
> +
> +	return UTRACE_DETACH;
> +}
> +
> +
> +
> +static const struct utrace_engine_ops gdb_utrace_ops = {
> +	.report_quiesce = gdb_utrace_report_quiesce,
> +	.report_signal = gdb_utrace_report_signal,
> +        .report_death = gdb_utrace_report_death,
> +	.report_exit = gdb_utrace_report_exit,
> +	.report_exec = gdb_utrace_report_exec,
> +	.report_clone = gdb_utrace_report_clone,
> +        /* XXX: syscall trapping is also possible. */
> +};
> +
> +
> +
> +/* XXX: arch-dependent lookup of gdb remote protocol register
> + * numbering.  The register numbers (user-side) & expected sizes come
> + * from gdb's regformats/FOO-linux.dat.  The regset (kernel-side)
> + * numbers could come from offsetof/sizeof constructs based upon each
> + * arch's asm/user*.h.
> + */
> +
> +struct gdb_map_regset {
> +        unsigned pos;   /* regset offset */
> +        unsigned count; /* regset byte count */
> +        unsigned rsn;   /* regset number */
> +        unsigned bytes; /* gdb's view of register width; <= count */
> +};
> +
> +struct gdb_map_regset arch_i386_map_regset[] = {
> +        [0]={  /* eax */     6*4,  4, NT_PRSTATUS, 4, },
> +        [1]={  /* ecx */     1*4,  4, NT_PRSTATUS, 4, },
> +        [2]={  /* edx */     2*4,  4, NT_PRSTATUS, 4, },
> +        [3]={  /* ebx */     0*4,  4, NT_PRSTATUS, 4, },
> +        [4]={  /* esp */    15*4,  4, NT_PRSTATUS, 4, },
> +        [5]={  /* ebp */     5*4,  4, NT_PRSTATUS, 4, },
> +        [6]={  /* esi */     3*4,  4, NT_PRSTATUS, 4, },
> +        [7]={  /* edi */     4*4,  4, NT_PRSTATUS, 4, },
> +        [8]={  /* eip */    12*4,  4, NT_PRSTATUS, 4, },
> +        [9]={  /* eflags */ 14*4,  4, NT_PRSTATUS, 4, },
> +        [10]={ /* cs */     13*4,  4, NT_PRSTATUS, 4, },
> +        [11]={ /* ss */     16*4,  4, NT_PRSTATUS, 4, },
> +        [12]={ /* ds */      7*4,  4, NT_PRSTATUS, 4, },
> +        [13]={ /* es */      8*4,  4, NT_PRSTATUS, 4, },
> +        [14]={ /* fs */      9*4,  4, NT_PRSTATUS, 4, },
> +        [15]={ /* gs */     10*4,  4, NT_PRSTATUS, 4, },
> +        [16]={ /* st0 */       0,  0, NT_PRFPREG, 10, },
> +        [17]={ /* st1 */       0,  0, NT_PRFPREG, 10, },
> +        [18]={ /* st2 */       0,  0, NT_PRFPREG, 10, },
> +        [19]={ /* st3 */       0,  0, NT_PRFPREG, 10, },
> +        [20]={ /* st4 */       0,  0, NT_PRFPREG, 10, },
> +        [21]={ /* st5 */       0,  0, NT_PRFPREG, 10, },
> +        [22]={ /* st6 */       0,  0, NT_PRFPREG, 10, },
> +        [23]={ /* st7 */       0,  0, NT_PRFPREG, 10, },
> +        [24]={ /* fctrl */     0,  0, NT_PRFPREG, 4, },
> +        [25]={ /* fstat */     0,  0, NT_PRFPREG, 4, },
> +        [26]={ /* ftag */      0,  0, NT_PRFPREG, 4, },
> +        [27]={ /* fiseg */     0,  0, NT_PRFPREG, 4, },
> +        [28]={ /* fioff */     0,  0, NT_PRFPREG, 4, },
> +        [29]={ /* foseg */     0,  0, NT_PRFPREG, 4, },
> +        [30]={ /* fooff */     0,  0, NT_PRFPREG, 4, },
> +        [31]={ /* fop */       0,  0, NT_PRFPREG, 4, },
> +        [32]={ /* xmm0 */      0,  0, NT_PRFPREG, 16, },
> +        [33]={ /* xmm1 */      0,  0, NT_PRFPREG, 16, },
> +        [34]={ /* xmm2 */      0,  0, NT_PRFPREG, 16, },
> +        [35]={ /* xmm3 */      0,  0, NT_PRFPREG, 16, },
> +        [36]={ /* xmm4 */      0,  0, NT_PRFPREG, 16, },
> +        [37]={ /* xmm5 */      0,  0, NT_PRFPREG, 16, },
> +        [38]={ /* xmm6 */      0,  0, NT_PRFPREG, 16, },
> +        [39]={ /* xmm7 */      0,  0, NT_PRFPREG, 16, },
> +        [40]={ /* mxcsr */     0,  0, NT_PRFPREG, 4, },
> +        [41]={ /* orig_eax*/   0,  0, NT_PRSTATUS, 4, },
> +};
> +
> +
> +struct gdb_map_regset arch_x86_64_map_regset[] = {
> +        [0]={  /* rax */    10*8,  8, NT_PRSTATUS, 8, },
> +        [1]={  /* rbx */     5*8,  8, NT_PRSTATUS, 8, },
> +        [2]={  /* rcx */    11*8,  8, NT_PRSTATUS, 8, },
> +        [3]={  /* rdx */    12*8,  8, NT_PRSTATUS, 8, },
> +        [4]={  /* rsi */    13*8,  8, NT_PRSTATUS, 8, },
> +        [5]={  /* rdi */    14*8,  8, NT_PRSTATUS, 8, },
> +        [6]={  /* rbp */     4*8,  8, NT_PRSTATUS, 8, },
> +        [7]={  /* rsp */    19*8,  8, NT_PRSTATUS, 8, },
> +        [8]={  /* r8 */      9*8,  8, NT_PRSTATUS, 8, },
> +        [9]={  /* r9 */      8*8,  8, NT_PRSTATUS, 8, },
> +        [10]={ /* r10 */     7*8,  8, NT_PRSTATUS, 8, },
> +        [11]={ /* r11 */     6*8,  8, NT_PRSTATUS, 8, },
> +        [12]={ /* r12 */     3*8,  8, NT_PRSTATUS, 8, },
> +        [13]={ /* r13 */     2*8,  8, NT_PRSTATUS, 8, },
> +        [14]={ /* r14 */     1*8,  8, NT_PRSTATUS, 8, },
> +        [15]={ /* r15 */     0*8,  8, NT_PRSTATUS, 8, },
> +        [16]={ /* rip */    16*8,  8, NT_PRSTATUS, 8, },
> +        [17]={ /* flags */  18*8,  8, NT_PRSTATUS, 4, },
> +        [18]={ /* cs */     17*8,  8, NT_PRSTATUS, 4, },
> +        [19]={ /* ss */     20*8,  8, NT_PRSTATUS, 4, },
> +        [20]={ /* ds */     23*8,  8, NT_PRSTATUS, 4, },
> +        [21]={ /* es */     24*8,  8, NT_PRSTATUS, 4, },
> +        [22]={ /* fs */     25*8,  8, NT_PRSTATUS, 4, },
> +        [23]={ /* gs */     26*8,  8, NT_PRSTATUS, 4, },
> +        [24]={ /* st0 */       0,  0, NT_PRFPREG, 10, },
> +        [25]={ /* st1 */       0,  0, NT_PRFPREG, 10, },
> +        [26]={ /* st2 */       0,  0, NT_PRFPREG, 10, },
> +        [27]={ /* st3 */       0,  0, NT_PRFPREG, 10, },
> +        [28]={ /* st4 */       0,  0, NT_PRFPREG, 10, },
> +        [29]={ /* st5 */       0,  0, NT_PRFPREG, 10, },
> +        [30]={ /* st6 */       0,  0, NT_PRFPREG, 10, },
> +        [31]={ /* st7 */       0,  0, NT_PRFPREG, 10, },
> +        [32]={ /* fctrl */     0,  0, NT_PRFPREG, 4, },
> +        [33]={ /* fstat */     0,  0, NT_PRFPREG, 4, },
> +        [34]={ /* ftag */      0,  0, NT_PRFPREG, 4, },
> +        [35]={ /* fiseg */     0,  0, NT_PRFPREG, 4, },
> +        [36]={ /* fioff */     0,  0, NT_PRFPREG, 4, },
> +        [37]={ /* foseg */     0,  0, NT_PRFPREG, 4, },
> +        [38]={ /* fooff */     0,  0, NT_PRFPREG, 4, },
> +        [39]={ /* fop */       0,  0, NT_PRFPREG, 4, },
> +        [40]={ /* xmm0 */      0,  0, NT_PRFPREG, 16, },
> +        [41]={ /* xmm1 */      0,  0, NT_PRFPREG, 16, },
> +        [42]={ /* xmm2 */      0,  0, NT_PRFPREG, 16, },
> +        [43]={ /* xmm3 */      0,  0, NT_PRFPREG, 16, },
> +        [44]={ /* xmm4 */      0,  0, NT_PRFPREG, 16, },
> +        [45]={ /* xmm5 */      0,  0, NT_PRFPREG, 16, },
> +        [46]={ /* xmm6 */      0,  0, NT_PRFPREG, 16, },
> +        [47]={ /* xmm7 */      0,  0, NT_PRFPREG, 16, },
> +        [48]={ /* xmm8 */      0,  0, NT_PRFPREG, 16, },
> +        [49]={ /* xmm9 */      0,  0, NT_PRFPREG, 16, },
> +        [50]={ /* xmm10 */      0,  0, NT_PRFPREG, 16, },
> +        [51]={ /* xmm11 */      0,  0, NT_PRFPREG, 16, },
> +        [52]={ /* xmm12 */      0,  0, NT_PRFPREG, 16, },
> +        [53]={ /* xmm13 */      0,  0, NT_PRFPREG, 16, },
> +        [54]={ /* xmm14 */      0,  0, NT_PRFPREG, 16, },
> +        [55]={ /* xmm15 */      0,  0, NT_PRFPREG, 16, },
> +        [56]={ /* mxcsr */      0,  0, NT_PRFPREG, 4, },
> +        [57]={ /* orig_rax*/ 15*8,  8, NT_PRSTATUS, 8, },
> +};
> +
> +
> +
> +static int gdb_remote_register_info(struct gdb_connection *p,
> +                                    struct task_struct *task,
> +                                    unsigned number, 
> +                                    unsigned *pos, unsigned *count,
> +                                    unsigned *bytes)
> +{
> +        const struct user_regset_view *rs = task_user_regset_view(task);
> +        int rsn = -1;
> +
> +        if(rs == 0)
> +                return -ENOENT;
> +
> +        /* pr_debug ("gdb_remote_register_info rs=%p rs->n=%u\n", rs, rs->n); */
> +
> +#define GMRSIZE (sizeof(struct gdb_map_regset))
> +
> +        if(rs->e_machine == EM_386) {
> +                if (number < sizeof(arch_i386_map_regset)/GMRSIZE) {
> +                        *pos = arch_i386_map_regset[number].pos;
> +                        *count = arch_i386_map_regset[number].count;
> +                        *bytes = arch_i386_map_regset[number].bytes;
> +                        rsn = arch_i386_map_regset[number].rsn;
> +                }
> +        } else if(rs->e_machine == EM_X86_64) {
> +                if (number < sizeof(arch_x86_64_map_regset)/GMRSIZE) {
> +                        *pos = arch_x86_64_map_regset[number].pos;
> +                        *count = arch_x86_64_map_regset[number].count;
> +                        *bytes = arch_x86_64_map_regset[number].bytes;
> +                        rsn = arch_x86_64_map_regset[number].rsn;
> +                }
> +        } /* else ... rsn stays -1. */
> +
> +#undef GMRSIZE
> +
> +        /* Now map to the per-architecture regset index, based on the
> +           elf core_note_type we found. */
> +        if (rsn >= 0) {
> +                unsigned j;
> +                for(j=0; j<rs->n; j++) {
> +                        if(rs->regsets[j].core_note_type == rsn)
> +                                return j;
> +                }
> +        }
> +        
> +        /* Invalid machines, register numbers, rsns, or unset rsns all
> +         * fall through here.
> +         */
> +        return -ENOENT;
> +}
> +
> +
> +
> +/* Process an entire, checksum-confirmed $command# at the front of
> + * p->input_buf[].  The input and output mutexes are being held.
> + */
> +static void handle_gdb_command_packet (struct gdb_connection *p, struct task_struct *task)
> +{
> +        unsigned long arg1, arg2, arg3;
> +        size_t op_start;
> +        int rc = 0;
> +        int i, j;
> +
> +        pr_debug ("gdb packet code %c\n", p->input_buf[1]);
> +
> +        switch (p->input_buf[1]) {
> +        case '?':
> +                if (p->at_quiesce_do != UTRACE_STOP) {
> +                        /* shouldn't happen */
> +                        send_sig(SIGTRAP, task, 1);
> +#if 0
> +                        rc = utrace_control (task, p->engine, UTRACE_INTERRUPT);
> +                        if (rc == -EINPROGRESS)
> +                                rc = utrace_barrier(task, p->engine);
> +#endif
> +
> +                        /* Note that we don't enqueue a reply packet here,
> +                           but make gdb wait for a response from the
> +                           utrace report_FOO callbacks. */
> +                        p->skip_signals ++;
> +                } else {
> +                        push_output_packet (p, p->stopcode);                
> +                }
> +                break;
> +
> +        case 'i': /* [ADDR[,NNN]] */
> +        case 's': /* [ADDR] */
> +                /* XXX: if !arch_has_single_step() ... then what? */
> +        case 'c': /* [ADDR] */
> +                rc = sscanf(& p->input_buf[2], "%lx,%lx", &arg1, &arg2);
> +                if (rc >= 1) { /* Have a PC? */
> +                        /* XXX: set it */
> +                }
> +                if (rc >= 2) { /* ,NNN present */
> +                        /* XXX: disregard it. */
> +                }
> +                /* XXX: args ignored */
> +                p->stopcode[0]='\0';
> +                p->at_quiesce_do = 
> +                        ((p->input_buf[1]=='c' || !arch_has_single_step())
> +                         ? UTRACE_RESUME : UTRACE_SINGLESTEP);
> +                if (p->at_quiesce_do == UTRACE_SINGLESTEP)
> +                        p->stop_signals ++;
> +                utrace_control (task, p->engine, p->at_quiesce_do);
> +                break;
> +        case 'C': /* SIG[;ADDR] */
> +        case 'S': /* SIG[;ADDR] */
> +                /* XXX: if !arch_has_single_step() ... then what? */
> +        case 'I': /* SIG[;ADDR[,NNN?]] */
> +                rc = sscanf(& p->input_buf[2], "%lx;%lx,%lx", &arg1, &arg2, &arg3);
> +                if (rc >= 1) { /* SIG present */
> +                        send_sig ((int)arg1, task, 1);
> +                }
> +                if (rc >= 2) { /* ;ADDR present */
> +                        /* XXX: not done */
> +                }
> +                if (rc >= 3) { /* ,NNN present */
> +                        /* XXX: disregard it. */
> +                }
> +                p->skip_signals ++;
> +                p->stopcode[0]='\0';
> +                p->at_quiesce_do = 
> +                        ((p->input_buf[1]=='C' || !arch_has_single_step())
> +                         ? UTRACE_RESUME : UTRACE_SINGLESTEP);
> +                if (p->at_quiesce_do == UTRACE_SINGLESTEP)
> +                        p->stop_signals ++;
> +                utrace_control (task, p->engine, p->at_quiesce_do);
> +                /* Response will come at next report_signal. */
> +                break;
> +        case 'D':
> +                push_output_packet (p, "OK");
> +                /* NB: the .release fop callback performs actual utrace detach. */
> +                break;
> +        case 'g':
> +                op_start = push_output_packet_start(p);
> +                /* GDB_BUFMAX stands for some random large number,
> +                 * known to be larger than the number of gdb indexed
> +                 * registers. */
> +                for (i=0; i<GDB_BUFMAX; i++) {
> +                        unsigned rs_count;
> +                        unsigned rs_pos;
> +                        unsigned bytes;
> +                        const struct user_regset_view* rsv;
> +                        const struct user_regset* rs;
> +                        unsigned char reg_contents[16]; /* maximum reg. width */
> +
> +                        int rsn = gdb_remote_register_info(p, task, i,
> +                                                           &rs_pos, &rs_count, 
> +                                                           &bytes);
> +
> +                        if (rsn < 0)
> +                                break;
> +
> +                        /* If we want to extract register data, make sure
> +                           we're fetching at least that much. */
> +                        BUG_ON (rs_count > 0 && rs_count < bytes);
> +                        /* Assert reg_contents size is right. */
> +                        BUG_ON(sizeof(reg_contents) < bytes ||
> +                               sizeof(reg_contents) < rs_count);
> +
> +                        if (rs_count) { /* real register */
> +                                rsv = task_user_regset_view(task);
> +                                BUG_ON(rsn >= rsv->n);
> +                                rs = & rsv->regsets[rsn];
> +                                
> +                                /* Extract the register value into reg_contents[]. */
> +                                rc = (rs->get) (task, rs, rs_pos, rs_count,
> +                                                reg_contents, NULL);
> +                                if (rc)
> +                                        break;
> +                        } else { /* dummy value */
> +                                memset (reg_contents, 0, sizeof(reg_contents));
> +                        }
> +
> +                        /* Hex-dump it. */
> +                        /* pr_debug ("gdb register %d => rsn %d p%u c%u b%u (",
> +                           i, rsn, rs_pos, rs_count, bytes); */
> +                        /* XXX: endianness adjust for count != bytes */
> +                        for(j=0; j<bytes; j++) {
> +                                /* pr_debug("%02x", reg_contents[j]);*/
> +                                push_output_hex(p, reg_contents[j]);
> +                        }
> +                        /* pr_debug(")\n"); */
> +
> +                }
> +                push_output_packet_end(p, op_start);
> +                break;
> +        case 'G':
> +                i = 0;
> +                op_start = 2; /* use as input pointer, past $G in command */
> +                while(p->input_buf[op_start] != '#' &&
> +                      op_start < p->input_buf_size) {
> +                        unsigned rs_count;
> +                        unsigned rs_pos;
> +                        unsigned bytes;
> +                        const struct user_regset_view* rsv;
> +                        const struct user_regset* rs;
> +                        unsigned char reg_contents[16]; /* maximum reg. width */
> +
> +                        int rsn = gdb_remote_register_info(p, task, i,
> +                                                           &rs_pos, &rs_count,
> +                                                           &bytes);
> +                        /* pr_debug ("gdb register %d => rsn %d p%u c%u b%u\n",
> +                           i, rsn, rs_pos, rs_count, bytes); */
> +
> +                        if (rsn < 0)
> +                                break;
> +
> +                        /* If we want to extract register data, make sure
> +                           we're fetching at least that much. */
> +                        BUG_ON(rs_count > 0 && rs_count < bytes);
> +                        /* Assert reg_contents size is right. */
> +                        BUG_ON(sizeof(reg_contents) < bytes ||
> +                               sizeof(reg_contents) < rs_count);
> +
> +                        /* Remaining packet too short? */
> +                        if ((op_start + 2*bytes + 3) < p->input_buf_size)
> +                                break;
> +
> +                        /* 0-fill the register copy.  XXX initialize
> +                         * it from rs->get() instead?
> +                         */
> +                        memset (reg_contents, 0, sizeof(reg_contents));
> +                        
> +                        /* Hex-unconvert all the bytes. */
> +                        /* XXX: endianness adjust for count != bytes */
> +                        for(j=0; j<bytes; j++)
> +                                reg_contents[j]=byteme(p->input_buf[op_start+2*j],
> +                                                       p->input_buf[op_start+2*j+1]);
> +                        op_start += 2*bytes;
> +                        
> +                        if (rs_count) { /* real register */
> +                                BUG_ON(rs_count > sizeof(reg_contents));
> +                                rsv = task_user_regset_view(task);
> +                                BUG_ON(rsn >= rsv->n);
> +                                rs = & rsv->regsets[rsn];
> +                                
> +                                /* Set the register value from reg_contents[]. */
> +                                rc = (rs->set) (task, rs, rs_pos, rs_count, 
> +                                                reg_contents, NULL);
> +                                if (rc)
> +                                        break;
> +                        } else { /* dummy register */
> +                                ;
> +                        }
> +                }
> +                if (p->input_buf[op_start] == '#' && rc == 0)
> +                        push_output_packet (p, "OK");
> +                else
> +                        push_output_packet (p, "E01");                        
> +                break;
> +        case 'p': /* REG */
> +                break;
> +        case 'P': /* REG=VAL */
> +                break;
> +        case 'm': /* ADDR,LENGTH */
> +                rc = sscanf(& p->input_buf[2], "%lx,%lx", &arg1, &arg2);
> +                if (rc != 2)
> +                        push_output_packet(p, "E01");
> +                else {
> +                        size_t o = push_output_packet_start (p);
> +                        while (arg2 > 0) {
> +                                unsigned char value;
> +
> +                                /* Simply stop looping if requested
> +                                   length was too large.  gdb will
> +                                   probably retry from this point
> +                                   on. */
> +                                if (p->output_buf_size + 5 > GDB_BUFMAX)
> +                                        break;
> +
> +                                rc = access_process_vm(task, arg1, &value, 1, 0);
> +                                if (rc != 1) 
> +                                        break; /* EFAULT */
> +                                else
> +                                        push_output_hex (p, value);
> +
> +                                arg1++;
> +                                arg2--;
> +                        }
> +                        push_output_packet_end (p, o);
> +                }
> +                break;
> +        case 'M': /* ADDR,LENGTH:XX */
> +                /* `i' will index p->input_buf to consume XX hex bytes. */
> +                rc = sscanf(& p->input_buf[2], "%lx,%lx:%n",
> +                            &arg1, &arg2, &i);
> +                op_start = i + 2; /* Skip the leading $M also. */
> +                if (rc < 2) {
> +                        push_output_packet(p, "E01");
> +                        break;
> +                }
> +                while (arg2 > 0) {
> +                        unsigned char value;
> +
> +                        /* Check that enough input bytes left for
> +                         * these two hex chars, plus the #XX checksum.
> +                         */
> +                        if (i+4 >= p->input_buf_size)
> +                                break;
> +
> +                        value = byteme(p->input_buf[i],
> +                                       p->input_buf[i+1]);
> +                        rc = access_process_vm(task, arg1, &value, 1, 1);
> +                        if (rc != 1) 
> +                                break; /* EFAULT */
> +                        
> +                        i += 2;
> +                        arg1++;
> +                        arg2--;
> +                }
> +                if (arg2 != 0)
> +                        push_output_packet(p, "E02");
> +                else
> +                        push_output_packet(p, "OK");
> +                break;
> +        default:
> +                push_output_packet (p, "");
> +        }
> +}
> +
> +
> +
> +
> +/* ------------------------------------------------------------------------ */
> +
> +/* gdb control callbacks */
> +
> +#define get_proc_task(inode) get_pid_task(PROC_I((inode))->pid, PIDTYPE_PID)
> +
> +static int proc_gdb_open(struct inode *inode, struct file *filp)
> +{
> +	struct task_struct *task = get_proc_task(inode);
> +        int ret = -EBUSY;
> +        struct gdb_connection *p;
> +        struct list_head *l;
> +
> +        pr_debug ("opened /proc/%d/gdb\n", task->pid);
> +
> +        /* Reject kernel threads. */
> +        if (task->flags & PF_KTHREAD) {
> +                ret = -EINVAL;
> +                goto out;
> +        }
> +
> +        /* Reject if connection is for other than tg-leader thread. */
> +        if (task_pid_nr(task) != task_tgid_nr(task)) {
> +                ret = -EINVAL;
> +                goto out;
> +        }
> +
> +        mutex_lock (& gdb_connections_mutex);
> +
> +        /* Reject if a connection exists for the thread group
> +         * leader. 
> +         */
> +        list_for_each(l, &gdb_connections) {
> +                p = list_entry (l, struct gdb_connection, link);
> +                if (p->target == task_tgid_nr(task)) {
> +                        ret = -EBUSY;
> +                        goto out_mutex;
> +                }
> +        }
> +        /* (Don't unlock yet, to defeat a race of two concurrent opens.) */
> +
> +        p = kzalloc(sizeof (struct gdb_connection), GFP_KERNEL);
> +        if (!p) {
> +                ret = -ENOMEM;
> +                goto out_mutex;
> +        }
> +
> +        /* Send initial ping to gdb. */
> +        push_output_packet (p, "");
> +
> +        mutex_init(& p->output_mutex);
> +        init_waitqueue_head(& p->output_wait);
> +
> +        mutex_init(& p->input_mutex);
> +        init_waitqueue_head(& p->input_wait);
> +
> +        p->target = task->tgid;
> +
> +        /* NB: During attach, we don't want to bother the target.
> +           Soon though a send_sig will interrupt it. */
> +        p->at_quiesce_do = UTRACE_RESUME;
> +
> +        p->engine = utrace_attach_task(task,
> +                                       UTRACE_ATTACH_CREATE |
> +                                       UTRACE_ATTACH_EXCLUSIVE,
> +                                       &gdb_utrace_ops, 
> +                                       p);
> +        if (IS_ERR(p->engine) || p->engine==NULL) {
> +                ret = -EINVAL;
> +                goto out_free;
> +        }
> +
> +        ret = utrace_set_events(task, p->engine,
> +                               UTRACE_EVENT_SIGNAL_ALL|
> +                               UTRACE_EVENT(QUIESCE)|
> +                               UTRACE_EVENT(DEATH)|
> +                               UTRACE_EVENT(EXIT)|
> +                               UTRACE_EVENT(EXEC)|
> +                               UTRACE_EVENT(CLONE));
> +        pr_debug ("utrace_set_events sent, ret=%d\n", ret);
> +        if (!ret)
> +                ;
> +
> +        filp->private_data = p;
> +
> +        INIT_LIST_HEAD(& p->link);
> +        list_add(&gdb_connections, &p->link);
> +
> +        p->stop_signals ++;
> +        send_sig(SIGTRAP, task, 1);
> +#if 0
> +        ret = utrace_control(task, p->engine, UTRACE_INTERRUPT);
> +        if (ret == -EINPROGRESS)
> +                ret = utrace_barrier(task, p->engine);
> +#endif
> +
> +        goto out_mutex;
> +
> +out_free:
> +        kfree(p);
> +out_mutex:
> +        mutex_unlock (& gdb_connections_mutex);
> +out:
> +	return ret;
> +}
> +
> +
> +static int proc_gdb_release(struct inode *inode, struct file *filp)
> +{
> +	struct task_struct *task = get_proc_task(inode);
> +        struct gdb_connection *p = filp->private_data;
> +        int ret = 0;
> +
> +        mutex_lock (& gdb_connections_mutex);
> +
> +        if (task == NULL) {
> +                /* The thread is already gone; report_death was already called. */
> +                pr_debug ("gdb %d releasing old\n", p->target);
> +        } else {
> +                pr_debug ("gdb %d releasing current\n", p->target);
> +
> +                ret = utrace_set_events(task, p->engine, 0);
> +                if (ret == -EINPROGRESS)
> +                        ret = utrace_barrier(task, p->engine);
> +                /* No more callbacks will be received! */
> +                
> +                ret = utrace_control(task, p->engine, UTRACE_DETACH); /* => RESUME */
> +                if (ret == -EINPROGRESS)
> +                        ret = utrace_barrier(task, p->engine);
> +                
> +                utrace_engine_put (p->engine);
> +        }
> +
> +        list_del(&p->link);
> +        kfree(p);
> +
> +        mutex_unlock (& gdb_connections_mutex);
> +
> +	return ret;
> +}
> +
> +
> +
> +static int proc_gdb_ioctl(struct inode *inode, struct file *file,
> +                           unsigned int cmd, unsigned long arg)
> +{
> +        /* XXX: GDB usually thinks that a file name for "target
> +         * remote" implies a serial port with tty-ish ioctl's
> +         * available.  We pretend to accept them all. */
> +        return 0;
> +}
> +
> +
> +
> +static ssize_t proc_gdb_read(struct file *filp, char __user *buf,
> +                             size_t count, loff_t *ppos)
> +{
> +        struct gdb_connection *p = filp->private_data;
> +	struct task_struct *task;
> +        int rc = 0;
> +        size_t len;
> +
> +        task = find_task_by_vpid (p->target);
> +        if (!task)
> +                return -EINVAL;
> +
> +        if ((p->output_buf_size <= p->output_buf_read) && 
> +            filp->f_flags & O_NONBLOCK)
> +                return -EAGAIN;
> +
> +again:
> +        rc = wait_event_interruptible (p->output_wait, 
> +                                       (p->output_buf_size > p->output_buf_read));
> +        if (rc)
> +                goto out;
> +
> +        mutex_lock(&p->output_mutex);
> +
> +        if(p->output_buf_size <= p->output_buf_read) {
> +                mutex_unlock(&p->output_mutex);
> +                goto again;
> +        }
> +
> +        len = min (count, (size_t)(p->output_buf_size - p->output_buf_read));
> +        if (copy_to_user (buf, & p->output_buf[p->output_buf_read], len)) {
> +                rc = -EFAULT;
> +                goto out_unlock;
> +        }
> +
> +        pr_debug ("sent %u bytes (%ld left) data (%.*s)\n",
> +                  (unsigned)len,
> +                  ((long)p->output_buf_size-(long)p->output_buf_read)-len,
> +                  (int)len, & p->output_buf[p->output_buf_read]);
> +
> +        p->output_buf_read += len;
> +        rc = len;
> +
> +        /* If whole packet is consumed, reset for next one. */ 
> +        BUG_ON (p->output_buf_read > p->output_buf_size);
> +        if (p->output_buf_read == p->output_buf_size) {
> +                p->output_buf_read = 0;
> +                p->output_buf_size = 0;
> +        }
> +
> +out_unlock:
> +        mutex_unlock(&p->output_mutex);
> +
> +out:
> +        return rc;
> +}
> +
> +
> +static ssize_t proc_gdb_write(struct file *filp, const char __user *buf,
> +                              size_t count, loff_t *ppos)
> +{
> +        struct gdb_connection *p = filp->private_data;
> +        size_t last_input_buf_size;
> +	struct task_struct *task;
> +        size_t len;
> +        int ret = 0;
> +
> +        task = find_task_by_vpid (p->target);
> +        if (!task)
> +                return -EINVAL;
> +
> +again:
> +        ret = wait_event_interruptible (p->input_wait, 
> +                                       (p->input_buf_size < GDB_BUFMAX));
> +        if (ret)
> +                goto out;
> +
> +        mutex_lock(&p->input_mutex);
> +        if (p->input_buf_size == GDB_BUFMAX) {
> +                mutex_unlock(&p->input_mutex);
> +                goto again;
> +        }
> +        mutex_lock(&p->output_mutex);
> +
> +        /* We now know there is some room in the input buffer.  Upon
> +           entry, the input_buf will either be empty, or contain a
> +           partial gdb request packet. */
> +
> +        /* Copy the data. */
> +        len = min (count, (size_t)(GDB_BUFMAX - p->input_buf_size));
> +        if (copy_from_user (& p->input_buf[p->input_buf_size], buf, len)) {
> +                ret = -EFAULT;
> +                goto out_unlock;
> +        }
> +
> +        /* pr_debug ("received data %.*s\n", (int)len, & p->input_buf[p->input_buf_size]); */
> +
> +        p->input_buf_size += len;
> +        ret = len;
> +
> +        /* Process any packets in the buffer to restore the incoming
> +           invariant.  (Normal GDB will not send more than one packet
> +           before waiting for a response.) */
> +        
> +        /* We iterate until we can no longer shrink the input buffer.  Usually
> +           we will not iterate more than once, since there may be one +/-
> +           ack byte and/or one gdb packet. */
> +        last_input_buf_size = 0;
> +        while (p->input_buf_size
> +               && p->input_buf_size != last_input_buf_size) {
> +                last_input_buf_size = p->input_buf_size;
> +
> +                if (p->input_buf[0] == '+') {
> +                        /* This must have been an ack to our
> +                         * previously output packet. 
> +                         * Consume the input. 
> +                         */
> +                        memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> +                } else if (p->input_buf[0] == '-') {
> +                        /* Whoops, a nak.  Unfortunately, we don't
> +                         * handle transmission errors by
> +                         * retransmitting the last output_buf; it's
> +                         * already gone.  OTOH we should not encounter
> +                         * transmission errors on a reliable channel
> +                         * such as a read syscall.
> +                         * Consume the input.
> +                         */
> +                        printk(KERN_WARNING "Unexpected NAK received"
> +                               "on /proc/%d/gdb connection.\n", task_pid_nr(task));
> +                        memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> +                } else if (p->input_buf[0] == 3) { /* ^C == INTR */
> +                        /* NB: don't overwrite 'ret'. */
> +                        pr_debug ("received gdb interrupt\n");
> +                        p->stop_signals ++;
> +                        send_sig(SIGTRAP, task, 1);
> +#if 0
> +                        int rc = utrace_control(task, p->engine, UTRACE_INTERRUPT);
> +                        if (rc == -EINPROGRESS)
> +                                rc = utrace_barrier(task, p->engine);
> +#endif
> +                        /* p->at_quiesce_do will be set in report_signal(SIGNAL_REPORT) */
> +                        /* NB: this packet does not generate an +/- ack.
> +                           Consume the input. */
> +                        memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> +                } else if (p->input_buf[0] == '$') { /* command packet */
> +                        int j;
> +                        unsigned char checksum = 0;
> +                        for (j=1; j<p->input_buf_size-2; j++) {
> +                                if (p->input_buf[j] == '#') {
> +                                        unsigned char checksum2;
> +                                        checksum2 = byteme (p->input_buf[j+1],
> +                                                            p->input_buf[j+2]);
> +                                        pr_debug ("received gdb packet %.*s\n",
> +                                                  j+3, & p->input_buf[0]);
> +                                        if (checksum == checksum2) {
> +                                                push_output (p, '+');
> +                                                handle_gdb_command_packet (p, task);
> +                                        } else {
> +                                                push_output (p, '-');
> +                                        }
> +                                        /* Consume the whole packet. */
> +                                        p->input_buf_size -= (j+3);
> +                                        memmove(&p->input_buf[0], &p->input_buf[j+3],
> +                                                p->input_buf_size);
> +                                        break;
> +                                } else {
> +                                        checksum += p->input_buf[j];
> +                                }
> +                        } /* End searching for end of packet */
> +
> +                        /* We may not have found the #<hex><hex>
> +                         * checksum.  If so, leave the partial packet
> +                         * in input_buf.  Since input_buf_size will
> +                         * not have decreased, the while() loop above
> +                         * will detect a fixpoint and exit. 
> +                         *
> +                         * Alternately, there could be another gdb packet
> +                         * just behind the one we just consumed.  In this
> +                         * we'll iterate one more time in this loop.
> +                         */
> +                } else { /* junk character */
> +                        printk(KERN_WARNING "Unexpected character (%x) received"
> +                               " on /proc/%d/gdb connection.\n",
> +                               (int) p->input_buf[0], task_pid_nr(task));
> +                        /* Consume the input. */
> +                        memmove (&p->input_buf[0], &p->input_buf[1], --p->input_buf_size);
> +                }
> +        }
> +        
> +out_unlock:
> +        wake_up(&p->input_wait); /* Probably have more room in input_buf. */
> +        wake_up(&p->output_wait); /* Probably have data in output_buf. */
> +
> +        mutex_unlock(&p->output_mutex);
> +        mutex_unlock(&p->input_mutex);
> +out:
> +        return ret;
> +}
> +
> +
> +const struct file_operations proc_gdb_operations = {
> +        .open = proc_gdb_open,
> +        .read = proc_gdb_read,
> +        .write = proc_gdb_write,
> +        .release = proc_gdb_release,
> +        .ioctl = proc_gdb_ioctl,
> +};
> +
> +


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]