/*
 * Various logging and debugging hooks related to process
 * management.
 * Copyright (c) 1999 TiVo Inc.
 */


#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/user.h>
#include <linux/elf.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/wait.h>

#if defined(CONFIG_XMON) && defined(CONFIG_TIVO)

#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/ppcops.h>
#include <asm/poll.h>
#include <asm/process.h>

void debug(char *, struct pt_regs *);

#define PLOG_PR_ALL	0x1
#define PLOG_PR_INTR	0x2
#define PLOG_PR_BH	0x4
#define PLOG_PR_RQ	0x8
#define PLOG_PR_SW	0x10
#define PLOG_PR_SC	0x20
#define PLOG_PR_GEN	0x40

// Anyone can open, right?  Ultimately, we should limit this to one guy, but
// this doesn't really matter.
static int plog_open(struct inode * inode, struct file * file)
{
    return 0;
}

// close?  Yea, whatever
static int plog_release(struct inode * inode, struct file * file)
{
    return 0;
}

char *syscallNames[] = {
    "setup",
    "exit",
    "fork",
    "read",
    "write",
    "open",
    "close",
    "waitpid",
    "creat",
    "link",
    "unlink",
    "execve",
    "chdir",
    "time",
    "mknod",
    "chmod",
    "chown",
    "break",
    "stat",
    "lseek",
    "getpid",
    "mount",
    "umount",
    "setuid",
    "getuid",
    "stime",
    "ptrace",
    "alarm",
    "fstat",
    "pause",
    "utime",
    "stty",
    "gtty",
    "access",
    "nice",
    "ftime",
    "sync",
    "kill",
    "rename",
    "mkdir",
    "rmdir",
    "dup",
    "pipe",
    "times",
    "prof",
    "brk",
    "setgid",
    "getgid",
    "signal",
    "geteuid",
    "getegid",
    "acct",
    "phys",
    "lock",
    "ioctl",
    "fcntl",
    "mpx",
    "setpgid",
    "ulimit",
    "olduname",
    "umask",
    "chroot",
    "ustat",
    "dup2",
    "getppid",
    "getpgrp",
    "setsid",
    "sigaction",
    "sgetmask",
    "ssetmask",
    "setreuid",
    "setregid",
    "sigsuspend",
    "sigpending",
    "sethostname",
    "setrlimit",
    "getrlimit",
    "getrusage",
    "gettimeofday",
    "settimeofday",
    "getgroups",
    "setgroups",
    "old_select",
    "symlink",
    "lstat",
    "readlink",
    "uselib",
    "swapon",
    "reboot",
    "old_readdir",
    "mmap",
    "munmap",
    "truncate",
    "ftruncate",
    "fchmod",
    "fchown",
    "getpriority",
    "setpriority",
    "profil",
    "statfs",
    "fstatfs",
    "ioperm",
    "socketcall",
    "syslog",
    "setitimer",
    "getitimer",
    "newstat",
    "newlstat",
    "newfstat",
    "uname",
    "iopl",
    "vhangup",
    "idle",
    "vm86",
    "wait4",
    "swapoff",
    "sysinfo",
    "ipc",
    "fsync",
    "sigreturn",
    "clone",
    "setdomainname",
    "newuname",
    "modify_ldt",
    "adjtimex",
    "mprotect",
    "sigprocmask",
    "create_module",
    "init_module",
    "delete_module",
    "get_kernel_syms",
    "quotactl",
    "getpgid",
    "fchdir",
    "bdflush",
    "sysfs",
    "personality",
    "???",
    "setfsuid",
    "setfsgid",
    "llseek",
    "getdents",
    "newselect",
    "flock",
    "msync",
    "readv",
    "writev",
    "getsid",
    "fdatasync",
    "sysctl",
    "mlock",
    "munlock",
    "mlockall",
    "munlockall",
    "sched_setparam",
    "sched_getparam",
    "sched_setscheduler",
    "sched_getscheduler",
    "sched_yield",
    "sched_get_priority_max",
    "sched_get_priority_min",
    "sched_rr_get_interval",
    "nanosleep",
    "mremap",
    "setresuid",
    "getresuid",
    "query_module",
    "poll",
    "readsectors",
    "writesectors",
};

