/*
 * Copyright (C) 2000-2003 ASANO Masahiro
 */

#include "def.h"

#define __KERNEL__
#include <linux/sched.h>
#include <linux/file.h>

#include "stacksize.h"
#include "flags_sched.h"
#include "flags_signal.h"

extern void prhead_vm_area_struct();
extern addr_t print_vm_area_struct();

int saved_sw_task;

addr_t
print_files_struct(addr)
	addr_t addr;
{
	int i, cnt;
	struct files_struct files;
	addr_t fds[256];
	fd_set cfd;

	mprintf("   "SPTR" CNT   FDS FDSET  NEXT "SPTR" "SPTR" "SPTR"\n",
		"FILES", "FD", "CLOSEXEC", "OPENFDS");
	if (addr == 0) {
		mprintf("   " SPTR "\n", "-");
		return 0;
	}

	mprintf("   " FPTR " ", addr);
	memread(addr, sizeof(files), &files, "files_struct");
	mprintf("%3x %5d %5d %5d", ATOMIC_READ(files.count), files.max_fds, files.max_fdset, files.next_fd);
	mprintf(" " FPTR " " FPTR " " FPTR "\n", files.fd, files.close_on_exec, files.open_fds);
	if ((addr_t)files.fd == addr + OFFSET(struct files_struct, fd_array))
		memcpy(fds, files.fd_array, sizeof(files.fd_array));
	else
		memread((addr_t)files.fd, sizeof(fds), &fds, "file");
	if ((addr_t)files.close_on_exec == addr + OFFSET(struct files_struct, close_on_exec_init))
		cfd = files.close_on_exec_init;
	else
		memread((addr_t)files.close_on_exec, sizeof(fd_set), &cfd, "close_on_exec");

	cnt = 0;
	for (i = 0; i < LENGTHOF(fds) && i < files.max_fds; i++) {
		if (fds[i]) {
			if (cnt * (sizeof(addr_t) + 3) > 30) {
				cnt = 0;
				mprintf("\n");
			}
			if (cfd.fds_bits[__FDELT(i)] & __FDMASK(i)) {
				mprintf("%5d*" FPTR, i, (addr_t)fds[i]);
			} else {
				mprintf("%5d:" FPTR, i, (addr_t)fds[i]);
			}
			cnt++;
		}
	}
	mprintf("\n");
	return 0;
}

void
prhead_task(sw)
	int sw;
{
	const char *p;
 retry:
	switch (sw) {
	case 0:
		p = "MM";	break;
	case 1:
		p = "TTY";	break;
	case 2:
		p = "FS";	break;
	case 3:
		p = "FILES";	break;
	case 4:
		p = "USER";	break;
	case 5:
		p = "SIGHAND";	break;
	case 6:
		p = "NAMESPAC";	break;
	default:
		sw = saved_sw_task;
		goto retry;
	}
	saved_sw_task = sw;
	mprintf(SPTR" S   PID  SESS   UID  EUID "SPTR" NAME             FLAGS\n",
		"ADDR", p);
}

