/*
 * ps_subr.c -	functions shared by ps and top
 *
 * Copyright (c) 1993 Branko Lankester
 *
 */

#define __KERNEL__
#include <stdio.h>
#include <string.h>
#include <sys/sysmacros.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include "ps.h"
#include "psdata.h"

unsigned long nr_pages;
unsigned long jiffies;
unsigned short *memmap;

char *
status(struct task_struct *task)
{
    static char buf[16];
    char *p = buf;

    if (task->state == TASK_INTERRUPTIBLE
	    && task->counter == task->counter / 2 + task->priority)
	*p++ = 'I';
    else
	*p++ = "RSDZTP" [task->state];
    if (task->mm->rss == 0 && task->state != TASK_ZOMBIE && task->pid != 0)
	*p++ = 'W';
    if (task->priority > PZERO)
	*p++ = '<';
    else if (task->priority < PZERO)
	*p++ = 'N';
    if (task->flags & PF_PTRACED)
	*p++ = ((task->flags & PF_TRACESYS) ? 'X' : 'x');
    if (task->leader)
	*p++ = 's';
    if (TASK_TTY(task) != -1 && TASK_TTY_PGRP(task) == task->pgrp)
	*p++ = '+';
    *p = '\0';
    return(buf);
}


char *
wchan(reg_t ebp, reg_t *stack, int numeric)
{
    reg_t eip;
    int bp;
    static char buf[16], *p;

    bp = (ebp & ~PAGE_MASK) >> 2;
    eip = stack[bp + 1];
    p = find_func(eip);
    if (strcmp(p, "_sleep_on") == 0 ||
		strcmp(p, "_interruptible_sleep_on") == 0)
	return(wchan(stack[bp], stack, numeric));
    
    if (numeric) {
	sprintf(buf, "%x", eip);
	return(buf);
    }
    if (strncmp(p, "_sys_", 4) == 0)
	p += 5;
    while (*p == '_')
	++p;
    return(p);
}

void
get_memmap(void)
{
#ifdef USE_MMAP
    if (memmap == NULL)
	memmap = KPTR(KWORD(k_addr("_mem_map")));
#else
    static unsigned long _mem_map;

    if (memmap == NULL) {
	_mem_map = get_kword(k_addr("_mem_map"));
	memmap = (unsigned short *) xmalloc(nr_pages * sizeof *memmap);
    }
    kmemread(memmap, _mem_map, nr_pages * sizeof *memmap);
#endif
}

void
read_globals(void)
{

    nr_pages = get_kword(k_addr("_high_memory")) / 4096;
    jiffies = get_kword(k_addr("_jiffies"));
}

struct mem_info *
get_mem_info(struct task_struct *task)
{
    int i;
#ifdef USE_MMAP
    unsigned long *pagetbl, *pte, *pagedir;
#else
    unsigned long pagetbl[PAGE_SIZE/4], *pte;
    unsigned long pagedir[0x300];
#endif
    unsigned long ptbl;
    int size=0;
    int tpag = task->mm->end_code / PAGE_SIZE;
    unsigned map_nr;
    static struct mem_info mi;

    memset(&mi, 0, sizeof(struct mem_info));
    if (task->state == TASK_ZOMBIE || task->pid == 0)
	return &mi;
    if (!memmap)
	get_memmap();

#ifdef USE_MMAP
    if (mmap_page(task->tss.cr3) < 0)
	return &mi;
    pagedir = KPTR(task->tss.cr3);
#else
    if (kmemread(pagedir, task->tss.cr3 + (task->mm->start_code >> 20),
	    sizeof pagedir) < 0)
	return &mi;
#endif

    for (i = 0; i < 0x300; ++i) {
	if ((ptbl = pagedir[i]) == 0) {
	    tpag -= 1024;
	    continue;
	}
#ifdef USE_MMAP
	if (mmap_page(ptbl) < 0)
	    break;
	pagetbl = KPTR(ptbl & 0xfffff000);
#else
	if (kmemread(pagetbl, ptbl & 0xfffff000, sizeof pagetbl) < 0)
	    break;
#endif
	for (pte = pagetbl; pte < &pagetbl[1024]; ++pte) {
	    if (*pte != 0) {
		++size;
		if (*pte & PAGE_PRESENT) {
		    map_nr = MAP_NR(*pte);
		    if (map_nr >= nr_pages)
			continue;
		    if (memmap[map_nr] & 0x8000)
			continue;
		    ++mi.rss;
		    if (tpag > 0)
			++mi.trs;
		    else
			++mi.drs;
		    if (i >= 15 && i < 0x2f0) {
			++mi.lrs;
			if (*pte & 0x40)
			    ++mi.dt;
			else
			    --mi.drs;
		    }
		    if (memmap[map_nr] > 1) {
			++mi.share;
			mi.pmem += 1000 / memmap[map_nr];
		    } else
			mi.pmem += 1000;

		    if ((*pte & PAGE_RW) == 0) ++mi.wp;
		    if (*pte & PAGE_DIRTY) ++mi.dirt;
		    if (*pte & PAGE_ACCESSED) ++mi.acc;
		} else
		    ++mi.swap;
	    }
	    --tpag;
	}
    }
    mi.pmem /= nr_pages;
    mi.size = size;
    return &mi;
}