#define NENTRIES(array) (sizeof(array)/sizeof(array[0]))

/* Event header:
 *
 *  int eventLen;   // length of event
 *  int eventType;  // predefined type
 *  unsigned long timeStamp;    // CPU instruction clock
 *
 *  int pid;        // For user events only.
 */

static int processLog[32768];     // 128K for now.
#define PLOG_LENGTH sizeof(processLog)

#define MAX_EVENT   8192
// A 1 in this mask means that the event should not be logged.
static int ptypeMask[MAX_EVENT >> 5];

// The size of a "safety zome" between the write pointer and the read pointer.
// This zone is to mostly insure that when plog_read() tries to read the buffer
// and re-enables interrupts that the place in the buffer that it starts
// reading from doesn't immediately get overwritten by things getting logged.
#define PLOG_SAFETY_ZONE    1024

#define PLOG_MAX_DATASIZE   100

/* Read/Write pointers are in bytes */
int plog_wp = 0;
int plog_rp = 0;

static wait_queue_head_t plog_wait = __WAIT_QUEUE_HEAD_INITIALIZER(plog_wait);

static long plog_read(struct file * file, char * buf, size_t len, loff_t * off)
{
    int error = -EPERM;
    int eventLen;
    int readable;
    int *s;
    int i;
    int *userBuf;

    // The user buffer *must* be word aligned for efficiency (would hate to
    // slow things down significantly as we are trying to profile them).
    error = -EINVAL;
    if (!buf || len < 0) goto out;
    error = 0;
    if (((int) buf) & 0x3) goto out;
    if (!len) goto out;
    error = verify_area(VERIFY_WRITE,buf,len);
    if (error) goto out;

    userBuf = (int *) buf;

    cli();
    if (plog_wp == PLOG_LENGTH) plog_wp = 0;

    error = -ERESTARTSYS;
    if (plog_wp > plog_rp) {
        readable = plog_wp - plog_rp;
    } else {
        readable = plog_wp + (PLOG_LENGTH - plog_rp);
    }
    // Must have 4K available before we let them read anything.
    while (readable < 4096) {
        if (current->sigpending) {
            sti();
            goto out;
        }
// printk("sleeping on plog_wait\n");
        interruptible_sleep_on(&plog_wait);
        if (plog_wp >= plog_rp) {
            readable = plog_wp - plog_rp;
        } else {
            readable = plog_wp + (PLOG_LENGTH - plog_rp);
        }
    }
    i = 0;
    while (plog_rp != plog_wp && len > 0) {
        eventLen = processLog[plog_rp >> 2];
        if (eventLen == 4) {
            // null event
            plog_rp += 4;
            if (plog_rp == PLOG_LENGTH) plog_rp = 0;
            continue;
        }

        if (len >= eventLen) {
            len -= eventLen;
            s = processLog + (plog_rp >> 2);
            plog_rp += eventLen;
            if (plog_rp == PLOG_LENGTH) plog_rp = 0;
            i += eventLen;

            // I believe that interrupts must be left on for this...
            sti();
            while (eventLen > 0) {
                __put_user(*s, userBuf);
                eventLen -= 4;
                s++;
                userBuf++;
            }
            cli();
        } else break;
    }
    sti();
    error = i;
// printk("Sent out %d/%ld bytes\n", i, len+i);

out:
    return error;
}