addr_t
print_task(addr, full)
	addr_t addr;
	int full;
{
	struct task_struct ts;
	char c;

	memread(addr, sizeof(ts), &ts, "task_struct");
	mprintf(FPTR " ", addr);

	switch (ts.state) {
	case TASK_RUNNING:		c = 'R';	break;
	case TASK_INTERRUPTIBLE:	c = 'I';	break;
	case TASK_UNINTERRUPTIBLE:	c = 'U';	break;
	case TASK_ZOMBIE:		c = 'Z';	break;
	case TASK_STOPPED:		c = 'T';	break;
#ifdef TASK_SWAPPING
	case TASK_SWAPPING:		c = 'S';	break;
#endif /*TASK_SWAPPING*/
	default:			c = '?';	break;
	}
	mprintf("%c %5d %5d %5d %5d ", c, ts.pid, ts.session, ts.uid, ts.euid);
	switch (saved_sw_task) {
	default:
		mprintf(FPTR, ts.mm);	break;
	case 1:
		mprintf(FPTR, ts.tty);	break;
	case 2:
		mprintf(FPTR, ts.fs);	break;
	case 3:
		mprintf(FPTR, ts.files);break;
	case 4:
		mprintf(FPTR, ts.user);	break;
	case 5:
		mprintf(FPTR, ts.sighand);	break;
	case 6:
		mprintf(FPTR, ts.namespace);	break;
	}
	mprintf(" %-16s", ts.comm);
	mprintbit(tsflags, ts.flags);
	mprintf("\n");

	if (!full)
		goto out;

	mprintf("\n");
	print_files_struct(ts.files);

	mprintf("\tthread_info:" FPTR "\n", ts.thread_info);
	mprintf("\tlock_depth: %d\n",       ts.lock_depth);
	mprintf("\tprio:       %x\n",       ts.prio);
	mprintf("\tmm:         " FPTR "  (mm_struct)\n",   ts.mm);
	mprintf("\tbinfmt:     " FPTR "  (binfmt)\n",      ts.binfmt);
	mprintf("\tparent:     " FPTR "  (task_struct)\n", ts.parent);
	mprintf("\ttimes:      %lx %lx - %lx %lx\n",
		ts.utime, ts.stime, ts.cutime, ts.cstime);
	mprintf("\tstart_time: %llx\n", ts.start_time);
	mprintf("\tcap:        %x\n", ts.cap_effective);
	mprintf("\tuser:       " FPTR "  (user_struct)\n", ts.user);
	mprintf("\ttty:        " FPTR "  (tty_struct)\n",  ts.tty);
	mprintf("\tfs:         " FPTR "  (fs_struct)\n",   ts.fs);
	mprintf("\tnamespace:  " FPTR "  (namespace)\n",   ts.namespace);
	mprintf("\tthread:     %lx\n", addr + OFFSET(struct task_struct, thread));
	mprintf("\tsignal:     " FPTR "  (signal_struct)\n", ts.signal);
	mprintf("\tsighand:    " FPTR "  (sighand_struct)\n", ts.sighand);
	mprintf("\tblocked:    %08lx\n", ts.blocked.sig[0]);
	mprintf("\tpending:    %08lx\n", ts.pending.signal.sig[0]);

#if defined(ARCH_i386)
	mprintf("\teip:%lx  esp:%lx  esp0:%lx\n", ts.thread.eip, ts.thread.esp, ts.thread.esp0);
#endif /*ARCH_i386*/
#if defined(ARCH_ia64)
	mprintf("\tksp:%lx  flags:%lx\n", ts.thread.ksp, ts.thread.flags);
	mprintf("\tmap_base:%lx  task_size:%lx\n", ts.thread.map_base, ts.thread.task_size);
#endif /*ARCH_ia64*/
#if defined(ARCH_ppc)
	mprintf("\tksp:%lx\n", ts.thread.ksp);
#endif /*ARCH_ppc*/
	mprintf("\n");

out:
	return (addr_t)ts.tasks.next - OFFSET(struct task_struct, tasks);
}

int
getinfo_fromtask(addr, ip, sp, stack)
	addr_t addr;
	addr_t *ip, *sp, *stack;
{
	struct task_struct tsk;

	memread(addr, sizeof(tsk), &tsk, "task_struct");

#if defined(ARCH_i386)
	if (ip)
		*ip = tsk.thread.eip;
	if (sp)
		*sp = tsk.thread.esp;
#else
#if defined(ARCH_ia64) || defined(ARCH_ppc)
	if (ip)
		*ip = NULL;
	if (sp)
		*sp = tsk.thread.ksp;
#else
	/* others */
	if (ip)
		*ip = NULL;
	if (sp)
		*sp = NULL;
#endif
#endif
	if (stack)
		*stack = (addr_t)tsk.thread_info;
	return tsk.pid;
}

