public inbox for archer@sourceware.org
 help / color / mirror / Atom feed
* linux kernel gdb stub for userspace processes, prototype version 3
@ 2009-07-07  4:26 Frank Ch. Eigler
  2009-07-09  0:55 ` a question about process information tgh
  2009-07-09 15:43 ` linux kernel gdb stub for userspace processes, prototype version 3 Srikar Dronamraju
  0 siblings, 2 replies; 3+ messages in thread
From: Frank Ch. Eigler @ 2009-07-07  4:26 UTC (permalink / raw)
  To: systemtap, utrace-devel, archer

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,
+};
+
+

^ permalink raw reply	[flat|nested] 3+ messages in thread

* a question about process information
  2009-07-07  4:26 linux kernel gdb stub for userspace processes, prototype version 3 Frank Ch. Eigler
@ 2009-07-09  0:55 ` tgh
  2009-07-09 15:43 ` linux kernel gdb stub for userspace processes, prototype version 3 Srikar Dronamraju
  1 sibling, 0 replies; 3+ messages in thread
From: tgh @ 2009-07-09  0:55 UTC (permalink / raw)
  To: 'Frank Ch. Eigler', systemtap, utrace-devel, archer

hi
	I have a question about linux , I hear that linux2.6.27 provide
information about resource usage for each process, such as, CPU, memory,
I/O, and I want to know how to get it, does some proc or sysfs have these
information ,or something else has it, or how to get it from systemtap
	Could you help me

Thank you 


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: linux kernel gdb stub for userspace processes, prototype version 3
  2009-07-07  4:26 linux kernel gdb stub for userspace processes, prototype version 3 Frank Ch. Eigler
  2009-07-09  0:55 ` a question about process information tgh
@ 2009-07-09 15:43 ` Srikar Dronamraju
  1 sibling, 0 replies; 3+ messages in thread
From: Srikar Dronamraju @ 2009-07-09 15:43 UTC (permalink / raw)
  To: Frank Ch. Eigler; +Cc: systemtap, utrace-devel, archer

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,
> +};
> +
> +

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2009-07-09 15:43 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-07-07  4:26 linux kernel gdb stub for userspace processes, prototype version 3 Frank Ch. Eigler
2009-07-09  0:55 ` a question about process information tgh
2009-07-09 15:43 ` linux kernel gdb stub for userspace processes, prototype version 3 Srikar Dronamraju

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).