static long plog_write(struct file * file, const char * buf, size_t len, loff_t * off)
{
    int error = -EPERM;
    char tempbuf[1024];
    const char *s;
    char *d;

    error = -EINVAL;
    if (!buf || len < 0 || len >= 1023) goto out;
    error = 0;
    if (!len) goto out;
    error = verify_area(VERIFY_READ,buf,len);
    if (error) goto out;

    s = buf;
    d = tempbuf;
    error = len;
    while (len > 0) {
        __get_user(*d, s);
        s++;
        d++;
        len--;
    }
    *d = 0;

    // Parse user's request
    // Request should be in form of "[+-]e1[-e2][,...]"
    s = tempbuf;
    while (*s != 0) {
        int add;
        int start, end;
        int i;

        if (*s == '+') {
            add = 1;
        } else if (*s == '-') {
            add = 0;
        } else {
            error = -EINVAL;
            break;
        }
        s++;
        start = 0;
        while (*s >= '0' && *s <= '9') {
            start = start*10 + (*s - '0');
            s++;
        }
        if (*s == '-') {
            s++;
            end = 0;
            while (*s >= '0' && *s <= '9') {
                end = end*10 + (*s - '0');
                s++;
            }
        } else {
            end = start;
        }
        if (*s != 0 && *s != ',' && *s != '\n') {
            error = -EINVAL;
            break;
        }
        if (*s == ',') s++;
        if (*s == '\n') s++;

        if (start < 0) start = 0;
        if (end >= MAX_EVENT) end = MAX_EVENT - 1;
        if (end >= start) {
            for (i = start; i <= end; i++) {
                if (add) {
                    ptypeMask[i >> 5] &= ~(1 << (i & 0x1f));
                } else {
                    ptypeMask[i >> 5] |= (1 << (i & 0x1f));
                }
            }
        }
    }

out:
    return error;
}

#if 0 
// Untested,  Not clear we need it.
static unsigned int plog_poll(struct file *file, poll_table * wait)
{
    int readable;

// printk("Polling?\n");
    poll_wait(&plog_wait, wait);
    cli();
    if (plog_wp >= plog_rp) {
        readable = plog_wp - plog_rp;
    } else {
        readable = plog_wp + (PLOG_LENGTH - plog_rp);
    }
    sti();
    if (readable >= 4096) return POLLIN | POLLRDNORM;
    return 0;
}
#endif

/* Write to the process log. */
static void _plLogEvent(int type, int dataLength, void *data)
{
    int *d, *s;
    int spaceNeeded;
    unsigned long flags;
    int readable;
    int skipLength;

    // Are we rejecting this type of event?
    if (type >= 0 && type < MAX_EVENT && 
            (ptypeMask[type >> 5] & (1 << (type & 0x1f))) != 0) {
        return;
    }

    /* Data can only be stored in 4 byte increments. */
    dataLength = (dataLength + 3) & ~0x3;

    if (dataLength > PLOG_MAX_DATASIZE) {
        dataLength = PLOG_MAX_DATASIZE;
    }

    /* Allocate space. */
    spaceNeeded = 12 + dataLength;

    /* This code is *not* multithread safe, so we lock it */
    save_flags(flags);
    cli();

    if (plog_wp >= plog_rp) {
        readable = plog_wp - plog_rp;
    } else {
        readable = plog_wp + (PLOG_LENGTH - plog_rp);
    }

    // Compute how much of the buffer we need to auto-consume to leave the
    // SAFETY_ZONE there.
    skipLength = PLOG_SAFETY_ZONE - (PLOG_LENGTH - readable);
    if (PLOG_LENGTH - plog_wp < spaceNeeded) {
        skipLength += PLOG_LENGTH - plog_wp;
    }

    // Advance the read pointer to leave the safety zone large enough.
    while (skipLength > 0) {
        int eventSize = processLog[plog_rp >> 2];

        skipLength -= eventSize;
        plog_rp += eventSize;
        if (plog_rp == PLOG_LENGTH) {
            plog_rp = 0;
        }
    }

    // Advance the write pointer if we cannot fit the event right here.
    if (PLOG_LENGTH - plog_wp < spaceNeeded) {
        while (plog_wp != PLOG_LENGTH) {
            processLog[plog_wp >> 2] = 4;
            plog_wp += 4;
        }
        plog_wp = 0;
    }

    /* Write log entry */
    d = processLog + (plog_wp >> 2);
    plog_wp += spaceNeeded;
    *d++ = spaceNeeded;
    *d++ = type;
    *d++ = mfspr(SPRN_TBLO);
    s = data;
    while (dataLength > 0) {
        *d++ = *s++;
        dataLength -= 4;
    }

    restore_flags(flags);

    // Wake someone up?
    if (readable >= 4096 && waitqueue_active(&plog_wait)) {
        wake_up_interruptible(&plog_wait);
    }
}

