/* drivers/android/kernel_debugger.c
 *
 * Guts of the kernel debugger.
 * Needs something to actually push commands to it.
 *
 * Copyright (C) 2007-2008 Google, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 *
 */

#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/sysrq.h>
#include <linux/kernel_debugger.h>

#define dprintf(fmt...) (ctxt->printf(ctxt->cookie, fmt))

static void do_ps(struct kdbg_ctxt *ctxt)
{
	struct task_struct *g, *p;
	unsigned state;
	static const char stat_nam[] = "RSDTtZX";

	dprintf("pid   ppid  prio task            pc\n");
	read_lock(&tasklist_lock);
	do_each_thread(g, p) {
		state = p->state ? __ffs(p->state) + 1 : 0;
		dprintf("%5d %5d %4d ", p->pid, p->parent->pid, p->prio);
		dprintf("%-13.13s %c", p->comm,
			state >= sizeof(stat_nam) ? '?' : stat_nam[state]);
		if (state == TASK_RUNNING)
			dprintf(" running\n");
		else
			dprintf(" %08lx\n", thread_saved_pc(p));
	} while_each_thread(g, p);
	read_unlock(&tasklist_lock);
}

int log_buf_copy(char *dest, int idx, int len);
extern int do_syslog(int type, char __user *bug, int count);
static void do_sysrq(struct kdbg_ctxt *ctxt, char rq)
{
	char buf[128];
	int ret;
	int idx = 0;
	do_syslog(5 /* clear */, NULL, 0);
	handle_sysrq(rq);
	while (1) {
		ret = log_buf_copy(buf, idx, sizeof(buf) - 1);
		if (ret <= 0)
			break;
		buf[ret] = 0;
		dprintf("%s", buf);
		idx += ret;
	}
}

static void do_help(struct kdbg_ctxt *ctxt)
{
	dprintf("Kernel Debugger commands:\n");
	dprintf(" ps            Process list\n");
	dprintf(" sysrq         sysrq options\n");
	dprintf(" sysrq <param> Execute sysrq with <param>\n");
}

int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd)
{
	if (!strcmp(cmd, "ps"))
		do_ps(ctxt);
	if (!strcmp(cmd, "sysrq"))
		do_sysrq(ctxt, 'h');
	if (!strncmp(cmd, "sysrq ", 6))
		do_sysrq(ctxt, cmd[6]);
	if (!strcmp(cmd, "help"))
		do_help(ctxt);

	return 0;
}