[PATCH]KGTP (Linux Kernel debugger and tracer) lite patch for review

From: Hui Zhu
Date: Wed May 09 2012 - 03:14:34 EST


Hi guys,

According to the comments of Andi, I make a patch for review that remove most of the function inside it. It just support function set tracepoint collect the register info (http://code.google.com/p/kgtp/wiki/HOWTO#Use_tracepoint_get_register_info_from_a_point_of_kernel) and access memory directly (http://code.google.com/p/kgtp/wiki/HOWTO#Access_memory_directly).
Please help me review it.

Following part is the introduce of KGTP:
KGTP is a realtime and lightweight Linux Kernel debugger and tracer.
It makes Linux Kernel supply a GDB remote debug interface. Then GDB in current machine or remote machine can debug and trace Linux through GDB tracepoint and some other functions without stopping the Linux Kernel.
And even if the board doesn't have GDB on it and doesn't have interface for remote debug. It can debug the Linux Kernel using offline debug (See http://code.google.com/p/kgtp/wiki/HOWTO#/sys/kernel/debug/gtpframe_and_offline_debug).
And it can work with Android (See http://code.google.com/p/kgtp/wiki/HowToUseKGTPinAndroid).
Now, it supports X86-32, X86-64, MIPS and ARM.
Please go to http://code.google.com/p/kgtp/wiki/HOWTO or http://code.google.com/p/kgtp/wiki/HOWTO (Chinese) to get more info about howto use KGTP.

Thanks,
Hui

Signed-off-by: Hui Zhu <teawater@xxxxxxxxx>
---
---
arch/arm/include/asm/gtp.h | 34 +
arch/mips/include/asm/gtp.h | 68 +++
arch/x86/include/asm/gtp.h | 96 ++++
kernel/Makefile | 2
kernel/gtp.c | 966 ++++++++++++++++++++++++++++++++++++++++++++
lib/Kconfig.debug | 8
6 files changed, 1174 insertions(+)

--- /dev/null
+++ b/arch/arm/include/asm/gtp.h
@@ -0,0 +1,34 @@
+#ifndef _ASM_ARM_GTP_H_
+#define _ASM_ARM_GTP_H_
+
+#define ULONGEST uint64_t
+#define CORE_ADDR unsigned long
+
+#define GTP_REG_ASCII_SIZE 336
+
+static inline void
+gtp_regs2ascii(struct pt_regs *regs, char *buf)
+{
+#ifdef __LITTLE_ENDIAN
+#define SWAB(a) swab32(a)
+#else
+#define SWAB(a) (a)
+#endif
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ sprintf(buf, "%08lx", (unsigned long) SWAB(regs->uregs[i]));
+ buf += 8;
+ }
+
+ /* f0-f7 fps */
+ memset(buf, '0', 200);
+ buf += 200;
+
+ sprintf(buf, "%08lx",
+ (unsigned long) SWAB(regs->uregs[16]));
+ buf += 8;
+#undef SWAB
+}
+
+#endif
--- /dev/null
+++ b/arch/mips/include/asm/gtp.h
@@ -0,0 +1,68 @@
+#ifndef _ASM_MIPS_GTP_H_
+#define _ASM_MIPS_GTP_H_
+
+#define ULONGEST uint64_t
+#define CORE_ADDR unsigned long
+
+#ifdef CONFIG_32BIT
+#define GTP_REG_ASCII_SIZE 304
+#else
+#define GTP_REG_ASCII_SIZE 608
+#endif
+
+#define GTP_SP_NUM 29
+
+static inline void
+gtp_regs2ascii(struct pt_regs *regs, char *buf)
+{
+#ifdef CONFIG_32BIT
+#define OUTFORMAT "%08lx"
+#define REGSIZE 8
+#ifdef __LITTLE_ENDIAN
+#define SWAB(a) swab32(a)
+#else
+#define SWAB(a) (a)
+#endif
+#else
+#define OUTFORMAT "%016lx"
+#define REGSIZE 16
+#ifdef __LITTLE_ENDIAN
+#define SWAB(a) swab64(a)
+#else
+#define SWAB(a) (a)
+#endif
+#endif
+ {
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->regs[i]));
+ buf += REGSIZE;
+ }
+ }
+
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_status));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->lo));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->hi));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_badvaddr));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_cause));
+ buf += REGSIZE;
+ sprintf(buf, OUTFORMAT,
+ (unsigned long) SWAB(regs->cp0_epc));
+ buf += REGSIZE;
+#undef OUTFORMAT
+#undef REGSIZE
+#undef SWAB
+}
+
+#endif
--- /dev/null
+++ b/arch/x86/include/asm/gtp.h
@@ -0,0 +1,96 @@
+#ifndef _ASM_X86_GTP_H_
+#define _ASM_X86_GTP_H_
+
+#define ULONGEST uint64_t
+#define CORE_ADDR unsigned long
+
+#ifdef CONFIG_X86_32
+#define GTP_REG_ASCII_SIZE 128
+#else
+#define GTP_REG_ASCII_SIZE 296
+#endif
+
+static inline void
+gtp_regs2ascii(struct pt_regs *regs, char *buf)
+{
+#ifdef CONFIG_X86_32
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ax));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->cx));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->dx));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->bx));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->sp));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->bp));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->si));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->di));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ip));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->flags));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->cs));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ss));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->ds));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->es));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->fs));
+ buf += 8;
+ sprintf(buf, "%08x", (unsigned int) swab32(regs->gs));
+ buf += 8;
+#else
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->ax));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->bx));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->cx));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->dx));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->si));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->di));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->bp));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->sp));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r8));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r9));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r10));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r11));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r12));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r13));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r14));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->r15));
+ buf += 16;
+ sprintf(buf, "%016lx", (unsigned long) swab64(regs->ip));
+ buf += 16;
+ sprintf(buf, "%08x",
+ (unsigned int) swab32((unsigned int)regs->flags));
+ buf += 8;
+ sprintf(buf, "%08x",
+ (unsigned int) swab32((unsigned int)regs->cs));
+ buf += 8;
+ sprintf(buf, "%08x",
+ (unsigned int) swab32((unsigned int)regs->ss));
+ buf += 8;
+#endif
+}
+
+#endif
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -107,6 +107,8 @@ obj-$(CONFIG_PADATA) += padata.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
+obj-$(CONFIG_GTP) += gtp.o
+
$(obj)/configs.o: $(obj)/config_data.h
# config_data.h contains the same information as ikconfig.h but gzipped.
--- /dev/null
+++ b/kernel/gtp.c
@@ -0,0 +1,966 @@
+/*
+ * Kernel GDB tracepoint module.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright(C) KGTP team (https://code.google.com/p/kgtp/), 2010, 2011, 2012
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/poll.h>
+#include <linux/kprobes.h>
+#include <linux/interrupt.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <asm/gtp.h>
+
+#define GTP_DEBUG KERN_WARNING
+
+#define GTP_RW_MAX 16384
+
+#define GTP_FRAME_SIZE 5242880
+#define GTP_FRAME_HEAD_SIZE (1 + sizeof(struct gtp_frame_head))
+#define GTP_FRAME_REG_SIZE (1 + sizeof(struct gtp_frame_reg))
+
+#define TOHEX(h) ((h) > 9 ? (h) + 'a' - 10 : (h) + '0')
+
+struct action {
+ struct action *next;
+ char type;
+ union {
+ ULONGEST reg_mask;
+ } u;
+};
+
+struct gtp_entry {
+ struct gtp_entry *next;
+ ULONGEST num;
+ ULONGEST addr;
+ ULONGEST step;
+ ULONGEST pass;
+ int nopass;
+ int kpreg;
+ struct kprobe kp;
+ struct action *action_list;
+ struct action *action_list_tail;
+} *gtp_list = NULL, *gtp_list_tail = NULL;
+
+struct gtp_frame_head {
+ int frame_num;
+ ULONGEST trace_num;
+ char *next;
+};
+
+struct gtp_frame_reg {
+ struct pt_regs regs;
+ char *next;
+};
+
+static atomic_t gtp_count;
+static char gtp_read_ack;
+static char *gtp_rw_buf;
+static char *gtp_rw_bufp;
+static size_t gtp_rw_size;
+
+static int gtp_start;
+
+static int gtp_disconnected_tracing;
+static int gtp_circular;
+
+static DEFINE_SPINLOCK(gtp_frame_lock);
+static int gtp_frame_num;
+static char *gtp_frame;
+static char *gtp_frame_r_start;
+static char *gtp_frame_w_start;
+static char *gtp_frame_w_end;
+static char *gtp_frame_r_cache;
+static int gtp_frame_is_circular;
+static struct gtp_frame_head *gtp_frame_current;
+
+static char *
+gtp_frame_alloc(size_t size)
+{
+ char *ret = NULL;
+
+ if (size > GTP_FRAME_SIZE)
+ return NULL;
+
+ spin_lock(&gtp_frame_lock);
+
+ if (gtp_frame_w_start + size > gtp_frame_w_end) {
+ if (gtp_circular) {
+ gtp_frame_is_circular = 1;
+ gtp_frame_w_start = gtp_frame;
+ gtp_frame_r_start = gtp_frame;
+ } else
+ goto out;
+ }
+
+ if (gtp_frame_is_circular) {
+ /* Release some frame entry to get some place.
+ XXX: When support new frame type, need add new handler
+ to switch. */
+ while (gtp_frame_w_start + size > gtp_frame_r_start) {
+ switch (gtp_frame_r_start[0]) {
+ case 'h':
+ gtp_frame_r_start += GTP_FRAME_HEAD_SIZE;
+ break;
+ case 'r':
+ gtp_frame_r_start += GTP_FRAME_REG_SIZE;
+ break;
+ default:
+ goto out;
+ }
+ }
+ }
+
+ ret = gtp_frame_w_start;
+ gtp_frame_w_start += size;
+
+out:
+ spin_unlock(&gtp_frame_lock);
+ return ret;
+}
+
+static inline char **
+gtp_action_r(struct pt_regs *regs, struct action *ae, char **next)
+{
+ struct gtp_frame_reg *freg;
+ char *tmp;
+
+ tmp = gtp_frame_alloc(GTP_FRAME_REG_SIZE);
+ if (!tmp)
+ return NULL;
+
+ *next = tmp;
+ tmp[0] = 'r';
+ freg = (struct gtp_frame_reg *) (tmp + 1);
+ memcpy(&freg->regs, regs, sizeof(struct pt_regs));
+#if !defined CONFIG_X86_32 && !defined CONFIG_X86_64
+ freg->regs.sp = (unsigned long)&regs->sp;
+#endif /* CONFIG_X86_32 CONFIG_X86_64 */
+ freg->next = NULL;
+
+ return &freg->next;
+}
+
+static int
+gtp_kp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct gtp_entry *tpe = container_of(p, struct gtp_entry, kp);
+ struct gtp_frame_head *head;
+ char *tmp;
+ struct action *ae;
+ char **next;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_kp_pre_handler: tracepoint %d\n",
+ (int)tpe->num);
+#endif
+
+ /* Get the head. */
+ tmp = gtp_frame_alloc(GTP_FRAME_HEAD_SIZE);
+ if (!tmp)
+ goto no_memory;
+ tmp[0] = 'h';
+ head = (struct gtp_frame_head *) (tmp + 1);
+ /* Get a new frame num from gtp_frame_num. */
+ spin_lock(&gtp_frame_lock);
+ if (gtp_frame_num < 0)
+ gtp_frame_num = head->frame_num = 0;
+ else
+ head->frame_num = gtp_frame_num++;
+ spin_unlock(&gtp_frame_lock);
+ head->trace_num = tpe->num;
+ head->next = NULL;
+ next = &head->next;
+
+ /* Handle actions. */
+ for (ae = tpe->action_list; ae; ae = ae->next) {
+ switch (ae->type) {
+ case 'r':
+ next = gtp_action_r(regs, ae, next);
+ if (!next)
+ goto no_memory;
+ break;
+ }
+ }
+
+ return 0;
+
+no_memory:
+ printk(KERN_WARNING "gtp_kp_pre_handler: tracepoint %d no memory.\n",
+ (int)tpe->num);
+ return 0;
+}
+
+static struct action *
+gtp_action_alloc(char type)
+{
+ struct action *ret;
+
+ ret = kmalloc(sizeof(struct action), GFP_KERNEL);
+ if (!ret)
+ goto out;
+
+ memset(ret, '\0', sizeof(struct action));
+ ret->type = type;
+
+out:
+ return ret;
+}
+
+static void
+gtp_action_release(struct action *ae)
+{
+ struct action *ae2;
+
+ while (ae) {
+ ae2 = ae;
+ ae = ae->next;
+ /* Release ae2. */
+ kfree(ae2);
+ }
+}
+
+static struct gtp_entry *
+gtp_list_add(ULONGEST num, ULONGEST addr)
+{
+ struct gtp_entry *ret = kmalloc(sizeof(struct gtp_entry),
+ GFP_KERNEL);
+
+ if (!ret)
+ goto out;
+ memset(ret, '\0', sizeof(struct gtp_entry));
+ ret->num = num;
+ ret->addr = addr;
+ ret->kp.addr = (kprobe_opcode_t *) (CORE_ADDR)addr;
+ ret->kp.pre_handler = gtp_kp_pre_handler;
+
+ /* Add to gtp_list. */
+ if (!gtp_list) {
+ gtp_list = ret;
+ gtp_list_tail = ret;
+ } else {
+ gtp_list_tail->next = ret;
+ gtp_list_tail = ret;
+ }
+
+out:
+ return ret;
+}
+
+static struct gtp_entry *
+gtp_list_find(ULONGEST num, ULONGEST addr)
+{
+ struct gtp_entry *tpe;
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->num == num && tpe->addr == addr)
+ return tpe;
+ }
+
+ return NULL;
+}
+
+static void
+gtp_list_release(void)
+{
+ struct gtp_entry *tpe, *tpe2;
+
+ tpe = gtp_list;
+ while (tpe) {
+ tpe2 = tpe;
+ tpe = tpe->next;
+ /* Release tpe2. */
+ gtp_action_release(tpe2->action_list);
+ kfree(tpe2);
+ }
+ gtp_list = NULL;
+ gtp_list_tail = NULL;
+}
+
+static void
+gtp_frame_reset(void)
+{
+ gtp_frame_num = 0;
+ gtp_frame_r_start = gtp_frame;
+ gtp_frame_w_start = gtp_frame;
+ gtp_frame_w_end = gtp_frame + GTP_FRAME_SIZE;
+ gtp_frame_is_circular = 0;
+ gtp_frame_r_cache = NULL;
+ gtp_frame_current = NULL;
+}
+
+static int
+hex2int(char hex, int *i)
+{
+ if ((hex >= '0') && (hex <= '9')) {
+ *i = hex - '0';
+ return 1;
+ }
+ if ((hex >= 'a') && (hex <= 'f')) {
+ *i = hex - 'a' + 10;
+ return 1;
+ }
+ if ((hex >= 'A') && (hex <= 'F')) {
+ *i = hex - 'A' + 10;
+ return 1;
+ }
+
+ return 0;
+}
+
+static char *
+hex2ulongest(char *pkg, ULONGEST *u64)
+{
+ int i;
+
+ *u64 = 0;
+ while (hex2int(pkg[0], &i)) {
+ pkg++;
+ *u64 = (*u64) << 4;
+ *u64 |= i & 0xf;
+ }
+
+ return pkg;
+}
+
+static int
+gtp_gdbrsp_qtinit(void)
+{
+ if (gtp_start)
+ return -EBUSY;
+
+ gtp_list_release();
+
+ if (gtp_frame)
+ gtp_frame_reset();
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtdp(char *pkg)
+{
+ int addnew = 1;
+ ULONGEST num, addr;
+ struct gtp_entry *tpe;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qtdp: %s\n", pkg);
+#endif
+
+ if (gtp_start)
+ return -EBUSY;
+
+ if (pkg[0] == '-') {
+ pkg++;
+ addnew = 0;
+ }
+
+ /* Get num and addr. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &num);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &addr);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+
+ tpe = gtp_list_find(num, addr);
+ if (addnew) {
+ if (tpe)
+ return -EINVAL;
+ if (pkg[0] == 'D')
+ return 0;
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+
+ tpe = gtp_list_add(num, addr);
+ if (tpe == NULL)
+ return -ENOMEM;
+
+ /* Get step and pass. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &tpe->step);
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &tpe->pass);
+ if (tpe->pass == 0)
+ tpe->nopass = 1;
+ } else if (tpe) {
+ /* Add action to tpe. */
+ int step_action = 0;
+
+ if (pkg[0] == 'S') {
+ pkg++;
+ step_action = 1;
+ /* XXX: Still not support step. */
+ return 1;
+ }
+ while (pkg[0]) {
+ struct action *ae;
+
+ switch (pkg[0]) {
+ case 'R':
+ /* XXX: reg_mask is ignore. */
+ ae = gtp_action_alloc(pkg[0]);
+ if (!ae)
+ return -ENOMEM;
+ pkg++;
+ ae->type = 'r';
+ pkg = hex2ulongest(pkg, &ae->u.reg_mask);
+ break;
+ default:
+ /* XXX: Not support. */
+ return 1;
+ }
+
+ if (ae) {
+ /* Add ae to tpe. */
+ if (!tpe->action_list) {
+ tpe->action_list = ae;
+ tpe->action_list_tail = ae;
+ } else {
+ tpe->action_list_tail->next = ae;
+ tpe->action_list_tail = ae;
+ }
+ }
+ }
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtdisconnected(char *pkg)
+{
+ ULONGEST setting;
+
+ if (gtp_start)
+ return -EBUSY;
+ if (pkg[0] == '\0')
+ return -EINVAL;
+
+ hex2ulongest(pkg, &setting);
+ gtp_disconnected_tracing = (int) setting;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtbuffer(char *pkg)
+{
+ if (strncmp("circular:", pkg, 9) == 0) {
+ ULONGEST setting;
+
+ pkg += 9;
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ hex2ulongest(pkg, &setting);
+ gtp_circular = (int)setting;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct gtp_frame_head *
+gtp_frame_head_find(int num)
+{
+ struct gtp_frame_head *ret = NULL;
+ char *tmp;
+
+ if (gtp_frame_r_cache) {
+ tmp = gtp_frame_r_cache;
+
+ while (tmp < gtp_frame_w_start) {
+ switch (tmp[0]) {
+ case 'h':
+ ret = (struct gtp_frame_head *)(tmp + 1);
+ goto cache_check;
+ break;
+ case 'r':
+ tmp += GTP_FRAME_REG_SIZE;
+ break;
+ default:
+ goto cache_check;
+ break;
+ }
+ }
+
+cache_check:
+ if (ret && ret->frame_num == num)
+ goto out;
+ }
+
+ tmp = gtp_frame_r_start;
+ while (tmp < gtp_frame_w_start) {
+ switch (tmp[0]) {
+ case 'h':
+ ret = (struct gtp_frame_head *) (tmp + 1);
+ if (ret->frame_num == num)
+ goto out;
+ ret = NULL;
+ tmp += GTP_FRAME_HEAD_SIZE;
+ break;
+ case 'r':
+ tmp += GTP_FRAME_REG_SIZE;
+ break;
+ default:
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int
+gtp_gdbrsp_qtframe(char *pkg)
+{
+ if (gtp_start)
+ return -EBUSY;
+
+ if (strncmp(pkg, "pc:", 3) == 0) /* XXX */
+ return 1;
+ else if (strncmp(pkg, "tdp:", 4) == 0) /* XXX */
+ return 1;
+ else if (strncmp(pkg, "range:", 6) == 0) /* XXX */
+ return 1;
+ else if (strncmp(pkg, "outside:", 8) == 0) /* XXX */
+ return 1;
+ else {
+ ULONGEST num;
+ struct gtp_frame_head *ret;
+
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ hex2ulongest(pkg, &num);
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_qtframe: %d\n", (int) num);
+#endif
+ if (((int) num) < 0) {
+ /* Return to current. */
+ gtp_frame_current = NULL;
+
+ return 0;
+ }
+ ret = gtp_frame_head_find((int) num);
+ if (ret) {
+ gtp_frame_current = ret;
+ gtp_frame_r_cache = (char *)ret;
+ gtp_frame_r_cache += sizeof(struct gtp_frame_head);
+ sprintf(gtp_rw_bufp, "F%xT%x",
+ gtp_frame_current->frame_num,
+ (unsigned int)gtp_frame_current->trace_num);
+ gtp_rw_size += strlen(gtp_rw_bufp);
+ gtp_rw_bufp += strlen(gtp_rw_bufp);
+ } else {
+ strcpy(gtp_rw_bufp, "F-1");
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+ }
+ }
+
+ return 1;
+}
+
+static int
+gtp_gdbrsp_qtstop(void)
+{
+ struct gtp_entry *tpe;
+
+ if (!gtp_start)
+ return -EBUSY;
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->kpreg) {
+ unregister_kprobe(&tpe->kp);
+ tpe->kpreg = 0;
+ }
+ }
+
+ gtp_start = 0;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_qtstart(void)
+{
+ struct gtp_entry *tpe;
+
+ if (gtp_start)
+ return -EBUSY;
+
+ if (!gtp_frame) {
+ gtp_frame = vmalloc(GTP_FRAME_SIZE);
+ if (!gtp_frame)
+ return -ENOMEM;
+
+ gtp_frame_reset();
+ }
+
+ for (tpe = gtp_list; tpe; tpe = tpe->next) {
+ if (tpe->action_list) {
+ int ret = register_kprobe(&tpe->kp);
+ if (ret < 0) {
+ gtp_gdbrsp_qtstop();
+ return ret;
+ }
+ tpe->kpreg = 1;
+ }
+ }
+
+ gtp_start = 1;
+
+ return 0;
+}
+
+static int
+gtp_gdbrsp_QT(char *pkg)
+{
+ int ret = 1;
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_QT: %s\n", pkg);
+#endif
+
+ if (strcmp("init", pkg) == 0)
+ ret = gtp_gdbrsp_qtinit();
+ else if (strncmp("DP:", pkg, 3) == 0)
+ ret = gtp_gdbrsp_qtdp(pkg + 3);
+ else if (strncmp("Disconnected:", pkg, 13) == 0)
+ ret = gtp_gdbrsp_qtdisconnected(pkg + 13);
+ else if (strncmp("Buffer:", pkg, 7) == 0)
+ ret = gtp_gdbrsp_qtbuffer(pkg + 7);
+ else if (strncmp("Frame:", pkg, 6) == 0)
+ ret = gtp_gdbrsp_qtframe(pkg + 6);
+ else if (strcmp("Start", pkg) == 0)
+ ret = gtp_gdbrsp_qtstart();
+ else if (strcmp("Stop", pkg) == 0)
+ ret = gtp_gdbrsp_qtstop();
+
+ return ret;
+}
+
+static unsigned char gtp_m_buffer[0xffff];
+
+static int
+gtp_gdbrsp_m(char *pkg)
+{
+ int i;
+ ULONGEST addr, len;
+
+ /* Get add and len. */
+ if (pkg[0] == '\0')
+ return -EINVAL;
+ pkg = hex2ulongest(pkg, &addr);
+ if (pkg[0] != ',')
+ return -EINVAL;
+ pkg++;
+ pkg = hex2ulongest(pkg, &len);
+ if (len == 0)
+ return -EINVAL;
+ len &= 0xffff;
+ len = (ULONGEST) min((int)((GTP_RW_MAX - 4 - gtp_rw_size) / 2),
+ (int)len);
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_gdbrsp_m: addr = 0x%lx len = %d\n",
+ (unsigned long) addr, (int) len);
+#endif
+
+ if (probe_kernel_read(gtp_m_buffer, (void *)(CORE_ADDR)addr,
+ (size_t)len))
+ return -EFAULT;
+
+ for (i = 0; i < (int)len; i++) {
+ sprintf(gtp_rw_bufp, "%02x", gtp_m_buffer[i]);
+ gtp_rw_bufp += 2;
+ gtp_rw_size += 2;
+ }
+
+ return 1;
+}
+
+static int
+gtp_gdbrsp_g(void)
+{
+ char *next;
+ struct gtp_frame_reg *fr = NULL;
+
+ if (GTP_RW_MAX - 4 - gtp_rw_size < GTP_REG_ASCII_SIZE)
+ return -E2BIG;
+
+ if (gtp_start || !gtp_frame_current)
+ goto check;
+
+ /* Get the fr. */
+ next = gtp_frame_current->next;
+ while (next) {
+ switch (next[0]) {
+ case 'r':
+ fr = (struct gtp_frame_reg *) (next + 1);
+ goto check;
+ break;
+ default:
+ next = NULL;
+ break;
+ }
+ }
+check:
+ if (fr)
+ gtp_regs2ascii(&fr->regs, gtp_rw_bufp);
+ else
+ memset(gtp_rw_bufp, '0', GTP_REG_ASCII_SIZE);
+ gtp_rw_bufp += GTP_REG_ASCII_SIZE;
+ gtp_rw_size += GTP_REG_ASCII_SIZE;
+ return 1;
+}
+
+static int
+gtp_open(struct inode *inode, struct file *file)
+{
+ if (atomic_inc_return(&gtp_count) > 1) {
+ atomic_dec(&gtp_count);
+ return -EBUSY;
+ }
+
+ gtp_read_ack = 0;
+ gtp_rw_buf = vmalloc(GTP_RW_MAX);
+ if (!gtp_rw_buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+gtp_release(struct inode *inode, struct file *file)
+{
+ vfree(gtp_rw_buf);
+
+ /* XXX: not handle gtp_disconnected_tracing. */
+ gtp_gdbrsp_qtstop();
+ gtp_gdbrsp_qtinit();
+ vfree(gtp_frame);
+ gtp_frame = NULL;
+
+ atomic_dec(&gtp_count);
+
+ return 0;
+}
+
+static long
+gtp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_ioctl: %x\n", cmd);
+#endif
+ /* This function will make GDB happy. */
+
+ return 0;
+}
+
+static ssize_t
+gtp_write(struct file *file, const char __user *buf, size_t size,
+ loff_t *ppos)
+{
+ char *rsppkg = NULL;
+ int i, ret;
+ unsigned char csum = 0;
+
+ size = min_t(size_t, size, GTP_RW_MAX);
+ if (copy_from_user(gtp_rw_buf, buf, size))
+ return -EFAULT;
+
+ if (gtp_rw_buf[0] == '+' || gtp_rw_buf[0] == '-'
+ || gtp_rw_buf[0] == '\3')
+ goto out;
+
+ /* Check format and crc and get the rsppkg. */
+ for (i = 0; i < size - 2; i++) {
+ if (rsppkg == NULL) {
+ if (gtp_rw_buf[i] == '$')
+ rsppkg = gtp_rw_buf + i + 1;
+ } else {
+ if (gtp_rw_buf[i] == '#')
+ break;
+ else
+ csum += gtp_rw_buf[i];
+ }
+ }
+ if (rsppkg && gtp_rw_buf[i] == '#') {
+ /* Format is OK. Check crc. */
+ unsigned char c1, c2;
+
+ gtp_rw_buf[i] = '\0';
+
+ c1 = gtp_rw_buf[i+1];
+ c2 = gtp_rw_buf[i+2];
+ if (csum == (c1 << 4) + c2) {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: crc error\n");
+#endif
+ gtp_read_ack = '-';
+ goto out;
+ }
+ } else {
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: format error\n");
+#endif
+ gtp_read_ack = '-';
+ goto out;
+ }
+ gtp_read_ack = '+';
+
+#ifdef GTP_DEBUG
+ printk(GTP_DEBUG "gtp_write: %s\n", rsppkg);
+#endif
+
+ /* Handle rsppkg and put return to gtp_rw_buf. */
+ gtp_rw_buf[0] = '$';
+ gtp_rw_bufp = gtp_rw_buf + 1;
+ gtp_rw_size = 0;
+ ret = 1;
+ switch (rsppkg[0]) {
+ case '?':
+ strcpy(gtp_rw_bufp, "S05");
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+ break;
+ case 'g':
+ ret = gtp_gdbrsp_g();
+ break;
+ case 'm':
+ ret = gtp_gdbrsp_m(rsppkg + 1);
+ break;
+ case 'Q':
+ if (rsppkg[1] == 'T')
+ ret = gtp_gdbrsp_QT(rsppkg + 2);
+ break;
+ }
+ if (ret == 0) {
+ strcpy(gtp_rw_bufp, "OK");
+ gtp_rw_bufp += 2;
+ gtp_rw_size += 2;
+ } else if (ret < 0) {
+ sprintf(gtp_rw_bufp, "E%02x", -ret);
+ gtp_rw_bufp += 3;
+ gtp_rw_size += 3;
+ }
+
+ gtp_rw_bufp[0] = '#';
+ csum = 0;
+ for (i = 1; i < gtp_rw_size + 1; i++)
+ csum += gtp_rw_buf[i];
+ gtp_rw_bufp[1] = TOHEX(csum >> 4);
+ gtp_rw_bufp[2] = TOHEX(csum & 0x0f);
+ gtp_rw_bufp = gtp_rw_buf;
+ gtp_rw_size += 4;
+
+out:
+ return size;
+}
+
+static ssize_t
+gtp_read(struct file *file, char __user *buf, size_t size,
+ loff_t *ppos)
+{
+ if (gtp_read_ack) {
+ int err = put_user(gtp_read_ack, buf);
+ if (err)
+ return -err;
+ gtp_read_ack = 0;
+ return 1;
+ }
+
+ size = min(gtp_rw_size, size);
+
+ if (copy_to_user(buf, gtp_rw_bufp, size))
+ return -EFAULT;
+ gtp_rw_bufp += size;
+ gtp_rw_size -= size;
+
+ return size;
+}
+
+static const struct file_operations gtp_operations = {
+ .owner = THIS_MODULE,
+ .open = gtp_open,
+ .release = gtp_release,
+ .unlocked_ioctl = gtp_ioctl,
+ .compat_ioctl = gtp_ioctl,
+ .read = gtp_read,
+ .write = gtp_write,
+};
+
+struct dentry *gtp_dir;
+
+static int __init gtp_init(void)
+{
+ atomic_set(&gtp_count, 0);
+ gtp_read_ack = 0;
+ gtp_rw_buf = NULL;
+ gtp_rw_bufp = NULL;
+ gtp_rw_size = 0;
+ gtp_start = 0;
+ gtp_disconnected_tracing = 0;
+ gtp_circular = 0;
+ gtp_frame_num = 0;
+ gtp_frame = NULL;
+ gtp_frame_r_start = NULL;
+ gtp_frame_w_start = NULL;
+ gtp_frame_w_end = NULL;
+ gtp_frame_r_cache = NULL;
+ gtp_frame_is_circular = 0;
+ gtp_frame_current = NULL;
+
+ gtp_dir = debugfs_create_file("gtp", S_IFIFO | S_IRUSR | S_IWUSR, NULL,
+ NULL, &gtp_operations);
+ if (gtp_dir == NULL || gtp_dir == ERR_PTR(-ENODEV))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __exit gtp_exit(void)
+{
+ if (gtp_dir)
+ debugfs_remove_recursive(gtp_dir);
+}
+
+module_init(gtp_init)
+module_exit(gtp_exit)
+
+MODULE_AUTHOR("Hui Zhu <teawater@xxxxxxxxx>");
+MODULE_LICENSE("GPL");
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1289,6 +1289,14 @@ config ASYNC_RAID6_TEST
If unsure, say N.
+config GTP
+ tristate "GDB tracepoint support"
+ depends on X86 || ARM || MIPS
+ select KPROBES
+ select DEBUG_FS
+ ---help---
+ Supply GDB tracepoint interface in /sys/kernel/debug/gtp.
+
source "samples/Kconfig"
source "lib/Kconfig.kgdb"
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/