asmlinkage long sys_process_log(int type, int data_len, void *data)
{
    int error = -EPERM;
    int buffer[PLOG_MAX_DATASIZE >> 2];
    int *s, *d;
    int copy_len;

    buffer[0] = current->pid;
    error = -EINVAL;
    if (data_len < 0) goto out;
    // We are already using 4 bytes for pid, so we reduce data_len
    // accordingly.
    if (data_len > PLOG_MAX_DATASIZE - 4) data_len = PLOG_MAX_DATASIZE - 4;
    if (data_len != 0) {
        if (((int) data) & 0x3) goto out;
        error = verify_area(VERIFY_READ,data,data_len);
        if (error) goto out;
    }

    s = (int *) data;
    d = buffer + 1;
    copy_len = data_len;
    while (copy_len > 0) {
        __get_user(*d, s);
        s++;
        d++;
        copy_len -= 4;
    }

    _plLogEvent(type, data_len+4, buffer);
    error = 0;

out:
    return error;
}

void _swcpu(struct task_struct *from, struct task_struct *to) 
{
    struct _pl_swe entry;

    entry.frompid = from->pid;
    entry.topid = to->pid;
    _plLogEvent(PLOG_TYPE_SWE, sizeof(entry), &entry);
}

void _swcpu_end(void) 
{
    _plLogEvent(PLOG_TYPE_SWL, 0, NULL);
}

int _sc_enter(int pid, int sc) 
{
    struct _pl_sce entry;

    entry.pid = pid;
    entry.scnum = sc;
    _plLogEvent(PLOG_TYPE_SCE, sizeof(entry), &entry);

    return pid;
}

void _sc_leave(int pid, int rv) 
{
    struct _pl_scl entry;

    entry.pid = pid;
    entry.rv = rv;
    _plLogEvent(PLOG_TYPE_SCL, sizeof(entry), &entry);
}

void rq_add(struct task_struct *tp) 
{
    struct _pl_rq entry;
    extern int ppc_local_irq_count;

    entry.pid = tp->pid;
    entry.fromintr = ppc_local_irq_count ? 1 : 0;
    entry.preempt = current->need_resched ? 1 : 0;
    _plLogEvent(PLOG_TYPE_RQ, sizeof(entry), &entry);
}

int intr_enter(int num) 
{
    _plLogEvent(PLOG_TYPE_INTRE, sizeof(num), &num);
    return num;
}

void
intr_leave(int num) 
{
    _plLogEvent(PLOG_TYPE_INTRL, sizeof(num), &num);
}

int bh_enter(int num) 
{
    _plLogEvent(PLOG_TYPE_BHE, sizeof(num), &num);
    return num;
}

void bh_leave(int num) 
{
    _plLogEvent(PLOG_TYPE_BHL, sizeof(num), &num);
}

void _pl_shared_page_swap(int virtPage)
{
    _plLogEvent(PLOG_TYPE_SPS, sizeof(virtPage), &virtPage);
}

void _pl_filemap_page_swap(int pid, int virtPage)
{
    int buffer[2];

    buffer[0] = pid;
    buffer[1] = virtPage;
    _plLogEvent(PLOG_TYPE_FMS, sizeof(buffer), buffer);
}

void _pl_generic_page_swap(int pid, int virtPage)
{
    int buffer[2];

    buffer[0] = pid;
    buffer[1] = virtPage;
    _plLogEvent(PLOG_TYPE_GPS, sizeof(buffer), buffer);
}