addr_t
print_fs_struct(addr)
	addr_t addr;
{
	struct fs_struct fs;

	memread(addr, sizeof(fs), &fs, "fs_struct");
	mprintf("count:      %x\n", ATOMIC_READ(fs.count));
	mprintf("umask:      0%o\n", fs.umask);
	mprintf("root:       " FPTR "\n", fs.root);
	mprintf("pwd:        " FPTR "\n", fs.pwd);
	mprintf("altroot:    " FPTR "\n", fs.altroot);
	mprintf("rootmnt:    " FPTR "\n", fs.rootmnt);
	mprintf("pwdmnt:     " FPTR "\n", fs.pwdmnt);
	mprintf("altrootmnt: " FPTR "\n", fs.altrootmnt);
	return 0;
}

void
prhead_mm_struct()
{
	mprintf(SPTR" "SPTR" "SPTR" USR CNT MAP "SPTR" "SPTR" TOTAL_VM\n",
		"ADDR", "MMAP", "PGD", "STARTBRK", "BRK");
}

addr_t
print_mm_struct(addr, vflag)
	addr_t addr;
	int vflag;
{
	struct mm_struct mm;

	memread(addr, sizeof(mm), &mm, "mm_struct");
	if (vflag & 2) {
		mprintf(FPTR " " FPTR " " FPTR " ",
			addr, mm.mmap, mm.pgd);
		mprintf("%3x %3x %3x ",
			ATOMIC_READ(mm.mm_users), ATOMIC_READ(mm.mm_count),
			mm.map_count);
		mprintf(FPTR " " FPTR " %8lx\n",
			mm.start_brk,  mm.brk, mm.total_vm);
	} else {
		mprintf("addr:       " FPTR "\n", addr);
		mprintf("mmap:       " FPTR "\n", mm.mmap);
		mprintf("mmap_cache: " FPTR "\n", mm.mmap_cache);
		mprintf("pgd:        " FPTR "\n", mm.pgd);
		mprintf("mm_users:   %x\n", ATOMIC_READ(mm.mm_users));
		mprintf("mm_count:   %x\n", ATOMIC_READ(mm.mm_count));
		mprintf("map_count:  %x\n", mm.map_count);
		mprintf("mmlist:     " FPTR " " FPTR "\n",
			mm.mmlist.next, mm.mmlist.prev);
		mprintf("code:       " FPTR " " FPTR "\n",
			mm.start_code, mm.end_code);
		mprintf("data:       " FPTR " " FPTR "\n",
			mm.start_data, mm.end_data);
		mprintf("brk:        " FPTR " " FPTR "\n",
			mm.start_brk, mm.brk);
		mprintf("stack:      " SPTR " " FPTR "\n", "", mm.start_stack);
		mprintf("arg:        " FPTR " " FPTR "\n",
			mm.arg_start, mm.arg_end);
		mprintf("env:        " FPTR " " FPTR "\n",
			mm.env_start, mm.env_end);
		mprintf("rss:        %lx\n", mm.rss);
		mprintf("total_vm    %lx\n", mm.total_vm);
		mprintf("locked_vm:  %lx\n", mm.locked_vm);
		mprintf("def_flags:  %lx\n", mm.def_flags);
		mprintf("cpu_vm_mask:%lx\n", mm.cpu_vm_mask);
	}

	if (vflag & 1) {
		prhead_vm_area_struct();
		addr = (addr_t)mm.mmap;
		while (addr) {
			addr = print_vm_area_struct(addr, 0);
		}
	}
	return (addr_t)mm.mmlist.next - OFFSET(struct mm_struct, mmlist);
}

int
search_task(addr)
	addr_t addr;
{
	struct task_struct tsk;
	addr_t taddr;
	extern addr_t init_task_addr;

	if (init_task_addr == 0)
		return 0;
	taddr = init_task_addr;
	do {
		memread(taddr, sizeof(tsk), &tsk, "task_struct");
		if (addr >= taddr && addr < taddr + sizeof(tsk)) {
			mprintf("task_struct pid %d offset %lx\n",
				tsk.pid, addr - taddr);
			return 1;
		}
		if (tsk.thread_info && addr >= (addr_t)tsk.thread_info && addr < (addr_t)tsk.thread_info + STACKSIZE) {
			mprintf("thread_info pid %d offset %lx\n",
				tsk.pid, addr - (addr_t)tsk.thread_info);
			return 1;
		}
		taddr = (addr_t)tsk.tasks.next - OFFSET(struct task_struct, tasks);
	} while (taddr != init_task_addr);

	return 0;	/* not found */
}