void _pl_reswap(int pid, int virtPage)
{
    int buffer[2];

    buffer[0] = pid;
    buffer[1] = virtPage;
    _plLogEvent(PLOG_TYPE_RESWAP, sizeof(buffer), buffer);
}

void _pl_pageout(int pid, int virtPage)
{
    int buffer[2];

    buffer[0] = pid;
    buffer[1] = virtPage;
    _plLogEvent(PLOG_TYPE_PAGEOUT, sizeof(buffer), buffer);
}

void _pl_ninesread(void)
{
    int pid = current->pid;
    _plLogEvent(PLOG_TYPE_NINES, sizeof(pid), &pid);
}

void _pl_generic_event(int event, int somedata)
{
    int buffer[2];
    if (current == NULL) {
        buffer[0] = -1;
    } else {
        buffer[0] = current->pid;
    }
    buffer[1] = somedata;
    _plLogEvent(event, sizeof(buffer), buffer);
}

void _pl_generic_event2(int event, int data1, int data2)
{
    int buffer[3];
    if (current == NULL) {
        buffer[0] = -1;
    } else {
        buffer[0] = current->pid;
    }
    buffer[1] = data1;
    buffer[2] = data2;
    _plLogEvent(event, sizeof(buffer), buffer);
}

static int eventWanted(int *event, int flags, int pid)
{
    switch (event[1]) {
      case PLOG_TYPE_SWE:
        {
            struct _pl_swe *logEntry;

            logEntry = (struct _pl_swe *) (event + 3);
            if (pid && (logEntry->frompid == pid || logEntry->topid == pid)) {
                return 1;
            }
            if (!(flags & PLOG_PR_SW)) return 0;
        }
        break;

      case PLOG_TYPE_SWL:
        if (!(flags & PLOG_PR_SW)) return 0;
        break;

      case PLOG_TYPE_SCE:
        {
            struct _pl_sce *logEntry;

            logEntry = (struct _pl_sce *) (event + 3);
            if (pid && logEntry->pid == pid) return 1;
            if (!(flags & PLOG_PR_SC)) return 0;
        }
        break;

      case PLOG_TYPE_SCL:
        {
            struct _pl_scl *logEntry;

            logEntry = (struct _pl_scl *) (event + 3);
            if (pid && logEntry->pid == pid) return 1;
            if (!(flags & PLOG_PR_SC)) return 0;
        }
        break;

      case PLOG_TYPE_BHE:
      case PLOG_TYPE_BHL:
        if (!(flags & PLOG_PR_BH)) return 0;
        break;

      case PLOG_TYPE_RQ:
        {
            struct _pl_rq *logEntry;

            logEntry = (struct _pl_rq *) (event + 3);
            if (pid && logEntry->pid == pid) return 1;
            if (!(flags & PLOG_PR_RQ)) return 0;
        }
        break;

      case PLOG_TYPE_INTRE:
      case PLOG_TYPE_INTRL:
        if (!(flags & PLOG_PR_INTR)) return 0;
        break;

      default:
        break;
    }
    return 1;
}