addr_t
define_task(addr)
	addr_t addr;
{
	struct task_struct tsk;
	struct mm_struct mm;

	memread(addr, sizeof(tsk), &tsk, "task_struct");
	if (tsk.mm) {
		memread((addr_t)tsk.mm, sizeof(mm), &mm, "mm_struct");
		load_pgd((addr_t)mm.pgd);
		return (addr_t)mm.pgd;
	}
	return 0;
}

addr_t
print_sighand_struct(addr)
	addr_t addr;
{
	int i;
	struct sighand_struct sig;
	struct sigaction *sa;

	memread(addr, sizeof(sig), &sig, "sighand_struct");
	mprintf("count:  %x\n", ATOMIC_READ(sig.count));
	mprintf("##: " SPTR, "HANDLER");
#ifdef _sigaction_has_sa_restorer
	mprintf(" " SPTR, "RESTORER");
#endif /*_sigaction_has_sa_restorer*/
	mprintf("      MASK  FLAGS\n");

	for (i = 0; i < _NSIG; i++) {
		/* WARN: k_sigaction is defined as architectural dependent */
		sa = &sig.action[i].sa;
		mprintf("%2d: ", i + 1);
		switch ((long)sa->sa_handler) {
		case (long)SIG_DFL:
			mprintf(SPTR, "DFL");   break;
		case (long)SIG_IGN:
			mprintf(SPTR, "IGNORE");        break;
		case (long)SIG_ERR:
			mprintf(SPTR, "ERR");   break;
		default:
			mprintf(FPTR, sa->sa_handler);
		}
#ifdef _sigaction_has_sa_restorer
		mprintf(" " FPTR, sa->sa_restorer);
#endif /*_sigaction_has_sa_restorer*/
		mprintf("  %08lx ", sa->sa_mask.sig[0]);
 		mprintbit(saflags, sa->sa_flags);
		mprintf("\n");
	}
	return 0;
}

void
prhead_signal_struct()
{
	mprintf(SPTR" CNT "SPTR"  GX GXT "SPTR" NTF_CNT GSC\n",
		"ADDR", "CURR_TGT", "G_X_TASK");
}

addr_t
print_signal_struct(addr)
	addr_t addr;
{
	struct signal_struct sig;

	memread(addr, sizeof(sig), &sig, "signal_struct");
	mprintf(FPTR " %3x " FPTR " ", addr,
		ATOMIC_READ(sig.count), sig.curr_target);
	mprintf("%3x %3x " FPTR " %7x %3x",
		sig.group_exit, sig.group_exit_code,
		sig.group_exit_task, sig.notify_count, sig.group_stop_count);
	mprintf("\n");
	return 0;
}

void
prhead_user_struct()
{
	mprintf(SPTR " COUNT PROCS FILES  UID(10)\n", "ADDR");
}

addr_t
print_user_struct(addr, follow_list)
	addr_t addr;
	int follow_list;
{
	struct user_struct us;
	addr_t lh_addr = addr;

	if (follow_list) {
		struct list_head lh;
		memread(lh_addr, sizeof(lh), &lh, "user_struct");
		addr = (addr_t)lh.next;
		if (addr == 0 || addr == lh_addr)
			return addr;
		addr -= OFFSET(struct user_struct, uidhash_list);
	}
 next:
	memread(addr, sizeof(us), &us, "user_struct");
	mprintf(FPTR " %5x %5x %5x %7d\n", addr,
		ATOMIC_READ(us.__count), ATOMIC_READ(us.processes),
		ATOMIC_READ(us.files), us.uid);

	addr = (addr_t)us.uidhash_list.next;
	if (follow_list && addr && addr != lh_addr) {
		addr -= OFFSET(struct user_struct, uidhash_list);
		goto next;
	}
	return addr;
}