void _dump_swbuf(int max, int swflags) 
{
    int plog_iter;
    int count;
    int eventSize;
    int first;
    unsigned long lastTime = 0;
    unsigned long firstTime = 0;
    int eventType;
    int nextEventType;
    unsigned long thisTime;
    unsigned long nextTime = 0;
    void *data;
    void *nextData = NULL;
    unsigned long flags;
    int pid;
    int nextUsed;

    save_flags(flags);
    cli();

    if (max == 0) max = 200;
    if (plog_wp == PLOG_LENGTH) plog_wp = 0;
    if (max < 0) {
        pid = -max;
        max = 400;
    } else pid = 0;

    plog_iter = plog_rp;
    count = 0;
    while (plog_iter != plog_wp) {
        eventSize = processLog[plog_iter >> 2];
        if (eventSize != 4 && 
                eventWanted(processLog + (plog_iter >> 2), swflags, pid)) {
            count++;
        }
        plog_iter += eventSize;
        if (plog_iter == PLOG_LENGTH) plog_iter = 0;
    }

    printk("%d relevant events captured\n", count);
    plog_iter = plog_rp;
    if (count > max) {
        count -= max;
        printk("Skipping %d events\n", count);
        while (count > 0) {
            eventSize = processLog[plog_iter >> 2];
            if (eventSize != 4 && 
                    eventWanted(processLog + (plog_iter >> 2), swflags, pid)) {
                count--;
            }
            plog_iter += eventSize;
            if (plog_iter == PLOG_LENGTH) plog_iter = 0;
        }
    }

    first = 1;
    while (plog_iter != plog_wp) {
        eventSize = processLog[plog_iter >> 2];
        if (eventSize == 4 || 
                !eventWanted(processLog + (plog_iter >> 2), swflags, pid)) {
            plog_iter += eventSize;
            continue;
        }
        eventType = processLog[1 + (plog_iter >> 2)];
        thisTime = processLog[2 + (plog_iter >> 2)];
        if (first) {
            first = 0;
            lastTime = thisTime;
            firstTime = thisTime;
        }
        data = (void *) (processLog + 3 + (plog_iter >> 2));
        plog_iter += eventSize;
        if (plog_iter == PLOG_LENGTH) plog_iter = 0;

        /* Find next event for event start/end merging */
        while (plog_iter != plog_wp && processLog[(plog_iter>>2)] == 4) {
            plog_iter += 4;
            if (plog_iter == PLOG_LENGTH) plog_iter = 0;
        }
        if (plog_iter != plog_wp) {
            nextEventType = processLog[1 + (plog_iter >> 2)];
            nextTime = processLog[2 + (plog_iter >> 2)];
            nextData = (void *) (processLog + 3 + (plog_iter >> 2));
        } else {
            nextEventType = 0;
        }
        nextUsed = 0;
                
        printk("%-10ld | ^%-6lu ", PLOG_DIFF(thisTime, firstTime),
                PLOG_DIFF(thisTime, lastTime));
        lastTime = thisTime;
        switch (eventType) {
          case PLOG_TYPE_SWE:
            if (nextEventType == PLOG_TYPE_SWL) {
                printk("Switch: %d->%d (%lu us)\n",
                        ((struct _pl_swe *) data)->frompid,
                        ((struct _pl_swe *) data)->topid,
                        PLOG_DIFF(nextTime, thisTime));
                nextUsed = 1;
            } else {
                printk("Switch Start: %d->%d\n", 
                        ((struct _pl_swe *) data)->frompid,
                        ((struct _pl_swe *) data)->topid);
            }
            break;

          case PLOG_TYPE_SWL:
            printk("Switch End\n");
            break;

          case PLOG_TYPE_SCE:
            {
                int callnum = ((struct _pl_sce *) data)->scnum;

                printk("syscall%s: pid=%d: ", 
                        nextEventType == PLOG_TYPE_SCL ? "" : " start",
                        ((struct _pl_sce *) data)->pid);
                if (callnum >= 0 && callnum < NENTRIES(syscallNames)) {
                    printk("%s()", syscallNames[callnum]);
                } else {
                    printk("syscall(%d)", callnum);
                }
                if (nextEventType == PLOG_TYPE_SCL) {
                    printk(" == %d (%lu us)",
                            ((struct _pl_scl *) nextData)->rv,
                            PLOG_DIFF(nextTime, thisTime));
                    nextUsed = 1;
                }
                printk("\n");
            }
            break;

          case PLOG_TYPE_SCL:
            printk("syscall finish: pid=%d, rv=%d\n",
                    ((struct _pl_scl *) data)->pid,
                    ((struct _pl_scl *) data)->rv);
            break;

          case PLOG_TYPE_RQ:
            printk("pid %d runnable (%sfromintr, %spreempt)\n",
                    ((struct _pl_rq *) data)->pid,
                    ((struct _pl_rq *) data)->fromintr ? "" : "!",
                    ((struct _pl_rq *) data)->preempt ? "" : "!");
            break;

          case PLOG_TYPE_INTRE:
            if (nextEventType == PLOG_TYPE_INTRL) {
                nextUsed = 1;
                printk("interrupt: inum=%d (%lu us)\n",
                        *(int *) data,
                        PLOG_DIFF(nextTime, thisTime));
            } else {
                printk("interrupt start: inum=%d\n",
                        *(int *) data);
            }
            break;

          case PLOG_TYPE_INTRL:
            printk("interrupt end: inum=%d\n",
                    *(int *) data);
            break;

          case PLOG_TYPE_BHE:
            if (nextEventType == PLOG_TYPE_BHL) {
                nextUsed = 1;
                printk("Bottom half: %d (%lu us)\n",
                        *(int *) data,
                        PLOG_DIFF(nextTime, thisTime));
            } else {
                printk("Bottom half start: %d\n",
                        *(int *) data);
            }
            break;

          case PLOG_TYPE_BHL:
            printk("Bottom half end: %d\n",
                    *(int *) data);
            break;


          default:
            printk("Event %d: pid=%d, Len=%d",
                    eventType, *(int *) data, eventSize-16);
            if (eventSize > 16) {
                int *idata = ((int *) data) + 1;
                int chlen = eventSize - 16;
                printk (" [");
                while (chlen > 0) {
                    printk("%d", *idata);
                    idata++;
                    chlen -= 4;
                    if (chlen > 0) printk(" ");
                }
                printk ("]\n");
            } else {
                printk ("\n");
            }
        }
        /* If we already consumed the next event, eat it */
        if (nextUsed) {
            eventSize = processLog[plog_iter >> 2];
            plog_iter += eventSize;
            if (plog_iter == PLOG_LENGTH) plog_iter = 0;
        }
    }

    restore_flags(flags);
}

/* Support for /proc/plog */
static struct file_operations proc_plog_operations = {
	NULL,		/* plog_lseek */
	plog_read,
	plog_write,
	NULL,		/* plog_readdir */
	NULL,	        /* plog_poll */
	NULL,		/* plog_ioctl */
	NULL,		/* mmap */
	plog_open,
    NULL,       /* flush */
	plog_release,
	NULL,		/* can't fsync */
    NULL,
    NULL,
    NULL,
    NULL
};

struct inode_operations proc_plog_inode_operations = {
	&proc_plog_operations,	/* default base directory file-ops */
	NULL,			/* create */
	NULL,			/* lookup */
	NULL,			/* link */
	NULL,			/* unlink */
	NULL,			/* symlink */
	NULL,			/* mkdir */
	NULL,			/* rmdir */
	NULL,			/* mknod */
	NULL,			/* rename */
	NULL,			/* readlink */
	NULL,			/* follow_link */
	NULL,			/* readpage */
	NULL,			/* writepage */
	NULL,			/* bmap */
	NULL,			/* truncate */
	NULL			/* permission */
};

void
switch_to_4xx(struct task_struct *prev, struct task_struct *new)
{
#if 0
        static unsigned int dbp = -1;

        // Let's set a debug break point on writes to the last line of the 
        // kernel stack.  If we ever write there, this code will catch us 
        // red handed.
        if (dbp != -1) {
            extern void xRemoveDataBp(unsigned int addr);

            xRemoveDataBp(dbp);
            dbp = -1;
        }
        {
            extern unsigned int xSetDataBp(unsigned int addr, int size, int rw);

            // Set break on writes to last line of kernel stack.
            dbp = xSetDataBp((unsigned int)new, 3, 1);
        }
#endif
}

#else
asmlinkage long sys_process_log(int type, int data_len, void *data)
{
    return -ENOSYS;
}
#endif
